diff --git a/Add-ons/UmbracoForms/Developer/Configuration/index-v7.md b/Add-ons/UmbracoForms/Developer/Configuration/index-v7.md new file mode 100644 index 00000000000..1ea8c7ff5ce --- /dev/null +++ b/Add-ons/UmbracoForms/Developer/Configuration/index-v7.md @@ -0,0 +1,118 @@ +--- +versionFrom: 7.0.0 +meta.Title: "Umbraco Forms configuration" +meta.Description: "In Umbraco Forms it's possible to customize the functionality with various configuration values." +--- + +# Configuration +With Umbraco Forms it's possible to customize the functionality with various configuration values. + +## Editing configuration values +The configuration for Umbraco Forms can be changed by modifying the XML based config file found at `/App_Plugins/UmbracoForms/UmbracoForms.config` + +### UploadStorageDirectory +This is *legacy and is no longer in use* - Forms that use an upload field will use the same IFileSystem as the [media section](../IFileSystem/#forms-containing-upload-fields) + +### IgnoreWorkFlowsOnEdit +This configuration expects `True` or `False` and allows you to toggle if a form submission is edited again, that the workflows on the form will re-fire after an update to the form submission. This is used in conjunction with the `AllowEditableFormSubmissions` configuration value. + +### ExecuteWorkflowAsync +This configuration key is *experimental* and will allow Workflows to be executed in an async manner
+The value can be `True/False` or a list of form names that ignore workflows that are comma separated `form name,contact form` + +### DisableFormCaching +This configuration value expects a `True/False` value and can be used to toggle if Forms should be read from the JSON representation on disk or from the relevant Forms IFileSystem. Forms are cached for 10 minutes. + +### DisableDefaultWorkflow +This configuration value expects a `True/False` value and can be used to toggle if new forms that are created adds an email workflow to send the result of the form to the current user who created the form. + +### DisableAutomaticAdditionOfDataConsentField +This configuration value expects a `True/False` value and can be used to disable the feature where all new forms are provided with a default "Consent for storing submitted data" field on creation. + +### AllowEditableFormSubmissions +This configuration value expects a `True/False` value and can be used to toggle the functionality to allow a form submission to be editable and re-submitted. When the value is set to `True` it allows Form Submissions to be edited using the following querystring for the page containing the form on the site. `?recordId=GUID` Replace `GUID` with the GUID of the form submission. + +:::warning +Enable this feature ONLY if you do understand the security implications. +::: + +### RecaptchaPublicKey & RecaptchaPrivateKey +Both of these configuration values are needed in order to use the "*Recaptcha2*" field type implementing legacy ReCaptcha V2 from Google. You can obtain both of these values after signing up to create a ReCaptcha key here - https://www.google.com/recaptcha/admin + +Google has renamed these recently and the `Site Key` refers to `RecaptchaPublicKey` and `Secret Key` is to be used for `RecaptchaPrivateKey` + +### RecaptchaV3SiteKey & RecaptchaV3PrivateKey +Both of these configuration values are needed in order to use the "*reCAPTCHA V3 with Score*" field type implementing ReCaptcha V3 from Google. This field type is available in Umbraco Forms from v8.7+. + +You can obtain both of these values after signing up to create a ReCaptcha key here: https://www.google.com/recaptcha/admin. + +### DatePickerYearRange +This setting is used to configure the Date Picker form field range of years that is available in the date picker. By default this is a small range of 10 years. + +### EnableAntiForgeryToken +This setting needs to be a `True` or `False` value and will enable the ASP.NET Anti Forgery Token and we recommend that you enable this and set this to `True`. Due to older versions of Umbraco Forms not containing this, it has become an optional config setting and due to upgrade reasons we do not automatically set this to `True` for you. + +If you do set this to `True` then you need to add `@Html.AntiForgeryToken()` to your forms. The default template for Forms can be found in `~/Views/Partials/Forms/Form.cshtml` and should have `@Html.AntiForgeryToken()` in the `@using (Html.BeginUmbracoForm [...]` block. + +### StoreUmbracoFormsInDb +This setting needs to be set to `True` if you want your Forms data to be stored in the database instead of the .json files in the `App_Data/UmbracoForms` directory in the file system. + +For more information on this, read the [Forms in the Database](../Forms-in-the-Database) article. + +### UseLegacyPageService +In Umbraco Forms 8.7 an update was made to improve the performance of the service responsible for retrieving the content of the Umbraco page where a form is hosted. This service is used to populate the string placeholders - or "magic strings" - with the values of properties from the page. + +By setting the value of the `UseLegacyPageService` to `True` the old service can be reinstated. + +### DisallowedFileUploadExtensions +When using the File Upload field in a form, editors can choose which file extensions they want to accept. When an image is expected, they can for example specify that only `.jpg` or `.png` files are uploaded. + +There are certain file extensions that in almost all cases should never be allowed, which are held in this configuration value. This means that even if an editor has selected to allow all files, any files that match the extensions listed here will be blocked. + +By default, .NET related code files like `.config` and `.aspx` are included in this deny list. You can add or - if you are sure - remove values from this list to meet your needs. + +### MaxNumberOfColumnsInFormGroup +Added in 8.7.0, this setting controls the maximum number of columns that can be created by editors when they configure groups within a form. The default value used if the setting value is not provided is 12. + +### DefaultTheme +Added in 8.8.0, this setting allows you to configure the name of the theme to use when an editor has not specifically selected one for a form. If empty or missing, the default value of "default" is used. If a custom default theme is configured, it will be used for rendering forms where the requested file exists, and where not, will fall back to the out of the box default theme. + +## Default Settings +There are several configuration keys that start with `Default`. This allows you to configure the values for when a new form is created. + +### DefaultManualApproval +This setting needs to be a `True` or `False` value and will allow you to toggle if a form allows submissions to be post moderated. Most use cases are for publicly shown entries such as blog post comments or submissions for a social campaign. + +### DefaultDisableStylesheet +This setting needs to be a `True` or `False` value and will allow you to toggle if the form will include some default styling with the Umbraco Forms CSS stylesheet. + +### DefaultMarkFieldsIndicator +This setting can have the following values to allow you to toggle the mode of marking mandatory or optional fields +* `NoIndicator` +* `MarkMandatoryFields` +* `MarkOptionalFields` + +### DefaultIndicator +This setting is used to mark the mandatory or optional fields based on the setting above. By default this is an asterisk `*` + +### DefaultRequiredErrorMessage +This allows you to configure the required error validation message. By default this is `Please provide a value for {0}` where the `{0}` is used to replace the name of the field that is required. + +### DefaultInvalidErrorMessage +This allows you to configure the invalid error validation message. By default this is `Please provide a valid value for {0}` where the `{0}` is used to replace the name of the field that is invalid. + +### DefaultShowValidationSummary +This setting needs to be a `True` or `False` value and will allow you to toggle if the form will display all form validation error messages in a validation summary together. + +### DefaultHideFieldValidationLabels +This setting needs to be a `True` or `False` value and will allow you to toggle if the form will show inline validation error messages next to the form field that is invalid. + +### DefaultMessageOnSubmit +This allows you to configure what text is displayed when a form is submitted and is not being redirected to a different content node. + +### DefaultStoreRecordsLocally +This setting needs to be a `True` or `False` value and will allow you to toggle if form submission data will be stored in the Umbraco Forms database tables. By default this is set to `True`. + +### DefaultAutocompleteAttribute + +Added in 8.8.0, this setting provides a value to be used for the `autocomplete` attribute for newly created forms. By default the value is empty, but can be set to `on` or `off` to have that value applied as the attribute value used when rendering the form. diff --git a/Add-ons/UmbracoForms/Developer/Configuration/index-v9.md b/Add-ons/UmbracoForms/Developer/Configuration/index-v9.md deleted file mode 100644 index 543d363573f..00000000000 --- a/Add-ons/UmbracoForms/Developer/Configuration/index-v9.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -versionFrom: 9.0.0 -meta.Title: "Umbraco Forms configuration" -meta.Description: "In Umbraco Forms it's possible to customize the functionality with various configuration values." -state: complete -verified-against: beta-1 ---- - -# Configuration - -With Umbraco Forms it's possible to customize the functionality with various configuration values. - -## Editing configuration values - -All configuration for Umbraco Forms is held in the `appSettings.json` file found at the root of your Umbraco website. If the configuration has been customized to use another source, then the same keys and values discussed in this article can be applied there. - -The convention for Umbraco configuration is to have package based options stored as a child structure below the `Umbraco` element, and as a sibling of `CMS`. Forms configuration follows this pattern, i.e.: - -```json -{ - ... - "Umbraco": { - "CMS": { - ... - }, - "Forms": { - ... - } - } -} -``` - -All configuration for Forms is optional. In other words, all values have defaults that will be applied if no configuration is available for a particular key. - -For illustration purposes, the following structure represents the full set of options for configuration of Forms, along with the default values. This will help when you need to provide a different setting to understand where it should be applied. - -```json - "Forms": { - "FormDesign": { - "DisableAutomaticAdditionOfDataConsentField": false, - "DisableDefaultWorkflow": false, - "MaxNumberOfColumnsInFormGroup": 12, - "DefaultTheme": "default", - "Defaults": { - "ManualApproval": false, - "DisableStylesheet": false, - "MarkFieldsIndicator": "NoIndicator", - "Indicator": "*", - "RequiredErrorMessage": "Please provide a value for {0}", - "InvalidErrorMessage": "Please provide a valid value for {0}", - "ShowValidationSummary": false, - "HideFieldValidationLabels": false, - "MessageOnSubmit": "Thank you", - "StoreRecordsLocally": true, - "AutocompleteAttribute": "" - } - }, - "PackageOptions": { - "IgnoreWorkFlowsOnEdit": "True", - "ExecuteWorkflowAsync": "False", - "AllowEditableFormSubmisisons": false - }, - "Security": { - "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx", - "EnableAntiForgeryToken": true, - "SavePlainTextPasswords": false - }, - "FieldTypes": { - "DatePicker": { - "DatePickerYearRange": 10 - }, - "Recaptcha2": { - "PublicKey": "", - "PrivateKey": "" - }, - "Recaptcha3": { - "SiteKey": "", - "PrivateKey": "" - } - } - } -``` - -## Form design configuration - -### DisableAutomaticAdditionOfDataConsentField - -This configuration value expects a `true` or `false` value and can be used to disable the feature where all new forms are provided with a default "Consent for storing submitted data" field on creation. Defaults to `false`. - -### DisableDefaultWorkflow - -This configuration value expects a `true` or `false` value and can be used to toggle if new forms that are created adds an email workflow to send the result of the form to the current user who created the form. Defaults to `false`. - -### MaxNumberOfColumnsInFormGroup - -This setting controls the maximum number of columns that can be created by editors when they configure groups within a form. The default value used if the setting value is not provided is 12. - -### DefaultTheme -This setting allows you to configure the name of the theme to use when an editor has not specifically selected one for a form. If empty or missing, the default value of "default" is used. If a custom default theme is configured, it will be used for rendering forms where the requested file exists, and where not, will fall back to the out of the box default theme. - -### Form default settings configuration - -The following configured values are applied to all forms as they are created. They can then be amended on a per-form basis via the Umbraco backoffice. - -#### ManualApproval - -This setting needs to be a `true` or `false` value and will allow you to toggle if a form allows submissions to be post moderated. Most use cases are for publicly shown entries such as blog post comments or submissions for a social campaign. Defaults to `false`. - -#### DisableStylesheet - -This setting needs to be a `true` or `false` value and will allow you to toggle if the form will include some default styling with the Umbraco Forms CSS stylesheet. Defaults to `false`. - -#### MarkFieldsIndicator - -This setting can have the following values to allow you to toggle the mode of marking mandatory or optional fields: - -* `NoIndicator` (default) -* `MarkMandatoryFields` -* `MarkOptionalFields` - -#### Indicator - -This setting is used to mark the mandatory or optional fields based on the setting above. By default this is an asterisk `*`. - -#### RequiredErrorMessage - -This allows you to configure the required error validation message. By default this is `Please provide a value for {0}` where the `{0}` is used to replace the name of the field that is required. - -#### InvalidErrorMessage - -This allows you to configure the invalid error validation message. By default this is `Please provide a valid value for {0}` where the `{0}` is used to replace the name of the field that is invalid. - -#### ShowValidationSummary - -This setting needs to be a `true` or `false` value and will allow you to toggle if the form will display all form validation error messages in a validation summary together. Defaults to `false`. - -#### HideFieldValidationLabels - -This setting needs to be a `true` or `false` value and will allow you to toggle if the form will show inline validation error messages next to the form field that is invalid. Defaults to `false`. - -#### MessageOnSubmit - -This allows you to configure what text is displayed when a form is submitted and is not being redirected to a different content node. Defaults to `Thank you`. - -#### StoreRecordsLocally - -This setting needs to be a `True` or `False` value and will allow you to toggle if form submission data should be stored in the Umbraco Forms database tables. By default this is set to `True`. - -#### AutocompleteAttribute - -This setting provides a value to be used for the `autocomplete` attribute for newly created forms. By default the value is empty, but can be set to `on` or `off` to have that value applied as the attribute value used when rendering the form. - -## Package options configuration - -### IgnoreWorkFlowsOnEdit - -This configuration expects a `True` or `False` string value, or a comma-separated list of form names, and allows you to toggle if a form submission is edited again, that the workflows on the form will re-fire after an update to the form submission. This is used in conjunction with the `AllowEditableFormSubmissions` configuration value. Defaults to `True`. - -### ExecuteWorkflowAsync - -This configuration key is *experimental* and will allow Workflows to be executed in an asynchronous manner. The value can be a `True` or `False` string value, or a comma-separated list of form names. Defaults to `False`. - -### AllowEditableFormSubmissions - -This configuration value expects a `true` or `false` value and can be used to toggle the functionality to allow a form submission to be editable and re-submitted. When the value is set to `true` it allows Form Submissions to be edited using the following querystring for the page containing the form on the site. `?recordId=GUID` Replace `GUID` with the GUID of the form submission. Defaults to `false`. - -:::warning -Enable this feature ONLY if you understand the security implications. -::: - -## Security configuration - -### DisallowedFileUploadExtensions - -When using the File Upload field in a form, editors can choose which file extensions they want to accept. When an image is expected, they can for example specify that only `.jpg` or `.png` files are uploaded. - -There are certain file extensions that in almost all cases should never be allowed, which are held in this configuration value. This means that even if an editor has selected to allow all files, any files that match the extensions listed here will be blocked. - -By default, .NET related code files like `.config` and `.aspx` are included in this deny list. You can add or - if you are sure - remove values from this list to meet your needs. - -### EnableAntiForgeryToken - -This setting needs to be a `true` or `false` value and will enable the ASP.NET Anti Forgery Token and we recommend that you enable this option. Defaults to `true`. - -### SavePlainTextPasswords - -This setting needs to be a `true` or `false` value and controls whether password fields provided in forms will be saved to the database. Defaults to `false`. - -## Field type specific configuration - -### Date picker field type configuration - -#### DatePickerYearRange - -This setting is used to configure the Date Picker form field range of years that is available in the date picker. By default this is a small range of 10 years. - -### reCAPTCHA v2 field type configuration - -#### PublicKey & PrivateKey - -Both of these configuration values are needed in order to use the "*Recaptcha2*" field type implementing legacy ReCaptcha V2 from Google. You can obtain both of these values after signing up to create a ReCaptcha key here - - -Google has renamed these recently and the `Site Key` refers to `RecaptchaPublicKey` and `Secret Key` is to be used for `RecaptchaPrivateKey` - -### reCAPTCHA v3 field type configuration - -#### SiteKey & PrivateKey - -Both of these configuration values are needed in order to use the "*reCAPTCHA V3 with Score*" field type implementing ReCaptcha V3 from Google. - -You can obtain both of these values after signing up to create a ReCaptcha key here: . - ---- - -Prev: [Extending](../Extending/index.md)                 Next: [Magic Strings](../Magic-Strings/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Configuration/index.md b/Add-ons/UmbracoForms/Developer/Configuration/index.md index 1ea8c7ff5ce..543d363573f 100644 --- a/Add-ons/UmbracoForms/Developer/Configuration/index.md +++ b/Add-ons/UmbracoForms/Developer/Configuration/index.md @@ -1,118 +1,215 @@ --- -versionFrom: 7.0.0 +versionFrom: 9.0.0 meta.Title: "Umbraco Forms configuration" meta.Description: "In Umbraco Forms it's possible to customize the functionality with various configuration values." +state: complete +verified-against: beta-1 --- # Configuration + With Umbraco Forms it's possible to customize the functionality with various configuration values. ## Editing configuration values -The configuration for Umbraco Forms can be changed by modifying the XML based config file found at `/App_Plugins/UmbracoForms/UmbracoForms.config` - -### UploadStorageDirectory -This is *legacy and is no longer in use* - Forms that use an upload field will use the same IFileSystem as the [media section](../IFileSystem/#forms-containing-upload-fields) -### IgnoreWorkFlowsOnEdit -This configuration expects `True` or `False` and allows you to toggle if a form submission is edited again, that the workflows on the form will re-fire after an update to the form submission. This is used in conjunction with the `AllowEditableFormSubmissions` configuration value. +All configuration for Umbraco Forms is held in the `appSettings.json` file found at the root of your Umbraco website. If the configuration has been customized to use another source, then the same keys and values discussed in this article can be applied there. + +The convention for Umbraco configuration is to have package based options stored as a child structure below the `Umbraco` element, and as a sibling of `CMS`. Forms configuration follows this pattern, i.e.: + +```json +{ + ... + "Umbraco": { + "CMS": { + ... + }, + "Forms": { + ... + } + } +} +``` + +All configuration for Forms is optional. In other words, all values have defaults that will be applied if no configuration is available for a particular key. + +For illustration purposes, the following structure represents the full set of options for configuration of Forms, along with the default values. This will help when you need to provide a different setting to understand where it should be applied. + +```json + "Forms": { + "FormDesign": { + "DisableAutomaticAdditionOfDataConsentField": false, + "DisableDefaultWorkflow": false, + "MaxNumberOfColumnsInFormGroup": 12, + "DefaultTheme": "default", + "Defaults": { + "ManualApproval": false, + "DisableStylesheet": false, + "MarkFieldsIndicator": "NoIndicator", + "Indicator": "*", + "RequiredErrorMessage": "Please provide a value for {0}", + "InvalidErrorMessage": "Please provide a valid value for {0}", + "ShowValidationSummary": false, + "HideFieldValidationLabels": false, + "MessageOnSubmit": "Thank you", + "StoreRecordsLocally": true, + "AutocompleteAttribute": "" + } + }, + "PackageOptions": { + "IgnoreWorkFlowsOnEdit": "True", + "ExecuteWorkflowAsync": "False", + "AllowEditableFormSubmisisons": false + }, + "Security": { + "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx", + "EnableAntiForgeryToken": true, + "SavePlainTextPasswords": false + }, + "FieldTypes": { + "DatePicker": { + "DatePickerYearRange": 10 + }, + "Recaptcha2": { + "PublicKey": "", + "PrivateKey": "" + }, + "Recaptcha3": { + "SiteKey": "", + "PrivateKey": "" + } + } + } +``` + +## Form design configuration -### ExecuteWorkflowAsync -This configuration key is *experimental* and will allow Workflows to be executed in an async manner
-The value can be `True/False` or a list of form names that ignore workflows that are comma separated `form name,contact form` +### DisableAutomaticAdditionOfDataConsentField -### DisableFormCaching -This configuration value expects a `True/False` value and can be used to toggle if Forms should be read from the JSON representation on disk or from the relevant Forms IFileSystem. Forms are cached for 10 minutes. +This configuration value expects a `true` or `false` value and can be used to disable the feature where all new forms are provided with a default "Consent for storing submitted data" field on creation. Defaults to `false`. ### DisableDefaultWorkflow -This configuration value expects a `True/False` value and can be used to toggle if new forms that are created adds an email workflow to send the result of the form to the current user who created the form. -### DisableAutomaticAdditionOfDataConsentField -This configuration value expects a `True/False` value and can be used to disable the feature where all new forms are provided with a default "Consent for storing submitted data" field on creation. +This configuration value expects a `true` or `false` value and can be used to toggle if new forms that are created adds an email workflow to send the result of the form to the current user who created the form. Defaults to `false`. -### AllowEditableFormSubmissions -This configuration value expects a `True/False` value and can be used to toggle the functionality to allow a form submission to be editable and re-submitted. When the value is set to `True` it allows Form Submissions to be edited using the following querystring for the page containing the form on the site. `?recordId=GUID` Replace `GUID` with the GUID of the form submission. +### MaxNumberOfColumnsInFormGroup -:::warning -Enable this feature ONLY if you do understand the security implications. -::: +This setting controls the maximum number of columns that can be created by editors when they configure groups within a form. The default value used if the setting value is not provided is 12. -### RecaptchaPublicKey & RecaptchaPrivateKey -Both of these configuration values are needed in order to use the "*Recaptcha2*" field type implementing legacy ReCaptcha V2 from Google. You can obtain both of these values after signing up to create a ReCaptcha key here - https://www.google.com/recaptcha/admin +### DefaultTheme +This setting allows you to configure the name of the theme to use when an editor has not specifically selected one for a form. If empty or missing, the default value of "default" is used. If a custom default theme is configured, it will be used for rendering forms where the requested file exists, and where not, will fall back to the out of the box default theme. -Google has renamed these recently and the `Site Key` refers to `RecaptchaPublicKey` and `Secret Key` is to be used for `RecaptchaPrivateKey` +### Form default settings configuration -### RecaptchaV3SiteKey & RecaptchaV3PrivateKey -Both of these configuration values are needed in order to use the "*reCAPTCHA V3 with Score*" field type implementing ReCaptcha V3 from Google. This field type is available in Umbraco Forms from v8.7+. +The following configured values are applied to all forms as they are created. They can then be amended on a per-form basis via the Umbraco backoffice. -You can obtain both of these values after signing up to create a ReCaptcha key here: https://www.google.com/recaptcha/admin. +#### ManualApproval -### DatePickerYearRange -This setting is used to configure the Date Picker form field range of years that is available in the date picker. By default this is a small range of 10 years. +This setting needs to be a `true` or `false` value and will allow you to toggle if a form allows submissions to be post moderated. Most use cases are for publicly shown entries such as blog post comments or submissions for a social campaign. Defaults to `false`. -### EnableAntiForgeryToken -This setting needs to be a `True` or `False` value and will enable the ASP.NET Anti Forgery Token and we recommend that you enable this and set this to `True`. Due to older versions of Umbraco Forms not containing this, it has become an optional config setting and due to upgrade reasons we do not automatically set this to `True` for you. +#### DisableStylesheet + +This setting needs to be a `true` or `false` value and will allow you to toggle if the form will include some default styling with the Umbraco Forms CSS stylesheet. Defaults to `false`. + +#### MarkFieldsIndicator + +This setting can have the following values to allow you to toggle the mode of marking mandatory or optional fields: + +* `NoIndicator` (default) +* `MarkMandatoryFields` +* `MarkOptionalFields` + +#### Indicator + +This setting is used to mark the mandatory or optional fields based on the setting above. By default this is an asterisk `*`. + +#### RequiredErrorMessage + +This allows you to configure the required error validation message. By default this is `Please provide a value for {0}` where the `{0}` is used to replace the name of the field that is required. + +#### InvalidErrorMessage + +This allows you to configure the invalid error validation message. By default this is `Please provide a valid value for {0}` where the `{0}` is used to replace the name of the field that is invalid. + +#### ShowValidationSummary + +This setting needs to be a `true` or `false` value and will allow you to toggle if the form will display all form validation error messages in a validation summary together. Defaults to `false`. + +#### HideFieldValidationLabels -If you do set this to `True` then you need to add `@Html.AntiForgeryToken()` to your forms. The default template for Forms can be found in `~/Views/Partials/Forms/Form.cshtml` and should have `@Html.AntiForgeryToken()` in the `@using (Html.BeginUmbracoForm [...]` block. +This setting needs to be a `true` or `false` value and will allow you to toggle if the form will show inline validation error messages next to the form field that is invalid. Defaults to `false`. -### StoreUmbracoFormsInDb -This setting needs to be set to `True` if you want your Forms data to be stored in the database instead of the .json files in the `App_Data/UmbracoForms` directory in the file system. +#### MessageOnSubmit -For more information on this, read the [Forms in the Database](../Forms-in-the-Database) article. +This allows you to configure what text is displayed when a form is submitted and is not being redirected to a different content node. Defaults to `Thank you`. -### UseLegacyPageService -In Umbraco Forms 8.7 an update was made to improve the performance of the service responsible for retrieving the content of the Umbraco page where a form is hosted. This service is used to populate the string placeholders - or "magic strings" - with the values of properties from the page. +#### StoreRecordsLocally -By setting the value of the `UseLegacyPageService` to `True` the old service can be reinstated. +This setting needs to be a `True` or `False` value and will allow you to toggle if form submission data should be stored in the Umbraco Forms database tables. By default this is set to `True`. + +#### AutocompleteAttribute + +This setting provides a value to be used for the `autocomplete` attribute for newly created forms. By default the value is empty, but can be set to `on` or `off` to have that value applied as the attribute value used when rendering the form. + +## Package options configuration + +### IgnoreWorkFlowsOnEdit + +This configuration expects a `True` or `False` string value, or a comma-separated list of form names, and allows you to toggle if a form submission is edited again, that the workflows on the form will re-fire after an update to the form submission. This is used in conjunction with the `AllowEditableFormSubmissions` configuration value. Defaults to `True`. + +### ExecuteWorkflowAsync + +This configuration key is *experimental* and will allow Workflows to be executed in an asynchronous manner. The value can be a `True` or `False` string value, or a comma-separated list of form names. Defaults to `False`. + +### AllowEditableFormSubmissions + +This configuration value expects a `true` or `false` value and can be used to toggle the functionality to allow a form submission to be editable and re-submitted. When the value is set to `true` it allows Form Submissions to be edited using the following querystring for the page containing the form on the site. `?recordId=GUID` Replace `GUID` with the GUID of the form submission. Defaults to `false`. + +:::warning +Enable this feature ONLY if you understand the security implications. +::: + +## Security configuration ### DisallowedFileUploadExtensions + When using the File Upload field in a form, editors can choose which file extensions they want to accept. When an image is expected, they can for example specify that only `.jpg` or `.png` files are uploaded. There are certain file extensions that in almost all cases should never be allowed, which are held in this configuration value. This means that even if an editor has selected to allow all files, any files that match the extensions listed here will be blocked. By default, .NET related code files like `.config` and `.aspx` are included in this deny list. You can add or - if you are sure - remove values from this list to meet your needs. -### MaxNumberOfColumnsInFormGroup -Added in 8.7.0, this setting controls the maximum number of columns that can be created by editors when they configure groups within a form. The default value used if the setting value is not provided is 12. +### EnableAntiForgeryToken -### DefaultTheme -Added in 8.8.0, this setting allows you to configure the name of the theme to use when an editor has not specifically selected one for a form. If empty or missing, the default value of "default" is used. If a custom default theme is configured, it will be used for rendering forms where the requested file exists, and where not, will fall back to the out of the box default theme. +This setting needs to be a `true` or `false` value and will enable the ASP.NET Anti Forgery Token and we recommend that you enable this option. Defaults to `true`. -## Default Settings -There are several configuration keys that start with `Default`. This allows you to configure the values for when a new form is created. +### SavePlainTextPasswords -### DefaultManualApproval -This setting needs to be a `True` or `False` value and will allow you to toggle if a form allows submissions to be post moderated. Most use cases are for publicly shown entries such as blog post comments or submissions for a social campaign. +This setting needs to be a `true` or `false` value and controls whether password fields provided in forms will be saved to the database. Defaults to `false`. -### DefaultDisableStylesheet -This setting needs to be a `True` or `False` value and will allow you to toggle if the form will include some default styling with the Umbraco Forms CSS stylesheet. +## Field type specific configuration -### DefaultMarkFieldsIndicator -This setting can have the following values to allow you to toggle the mode of marking mandatory or optional fields -* `NoIndicator` -* `MarkMandatoryFields` -* `MarkOptionalFields` +### Date picker field type configuration -### DefaultIndicator -This setting is used to mark the mandatory or optional fields based on the setting above. By default this is an asterisk `*` +#### DatePickerYearRange -### DefaultRequiredErrorMessage -This allows you to configure the required error validation message. By default this is `Please provide a value for {0}` where the `{0}` is used to replace the name of the field that is required. +This setting is used to configure the Date Picker form field range of years that is available in the date picker. By default this is a small range of 10 years. -### DefaultInvalidErrorMessage -This allows you to configure the invalid error validation message. By default this is `Please provide a valid value for {0}` where the `{0}` is used to replace the name of the field that is invalid. +### reCAPTCHA v2 field type configuration -### DefaultShowValidationSummary -This setting needs to be a `True` or `False` value and will allow you to toggle if the form will display all form validation error messages in a validation summary together. +#### PublicKey & PrivateKey -### DefaultHideFieldValidationLabels -This setting needs to be a `True` or `False` value and will allow you to toggle if the form will show inline validation error messages next to the form field that is invalid. +Both of these configuration values are needed in order to use the "*Recaptcha2*" field type implementing legacy ReCaptcha V2 from Google. You can obtain both of these values after signing up to create a ReCaptcha key here - + +Google has renamed these recently and the `Site Key` refers to `RecaptchaPublicKey` and `Secret Key` is to be used for `RecaptchaPrivateKey` -### DefaultMessageOnSubmit -This allows you to configure what text is displayed when a form is submitted and is not being redirected to a different content node. +### reCAPTCHA v3 field type configuration -### DefaultStoreRecordsLocally -This setting needs to be a `True` or `False` value and will allow you to toggle if form submission data will be stored in the Umbraco Forms database tables. By default this is set to `True`. +#### SiteKey & PrivateKey -### DefaultAutocompleteAttribute +Both of these configuration values are needed in order to use the "*reCAPTCHA V3 with Score*" field type implementing ReCaptcha V3 from Google. + +You can obtain both of these values after signing up to create a ReCaptcha key here: . + +--- -Added in 8.8.0, this setting provides a value to be used for the `autocomplete` attribute for newly created forms. By default the value is empty, but can be set to `on` or `off` to have that value applied as the attribute value used when rendering the form. +Prev: [Extending](../Extending/index.md)                 Next: [Magic Strings](../Magic-Strings/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Custom-Markup/index.md b/Add-ons/UmbracoForms/Developer/Custom-Markup/index.md index 373c733e4e1..63b9c8d6d1c 100644 --- a/Add-ons/UmbracoForms/Developer/Custom-Markup/index.md +++ b/Add-ons/UmbracoForms/Developer/Custom-Markup/index.md @@ -79,4 +79,4 @@ You can also overwrite views for one or more fieldtypes by adding the views to t --- -Prev: [Themes](../Themes/index.md)                 Next: [Email Templates](../Email-Templates/index-v9.md) +Prev: [Themes](../Themes/index.md)                 Next: [Email Templates](../Email-Templates/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Email-Templates/index-v9.md b/Add-ons/UmbracoForms/Developer/Email-Templates/index-v7.md similarity index 84% rename from Add-ons/UmbracoForms/Developer/Email-Templates/index-v9.md rename to Add-ons/UmbracoForms/Developer/Email-Templates/index-v7.md index 55b9455997a..8057231e083 100644 --- a/Add-ons/UmbracoForms/Developer/Email-Templates/index-v9.md +++ b/Add-ons/UmbracoForms/Developer/Email-Templates/index-v7.md @@ -1,5 +1,6 @@ --- -versionFrom: 9.0.0 +versionFrom: 7.0.0 +versionTo: 8.0.0 meta.Title: "Umbraco Forms Email Templates" meta.Description: "Creating an email template for Umbraco Forms." --- @@ -8,6 +9,8 @@ meta.Description: "Creating an email template for Umbraco Forms." From version 6+, we now include a new Workflow **Send email with template (Razor)** that allows you to pick a Razor view file that can be used to send out a *pretty HTML email* for Form submissions. +We have included an example email template below to look and understand how it works. The email template can be found at `~/Views/Partials/Forms/Emails/` folder. + ## Creating an Email Template If you wish to have one or more templates to choose from the **Send email with template (Razor)**, you will need to place all email templates into the `~/Views/Partials/Forms/Emails/` folder. @@ -15,15 +18,17 @@ If you wish to have one or more templates to choose from the **Send email with t The Razor view must inherit from FormsHtmlModel: ```csharp -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage +@inherits UmbracoViewPage ``` You now have a model that contains your Form fields which can be used in your email HTML markup, along with the UmbracoHelper methods such as `Umbraco.TypedContent` and `Umbraco.TypedMedia` etc. -Below is an example of an email template from the `~/Views/Partials/Forms/Emails/` folder: +Below is an example of an email template: + +### For Version 8.0.0+ ```csharp -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage +@inherits UmbracoViewPage @{ //This is an example email template where you can use Razor Views to send HTML emails @@ -40,7 +45,7 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails //@foreach(var color in Model.DynamicFields.checkboxField //Images need to be absolute - so fetching domain to prefix with images - var siteDomain = Context.Request.Scheme + "://" + Context.Request.Host; + var siteDomain = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority); var assetUrl = siteDomain + "/App_plugins/UmbracoForms/Assets/Email-Example"; } @@ -74,13 +79,13 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails line-height: inherit !important; } - /* MOBILE STYLES */ - @@media screen and (max-width:600px){ - h1 { - font-size: 32px !important; - line-height: 32px !important; - } - } + /* MOBILE STYLES */ + @@media screen and (max-width:600px){ + h1 { + font-size: 32px !important; + line-height: 32px !important; + } + } /* ANDROID CENTER FIX */ div[style*="margin: 16px 0;"] { margin: 0 !important; } @@ -172,7 +177,7 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails - + @foreach (var field in Model.Fields) {

@field.Name

@@ -247,6 +252,60 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails ``` ---- +### For Version 7+ + +```csharp +@inherits UmbracoViewPage + +@{ + // This is an example email template where you can use Razor Views to send HTML emails + + // You can use Umbraco.TypedContent & Umbraco.TypedMedia etc to use Images & content from your site + // directly in your email templates too -Prev: [Custom Markup](../Custom-Markup/index.md)                 Next: [Working with Record Data](../Working-With-Data/index-v9.md) + // Strongly Typed + // @Model.GetValue("aliasFormField") + // @foreach (var color in Model.GetValues("checkboxField")){} + +} + +

Explicitly Named Fields

+

Name:

+@Model.GetValue("name") + +

Favourite Colors

+
    + @foreach (var color in Model.GetValues("favColors")) { +
  • @color
  • + } +
+ +
+ +

Generic/reusable template

+@foreach (var field in Model.Fields) +{ +

@field.Name

+ + switch (field.FieldType) + { + case "FieldType.FileUpload.cshtml": + @field.GetValue() + break; + + case "FieldType.DatePicker.cshtml": + @(Convert.ToDateTime(field.GetValue()).ToString("f")) + break; + + case "FieldType.CheckboxList.cshtml": + foreach (var color in field.GetValues()) + { + @color
+ } + break; + default: + @field.GetValue() + break; + } +} +``` diff --git a/Add-ons/UmbracoForms/Developer/Email-Templates/index.md b/Add-ons/UmbracoForms/Developer/Email-Templates/index.md index 8057231e083..7423e5beea9 100644 --- a/Add-ons/UmbracoForms/Developer/Email-Templates/index.md +++ b/Add-ons/UmbracoForms/Developer/Email-Templates/index.md @@ -1,6 +1,5 @@ --- -versionFrom: 7.0.0 -versionTo: 8.0.0 +versionFrom: 9.0.0 meta.Title: "Umbraco Forms Email Templates" meta.Description: "Creating an email template for Umbraco Forms." --- @@ -9,8 +8,6 @@ meta.Description: "Creating an email template for Umbraco Forms." From version 6+, we now include a new Workflow **Send email with template (Razor)** that allows you to pick a Razor view file that can be used to send out a *pretty HTML email* for Form submissions. -We have included an example email template below to look and understand how it works. The email template can be found at `~/Views/Partials/Forms/Emails/` folder. - ## Creating an Email Template If you wish to have one or more templates to choose from the **Send email with template (Razor)**, you will need to place all email templates into the `~/Views/Partials/Forms/Emails/` folder. @@ -18,17 +15,15 @@ If you wish to have one or more templates to choose from the **Send email with t The Razor view must inherit from FormsHtmlModel: ```csharp -@inherits UmbracoViewPage +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage ``` You now have a model that contains your Form fields which can be used in your email HTML markup, along with the UmbracoHelper methods such as `Umbraco.TypedContent` and `Umbraco.TypedMedia` etc. -Below is an example of an email template: - -### For Version 8.0.0+ +Below is an example of an email template from the `~/Views/Partials/Forms/Emails/` folder: ```csharp -@inherits UmbracoViewPage +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @{ //This is an example email template where you can use Razor Views to send HTML emails @@ -45,7 +40,7 @@ Below is an example of an email template: //@foreach(var color in Model.DynamicFields.checkboxField //Images need to be absolute - so fetching domain to prefix with images - var siteDomain = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority); + var siteDomain = Context.Request.Scheme + "://" + Context.Request.Host; var assetUrl = siteDomain + "/App_plugins/UmbracoForms/Assets/Email-Example"; } @@ -79,13 +74,13 @@ Below is an example of an email template: line-height: inherit !important; } - /* MOBILE STYLES */ - @@media screen and (max-width:600px){ - h1 { - font-size: 32px !important; - line-height: 32px !important; - } - } + /* MOBILE STYLES */ + @@media screen and (max-width:600px){ + h1 { + font-size: 32px !important; + line-height: 32px !important; + } + } /* ANDROID CENTER FIX */ div[style*="margin: 16px 0;"] { margin: 0 !important; } @@ -177,7 +172,7 @@ Below is an example of an email template: - + @foreach (var field in Model.Fields) {

@field.Name

@@ -252,60 +247,6 @@ Below is an example of an email template: ``` -### For Version 7+ - -```csharp -@inherits UmbracoViewPage - -@{ - // This is an example email template where you can use Razor Views to send HTML emails - - // You can use Umbraco.TypedContent & Umbraco.TypedMedia etc to use Images & content from your site - // directly in your email templates too - - // Strongly Typed - // @Model.GetValue("aliasFormField") - // @foreach (var color in Model.GetValues("checkboxField")){} - -} - -

Explicitly Named Fields

-

Name:

-@Model.GetValue("name") +--- -

Favourite Colors

-
    - @foreach (var color in Model.GetValues("favColors")) { -
  • @color
  • - } -
- -
- -

Generic/reusable template

-@foreach (var field in Model.Fields) -{ -

@field.Name

- - switch (field.FieldType) - { - case "FieldType.FileUpload.cshtml": - @field.GetValue() - break; - - case "FieldType.DatePicker.cshtml": - @(Convert.ToDateTime(field.GetValue()).ToString("f")) - break; - - case "FieldType.CheckboxList.cshtml": - foreach (var color in field.GetValues()) - { - @color
- } - break; - default: - @field.GetValue() - break; - } -} -``` +Prev: [Custom Markup](../Custom-Markup/index.md)                 Next: [Working with Record Data](../Working-With-Data/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v7.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v7.md new file mode 100644 index 00000000000..c22f6830325 --- /dev/null +++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v7.md @@ -0,0 +1,192 @@ +--- +versionFrom: 7.0.0 +meta.Title: "Adding a Export type to Umbraco Forms" +--- + +# Adding a Export type to Umbraco Forms +*This builds on the "[adding a type to the provider model](Adding-a-Type.md)" chapter and applies to Umbraco Forms version 4.4.1 and higher* + +Add a new class to your project and have it inherit from `Umbraco.Forms.Core.ExportType` and you have two options when implementing the class. + +## Basic Example +When implementing the method `public override string ExportRecords(RecordExportFilter filter)` in your export provider class. You need to return the final string you wish to write to a file. Such as .txt file or .csv and you can perform your logic to build up a comma separated string for a CSV file in the `ExportRecords` method. + +In the constructor of your provider, note that you will need a further two properties, `FileExtension` and `Icon`. The FileExtension property is the file extension such as `zip`, `txt` or `csv` of the file you will be generating & serving from the file system as the export file. + +In this example below we will create a single HTML file which takes all the submissions/entries to be displayed as a HTML report. We will do this in conjunction with a Razor partial view to help build up our HTML and thus merge it with the form submission data to generate a string of HTML. + +### Provider Class + +```csharp +public class ExportToHtmlReport : ExportType +{ + public ExportToHtmlReport() + { + this.Name = "Export as HTML"; + this.Description = "Export entries as a single HTML report"; + this.Id = new Guid("4117D352-FB41-4A4C-96F5-F6EF35B384D2"); + this.FileExtension = "html"; + this.Icon = "icon-article"; + } + + /// + /// We implement this method from the interface + /// As this method is called from ExportToFile & is expecting the file contents as a string to be written as a stream to a file + /// + public override string ExportRecords(RecordExportFilter filter) + { + var view = "~/Views/Partials/Forms/Export/html-report.cshtml"; + var model = BusinessLogic.FormRecordSearcher.QueryDataBase(filter); + return Helpers.ViewHelper.RenderPartialViewToString(view, model); + } +} +``` + +### Razor Partial View + +```csharp +@model Umbraco.Forms.Web.Models.Backoffice.EntrySearchResultCollection + +@{ + var submissions = Model.Results.ToList(); + var schemaItems = Model.schema.ToList(); +} + +

Form Submissions

+ +@foreach (var submission in submissions) +{ + var values = submission.Fields.ToList(); + + for (int i = 0; i < schemaItems.Count; i++) + { + @schemaItems[i].Name @values[i]
+ } + +
+} +``` + +## Advanced Example +This approach gives us more flexibility in creating the file we wish to serve as the exported file. We do this for the export to excel file export provider we ship in Umbraco Forms. With this we can use a library to create the excel file and store it in a temporary location before we send back the filepath for the browser to stream down the export file. + +In this example we will create a collection of text files, one for each submission which is then zipped up into a single file and served as the export file. + +```csharp +public class ExportToTextFile : ExportType +{ + public ExportToTextFile() + { + this.Name = "Export as text files"; + this.Description = "Export entries as text files inside a zip file"; + this.Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB"); + this.FileExtension = "zip"; + this.Icon = "icon-zip"; + } + + /// + /// We do not implement this method from the interface + /// As this method is called from ExportToFile that we also override here & is expecting the file contents as a string to be written as a stream to a file + /// Which would be OK if we were creating a CSV or a single based file that can have a simple string written as a string such as one large HTML report or XML file perhaps + /// + + public override string ExportRecords(RecordExportFilter filter) + { + throw new NotImplementedException(); + } + + /// + /// This gives us greater control of the export process + /// + /// + /// This filter contains the date range & other search parameters to limit the entries we are exporting + /// + /// + /// The filepath that the export file is expecting to be served from + /// So ensure that the zip of text files is saved at this location + /// + /// The final file path to serve up as the export - this is unlikely to change through the export logic + public override string ExportToFile(RecordExportFilter filter, string filepath) + { + // Before Save - Check Path, Directory & Previous File export does not exist + string pathToSaveZipFile = filepath; + + // Check our path does not contain \\ + // If not, use the filePath + if (filepath.Contains('\\') == false) + { + pathToSaveZipFile = IOHelper.MapPath(filepath); + } + + // Get the directory (strip out \\ if it exists) + var dir = filepath.Substring(0, filepath.LastIndexOf('\\')); + var tempFileDir = Path.Combine(dir, "text-files"); + + + // If the path does not end with our file extension, ensure it's added + if (pathToSaveZipFile.EndsWith("." + FileExtension) == false) + { + pathToSaveZipFile += "." + FileExtension; + } + + // Check that the directory where we will save the ZIP file temporarily exists + // If not just create it + if (Directory.Exists(tempFileDir) == false) + { + Directory.CreateDirectory(tempFileDir); + } + + // Check if the zip file exists already - if so delete it, as we have a new update + if (File.Exists(pathToSaveZipFile)) + { + File.Delete(pathToSaveZipFile); + } + + + // Query the DB for submissions to export based on the filter + var submissions = FormRecordSearcher.QueryDataBase(filter); + + // Get the schema objects to a list so we can get items using position index + var schemaItems = submissions.schema.ToList(); + + // We will use this to store our contents of our file to save as a text file + var fileContents = string.Empty; + + // For each submission we have build up a string to save to a text file + foreach (var submission in submissions.Results) + { + // The submitted data for the form submission + var submissionData = submission.Fields.ToList(); + + // For loop to match the schema position to the submission data + for (int i = 0; i < schemaItems.Count; i++) + { + // Concat a string of the name of the field & its stored data + fileContents += schemaItems[i].Name + ": " + submissionData[i] + Environment.NewLine; + } + + // Now save the contents to a text file + // Base it on the format of the record submission unique id + var textFileName = Path.Combine(tempFileDir, submission.UniqueId + ".txt"); + File.WriteAllText(textFileName, fileContents); + + // Reset fileContents to be empty again + fileContents = string.Empty; + } + + // Now we have a temp folder full of text files + // Generate a zip file containing them & save that + ZipFile.CreateFromDirectory(tempFileDir, pathToSaveZipFile); + + // Tidy up after ourselves & delete the temp folder of text files + if (Directory.Exists(tempFileDir)) + { + Directory.Delete(tempFileDir, true); + } + + // Return the path where we saved the zip file containing the text files + return pathToSaveZipFile; + } + +} +``` diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v9.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v9.md deleted file mode 100644 index 26ef7501f59..00000000000 --- a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v9.md +++ /dev/null @@ -1,219 +0,0 @@ ---- -versionFrom: 9.0.0 -meta.Title: "Adding a Export type to Umbraco Forms" -state: complete -verified-against: beta001 ---- - -# Adding a Export type to Umbraco Forms -*This builds on the "[adding a type to the provider model](Adding-a-Type-v9.md)" chapter and applies to Umbraco Forms version 4.4.1 and higher* - -Add a new class to your project and have it inherit from `Umbraco.Forms.Core.ExportType` and you have two options when implementing the class. - -## Basic Example -When implementing the method `public override string ExportRecords(RecordExportFilter filter)` in your export provider class. You need to return the final string you wish to write to a file. Such as .txt file or .csv and you can perform your logic to build up a comma separated string for a CSV file in the `ExportRecords` method. - -In the constructor of your provider, note that you will need a further two properties, `FileExtension` and `Icon`. The FileExtension property is the file extension such as `zip`, `txt` or `csv` of the file you will be generating & serving from the file system as the export file. - -In this example below we will create a single HTML file which takes all the submissions/entries to be displayed as a HTML report. We will do this in conjunction with a Razor partial view to help build up our HTML and thus merge it with the form submission data to generate a string of HTML. - -### Provider Class - -```csharp -using System; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Forms.Core; -using Umbraco.Forms.Core.Models; -using Umbraco.Forms.Core.Searchers; -using Umbraco.Forms.Web.Helpers; - -namespace MyFormsExtensions -{ - public class ExportToHtmlReport : ExportType - { - private readonly IFormRecordSearcher _formRecordSearcher; - - public ExportToHtmlReport( - IHostingEnvironment hostingEnvironment, - IFormRecordSearcher formRecordSearcher) - : base(hostingEnvironment) - { - _formRecordSearcher = formRecordSearcher; - - this.Name = "Export as HTML"; - this.Description = "Export entries as a single HTML report"; - this.Id = new Guid("4117D352-FB41-4A4C-96F5-F6EF35B384D2"); - this.FileExtension = "html"; - this.Icon = "icon-article"; } - - public override string ExportRecords(RecordExportFilter filter) - { - var view = "~/Views/Partials/Forms/Export/html-report.cshtml"; - EntrySearchResultCollection model = _formRecordSearcher.QueryDataBase(filter); - return ViewHelper.RenderPartialViewToString(view, model); - } - } -} -``` - -### Razor Partial View - -```csharp -@model Umbraco.Forms.Web.Models.Backoffice.EntrySearchResultCollection - -@{ - var submissions = Model.Results.ToList(); - var schemaItems = Model.schema.ToList(); -} - -

Form Submissions

- -@foreach (var submission in submissions) -{ - var values = submission.Fields.ToList(); - - for (int i = 0; i < schemaItems.Count; i++) - { - @schemaItems[i].Name @values[i]
- } - -
-} -``` - -## Advanced Example -This approach gives us more flexibility in creating the file we wish to serve as the exported file. We do this for the export to Excel file export provider we ship in Umbraco Forms. With this we can use a library to create the Excel file and store it in a temporary location before we send back the filepath for the browser to stream down the export file. - -In this example we will create a collection of text files, one for each submission which is then zipped up into a single file and served as the export file. - -```csharp -using System; -using System.IO; -using System.IO.Compression; -using System.Linq; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Forms.Core; -using Umbraco.Forms.Core.Models; -using Umbraco.Forms.Core.Searchers; - -namespace MyFormsExtensions -{ - public class ExportToTextFiles : ExportType - { - private readonly IFormRecordSearcher _formRecordSearcher; - - public ExportToTextFiles( - IHostingEnvironment hostingEnvironment, - IFormRecordSearcher formRecordSearcher) - : base(hostingEnvironment) - { - _formRecordSearcher = formRecordSearcher; - - this.Name = "Export as text files"; - this.Description = "Export entries as text files inside a zip file"; - this.Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB"); - this.FileExtension = "zip"; - this.Icon = "icon-zip"; - } - - /// - /// We do not implement this method from the interface - /// As this method is called from ExportToFile that we also override here & is expecting the file contents as a string to be written as a stream to a file - /// Which would be OK if we were creating a CSV or a single based file that can have a simple string written as a string such as one large HTML report or XML file perhaps - /// - public override string ExportRecords(RecordExportFilter filter) => throw new NotImplementedException(); - - /// - /// This gives us greater control of the export process - /// - /// - /// This filter contains the date range & other search parameters to limit the entries we are exporting - /// - /// - /// The filepath that the export file is expecting to be served from - /// So ensure that the zip of text files is saved at this location - /// - /// The final file path to serve up as the export - this is unlikely to change through the export logic - public override string ExportToFile(RecordExportFilter filter, string filepath) - { - // Before Save - Check Path, Directory & Previous File export does not exist - string pathToSaveZipFile = filepath; - - // Check our path does not contain \\ - // If not, use the filePath - if (filepath.Contains('\\') == false) - { - pathToSaveZipFile = HostingEnvironment.MapPathContentRoot(filepath); - } - - // Get the directory (strip out \\ if it exists) - var dir = filepath.Substring(0, filepath.LastIndexOf('\\')); - var tempFileDir = Path.Combine(dir, "text-files"); - - - // If the path does not end with our file extension, ensure it's added - if (pathToSaveZipFile.EndsWith("." + FileExtension) == false) - { - pathToSaveZipFile += "." + FileExtension; - } - - // Check that the directory where we will save the ZIP file temporarily exists - // If not just create it - if (Directory.Exists(tempFileDir) == false) - { - Directory.CreateDirectory(tempFileDir); - } - - // Check if the zip file exists already - if so delete it, as we have a new update - if (File.Exists(pathToSaveZipFile)) - { - File.Delete(pathToSaveZipFile); - } - - // Query the DB for submissions to export based on the filter - EntrySearchResultCollection submissions = _formRecordSearcher.QueryDataBase(filter); - - // Get the schema objects to a list so we can get items using position index - var schemaItems = submissions.schema.ToList(); - - // We will use this to store our contents of our file to save as a text file - var fileContents = string.Empty; - - // For each submission we have build up a string to save to a text file - foreach (EntrySearchResult submission in submissions.Results) - { - // The submitted data for the form submission - var submissionData = submission.Fields.ToList(); - - // For loop to match the schema position to the submission data - for (int i = 0; i < schemaItems.Count; i++) - { - // Concat a string of the name of the field & its stored data - fileContents += schemaItems[i].Name + ": " + submissionData[i] + Environment.NewLine; - } - - // Now save the contents to a text file - // Base it on the format of the record submission unique id - var textFileName = Path.Combine(tempFileDir, submission.UniqueId + ".txt"); - File.WriteAllText(textFileName, fileContents); - - // Reset fileContents to be empty again - fileContents = string.Empty; - } - - // Now we have a temp folder full of text files - // Generate a zip file containing them & save that - ZipFile.CreateFromDirectory(tempFileDir, pathToSaveZipFile); - - // Tidy up after ourselves & delete the temp folder of text files - if (Directory.Exists(tempFileDir)) - { - Directory.Delete(tempFileDir, true); - } - - // Return the path where we saved the zip file containing the text files - return pathToSaveZipFile; - } - } -} -``` diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype.md index c22f6830325..882545dccf8 100644 --- a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype.md +++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype.md @@ -1,6 +1,8 @@ --- -versionFrom: 7.0.0 +versionFrom: 9.0.0 meta.Title: "Adding a Export type to Umbraco Forms" +state: complete +verified-against: beta001 --- # Adding a Export type to Umbraco Forms @@ -18,26 +20,38 @@ In this example below we will create a single HTML file which takes all the subm ### Provider Class ```csharp -public class ExportToHtmlReport : ExportType +using System; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Forms.Core; +using Umbraco.Forms.Core.Models; +using Umbraco.Forms.Core.Searchers; +using Umbraco.Forms.Web.Helpers; + +namespace MyFormsExtensions { - public ExportToHtmlReport() + public class ExportToHtmlReport : ExportType { - this.Name = "Export as HTML"; - this.Description = "Export entries as a single HTML report"; - this.Id = new Guid("4117D352-FB41-4A4C-96F5-F6EF35B384D2"); - this.FileExtension = "html"; - this.Icon = "icon-article"; - } + private readonly IFormRecordSearcher _formRecordSearcher; - /// - /// We implement this method from the interface - /// As this method is called from ExportToFile & is expecting the file contents as a string to be written as a stream to a file - /// - public override string ExportRecords(RecordExportFilter filter) - { - var view = "~/Views/Partials/Forms/Export/html-report.cshtml"; - var model = BusinessLogic.FormRecordSearcher.QueryDataBase(filter); - return Helpers.ViewHelper.RenderPartialViewToString(view, model); + public ExportToHtmlReport( + IHostingEnvironment hostingEnvironment, + IFormRecordSearcher formRecordSearcher) + : base(hostingEnvironment) + { + _formRecordSearcher = formRecordSearcher; + + this.Name = "Export as HTML"; + this.Description = "Export entries as a single HTML report"; + this.Id = new Guid("4117D352-FB41-4A4C-96F5-F6EF35B384D2"); + this.FileExtension = "html"; + this.Icon = "icon-article"; } + + public override string ExportRecords(RecordExportFilter filter) + { + var view = "~/Views/Partials/Forms/Export/html-report.cshtml"; + EntrySearchResultCollection model = _formRecordSearcher.QueryDataBase(filter); + return ViewHelper.RenderPartialViewToString(view, model); + } } } ``` @@ -68,125 +82,138 @@ public class ExportToHtmlReport : ExportType ``` ## Advanced Example -This approach gives us more flexibility in creating the file we wish to serve as the exported file. We do this for the export to excel file export provider we ship in Umbraco Forms. With this we can use a library to create the excel file and store it in a temporary location before we send back the filepath for the browser to stream down the export file. +This approach gives us more flexibility in creating the file we wish to serve as the exported file. We do this for the export to Excel file export provider we ship in Umbraco Forms. With this we can use a library to create the Excel file and store it in a temporary location before we send back the filepath for the browser to stream down the export file. In this example we will create a collection of text files, one for each submission which is then zipped up into a single file and served as the export file. ```csharp -public class ExportToTextFile : ExportType +using System; +using System.IO; +using System.IO.Compression; +using System.Linq; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Forms.Core; +using Umbraco.Forms.Core.Models; +using Umbraco.Forms.Core.Searchers; + +namespace MyFormsExtensions { - public ExportToTextFile() + public class ExportToTextFiles : ExportType { - this.Name = "Export as text files"; - this.Description = "Export entries as text files inside a zip file"; - this.Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB"); - this.FileExtension = "zip"; - this.Icon = "icon-zip"; - } + private readonly IFormRecordSearcher _formRecordSearcher; - /// - /// We do not implement this method from the interface - /// As this method is called from ExportToFile that we also override here & is expecting the file contents as a string to be written as a stream to a file - /// Which would be OK if we were creating a CSV or a single based file that can have a simple string written as a string such as one large HTML report or XML file perhaps - /// - - public override string ExportRecords(RecordExportFilter filter) - { - throw new NotImplementedException(); - } - - /// - /// This gives us greater control of the export process - /// - /// - /// This filter contains the date range & other search parameters to limit the entries we are exporting - /// - /// - /// The filepath that the export file is expecting to be served from - /// So ensure that the zip of text files is saved at this location - /// - /// The final file path to serve up as the export - this is unlikely to change through the export logic - public override string ExportToFile(RecordExportFilter filter, string filepath) - { - // Before Save - Check Path, Directory & Previous File export does not exist - string pathToSaveZipFile = filepath; - - // Check our path does not contain \\ - // If not, use the filePath - if (filepath.Contains('\\') == false) + public ExportToTextFiles( + IHostingEnvironment hostingEnvironment, + IFormRecordSearcher formRecordSearcher) + : base(hostingEnvironment) { - pathToSaveZipFile = IOHelper.MapPath(filepath); + _formRecordSearcher = formRecordSearcher; + + this.Name = "Export as text files"; + this.Description = "Export entries as text files inside a zip file"; + this.Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB"); + this.FileExtension = "zip"; + this.Icon = "icon-zip"; } - // Get the directory (strip out \\ if it exists) - var dir = filepath.Substring(0, filepath.LastIndexOf('\\')); - var tempFileDir = Path.Combine(dir, "text-files"); + /// + /// We do not implement this method from the interface + /// As this method is called from ExportToFile that we also override here & is expecting the file contents as a string to be written as a stream to a file + /// Which would be OK if we were creating a CSV or a single based file that can have a simple string written as a string such as one large HTML report or XML file perhaps + /// + public override string ExportRecords(RecordExportFilter filter) => throw new NotImplementedException(); + + /// + /// This gives us greater control of the export process + /// + /// + /// This filter contains the date range & other search parameters to limit the entries we are exporting + /// + /// + /// The filepath that the export file is expecting to be served from + /// So ensure that the zip of text files is saved at this location + /// + /// The final file path to serve up as the export - this is unlikely to change through the export logic + public override string ExportToFile(RecordExportFilter filter, string filepath) + { + // Before Save - Check Path, Directory & Previous File export does not exist + string pathToSaveZipFile = filepath; + // Check our path does not contain \\ + // If not, use the filePath + if (filepath.Contains('\\') == false) + { + pathToSaveZipFile = HostingEnvironment.MapPathContentRoot(filepath); + } - // If the path does not end with our file extension, ensure it's added - if (pathToSaveZipFile.EndsWith("." + FileExtension) == false) - { - pathToSaveZipFile += "." + FileExtension; - } + // Get the directory (strip out \\ if it exists) + var dir = filepath.Substring(0, filepath.LastIndexOf('\\')); + var tempFileDir = Path.Combine(dir, "text-files"); - // Check that the directory where we will save the ZIP file temporarily exists - // If not just create it - if (Directory.Exists(tempFileDir) == false) - { - Directory.CreateDirectory(tempFileDir); - } - // Check if the zip file exists already - if so delete it, as we have a new update - if (File.Exists(pathToSaveZipFile)) - { - File.Delete(pathToSaveZipFile); - } + // If the path does not end with our file extension, ensure it's added + if (pathToSaveZipFile.EndsWith("." + FileExtension) == false) + { + pathToSaveZipFile += "." + FileExtension; + } + // Check that the directory where we will save the ZIP file temporarily exists + // If not just create it + if (Directory.Exists(tempFileDir) == false) + { + Directory.CreateDirectory(tempFileDir); + } - // Query the DB for submissions to export based on the filter - var submissions = FormRecordSearcher.QueryDataBase(filter); + // Check if the zip file exists already - if so delete it, as we have a new update + if (File.Exists(pathToSaveZipFile)) + { + File.Delete(pathToSaveZipFile); + } - // Get the schema objects to a list so we can get items using position index - var schemaItems = submissions.schema.ToList(); + // Query the DB for submissions to export based on the filter + EntrySearchResultCollection submissions = _formRecordSearcher.QueryDataBase(filter); - // We will use this to store our contents of our file to save as a text file - var fileContents = string.Empty; + // Get the schema objects to a list so we can get items using position index + var schemaItems = submissions.schema.ToList(); - // For each submission we have build up a string to save to a text file - foreach (var submission in submissions.Results) - { - // The submitted data for the form submission - var submissionData = submission.Fields.ToList(); + // We will use this to store our contents of our file to save as a text file + var fileContents = string.Empty; - // For loop to match the schema position to the submission data - for (int i = 0; i < schemaItems.Count; i++) + // For each submission we have build up a string to save to a text file + foreach (EntrySearchResult submission in submissions.Results) { - // Concat a string of the name of the field & its stored data - fileContents += schemaItems[i].Name + ": " + submissionData[i] + Environment.NewLine; + // The submitted data for the form submission + var submissionData = submission.Fields.ToList(); + + // For loop to match the schema position to the submission data + for (int i = 0; i < schemaItems.Count; i++) + { + // Concat a string of the name of the field & its stored data + fileContents += schemaItems[i].Name + ": " + submissionData[i] + Environment.NewLine; + } + + // Now save the contents to a text file + // Base it on the format of the record submission unique id + var textFileName = Path.Combine(tempFileDir, submission.UniqueId + ".txt"); + File.WriteAllText(textFileName, fileContents); + + // Reset fileContents to be empty again + fileContents = string.Empty; } - // Now save the contents to a text file - // Base it on the format of the record submission unique id - var textFileName = Path.Combine(tempFileDir, submission.UniqueId + ".txt"); - File.WriteAllText(textFileName, fileContents); + // Now we have a temp folder full of text files + // Generate a zip file containing them & save that + ZipFile.CreateFromDirectory(tempFileDir, pathToSaveZipFile); - // Reset fileContents to be empty again - fileContents = string.Empty; - } - - // Now we have a temp folder full of text files - // Generate a zip file containing them & save that - ZipFile.CreateFromDirectory(tempFileDir, pathToSaveZipFile); + // Tidy up after ourselves & delete the temp folder of text files + if (Directory.Exists(tempFileDir)) + { + Directory.Delete(tempFileDir, true); + } - // Tidy up after ourselves & delete the temp folder of text files - if (Directory.Exists(tempFileDir)) - { - Directory.Delete(tempFileDir, true); + // Return the path where we saved the zip file containing the text files + return pathToSaveZipFile; } - - // Return the path where we saved the zip file containing the text files - return pathToSaveZipFile; } - } ``` diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype-v7.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype-v7.md new file mode 100644 index 00000000000..c146f6501fc --- /dev/null +++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype-v7.md @@ -0,0 +1,99 @@ +--- +versionFrom: 7.0.0 +meta.Title: "Adding a field type to Umbraco Forms" +--- + +# Adding a field type to Umbraco Forms # + +*This builds on the "[adding a type to the provider model](Adding-a-Type.md)" chapter* + +## C# + +Add a new class to the Visual Studio solution, make it inherit from Umbraco.Forms.Core.FieldType and fill in the constructor: + +```csharp +using Umbraco.Forms.Core.Data.Storage; +using Umbraco.Forms.Core.Enums; +using Umbraco.Forms.Core.Models; + + + +public class MyCustomField : Umbraco.Forms.Core.FieldType +{ + public MyCustomField() + { + this.Id = new Guid("08b8057f-06c9-4ca5-8a42-fd1fc2a46eff"); // Replace this! + this.Name = "My Custom Field"; + this.Description = "Render a custom text field."; + this.Icon = "icon-autofill"; + this.DataType = FieldDataType.String; + this.SortOrder = 10; + this.SupportsRegex = true; + } + + // You can do custom validation in here which will occur when the form is submitted. + // Any strings returned will cause the submit to be invalid! + // Where as returning an empty ienumerable of strings will say that it's okay. + public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContextBase context, IFormStorage formStorage) + { + var returnStrings = new List(); + + if (!postedValues.Any(value => value.ToString().ToLower().Contains("custom"))) { + returnStrings.Add("You need to include 'custom' in the field!"); + } + + // Also validate it against the original default method. + returnStrings.AddRange(base.ValidateField(form, field, postedValues, context, formStorage)); + + return returnStrings; + } +} +``` + +In the constructor, we specify the standard provider information (remember to set the ID to a unique ID). + +And then we set the field type specific information. In this case a preview Icon for the form builder UI and what kind of data it will return, this can either be string, longstring, integer, datetime or boolean. + +## Partial view + +Then we will start building the view at `Views\Partials\Forms\Fieldtypes\FieldType.MyCustomField.cshtml`: + +```csharp +@model Umbraco.Forms.Mvc.Models.FieldViewModel + placeholder="@Model.PlaceholderText" }} + @{if (Model.Mandatory || Model.Validate) { data-val="true" }} + @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage" }} + @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)" }} /> +``` + +The view takes care of generating the UI control and setting its value. + +On the view, it is important to note that the ID attribute is fetched from `@Model.Id`. You'll also see that we are using jQuery validate unobtrusive to perform client side validation so that's why we are adding the data* attributes. + +### Partial view for themes + +We will also add a file for the default theme of the form at `Views\Partials\Forms\Themes\default\FieldTypes\FieldType.MyCustomField.cshtml` + +```csharp +@model Umbraco.Forms.Mvc.Models.FieldViewModel + placeholder="@Model.PlaceholderText" }} + @{if (Model.Mandatory || Model.Validate) { data-val="true" }} + @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage" }} + @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)" }} /> +``` + +This will be rendered when the default theme is used. For example purposes, it can be identical to the previous partial view. + +## Umbraco backoffice view + +The final step involves building the HTML view which will be rendered in Umbraco as an example of how our end result will look. We will create a file at `App_Plugins\UmbracoForms\Backoffice\Common\FieldTypes\mycustomfield.html` which will contain the following: + +```html + +``` diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype-v9.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype-v9.md deleted file mode 100644 index d84433260fb..00000000000 --- a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype-v9.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -versionFrom: 9.0.0 -meta.Title: "Adding a field type to Umbraco Forms" -state: complete -verified-against: beta-1 ---- - -# Adding a field type to Umbraco Forms # - -*This builds on the "[adding a type to the provider model](Adding-a-Type-v9.md)" chapter* - -## C# - -Add a new class to the Visual Studio solution, make it inherit from `Umbraco.Forms.Core.FieldType` and fill in the constructor: - -```csharp -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Http; -using Umbraco.Forms.Core.Enums; -using Umbraco.Forms.Core.Models; -using Umbraco.Forms.Core.Services; - -namespace MyFormsExtensions -{ - public class MyCustomField : Umbraco.Forms.Core.FieldType - { - public MyCustomField() - { - this.Id = new Guid("08b8057f-06c9-4ca5-8a42-fd1fc2a46eff"); // Replace this! - this.Name = "My Custom Field"; - this.Description = "Render a custom text field."; - this.Icon = "icon-autofill"; - this.DataType = FieldDataType.String; - this.SortOrder = 10; - this.SupportsRegex = true; - } - - // You can do custom validation in here which will occur when the form is submitted. - // Any strings returned will cause the submit to be invalid! - // Where as returning an empty ienumerable of strings will say that it's okay. - public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService) - { - var returnStrings = new List(); - - if (!postedValues.Any(value => value.ToString().ToLower().Contains("custom"))) - { - returnStrings.Add("You need to include 'custom' in the field!"); - } - - // Also validate it against the original default method. - returnStrings.AddRange(base.ValidateField(form, field, postedValues, context, placeholderParsingService)); - - return returnStrings; - } - } -} - -``` - -In the constructor, we specify the standard provider information (remember to set the ID to a unique ID). - -And then we set the field type specific information. In this case a preview Icon for the form builder UI and what kind of data it will return, this can either be string, longstring, integer, datetime or boolean. - -## Partial view - -Then we will start building the view for the default theme of the form at `Views\Partials\Forms\Themes\default\FieldTypes\FieldType.MyCustomField.cshtml` - -```csharp -@model Umbraco.Forms.Mvc.Models.FieldViewModel - placeholder="@Model.PlaceholderText" }} - @{if (Model.Mandatory || Model.Validate) { data-val="true" }} - @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage" }} - @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)" }} /> -``` - -This will be rendered when the default theme is used. - -## Umbraco backoffice view - -The final step involves building the HTML view which will be rendered in Umbraco as an example of how our end result will look. We will create a file at `App_Plugins\UmbracoForms\Backoffice\Common\FieldTypes\mycustomfield.html` which will contain the following: - -```html - -``` diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype.md index c146f6501fc..f337bdae448 100644 --- a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype.md +++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype.md @@ -1,6 +1,8 @@ --- -versionFrom: 7.0.0 +versionFrom: 9.0.0 meta.Title: "Adding a field type to Umbraco Forms" +state: complete +verified-against: beta-1 --- # Adding a field type to Umbraco Forms # @@ -9,45 +11,52 @@ meta.Title: "Adding a field type to Umbraco Forms" ## C# -Add a new class to the Visual Studio solution, make it inherit from Umbraco.Forms.Core.FieldType and fill in the constructor: +Add a new class to the Visual Studio solution, make it inherit from `Umbraco.Forms.Core.FieldType` and fill in the constructor: ```csharp -using Umbraco.Forms.Core.Data.Storage; +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Http; using Umbraco.Forms.Core.Enums; using Umbraco.Forms.Core.Models; +using Umbraco.Forms.Core.Services; - - -public class MyCustomField : Umbraco.Forms.Core.FieldType +namespace MyFormsExtensions { - public MyCustomField() + public class MyCustomField : Umbraco.Forms.Core.FieldType { - this.Id = new Guid("08b8057f-06c9-4ca5-8a42-fd1fc2a46eff"); // Replace this! - this.Name = "My Custom Field"; - this.Description = "Render a custom text field."; - this.Icon = "icon-autofill"; - this.DataType = FieldDataType.String; - this.SortOrder = 10; - this.SupportsRegex = true; - } + public MyCustomField() + { + this.Id = new Guid("08b8057f-06c9-4ca5-8a42-fd1fc2a46eff"); // Replace this! + this.Name = "My Custom Field"; + this.Description = "Render a custom text field."; + this.Icon = "icon-autofill"; + this.DataType = FieldDataType.String; + this.SortOrder = 10; + this.SupportsRegex = true; + } - // You can do custom validation in here which will occur when the form is submitted. - // Any strings returned will cause the submit to be invalid! - // Where as returning an empty ienumerable of strings will say that it's okay. - public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContextBase context, IFormStorage formStorage) - { - var returnStrings = new List(); + // You can do custom validation in here which will occur when the form is submitted. + // Any strings returned will cause the submit to be invalid! + // Where as returning an empty ienumerable of strings will say that it's okay. + public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService) + { + var returnStrings = new List(); - if (!postedValues.Any(value => value.ToString().ToLower().Contains("custom"))) { - returnStrings.Add("You need to include 'custom' in the field!"); - } + if (!postedValues.Any(value => value.ToString().ToLower().Contains("custom"))) + { + returnStrings.Add("You need to include 'custom' in the field!"); + } - // Also validate it against the original default method. - returnStrings.AddRange(base.ValidateField(form, field, postedValues, context, formStorage)); + // Also validate it against the original default method. + returnStrings.AddRange(base.ValidateField(form, field, postedValues, context, placeholderParsingService)); - return returnStrings; + return returnStrings; + } } } + ``` In the constructor, we specify the standard provider information (remember to set the ID to a unique ID). @@ -56,24 +65,7 @@ And then we set the field type specific information. In this case a preview Icon ## Partial view -Then we will start building the view at `Views\Partials\Forms\Fieldtypes\FieldType.MyCustomField.cshtml`: - -```csharp -@model Umbraco.Forms.Mvc.Models.FieldViewModel - placeholder="@Model.PlaceholderText" }} - @{if (Model.Mandatory || Model.Validate) { data-val="true" }} - @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage" }} - @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)" }} /> -``` - -The view takes care of generating the UI control and setting its value. - -On the view, it is important to note that the ID attribute is fetched from `@Model.Id`. You'll also see that we are using jQuery validate unobtrusive to perform client side validation so that's why we are adding the data* attributes. - -### Partial view for themes - -We will also add a file for the default theme of the form at `Views\Partials\Forms\Themes\default\FieldTypes\FieldType.MyCustomField.cshtml` +Then we will start building the view for the default theme of the form at `Views\Partials\Forms\Themes\default\FieldTypes\FieldType.MyCustomField.cshtml` ```csharp @model Umbraco.Forms.Mvc.Models.FieldViewModel @@ -84,7 +76,7 @@ We will also add a file for the default theme of the form at `Views\Partials\For @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)" }} /> ``` -This will be rendered when the default theme is used. For example purposes, it can be identical to the previous partial view. +This will be rendered when the default theme is used. ## Umbraco backoffice view diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Type-v9.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Type-v8.md similarity index 66% rename from Add-ons/UmbracoForms/Developer/Extending/Adding-a-Type-v9.md rename to Add-ons/UmbracoForms/Developer/Extending/Adding-a-Type-v8.md index d83e7bfb3a5..f942b5fb54c 100644 --- a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Type-v9.md +++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Type-v8.md @@ -1,8 +1,6 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 meta.Title: "Adding a type to the provider model" -state: complete -verified-against: beta-1 --- # Adding a type to the provider model @@ -11,23 +9,16 @@ To add a new type, no matter if it's a workflow, field, data source, etc, there ## Preparations -Create a new class library project in Visual Studio add references to the Umbraco.Forms.Core.dll (available via referencing the [NuGet package](https://www.nuget.org/packages/UmbracoForms.Core/)). +Create a new ASP.NET or class project in Visual Studio add references to the Umbraco.Forms.Core.dll. ## Adding the type to Forms -The Forms API contains a collection of classes that can be registered at startup or in an Umbraco component. So to add a new type to Forms you inherit from the right class. In the sample below we use the class for the workflow type. +The Forms API contains a collection of classes that the provider model automatically registers. So to add a new type to Forms you inherit from the right class. In the sample below we use the class for the workflow type. ```csharp public class LogWorkflow : Umbraco.Forms.Core.WorkflowType { - private readonly ILogger _logger; - - public LogWorkflow(ILogger logger) - { - _logger = logger; - } - - public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) + public override WorkflowExecutionStatus Execute(Umbraco.Forms.Core.Persistence.Dtos.Record record, RecordEventArgs e) { throw new NotImplementedException(); } @@ -40,19 +31,14 @@ public class LogWorkflow : Umbraco.Forms.Core.WorkflowType When you implement this class you get two methods added. One of them is Execute which performs the execution of the workflow and the other is a method which validates the workflow settings, we will get back to these settings later on. -Any dependencies required that are registered with the dependency injection container can be provided via the constructor. - Even though we have the class inheritance in place, we still need to add a bit of default information. ## Setting up basic type information -Even though we have the class inheritance in place, we still need to add a bit of default information. This information is added in the class's constructor like this: +Even though we have the class inheritance in place, we still need to add a bit of default information. This information is added in the class's empty constructor like this: ```csharp -public LogWorkflow(ILogger logger) { - - _logger = logger; - +public LogWorkflow() { this.Name = "The logging workflow"; this.Id = new Guid("D6A2C406-CF89-11DE-B075-55B055D89593"); this.Description = "This will save an entry to the log"; @@ -82,9 +68,9 @@ With the attribute in place, the property value is set every time the class is i View = "Pickers.Content")] public string Document { get; set; } -public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) { - _logger.LogInformation("Record submitted from: {IP}", context.Record.IP); - return WorkflowExecutionStatus.Completed; +public override WorkflowExecutionStatus Execute(Umbraco.Forms.Core.Persistence.Dtos.Record record, RecordEventArgs e) { + Umbraco.Core.Composing.Current.Logger.Info("{Document} record submitted from: {IP}", int.Parse(Document), record.IP); + return WorkflowExecutionStatus.Completed; } ``` @@ -92,7 +78,7 @@ For all types that use the provider model, settings work this way. By adding the ## Validate type settings with ValidateSettings() -The `ValidateSettings()` method which can be found on all types supporting dynamic settings, is used for making sure the data entered by the user is valid and works with the type. +The ValidateSettings() method which can be found on all types supporting dynamic settings, is used for making sure the data entered by the user is valid and works with the type. ```csharp public override List ValidateSettings() { @@ -106,23 +92,14 @@ public override List ValidateSettings() { ## Registering the class with Umbraco and Forms -To register the type, ensure your web application project has a reference to the class library - either via a project or NuGet reference - and add the following code into the startup pipeline. In this example, the registration is implemented as an extension method to `IUmbracoBuilder` and should be called from `Startup.cs`: - -```csharp -public static IUmbracoBuilder AddUmbracoFormsCoreProviders(this IUmbracoBuilder builder) -{ - builder.WithCollectionBuilder() - .Add(); -} -``` - -Also look in the reference chapter for complete class implementations of workflows, fields and export types. +Finally compile the project and copy the .dll to your website /bin folder or copy the .cs file to the app_code directory. The website will now restart and your type will be registered automatically, no configuration +needed. Also look in the reference chapter for complete class implementations of workflows, fields and export types ## Overriding default providers in Umbraco Forms -It is possible to override and inherit the original provider, be it a Field Type or Workflow etc. The only requirement when inheriting a fieldtype that you wish to override is to ensure you do not override/change the Id set for the provider, and make sure your class is public. +This is a new feature in **Forms 6.0.3+** that makes it possible to override & inherit the original provider, be it a Field Type or Workflow etc. The only requirement when inheriting a fieldtype that you wish to override is to ensure you do not override/change the Id set for the provider, and make sure your class is public. -Here is an example of overriding the Textarea field aka Long Answer. +Here is an example of overriding the Textarea field aka Long Answer that is taken from Per's CodeGarden 17 talk, which has been updated for Forms 8. ```csharp public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textarea diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Type.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Type.md index f942b5fb54c..d83e7bfb3a5 100644 --- a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Type.md +++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Type.md @@ -1,6 +1,8 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 meta.Title: "Adding a type to the provider model" +state: complete +verified-against: beta-1 --- # Adding a type to the provider model @@ -9,16 +11,23 @@ To add a new type, no matter if it's a workflow, field, data source, etc, there ## Preparations -Create a new ASP.NET or class project in Visual Studio add references to the Umbraco.Forms.Core.dll. +Create a new class library project in Visual Studio add references to the Umbraco.Forms.Core.dll (available via referencing the [NuGet package](https://www.nuget.org/packages/UmbracoForms.Core/)). ## Adding the type to Forms -The Forms API contains a collection of classes that the provider model automatically registers. So to add a new type to Forms you inherit from the right class. In the sample below we use the class for the workflow type. +The Forms API contains a collection of classes that can be registered at startup or in an Umbraco component. So to add a new type to Forms you inherit from the right class. In the sample below we use the class for the workflow type. ```csharp public class LogWorkflow : Umbraco.Forms.Core.WorkflowType { - public override WorkflowExecutionStatus Execute(Umbraco.Forms.Core.Persistence.Dtos.Record record, RecordEventArgs e) + private readonly ILogger _logger; + + public LogWorkflow(ILogger logger) + { + _logger = logger; + } + + public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) { throw new NotImplementedException(); } @@ -31,14 +40,19 @@ public class LogWorkflow : Umbraco.Forms.Core.WorkflowType When you implement this class you get two methods added. One of them is Execute which performs the execution of the workflow and the other is a method which validates the workflow settings, we will get back to these settings later on. +Any dependencies required that are registered with the dependency injection container can be provided via the constructor. + Even though we have the class inheritance in place, we still need to add a bit of default information. ## Setting up basic type information -Even though we have the class inheritance in place, we still need to add a bit of default information. This information is added in the class's empty constructor like this: +Even though we have the class inheritance in place, we still need to add a bit of default information. This information is added in the class's constructor like this: ```csharp -public LogWorkflow() { +public LogWorkflow(ILogger logger) { + + _logger = logger; + this.Name = "The logging workflow"; this.Id = new Guid("D6A2C406-CF89-11DE-B075-55B055D89593"); this.Description = "This will save an entry to the log"; @@ -68,9 +82,9 @@ With the attribute in place, the property value is set every time the class is i View = "Pickers.Content")] public string Document { get; set; } -public override WorkflowExecutionStatus Execute(Umbraco.Forms.Core.Persistence.Dtos.Record record, RecordEventArgs e) { - Umbraco.Core.Composing.Current.Logger.Info("{Document} record submitted from: {IP}", int.Parse(Document), record.IP); - return WorkflowExecutionStatus.Completed; +public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) { + _logger.LogInformation("Record submitted from: {IP}", context.Record.IP); + return WorkflowExecutionStatus.Completed; } ``` @@ -78,7 +92,7 @@ For all types that use the provider model, settings work this way. By adding the ## Validate type settings with ValidateSettings() -The ValidateSettings() method which can be found on all types supporting dynamic settings, is used for making sure the data entered by the user is valid and works with the type. +The `ValidateSettings()` method which can be found on all types supporting dynamic settings, is used for making sure the data entered by the user is valid and works with the type. ```csharp public override List ValidateSettings() { @@ -92,14 +106,23 @@ public override List ValidateSettings() { ## Registering the class with Umbraco and Forms -Finally compile the project and copy the .dll to your website /bin folder or copy the .cs file to the app_code directory. The website will now restart and your type will be registered automatically, no configuration -needed. Also look in the reference chapter for complete class implementations of workflows, fields and export types +To register the type, ensure your web application project has a reference to the class library - either via a project or NuGet reference - and add the following code into the startup pipeline. In this example, the registration is implemented as an extension method to `IUmbracoBuilder` and should be called from `Startup.cs`: + +```csharp +public static IUmbracoBuilder AddUmbracoFormsCoreProviders(this IUmbracoBuilder builder) +{ + builder.WithCollectionBuilder() + .Add(); +} +``` + +Also look in the reference chapter for complete class implementations of workflows, fields and export types. ## Overriding default providers in Umbraco Forms -This is a new feature in **Forms 6.0.3+** that makes it possible to override & inherit the original provider, be it a Field Type or Workflow etc. The only requirement when inheriting a fieldtype that you wish to override is to ensure you do not override/change the Id set for the provider, and make sure your class is public. +It is possible to override and inherit the original provider, be it a Field Type or Workflow etc. The only requirement when inheriting a fieldtype that you wish to override is to ensure you do not override/change the Id set for the provider, and make sure your class is public. -Here is an example of overriding the Textarea field aka Long Answer that is taken from Per's CodeGarden 17 talk, which has been updated for Forms 8. +Here is an example of overriding the Textarea field aka Long Answer. ```csharp public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textarea diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Workflowtype-v9.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Workflowtype-v8.md similarity index 57% rename from Add-ons/UmbracoForms/Developer/Extending/Adding-a-Workflowtype-v9.md rename to Add-ons/UmbracoForms/Developer/Extending/Adding-a-Workflowtype-v8.md index 2296a972332..5b530c3752d 100644 --- a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Workflowtype-v9.md +++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Workflowtype-v8.md @@ -1,11 +1,9 @@ --- -versionFrom: 9.0.0 -state: complete -verified-again: beta-1 +versionFrom: 8.0.0 --- # Adding a workflow type to Umbraco Forms -*This builds on the "[adding a type to the provider model](Adding-a-Type-V9.md)" chapter* +*This builds on the "[adding a type to the provider model](Adding-a-Type.md)" chapter* Add a new class to your project and have it inherit from `Umbraco.Forms.Core.WorkflowType`, implement the class. For this sample we will focus on the execute method. This method process the current record (the data submitted by the form) and have the ability to change data and state. @@ -20,27 +18,25 @@ using Umbraco.Forms.Core.Persistence.Dtos; using Umbraco.Core.Logging; using Umbraco.Core.Composing; -namespace MyFormsExtensions +namespace PrereleaseForm8_4.Workflows { + /// + /// Summary description for TestWorkflow + /// public class TestWorkflow : WorkflowType { - private readonly ILogger _logger; - - public TestWorkflow(ILogger logger) + public TestWorkflow() { - _logger = logger; - this.Id = new Guid("ccbeb0d5-adaa-4729-8b4c-4bb439dc0202"); this.Name = "TestWorkflow"; this.Description = "This workflow is just for testing"; this.Icon = "icon-chat-active"; this.Group = "Services"; } - - public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) + public override WorkflowExecutionStatus Execute(Record record, RecordEventArgs e) { // first we log it - _logger.LogDebug("the IP " + context.Record.IP + " has submitted a record"); + Current.Logger.Debug("the IP " + record.IP + " has submitted a record"); // we can then iterate through the fields foreach (RecordField rf in record.RecordFields.Values) @@ -55,8 +51,8 @@ namespace MyFormsExtensions //Change the state record.State = FormState.Approved; - _logger.LogDebug("The record with unique id {RecordId} that was submitted via the Form {FormName} with id {FormId} has been changed to {RecordState} state", - record.UniqueId, context.Form.Name, context.Form.Id, "approved"); + Current.Logger.Debug("The record with unique id {RecordId} that was submitted via the Form {FormName} with id {FormId} has been changed to {RecordState} state", + record.UniqueId, e.Form.Name, e.Form.Id, "approved"); return WorkflowExecutionStatus.Completed; } @@ -64,9 +60,10 @@ namespace MyFormsExtensions public override List ValidateSettings() { return new List(); - } + } + } } ``` -The `Execute()` method gets a `WorkflowExecutionContext` which has properties for the related `Form`, `Record` and `FormState`. Essentially, this parameter contains all information related to the workflow. The `Record` contains all data and meta data submitted by the form. The `Form` references the form the record is from, and `FormState` provides it's state. Other context, such as the current `HttpContext`, if needed can be passed as constructor parameters (e.g. the `HttpContext` can be accessed by injecting `IHttpContextAccessor`). +The `Execute()` method gets a `Record` and a `RecordEventArgs` argument. These 2 arguments contains all information related to the workflow. The record contains all data and meta data submitted by the form. The RecordEventArgs contains references to what form the record is from, what state it is in and a reference to the current `HttpContext`. diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Workflowtype.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Workflowtype.md index 5b530c3752d..01aeda6277c 100644 --- a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Workflowtype.md +++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Workflowtype.md @@ -1,5 +1,7 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 +state: complete +verified-again: beta-1 --- # Adding a workflow type to Umbraco Forms @@ -18,25 +20,27 @@ using Umbraco.Forms.Core.Persistence.Dtos; using Umbraco.Core.Logging; using Umbraco.Core.Composing; -namespace PrereleaseForm8_4.Workflows +namespace MyFormsExtensions { - /// - /// Summary description for TestWorkflow - /// public class TestWorkflow : WorkflowType { - public TestWorkflow() + private readonly ILogger _logger; + + public TestWorkflow(ILogger logger) { + _logger = logger; + this.Id = new Guid("ccbeb0d5-adaa-4729-8b4c-4bb439dc0202"); this.Name = "TestWorkflow"; this.Description = "This workflow is just for testing"; this.Icon = "icon-chat-active"; this.Group = "Services"; } - public override WorkflowExecutionStatus Execute(Record record, RecordEventArgs e) + + public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) { // first we log it - Current.Logger.Debug("the IP " + record.IP + " has submitted a record"); + _logger.LogDebug("the IP " + context.Record.IP + " has submitted a record"); // we can then iterate through the fields foreach (RecordField rf in record.RecordFields.Values) @@ -51,8 +55,8 @@ namespace PrereleaseForm8_4.Workflows //Change the state record.State = FormState.Approved; - Current.Logger.Debug("The record with unique id {RecordId} that was submitted via the Form {FormName} with id {FormId} has been changed to {RecordState} state", - record.UniqueId, e.Form.Name, e.Form.Id, "approved"); + _logger.LogDebug("The record with unique id {RecordId} that was submitted via the Form {FormName} with id {FormId} has been changed to {RecordState} state", + record.UniqueId, context.Form.Name, context.Form.Id, "approved"); return WorkflowExecutionStatus.Completed; } @@ -60,10 +64,9 @@ namespace PrereleaseForm8_4.Workflows public override List ValidateSettings() { return new List(); - } - + } } } ``` -The `Execute()` method gets a `Record` and a `RecordEventArgs` argument. These 2 arguments contains all information related to the workflow. The record contains all data and meta data submitted by the form. The RecordEventArgs contains references to what form the record is from, what state it is in and a reference to the current `HttpContext`. +The `Execute()` method gets a `WorkflowExecutionContext` which has properties for the related `Form`, `Record` and `FormState`. Essentially, this parameter contains all information related to the workflow. The `Record` contains all data and meta data submitted by the form. The `Form` references the form the record is from, and `FormState` provides it's state. Other context, such as the current `HttpContext`, if needed can be passed as constructor parameters (e.g. the `HttpContext` can be accessed by injecting `IHttpContextAccessor`). diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-an-Event-Handler-v8.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-an-Event-Handler-v8.md new file mode 100644 index 00000000000..5a07067a1dc --- /dev/null +++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-an-Event-Handler-v8.md @@ -0,0 +1,75 @@ +--- +versionFrom: 8.0.0 +meta.Title: "Adding Event Handlers in Umbraco Forms" +meta.Description: "See an example of validating a form server-side" +--- + +# Adding a server-side event handler to Umbraco Forms + +:::note +The samples in this article applies to Umbraco Forms version 8 and later versions. +::: + +Add a new class to your project and have it inherit from `IUserComposer`, implement the `Compose()` method. This method will contain a handler for the `FormValidate` event. + +```csharp +using System; +using System.Collections.Generic; +using Umbraco.Core.Composing; + +namespace Forms8.EventHandlers +{ + /// + /// Catch form submissions before being saved and perform custom validation. + /// + public class FormEventsComposer : IUserComposer + { + + public void Compose(Composition composition) + { + // Attach a handler to the `FormValidate` event of UmbracoForms + Umbraco.Forms.Web.Controllers.UmbracoFormsController.FormValidate += FormsController_FormValidate; + } + + private void FormsController_FormValidate(object sender, Umbraco.Forms.Mvc.FormValidationEventArgs e) + { + // If needed, be selective about which form submissions you affect + if (e.Form.Name == "Form Name") + { + // Access the Controller that handled the Request + var controller = sender as Umbraco.Forms.Web.Controllers.UmbracoFormsController; + + // Check the ModelState + if (controller == null || !controller.ModelState.IsValid) + return; + + // A sample validation + var email = GetPostFieldValue(e, "email"); + var emailConfirm = GetPostFieldValue(e, "verifyEmail"); + + // If the validation fails, return a ModelError + if (email.ToLower() != emailConfirm.ToLower()) + controller.ModelState.AddModelError(GetPostField(e, "verifyEmail").Id.ToString(), "Email does not match"); + + } + } + + // Helper method + private static string GetPostFieldValue(Umbraco.Forms.Mvc.FormValidationEventArgs e, string key) + { + var field = GetPostField(e, key); + var value = e.Context.Request[field.Id.ToString()] ?? ""; + return value.Trim(); + } + + // Helper method + private static Umbraco.Forms.Core.Models.Field GetPostField(Umbraco.Forms.Mvc.FormValidationEventArgs e, string key) + { + return e.Form.AllFields.SingleOrDefault(f => f.Alias == key); + } + + } +} +``` + +The `FormValidate` event will pass a reference to the Controller and Form objects. Use them to check ModelState and Form Field values. If validation fails, return a ModelError. diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-an-Event-Handler-v9.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-an-Event-Handler-v9.md deleted file mode 100644 index 5fc7f7de182..00000000000 --- a/Add-ons/UmbracoForms/Developer/Extending/Adding-an-Event-Handler-v9.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -versionFrom: 9.0.0 -meta.Title: "Adding Notification Handlers in Umbraco Forms" -meta.Description: "See an example of validating a form server-side" -state: complete -verified-against: beta-1 ---- - -# Adding a server-side notification handler to Umbraco Forms - -Add a new class to your project as a handler for the `FormValidateNotification` notification: - -```csharp -using System.Linq; -using Microsoft.AspNetCore.Http; -using Umbraco.Cms.Core.Events; -using Umbraco.Forms.Core.Models; -using Umbraco.Forms.Core.Services.Notifications; - -namespace MyFormsExtensions -{ - /// - /// Catch form submissions before being saved and perform custom validation. - /// - public class FormValidateNotificationHandler : INotificationHandler - { - public void Handle(FormValidateNotification notification) - { - // If needed, be selective about which form submissions you affect. - if (notification.Form.Name == "Form Name") - { - // Check the ModelState - if (notification.ModelState.IsValid == false) - { - return; - } - - // A sample validation - var email = GetPostFieldValue(notification.Form, "email"); - var emailConfirm = GetPostFieldValue(notification.Form, notification.Context, "verifyEmail"); - - // If the validation fails, return a ModelError - if (email.ToLower() != emailConfirm.ToLower()) - { - notification.ModelState.AddModelError(GetPostField(e, "verifyEmail").Id.ToString(), "Email does not match"); - } - } - } - - private static string GetPostFieldValue(Form form, HttpContext context, string key) - { - Field field = GetPostField(form, key); - if (field == null) - { - return string.Empty; - } - - - return context.Request.HasFormContentType && context.Request.Form.Keys.Contains(field.Id.ToString()) - ? context.Request.Form[field.Id.ToString()].ToString().Trim() - : string.Empty; - } - - private static Field GetPostField(Form form, string key) => form.AllFields.SingleOrDefault(f => f.Alias == key); - } -} - -``` - -The handler will check the `ModelState` and `Form` field values provided in the notification. If validation fails, we add a ModelError. - -To register the handler, add the following code into the startup pipeline. In this example, the registration is implemented as an extension method to `IUmbracoBuilder` and should be called from `Startup.cs`: - -```csharp -public static IUmbracoBuilder AddUmbracoFormsCoreProviders(this IUmbracoBuilder builder) -{ - builder.AddNotificationHandler(); -} -``` diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-an-Event-Handler.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-an-Event-Handler.md index 5a07067a1dc..5fc7f7de182 100644 --- a/Add-ons/UmbracoForms/Developer/Extending/Adding-an-Event-Handler.md +++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-an-Event-Handler.md @@ -1,75 +1,79 @@ --- -versionFrom: 8.0.0 -meta.Title: "Adding Event Handlers in Umbraco Forms" +versionFrom: 9.0.0 +meta.Title: "Adding Notification Handlers in Umbraco Forms" meta.Description: "See an example of validating a form server-side" +state: complete +verified-against: beta-1 --- -# Adding a server-side event handler to Umbraco Forms +# Adding a server-side notification handler to Umbraco Forms -:::note -The samples in this article applies to Umbraco Forms version 8 and later versions. -::: - -Add a new class to your project and have it inherit from `IUserComposer`, implement the `Compose()` method. This method will contain a handler for the `FormValidate` event. +Add a new class to your project as a handler for the `FormValidateNotification` notification: ```csharp -using System; -using System.Collections.Generic; -using Umbraco.Core.Composing; +using System.Linq; +using Microsoft.AspNetCore.Http; +using Umbraco.Cms.Core.Events; +using Umbraco.Forms.Core.Models; +using Umbraco.Forms.Core.Services.Notifications; -namespace Forms8.EventHandlers +namespace MyFormsExtensions { /// /// Catch form submissions before being saved and perform custom validation. /// - public class FormEventsComposer : IUserComposer + public class FormValidateNotificationHandler : INotificationHandler { - - public void Compose(Composition composition) - { - // Attach a handler to the `FormValidate` event of UmbracoForms - Umbraco.Forms.Web.Controllers.UmbracoFormsController.FormValidate += FormsController_FormValidate; - } - - private void FormsController_FormValidate(object sender, Umbraco.Forms.Mvc.FormValidationEventArgs e) + public void Handle(FormValidateNotification notification) { - // If needed, be selective about which form submissions you affect - if (e.Form.Name == "Form Name") + // If needed, be selective about which form submissions you affect. + if (notification.Form.Name == "Form Name") { - // Access the Controller that handled the Request - var controller = sender as Umbraco.Forms.Web.Controllers.UmbracoFormsController; - // Check the ModelState - if (controller == null || !controller.ModelState.IsValid) + if (notification.ModelState.IsValid == false) + { return; + } // A sample validation - var email = GetPostFieldValue(e, "email"); - var emailConfirm = GetPostFieldValue(e, "verifyEmail"); - + var email = GetPostFieldValue(notification.Form, "email"); + var emailConfirm = GetPostFieldValue(notification.Form, notification.Context, "verifyEmail"); + // If the validation fails, return a ModelError if (email.ToLower() != emailConfirm.ToLower()) - controller.ModelState.AddModelError(GetPostField(e, "verifyEmail").Id.ToString(), "Email does not match"); - + { + notification.ModelState.AddModelError(GetPostField(e, "verifyEmail").Id.ToString(), "Email does not match"); + } } } - // Helper method - private static string GetPostFieldValue(Umbraco.Forms.Mvc.FormValidationEventArgs e, string key) - { - var field = GetPostField(e, key); - var value = e.Context.Request[field.Id.ToString()] ?? ""; - return value.Trim(); - } - - // Helper method - private static Umbraco.Forms.Core.Models.Field GetPostField(Umbraco.Forms.Mvc.FormValidationEventArgs e, string key) + private static string GetPostFieldValue(Form form, HttpContext context, string key) { - return e.Form.AllFields.SingleOrDefault(f => f.Alias == key); + Field field = GetPostField(form, key); + if (field == null) + { + return string.Empty; + } + + + return context.Request.HasFormContentType && context.Request.Form.Keys.Contains(field.Id.ToString()) + ? context.Request.Form[field.Id.ToString()].ToString().Trim() + : string.Empty; } - + + private static Field GetPostField(Form form, string key) => form.AllFields.SingleOrDefault(f => f.Alias == key); } } + ``` -The `FormValidate` event will pass a reference to the Controller and Form objects. Use them to check ModelState and Form Field values. If validation fails, return a ModelError. +The handler will check the `ModelState` and `Form` field values provided in the notification. If validation fails, we add a ModelError. + +To register the handler, add the following code into the startup pipeline. In this example, the registration is implemented as an extension method to `IUmbracoBuilder` and should be called from `Startup.cs`: + +```csharp +public static IUmbracoBuilder AddUmbracoFormsCoreProviders(this IUmbracoBuilder builder) +{ + builder.AddNotificationHandler(); +} +``` diff --git a/Add-ons/UmbracoForms/Developer/Extending/index.md b/Add-ons/UmbracoForms/Developer/Extending/index.md index 0e885e93ac2..02d7749e0cf 100644 --- a/Add-ons/UmbracoForms/Developer/Extending/index.md +++ b/Add-ons/UmbracoForms/Developer/Extending/index.md @@ -38,4 +38,4 @@ Form events are raised during the submission life cycle and can be handled for e --- -Prev: [Umbraco Forms in the Database](../Forms-in-the-Database/index-v9.md)                 Next: [Configuration](../Configuration/index-v9.md) +Prev: [Umbraco Forms in the Database](../Forms-in-the-Database/index.md)                 Next: [Configuration](../Configuration/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Forms-in-the-Database/index-v8.md b/Add-ons/UmbracoForms/Developer/Forms-in-the-Database/index-v8.md new file mode 100644 index 00000000000..1643bd77a01 --- /dev/null +++ b/Add-ons/UmbracoForms/Developer/Forms-in-the-Database/index-v8.md @@ -0,0 +1,61 @@ +--- +versionFrom: 8.0.0 +--- + +# Umbraco Forms in the database + +As of Umbraco Forms version 8.5.0 it is possible to persist all forms data in the Umbraco database. This includes definitions for each form and their fields, as well as workflow definitions and prevalues. + +In this article you will find instructions on how to migrate your Umbraco Forms data to the database. + +:::note +As of Umbraco Forms version 9.0.0 it is *only* possible to store form data in the database. As such, if upgrading to Umbraco 9 and using Forms, you should first migrate the forms to the database using Forms 8. +::: + +:::note +**Custom file system providers** + +If [custom file system providers are used on your project for storing Umbraco Forms data](../../../../Extending/FileSystemProviders/#custom-providers), the migration will not be able to run. + +In order to be able to persist your Umbraco Forms data in the database, you will need to revert back to a **standard Umbraco Forms configuration** using the default provider storing the Forms definition files in the default location. + +Then you need to ensure that your Forms definition files are moved from their previous location (this being a non-default file path, blob storage or similar) to the default location, `App_Data/UmbracoForms`, that Forms will now be using. + +Your configuration is now considered a standard configuration and you are able to perform the steps required for a normal migration. +::: + +## How to enable storing Forms definitions in the database + +Follow these steps, in order to persist Umbraco Forms definitions in the database: + +1. [Upgrade to at least Umbraco Forms version 8.5.2](../../Installation/ManualUpgrade.md) +2. Open the configuration file `App_Plugins\UmbracoForms\UmbracoForms.config` +3. Locate the `StoreUmbracoFormsInDb` key in the `` section, and make sure it has the following value: + + ```xml + + ``` + +4. Save the file + +If you are working with an Umbraco Cloud project, make sure you follow the migration steps outlined in the [Umbraco Forms on Cloud](../../../../Umbraco-Cloud/Deployment/Umbraco-Forms-on-Cloud) article. + +:::warning +Please be aware that enabling the persisting of Umbraco Forms in the database is irreversable. Once you've made the change, reverting to the file approach will not be an option. +::: + +When you save the file, the site will restart and run a migration step, migrating the files to the Umbraco database. + +## Migrating forms in files into a site + +If you have a Umbraco 8 site running that stores Forms in the database but you have the forms in file-format, you can force Forms to run the migration of those files again. + +First of all, you should ensure that you have enabled the setting that persists forms in the database, as the migration requires this (`StoreUmbracoFormsInDb`). We highly recommend that you test this on a local setup before applying it to your live site. + +1. Copy over the Forms, workflows, prevaluesources, and datasource files to the site into `~\App_Data\UmbracoForms\Data`. +1. Go to the database and find the `[umbracoKeyValue]` table. +1. Find the forms row and check that the value is `1d084819-84ba-4ac7-b152-2c3d167d22bc` (if not you are not currently working with forms in the database, changing the setting should be enough). +1. Change that value to `{forms-init-complete}` +1. Restart the site. + +The site will now try to migrate the forms files into the database. In the umbracoTraceLog you can follow the progress. It will throw errors if anything goes wrong and it will log out "The Umbraco Forms DB table {TableName} already exists" for the 4 Forms tables prior to starting the migration. diff --git a/Add-ons/UmbracoForms/Developer/Forms-in-the-Database/index-v9.md b/Add-ons/UmbracoForms/Developer/Forms-in-the-Database/index-v9.md deleted file mode 100644 index cf2024eeb7f..00000000000 --- a/Add-ons/UmbracoForms/Developer/Forms-in-the-Database/index-v9.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -versionFrom: 9.0.0 ---- - -# Umbraco Forms in the Database - -In Umbraco Forms version 9.0.0+, it is *only* possible to store Form data in the database. - -If you are upgrading to Umbraco 9 and using Forms, you should first migrate the Forms to the database using Forms 8. For more information on migrating your Umbraco Forms data to the database, see the [Umbraco Forms in the Database version 8.0.0+](../Forms-in-the-Database/index.md) article. - ---- - -Prev: [Working with Record Data](../Working-With-Data/index-v9.md)                 Next: [Extending](../Extending/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Forms-in-the-Database/index.md b/Add-ons/UmbracoForms/Developer/Forms-in-the-Database/index.md index 1643bd77a01..ead44d90e17 100644 --- a/Add-ons/UmbracoForms/Developer/Forms-in-the-Database/index.md +++ b/Add-ons/UmbracoForms/Developer/Forms-in-the-Database/index.md @@ -1,61 +1,13 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- -# Umbraco Forms in the database +# Umbraco Forms in the Database -As of Umbraco Forms version 8.5.0 it is possible to persist all forms data in the Umbraco database. This includes definitions for each form and their fields, as well as workflow definitions and prevalues. +In Umbraco Forms version 9.0.0+, it is *only* possible to store Form data in the database. -In this article you will find instructions on how to migrate your Umbraco Forms data to the database. +If you are upgrading to Umbraco 9 and using Forms, you should first migrate the Forms to the database using Forms 8. For more information on migrating your Umbraco Forms data to the database, see the [Umbraco Forms in the Database version 8.0.0+](../Forms-in-the-Database/index.md) article. -:::note -As of Umbraco Forms version 9.0.0 it is *only* possible to store form data in the database. As such, if upgrading to Umbraco 9 and using Forms, you should first migrate the forms to the database using Forms 8. -::: - -:::note -**Custom file system providers** - -If [custom file system providers are used on your project for storing Umbraco Forms data](../../../../Extending/FileSystemProviders/#custom-providers), the migration will not be able to run. - -In order to be able to persist your Umbraco Forms data in the database, you will need to revert back to a **standard Umbraco Forms configuration** using the default provider storing the Forms definition files in the default location. - -Then you need to ensure that your Forms definition files are moved from their previous location (this being a non-default file path, blob storage or similar) to the default location, `App_Data/UmbracoForms`, that Forms will now be using. - -Your configuration is now considered a standard configuration and you are able to perform the steps required for a normal migration. -::: - -## How to enable storing Forms definitions in the database - -Follow these steps, in order to persist Umbraco Forms definitions in the database: - -1. [Upgrade to at least Umbraco Forms version 8.5.2](../../Installation/ManualUpgrade.md) -2. Open the configuration file `App_Plugins\UmbracoForms\UmbracoForms.config` -3. Locate the `StoreUmbracoFormsInDb` key in the `` section, and make sure it has the following value: - - ```xml - - ``` - -4. Save the file - -If you are working with an Umbraco Cloud project, make sure you follow the migration steps outlined in the [Umbraco Forms on Cloud](../../../../Umbraco-Cloud/Deployment/Umbraco-Forms-on-Cloud) article. - -:::warning -Please be aware that enabling the persisting of Umbraco Forms in the database is irreversable. Once you've made the change, reverting to the file approach will not be an option. -::: - -When you save the file, the site will restart and run a migration step, migrating the files to the Umbraco database. - -## Migrating forms in files into a site - -If you have a Umbraco 8 site running that stores Forms in the database but you have the forms in file-format, you can force Forms to run the migration of those files again. - -First of all, you should ensure that you have enabled the setting that persists forms in the database, as the migration requires this (`StoreUmbracoFormsInDb`). We highly recommend that you test this on a local setup before applying it to your live site. - -1. Copy over the Forms, workflows, prevaluesources, and datasource files to the site into `~\App_Data\UmbracoForms\Data`. -1. Go to the database and find the `[umbracoKeyValue]` table. -1. Find the forms row and check that the value is `1d084819-84ba-4ac7-b152-2c3d167d22bc` (if not you are not currently working with forms in the database, changing the setting should be enough). -1. Change that value to `{forms-init-complete}` -1. Restart the site. +--- -The site will now try to migrate the forms files into the database. In the umbracoTraceLog you can follow the progress. It will throw errors if anything goes wrong and it will log out "The Umbraco Forms DB table {TableName} already exists" for the 4 Forms tables prior to starting the migration. +Prev: [Working with Record Data](../Working-With-Data/index.md)                 Next: [Extending](../Extending/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Healthchecks/index.md b/Add-ons/UmbracoForms/Developer/Healthchecks/index.md index 3a9a36ff92a..0af5e67d5c9 100644 --- a/Add-ons/UmbracoForms/Developer/Healthchecks/index.md +++ b/Add-ons/UmbracoForms/Developer/Healthchecks/index.md @@ -8,7 +8,7 @@ meta.Title: "Healthchecks" In this article, you will find information about Umbraco Forms-related health checks that can be run from the Umbraco backoffice to ensure that your installation is running seamlessly. -Read the [Health Check](../../../../Extending/Health-Check/index-v9.md) article to learn more about the feature in general. +Read the [Health Check](../../../../Extending/Health-Check/index.md) article to learn more about the feature in general. ## Database Integrity Health Check diff --git a/Add-ons/UmbracoForms/Developer/Magic-Strings/index.md b/Add-ons/UmbracoForms/Developer/Magic-Strings/index.md index 43fdda7e308..a964916e009 100644 --- a/Add-ons/UmbracoForms/Developer/Magic-Strings/index.md +++ b/Add-ons/UmbracoForms/Developer/Magic-Strings/index.md @@ -80,4 +80,4 @@ There is also a public extension method `ParsePlaceHolders()` extending the `str --- -Prev: [Configuration](../Configuration/index-v9.md)                 Next: [Health Checks](../Healthchecks/index.md) +Prev: [Configuration](../Configuration/index.md)                 Next: [Health Checks](../Healthchecks/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Prepping-Frontend/index.md b/Add-ons/UmbracoForms/Developer/Prepping-Frontend/index.md index 4a361fa1803..96e581db99e 100644 --- a/Add-ons/UmbracoForms/Developer/Prepping-Frontend/index.md +++ b/Add-ons/UmbracoForms/Developer/Prepping-Frontend/index.md @@ -74,4 +74,4 @@ When adding the script to the bottom of the page, you will also need to render t --- -Prev: [Developer Documentation](../index.md)                 Next: [Rendering Forms Scripts](../Rendering-Scripts/index-v9.md) +Prev: [Developer Documentation](../index.md)                 Next: [Rendering Forms Scripts](../Rendering-Scripts/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Rendering-Scripts/index-v7.md b/Add-ons/UmbracoForms/Developer/Rendering-Scripts/index-v7.md new file mode 100644 index 00000000000..1027bd526c2 --- /dev/null +++ b/Add-ons/UmbracoForms/Developer/Rendering-Scripts/index-v7.md @@ -0,0 +1,93 @@ +--- +versionFrom: 7.0.0 +versionTo: 8.0.0 +--- + +# Rendering Forms scripts where you want + +:::tip +If you are using Umbraco Forms v 4.x, you need to follow the guide found here: [When using Forms 4.x](#when-using-forms-4x) +::: + +Forms will output some JavaScript, and by default this is rendered right below the markup. + +In many cases you might prefer rendering your scripts at the bottom of the page, e.g. before the closing `` tag, as this generally improves site performance. + +In order to be able to render your scripts where you want, you need to add the following snippet to your template. Make sure you add it below your scripts, right before the closing `` tag: + +```csharp +@if (TempData["UmbracoForms"] != null) +{ + foreach (var form in (List)TempData["UmbracoForms"]) + { + Html.RenderAction("RenderFormScripts", "UmbracoForms", new { formid = form, theme = "yourTheme" }); + } +} +``` + +Whether you are inserting your form using a macro or adding it directly in your template, you need to make sure `ExcludeScripts` is checked/enabled. + +When inserting forms using the **Insert Form with Theme** macro: + +![Exclude scripts](images/exclude-scripts.png) + +When **inserting forms directly in your template**: + +```csharp +@Umbraco.RenderMacro("renderUmbracoForm", new {FormGuid="dfea5397-36cd-4596-8d3c-d210502b67de", FormTheme="bootstrap3-horizontal", ExcludeScripts="1"}) +``` + +## When using Forms 4.x + +### Change the Forms partial view macro +First we'll need to tell the Forms partial macro (that is used to render forms) to only render the markup and not the scripts. Navigate to the Developer section and open the Partial View Macro File > Insert Umbraco Form + +It should have the following contents + +```csharp +@inherits Umbraco.Web.Macros.PartialViewMacroPage + +@if (Model.MacroParameters["FormGuid"] != null) +{ + var s = Model.MacroParameters["FormGuid"].ToString(); + var g = new Guid(s); + + Html.RenderAction("Render", "UmbracoForms", new {formId = g}); +} +``` + +Here we'll make a small change: In the RenderAction call we'll provide an additional argument: `mode = "form"` + +So change this: + +```csharp +Html.RenderAction("Render", "UmbracoForms", new {formId = g}); +``` + +to this: + +```csharp +Html.RenderAction("Render", "UmbracoForms", new {formId = g, mode = "form"}); +``` + +### Place the Render Scripts macro on your template + +Now we'll need to let Forms know where we want to output the script instead. So navigate to the Settings section and select the template that should contain the scripts. Insert the *Render Umbraco Forms Scripts* macro where you need the scripts rendered: + +```csharp +@Umbraco.RenderMacro("FormsRenderScripts") +``` + +### Using RenderMacro in non Umbraco controllers + +Maybe you end up with an error like this "CS0234: The type or namespace name 'RenderMacro' does not exist in the namespace 'Umbraco' (are you missing an assembly reference?)". This is probably due to the fact that you're using custom controllers and viewmodels where the UmbracoContext is not exposed. The fix is to create your own UmbracoContext first: + +```csharp +@{ + // create your own Umbraco context + var umbraco = new UmbracoHelper(UmbracoContext.Current); +} +@umbraco.RenderMacro("FormsRenderForm", new { FormGuid = "1203e391-30bb-4ffc-8fe6-1785d6093108" }) +``` + +Please be aware, that is not the suggested way of inserting an Umbraco Form. We suggest you inherit from Umbraco Controllers. If you can not do that, you will need to create a new UmbracoContext. If you do so, please also read the Common Pitfalls. diff --git a/Add-ons/UmbracoForms/Developer/Rendering-Scripts/index-v9.md b/Add-ons/UmbracoForms/Developer/Rendering-Scripts/index-v9.md deleted file mode 100644 index 288b2400542..00000000000 --- a/Add-ons/UmbracoForms/Developer/Rendering-Scripts/index-v9.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -versionFrom: 9.0.0 ---- - -# Rendering Forms Scripts - -Forms output some JavaScript which is by default rendered right below the markup. - -In many cases, you might prefer rendering your scripts at the bottom of the page, e.g. before the closing `` tag, as this generally improves site performance. - -In order to render your scripts where you want, you need to add the following snippet to your template. Make sure you add it below your scripts, right before the closing `` tag: - -```csharp -@using Umbraco.Forms.Web.Extensions; - -@if (TempData["UmbracoForms"] != null) -{ - foreach (var form in TempData.Get>("UmbracoForms")) - { - @await Component.InvokeAsync("RenderFormScripts", new {formId = form, theme = "bootstrap3-horizontal"}) - } -} -``` - -## Enabling `ExcludeScripts` - -Whether you are inserting your Form using a macro or adding it directly in your template, you need to make sure `ExcludeScripts` is checked/enabled: - -To enable `ExcludeScripts`: - -- Using the **Insert Form with Theme** macro: - - ![Exclude scripts](images/exclude-scripts-v9.png) - -- While inserting Forms **directly** in your template: - - ```csharp - @await Umbraco.RenderMacroAsync("renderUmbracoForm", new {FormGuid="6c3f053c-1774-43fa-ad95-710a01d9cd12", FormTheme="bootstrap3-horizontal", ExcludeScripts="1"}) - ``` - ---- - -Prev: [Preparing your Frontend](../Prepping-Frontend/index.md)                 Next: [Themes](../Themes/index-v9.md) diff --git a/Add-ons/UmbracoForms/Developer/Rendering-Scripts/index.md b/Add-ons/UmbracoForms/Developer/Rendering-Scripts/index.md index 1027bd526c2..8c90ca5e04b 100644 --- a/Add-ons/UmbracoForms/Developer/Rendering-Scripts/index.md +++ b/Add-ons/UmbracoForms/Developer/Rendering-Scripts/index.md @@ -1,93 +1,43 @@ --- -versionFrom: 7.0.0 -versionTo: 8.0.0 +versionFrom: 9.0.0 --- -# Rendering Forms scripts where you want +# Rendering Forms Scripts -:::tip -If you are using Umbraco Forms v 4.x, you need to follow the guide found here: [When using Forms 4.x](#when-using-forms-4x) -::: +Forms output some JavaScript which is by default rendered right below the markup. -Forms will output some JavaScript, and by default this is rendered right below the markup. +In many cases, you might prefer rendering your scripts at the bottom of the page, e.g. before the closing `` tag, as this generally improves site performance. -In many cases you might prefer rendering your scripts at the bottom of the page, e.g. before the closing `` tag, as this generally improves site performance. - -In order to be able to render your scripts where you want, you need to add the following snippet to your template. Make sure you add it below your scripts, right before the closing `` tag: +In order to render your scripts where you want, you need to add the following snippet to your template. Make sure you add it below your scripts, right before the closing `` tag: ```csharp +@using Umbraco.Forms.Web.Extensions; + @if (TempData["UmbracoForms"] != null) { - foreach (var form in (List)TempData["UmbracoForms"]) + foreach (var form in TempData.Get>("UmbracoForms")) { - Html.RenderAction("RenderFormScripts", "UmbracoForms", new { formid = form, theme = "yourTheme" }); + @await Component.InvokeAsync("RenderFormScripts", new {formId = form, theme = "bootstrap3-horizontal"}) } } ``` -Whether you are inserting your form using a macro or adding it directly in your template, you need to make sure `ExcludeScripts` is checked/enabled. - -When inserting forms using the **Insert Form with Theme** macro: - -![Exclude scripts](images/exclude-scripts.png) - -When **inserting forms directly in your template**: - -```csharp -@Umbraco.RenderMacro("renderUmbracoForm", new {FormGuid="dfea5397-36cd-4596-8d3c-d210502b67de", FormTheme="bootstrap3-horizontal", ExcludeScripts="1"}) -``` - -## When using Forms 4.x - -### Change the Forms partial view macro -First we'll need to tell the Forms partial macro (that is used to render forms) to only render the markup and not the scripts. Navigate to the Developer section and open the Partial View Macro File > Insert Umbraco Form - -It should have the following contents - -```csharp -@inherits Umbraco.Web.Macros.PartialViewMacroPage - -@if (Model.MacroParameters["FormGuid"] != null) -{ - var s = Model.MacroParameters["FormGuid"].ToString(); - var g = new Guid(s); - - Html.RenderAction("Render", "UmbracoForms", new {formId = g}); -} -``` - -Here we'll make a small change: In the RenderAction call we'll provide an additional argument: `mode = "form"` - -So change this: - -```csharp -Html.RenderAction("Render", "UmbracoForms", new {formId = g}); -``` - -to this: +## Enabling `ExcludeScripts` -```csharp -Html.RenderAction("Render", "UmbracoForms", new {formId = g, mode = "form"}); -``` +Whether you are inserting your Form using a macro or adding it directly in your template, you need to make sure `ExcludeScripts` is checked/enabled: -### Place the Render Scripts macro on your template +To enable `ExcludeScripts`: -Now we'll need to let Forms know where we want to output the script instead. So navigate to the Settings section and select the template that should contain the scripts. Insert the *Render Umbraco Forms Scripts* macro where you need the scripts rendered: +- Using the **Insert Form with Theme** macro: -```csharp -@Umbraco.RenderMacro("FormsRenderScripts") -``` + ![Exclude scripts](images/exclude-scripts-v9.png) -### Using RenderMacro in non Umbraco controllers +- While inserting Forms **directly** in your template: -Maybe you end up with an error like this "CS0234: The type or namespace name 'RenderMacro' does not exist in the namespace 'Umbraco' (are you missing an assembly reference?)". This is probably due to the fact that you're using custom controllers and viewmodels where the UmbracoContext is not exposed. The fix is to create your own UmbracoContext first: + ```csharp + @await Umbraco.RenderMacroAsync("renderUmbracoForm", new {FormGuid="6c3f053c-1774-43fa-ad95-710a01d9cd12", FormTheme="bootstrap3-horizontal", ExcludeScripts="1"}) + ``` -```csharp -@{ - // create your own Umbraco context - var umbraco = new UmbracoHelper(UmbracoContext.Current); -} -@umbraco.RenderMacro("FormsRenderForm", new { FormGuid = "1203e391-30bb-4ffc-8fe6-1785d6093108" }) -``` +--- -Please be aware, that is not the suggested way of inserting an Umbraco Form. We suggest you inherit from Umbraco Controllers. If you can not do that, you will need to create a new UmbracoContext. If you do so, please also read the Common Pitfalls. +Prev: [Preparing your Frontend](../Prepping-Frontend/index.md)                 Next: [Themes](../Themes/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Themes/index-v9.md b/Add-ons/UmbracoForms/Developer/Themes/index-v8.md similarity index 72% rename from Add-ons/UmbracoForms/Developer/Themes/index-v9.md rename to Add-ons/UmbracoForms/Developer/Themes/index-v8.md index 24103a948eb..3138548b85c 100644 --- a/Add-ons/UmbracoForms/Developer/Themes/index-v9.md +++ b/Add-ons/UmbracoForms/Developer/Themes/index-v8.md @@ -1,16 +1,14 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 meta.Title: "Theming Umbraco Forms" meta.Description: "Documentation on how to apply custom themes to Umbraco Forms" --- # Themes - As of version 6.0.0 and newer Umbraco Forms supports Themes, allowing forms to be customised in a much simpler manner than found in version 4.x. ## Creating a Theme - -To create a theme, you need to create a folder at `/Views/Partials/Forms/Themes/`. The name of the folder is the name of theme that will be visible in the backoffice when choosing it. +To create a theme you need to create a folder at `/Views/Partials/Forms/Themes/`. The name of the folder is the name of theme that will be visible in the backoffice when choosing it. Copy the explicit files you wish to override in your theme, it may be a single file or all files from the default theme folder. Make the necessary changes you desire to CSS class names, markup etc. @@ -18,12 +16,11 @@ Copy the explicit files you wish to override in your theme, it may be a single f Umbraco Forms conditional JavaScript logic depends on some CSS classes currently and it is advised that you add any additional classes you require but **do not remove those already being set**. ::: -When using your own theme, you need to copy the `Script.cshtml` file from the `default` themes folder and amend the js references to reference your own js files. Your js files are probably best located in `/js` or `/scripts` along with your other js files. You can't put these in `/Views` since they won't be served because of restrictions on that folder by the web.config file. +When using your own theme, you need to copy the Script.cshtml file from the default theme folder and amend the js references to reference your own js files. Your js files are probably best located in `/js` or `/scripts` along with your other js files. You can't put these in `/Views` since they won't be served because of restrictions on that folder by the web.config file. -We highly recommend you never customize any files found in the `default` themes folder, as any customizations to these files will most likely be lost with any future upgrades you do to Umbraco Forms. +We highly recommend you never customize any files found in the `default` theme folder, as any customizations to these files will most likely be lost with any future upgrades you do to Umbraco Forms. ## Using a Theme - To use a theme with a Form use the "Insert Form" macro where you will be presented with the options of the form you wish to insert along with an option to pick a theme. This displays the list of theme folders found at `Views/Partials/Forms/Themes`. ![Choosing and using a theme](images/select-a-theme.png) @@ -31,13 +28,12 @@ To use a theme with a Form use the "Insert Form" macro where you will be present When you are rendering your form directly in your template, you need to specify your theme by filling out the `FormTheme` attribute: ```csharp -@await Umbraco.RenderMacroAsync("renderUmbracoForm", new {FormGuid="1ec026cb-d4d3-496c-b8e8-90e0758c78d8", FormTheme="MyFormTheme", ExcludeScripts="0"}) +@Umbraco.RenderMacro("renderUmbracoForm", new {FormGuid="dfea5397-36cd-4596-8d3c-d210502b67de", FormTheme="yourTheme", ExcludeScripts="0"}) ``` If you do not pick and/or set a theme, the `default` theme will be used to render the form. ## Theme Fallbacks - When using a theme, Umbraco Forms will try to use a view from the theme folder, but then fallback to the same view in the default theme folder if it can't be found. This allows you to create a theme by only modifying the files necessary to make your customizations. Files which can be overridden: @@ -45,16 +41,15 @@ Files which can be overridden: * Render.cshtml (overrides the entire form - usually not needed) * Form.cshtml (overrides the generation of the fields on the current page) * Script.cshtml (overrides the way files are included with the form) -* /Fieldtypes/FieldType.*.cshtml (overrides a specific view for a field) +* /Fieldtypes/FieldType*.cshtml (overrides a specific view for a field) ## Helper Methods ### SetFormThemeCssFile - Sets the primary form theme stylesheet path. This overrides an already assigned stylesheet and will be rendered out when inserting the form into the page ```csharp -Html.SetFormThemeCssFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css") +@Html.SetFormThemeCssFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css") ``` ### AddFormThemeScriptFile @@ -62,11 +57,10 @@ Html.SetFormThemeCssFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Defaul Add a JavaScript file path to include on form render ```csharp -Html.AddFormThemeScriptFile("~/App_Plugins/UmbracoForms/Assets/themes/default/umbracoforms.js"); +@Html.AddFormThemeScriptFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Default/umbracoforms-dependencies.js") ``` ### SetFormFieldClass - Adds a class to the form field HTML element of a given type. If no type is given, it will add the class to all fields ```csharp @@ -78,7 +72,6 @@ Adds a class to the form field HTML element of a given type. If no type is given ``` ### GetFormFieldClass - Retrieves all classes for a given field type, used when rendering form fieldtype partial views ```csharp @@ -86,7 +79,6 @@ class="@Html.GetFormFieldClass(Model.FieldTypeName)" ``` ### SetFormFieldWrapperClass - Adds a class to the div element wrapping around form fields of a given type. If no type is given, it will add the class to all fields ```csharp @@ -104,7 +96,3 @@ Retrieves all wrapper classes for a given field type, used when rendering form f ```csharp class="@Html.GetFormFieldWrapperClass(f.FieldTypeName)" ``` - ---- - -Prev: [Rendering Forms Scripts](../Rendering-Scripts/index-v9.md)                 Next: [Custom Markup](../Custom-Markup/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Themes/index.md b/Add-ons/UmbracoForms/Developer/Themes/index.md index 3138548b85c..433ebce5981 100644 --- a/Add-ons/UmbracoForms/Developer/Themes/index.md +++ b/Add-ons/UmbracoForms/Developer/Themes/index.md @@ -1,14 +1,16 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 meta.Title: "Theming Umbraco Forms" meta.Description: "Documentation on how to apply custom themes to Umbraco Forms" --- # Themes + As of version 6.0.0 and newer Umbraco Forms supports Themes, allowing forms to be customised in a much simpler manner than found in version 4.x. ## Creating a Theme -To create a theme you need to create a folder at `/Views/Partials/Forms/Themes/`. The name of the folder is the name of theme that will be visible in the backoffice when choosing it. + +To create a theme, you need to create a folder at `/Views/Partials/Forms/Themes/`. The name of the folder is the name of theme that will be visible in the backoffice when choosing it. Copy the explicit files you wish to override in your theme, it may be a single file or all files from the default theme folder. Make the necessary changes you desire to CSS class names, markup etc. @@ -16,11 +18,12 @@ Copy the explicit files you wish to override in your theme, it may be a single f Umbraco Forms conditional JavaScript logic depends on some CSS classes currently and it is advised that you add any additional classes you require but **do not remove those already being set**. ::: -When using your own theme, you need to copy the Script.cshtml file from the default theme folder and amend the js references to reference your own js files. Your js files are probably best located in `/js` or `/scripts` along with your other js files. You can't put these in `/Views` since they won't be served because of restrictions on that folder by the web.config file. +When using your own theme, you need to copy the `Script.cshtml` file from the `default` themes folder and amend the js references to reference your own js files. Your js files are probably best located in `/js` or `/scripts` along with your other js files. You can't put these in `/Views` since they won't be served because of restrictions on that folder by the web.config file. -We highly recommend you never customize any files found in the `default` theme folder, as any customizations to these files will most likely be lost with any future upgrades you do to Umbraco Forms. +We highly recommend you never customize any files found in the `default` themes folder, as any customizations to these files will most likely be lost with any future upgrades you do to Umbraco Forms. ## Using a Theme + To use a theme with a Form use the "Insert Form" macro where you will be presented with the options of the form you wish to insert along with an option to pick a theme. This displays the list of theme folders found at `Views/Partials/Forms/Themes`. ![Choosing and using a theme](images/select-a-theme.png) @@ -28,12 +31,13 @@ To use a theme with a Form use the "Insert Form" macro where you will be present When you are rendering your form directly in your template, you need to specify your theme by filling out the `FormTheme` attribute: ```csharp -@Umbraco.RenderMacro("renderUmbracoForm", new {FormGuid="dfea5397-36cd-4596-8d3c-d210502b67de", FormTheme="yourTheme", ExcludeScripts="0"}) +@await Umbraco.RenderMacroAsync("renderUmbracoForm", new {FormGuid="1ec026cb-d4d3-496c-b8e8-90e0758c78d8", FormTheme="MyFormTheme", ExcludeScripts="0"}) ``` If you do not pick and/or set a theme, the `default` theme will be used to render the form. ## Theme Fallbacks + When using a theme, Umbraco Forms will try to use a view from the theme folder, but then fallback to the same view in the default theme folder if it can't be found. This allows you to create a theme by only modifying the files necessary to make your customizations. Files which can be overridden: @@ -41,15 +45,16 @@ Files which can be overridden: * Render.cshtml (overrides the entire form - usually not needed) * Form.cshtml (overrides the generation of the fields on the current page) * Script.cshtml (overrides the way files are included with the form) -* /Fieldtypes/FieldType*.cshtml (overrides a specific view for a field) +* /Fieldtypes/FieldType.*.cshtml (overrides a specific view for a field) ## Helper Methods ### SetFormThemeCssFile + Sets the primary form theme stylesheet path. This overrides an already assigned stylesheet and will be rendered out when inserting the form into the page ```csharp -@Html.SetFormThemeCssFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css") +Html.SetFormThemeCssFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css") ``` ### AddFormThemeScriptFile @@ -57,10 +62,11 @@ Sets the primary form theme stylesheet path. This overrides an already assigned Add a JavaScript file path to include on form render ```csharp -@Html.AddFormThemeScriptFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Default/umbracoforms-dependencies.js") +Html.AddFormThemeScriptFile("~/App_Plugins/UmbracoForms/Assets/themes/default/umbracoforms.js"); ``` ### SetFormFieldClass + Adds a class to the form field HTML element of a given type. If no type is given, it will add the class to all fields ```csharp @@ -72,6 +78,7 @@ Adds a class to the form field HTML element of a given type. If no type is given ``` ### GetFormFieldClass + Retrieves all classes for a given field type, used when rendering form fieldtype partial views ```csharp @@ -79,6 +86,7 @@ class="@Html.GetFormFieldClass(Model.FieldTypeName)" ``` ### SetFormFieldWrapperClass + Adds a class to the div element wrapping around form fields of a given type. If no type is given, it will add the class to all fields ```csharp @@ -96,3 +104,7 @@ Retrieves all wrapper classes for a given field type, used when rendering form f ```csharp class="@Html.GetFormFieldWrapperClass(f.FieldTypeName)" ``` + +--- + +Prev: [Rendering Forms Scripts](../Rendering-Scripts/index.md)                 Next: [Custom Markup](../Custom-Markup/index.md) diff --git a/Add-ons/UmbracoForms/Developer/Working-With-Data/index-v9.md b/Add-ons/UmbracoForms/Developer/Working-With-Data/index-v8.md similarity index 53% rename from Add-ons/UmbracoForms/Developer/Working-With-Data/index-v9.md rename to Add-ons/UmbracoForms/Developer/Working-With-Data/index-v8.md index c571aaf7372..554ccf09155 100644 --- a/Add-ons/UmbracoForms/Developer/Working-With-Data/index-v9.md +++ b/Add-ons/UmbracoForms/Developer/Working-With-Data/index-v8.md @@ -1,15 +1,14 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 meta.Title: "Working with Umbraco Forms data" meta.Description: "Developer documentation on working with Forms record data." --- -# Working with Record Data +# Working with Record data -Umbraco Forms `v8.2` includes some helper methods that return records of a given Form, which can be used to output records in your templates using razor. - -## Available Methods +From Umbraco Forms `v8.2` includes some helper methods that return records of a given form, which can be used to output records in your templates using razor. +## Available methods The methods can be found by injecting the `Umbraco.Forms.Core.Services.IRecordReaderService` interface. For performance reasons, all these methods are paged. ### GetApprovedRecordsFromPage @@ -18,23 +17,23 @@ The methods can be found by injecting the `Umbraco.Forms.Core.Services.IRecordRe PagedResult GetApprovedRecordsFromPage(int pageId, int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from all Forms on the Umbraco page with the id = `pageId` . +Returns all records with the state set to approved from all forms on the Umbraco page with the id = `pageId` . ### GetApprovedRecordsFromFormOnPage ```csharp -PagedResult GetApprovedRecordsFromFormOnPage(int pageId, Guid.Parse("formId"), int pageNumber, int pageSize) +PagedResult GetApprovedRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records with the state set to approved from the form with the id = `formId` on the Umbraco page with the id = `pageId` as a PagedResult. ### GetApprovedRecordsFromForm ```csharp -PagedResult GetApprovedRecordsFromForm(Guid.Parse("formId"), int pageNumber, int pageSize) +PagedResult GetApprovedRecordsFromForm(Guid formId, int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from the Form with the ID = `formId` as a `PagedResult`. +Returns all records with the state set to approved from the form with the ID = `formId` as a PagedResult. ### GetRecordsFromPage @@ -42,23 +41,23 @@ Returns all records with the state set to approved from the Form with the ID = ` PagedResult GetRecordsFromPage(int pageId, int pageNumber, int pageSize) ``` -Returns all records from all Forms on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records from all forms on the Umbraco page with the id = `pageId` as a PagedResult. ### GetRecordsFromFormOnPage ```csharp -PagedResult GetRecordsFromFormOnPage(int pageId, Guid.Parse("formId"), int pageNumber, int pageSize) +PagedResult GetRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) ``` -Returns all records from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records from the form with the id = `formId` on the Umbraco page with the id = `pageId` as a PagedResult. ### GetRecordsFromForm ```csharp -PagedResult GetRecordsFromForm(Guid.Parse("formId"), int pageNumber, int pageSize) +PagedResult GetRecordsFromForm(Guid formId, int pageNumber, int pageSize) ``` -Returns all records from the Form with the ID = formId as a `PagedResult`. +Returns all records from the form with the ID = formId as a PagedResult ## The returned objects @@ -79,27 +78,28 @@ Guid UniqueId Dictionary RecordFields ``` -In order to access custom Form fields, these are available in the `RecordFields` property. +In order to access custom form fields, these are available in the `RecordFields` property. Furthermore there exists an extension method named `ValueAsString` on `IRecord` in `Umbraco.Forms.Core.Services`, such that you can get the value as string given the alias of the field. This extension method handle multi value fields by comma separating the values. E.g. "A, B, C" ## Sample razor script -Sample script that is outputting comments using a Form created with the default comment Form template. +Sample script that is outputting comments using a form created with the default comment form template. ```csharp @using Umbraco.Core; -@using Umbraco.Cms.Core.Composing; -@using Umbraco.Forms.Core.Services; -@inject IRecordReaderService _recordReaderService; - +@using Umbraco.Core.Composing +@using Umbraco.Forms.Core.Services +@{ + var recordReaderService = Current.Factory.GetInstance(); +}
    - @foreach (var record in _recordReaderService.GetApprovedRecordsFromPage(Model.Id, 1, 10).Items) + @foreach (var record in recordReaderService.GetApprovedRecordsFromPage(Model.Id, 1, 10).Items) {
  • @record.Created.ToString("dd MMMM yyy") - @if(string.IsNullOrEmpty(record.ValueAsString("email"))){ + @if(string.IsNullOrEmpty(record.ValueAsString("email")){ @record.ValueAsString("name") } else{ @@ -113,7 +113,3 @@ Sample script that is outputting comments using a Form created with the default }
``` - ---- - -Prev: [Email Templates](../Email-Templates/index-v9.md)                 Next: [Umbraco Forms in the Database](../Forms-in-the-Database/index-v9.md) diff --git a/Add-ons/UmbracoForms/Developer/Working-With-Data/index.md b/Add-ons/UmbracoForms/Developer/Working-With-Data/index.md index 554ccf09155..d98c0602904 100644 --- a/Add-ons/UmbracoForms/Developer/Working-With-Data/index.md +++ b/Add-ons/UmbracoForms/Developer/Working-With-Data/index.md @@ -1,14 +1,15 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 meta.Title: "Working with Umbraco Forms data" meta.Description: "Developer documentation on working with Forms record data." --- -# Working with Record data +# Working with Record Data -From Umbraco Forms `v8.2` includes some helper methods that return records of a given form, which can be used to output records in your templates using razor. +Umbraco Forms `v8.2` includes some helper methods that return records of a given Form, which can be used to output records in your templates using razor. + +## Available Methods -## Available methods The methods can be found by injecting the `Umbraco.Forms.Core.Services.IRecordReaderService` interface. For performance reasons, all these methods are paged. ### GetApprovedRecordsFromPage @@ -17,23 +18,23 @@ The methods can be found by injecting the `Umbraco.Forms.Core.Services.IRecordRe PagedResult GetApprovedRecordsFromPage(int pageId, int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from all forms on the Umbraco page with the id = `pageId` . +Returns all records with the state set to approved from all Forms on the Umbraco page with the id = `pageId` . ### GetApprovedRecordsFromFormOnPage ```csharp -PagedResult GetApprovedRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) +PagedResult GetApprovedRecordsFromFormOnPage(int pageId, Guid.Parse("formId"), int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from the form with the id = `formId` on the Umbraco page with the id = `pageId` as a PagedResult. +Returns all records with the state set to approved from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedResult`. ### GetApprovedRecordsFromForm ```csharp -PagedResult GetApprovedRecordsFromForm(Guid formId, int pageNumber, int pageSize) +PagedResult GetApprovedRecordsFromForm(Guid.Parse("formId"), int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from the form with the ID = `formId` as a PagedResult. +Returns all records with the state set to approved from the Form with the ID = `formId` as a `PagedResult`. ### GetRecordsFromPage @@ -41,23 +42,23 @@ Returns all records with the state set to approved from the form with the ID = ` PagedResult GetRecordsFromPage(int pageId, int pageNumber, int pageSize) ``` -Returns all records from all forms on the Umbraco page with the id = `pageId` as a PagedResult. +Returns all records from all Forms on the Umbraco page with the id = `pageId` as a `PagedResult`. ### GetRecordsFromFormOnPage ```csharp -PagedResult GetRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) +PagedResult GetRecordsFromFormOnPage(int pageId, Guid.Parse("formId"), int pageNumber, int pageSize) ``` -Returns all records from the form with the id = `formId` on the Umbraco page with the id = `pageId` as a PagedResult. +Returns all records from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedResult`. ### GetRecordsFromForm ```csharp -PagedResult GetRecordsFromForm(Guid formId, int pageNumber, int pageSize) +PagedResult GetRecordsFromForm(Guid.Parse("formId"), int pageNumber, int pageSize) ``` -Returns all records from the form with the ID = formId as a PagedResult +Returns all records from the Form with the ID = formId as a `PagedResult`. ## The returned objects @@ -78,28 +79,27 @@ Guid UniqueId Dictionary RecordFields ``` -In order to access custom form fields, these are available in the `RecordFields` property. +In order to access custom Form fields, these are available in the `RecordFields` property. Furthermore there exists an extension method named `ValueAsString` on `IRecord` in `Umbraco.Forms.Core.Services`, such that you can get the value as string given the alias of the field. This extension method handle multi value fields by comma separating the values. E.g. "A, B, C" ## Sample razor script -Sample script that is outputting comments using a form created with the default comment form template. +Sample script that is outputting comments using a Form created with the default comment Form template. ```csharp @using Umbraco.Core; -@using Umbraco.Core.Composing -@using Umbraco.Forms.Core.Services -@{ - var recordReaderService = Current.Factory.GetInstance(); -} +@using Umbraco.Cms.Core.Composing; +@using Umbraco.Forms.Core.Services; +@inject IRecordReaderService _recordReaderService; +
    - @foreach (var record in recordReaderService.GetApprovedRecordsFromPage(Model.Id, 1, 10).Items) + @foreach (var record in _recordReaderService.GetApprovedRecordsFromPage(Model.Id, 1, 10).Items) {
  • @record.Created.ToString("dd MMMM yyy") - @if(string.IsNullOrEmpty(record.ValueAsString("email")){ + @if(string.IsNullOrEmpty(record.ValueAsString("email"))){ @record.ValueAsString("name") } else{ @@ -113,3 +113,7 @@ Sample script that is outputting comments using a form created with the default }
``` + +--- + +Prev: [Email Templates](../Email-Templates/index.md)                 Next: [Umbraco Forms in the Database](../Forms-in-the-Database/index.md) diff --git a/Add-ons/UmbracoForms/Editor/Attaching-Workflows/Workflow-Types/index-v7.md b/Add-ons/UmbracoForms/Editor/Attaching-Workflows/Workflow-Types/index-v7.md new file mode 100644 index 00000000000..96ebfa053fd --- /dev/null +++ b/Add-ons/UmbracoForms/Editor/Attaching-Workflows/Workflow-Types/index-v7.md @@ -0,0 +1,127 @@ +--- +versionFrom: 7.0.0 +versionTo: 8.0.0 +--- + +# Overview of the default workflow types + +There are a couple of default workflow types that can be used to extend the functionality of your form. Here is an overview: + +## Change Record State + +![Change Record state](images/change-record-state.png) + +This workflow is used to automatically **approve** or **delete** a record once it is submitted. + +Configure some words that you want to check for, and select whether these words should trigger a deletion or an approval. + +## Post as XML + +![Post as XML](images/post-as-xml.png) + +This workflow is used to post the form as XML to a specified URL. + +Besides a name, the following configuration can be set: + +* URL (required) +* Method +* XsltFile - used to transform the XML +* Headers - map the needed files +* User and password + +## Save as an XML file + +![Save as XML](images/save-as-an-xml-file.png) + +This workflow will save the result of the form as an XML file by using XSLT. +In the configuration you can configure the following settings: + +* Path - where to save the XML file (required) +* File extension (required) +* XsltFile - used to transform the XML + +## Save as Umbraco Content Node + +![Save as content node](images/save-as-content-node.png) + +This workflow gives you the option to save a forms submission as a new content node. + +First of all, you need to choose a Document type and match the fields in the form with the properties on the selected Document Type. You can also choose to set a static value to fill in the properties. + +![Save as content node](images/create-new-node.png) + +In the example above, a Document Type called 'Blogpost' is selected to be used for creating the new content node. Furthermore, the value from the 'Name' field will be added as the 'Node Name' property in the new content node, and the value from the 'Email' field will be used for the 'Content' property. + +Other configuration: + +* Publish - choose whether to publish the node on submission +* Where to save - choose a section in the content tree where this new node should be added + +## Send email + +![Send email](images/send-email.png) + +Send the result of the form to a specified email address. + +The following settings can be configured: + +* Email (required) +* SenderEmail - also configurable in `Config/umbracosettings.config` +* Subject of the email +* Message (required) +* Attachment - specify whether file uploads should be attached to the email + +## Send email with template (Razor) + +![Send email with template](images/send-email-razor.png) + +This workflow will use a template to send the results of the form to a specified email address. + +You can create your own custom Razor templates to be used to send out emails upon forms submission. Read more about how to create these templates in the [Email Templates](../../../Developer/Email-Templates) article. + +The following settings can be configured: + +* Email (required) +* SenderEmail - also configurable in `Config/umbracosettings.config` +* Subject (required) +* Email Template - specify which template you want to use (required) +* Attachment - specify whether file uploads should be attached to the email + +## Send form to URL + +![Send to URL](images/send-to-URL.png) + +This workflow sends the form to a url, either as a HTTP POST or GET. + +The following settings can be configured: + +* URL (required) +* Method - POST, GET, PUT or DELETE (required) +* Fields - map the needed fields +* User and password + +## Send XSLT transformed email + +![Send XSLT Email](images/xslt-email.png) + +Send the result of the form to an email address and have full control over the email contents by supplying an xslt file. + +The following settings can be configured: + +* Email (required) +* SenderEmail - also configurable in `Config/umbracosettings.config` +* Subject (required) +* XSLT File - specify which file should be used to transform the content + +## Slack + +![Send to Slack](images/email-slack.png) + +This workflow lets you post the form data to a specific channel on Slack. + +The following settings can be configured: + +* API Token (required) +* Channel (required) +* Username (required) +* Avatar URL (required) diff --git a/Add-ons/UmbracoForms/Editor/Attaching-Workflows/Workflow-Types/index-v9.md b/Add-ons/UmbracoForms/Editor/Attaching-Workflows/Workflow-Types/index-v9.md deleted file mode 100644 index 1c377c6b700..00000000000 --- a/Add-ons/UmbracoForms/Editor/Attaching-Workflows/Workflow-Types/index-v9.md +++ /dev/null @@ -1,186 +0,0 @@ ---- -versionFrom: 9.0.0 ---- - -# Workflow Types - -There are a couple of default workflow types that can be used to extend the functionality of your Form. - -- [Change Record State](#change-record-state) -- [Post as XML](#post-as-xml) -- [Save as an XML file](#save-as-an-xml-file) -- [Save as Umbraco Content Node](#save-as-umbraco-content-node) -- [Send Email](#send-email) -- [Send Email with Template (Razor)](#send-email-with-template-razor) -- [Send Form to URL](#send-form-to-url) -- [Send XSLT Transformed Email](#send-xslt-transformed-email) -- [Slack](#slack) - -## **Change Record State** - -![Change Record state](images/change-record-state.png) - -Used to automatically **Approve Record** or **Delete Record** once it is submitted. Configure words that you want to match and select whether these words should trigger an approval or deletion of the record. - -## **Post as XML** - -![Post as XML](images/post-as-xml.png) - -Used to post the Form as an XML to a specified URL. The following configuration can be set: - -- Workflow Name -- URL (required) -- Method -- XsltFile - used to transform the XML -- Headers - map the needed files -- User -- Password - -## **Save as an XML file** - -![Save as XML](images/save-as-an-xml-file.png) - -Saves the result of the Form as an XML file by using XSLT. The following configuration can be set: - -- Workflow Name -- Path (required) - where to save the XML file -- File extension (required) -- XsltFile - used to transform the XML - -## **Save as Umbraco Content Node** - -![Save as content node](images/save-as-content-node.png) - -Saves a submitted Form as a new content node. You need to choose a Document type and match the fields in the Form with the properties on the selected Document Type. - -You can also choose to set a static value to fill in the properties: - -![Save as content node](images/create-new-node.png) - -In the example above, a Document Type called **Blogpost** is selected for creating the new Content node. - -The value from the **Name** field will be added as the **Node Name** property in the new Content node and the value from the **Email** field will be used as the **Content** property. - -The following configuration can be set: - -- Workflow Name -- Publish - choose whether to publish the node on submission -- Where to save - choose a section in the content tree where this new node should be added - -## **Send Email** - -![Send email](images/send-email.png) - -Sends the result of the Form to the specified email address. The following configuration can be set: - -- Workflow Name -- Message (required) -- Attachment - specify whether file uploads should be attached to the email -- Recipient Email (required) -- CC Email -- BCC Email -- SenderEmail - also configurable in `appsettings.json` under `Umbraco:CMS:Global:Smtp`. For more information, see the [Global Settings](../../../../../Reference/V9-Config/GlobalSettings/index.md) article. - -```json - "Umbraco": { - "CMS": { - "Global": { - "Smtp": { - "From": "person@umbraco.dk" - } - } - } - } -``` - -- Reply To Email -- Subject of the email (required) - -## **Send Email with Template (Razor)** - -![Send email with template](images/send-email-razor.png) - -Uses a template to send the results of the Form to a specified email address. - -You can create your own custom Razor templates to be used to send out emails upon Forms submission. Read more about how to create these templates in the [Email Templates](../../../Developer/Email-Templates) article. - -The following configuration can be set: - -- Workflow Name -- Email Template (required) - specify which template you want to use -- Attachment - specify whether file uploads should be attached to the email -- Recipient Email (required) -- CC Email -- BCC Email -- SenderEmail - also configurable in `appsettings.json` under `Umbraco:CMS:Global:Smtp`. For more information, see the [Global Settings](../../../../../Reference/V9-Config/GlobalSettings/index.md) article. - -```json - - "Umbraco": { - "CMS": { - "Global": { - "Smtp": { - "From": "person@umbraco.dk" - } - } - } - } -``` - -- Reply To Email -- Subject of the email (required) - -## **Send Form to URL** - -![Send to URL](images/send-to-URL.png) - -Sends the Form to a URL either as a HTTP POST or GET. The following configuration can be set: - -- Workflow Name -- URL (required) -- Method (required) - POST, GET, PUT or DELETE -- Fields - map the needed fields -- User -- Password - -## **Send XSLT Transformed Email** - -![Send XSLT Email](images/xslt-email.png) - -Sends the result of the Form to an email address with full control over the email contents by providing an xslt file. The following configuration can be set: - -- Workflow Name -- XSLT File - specify which file should be used to transform the content -- Recipient Email (required) -- CC Email -- BCC Email -- SenderEmail - also configurable in `appsettings.json` under `Umbraco:CMS:Global:Smtp`. For more information, see the [Global Settings](../../../../../Reference/V9-Config/GlobalSettings/index.md) article. - -```json - - "Umbraco": { - "CMS": { - "Global": { - "Smtp": { - "From": "person@umbraco.dk" - } - } - } - } -``` - -- Reply To Email -- Subject of the email (required) - -## **Slack** - -![Send to Slack](images/email-slack.png) - -Allows to post the Form data to a specific channel on Slack. The following configuration can be set: - -- Workflow Name -- Webhook URL (required) - ---- - -Prev: [Attaching Workflows](../index.md)                 Next: [Viewing and Exporting Entries](../../Viewing-and-Exporting-Entries/index.md) diff --git a/Add-ons/UmbracoForms/Editor/Attaching-Workflows/Workflow-Types/index.md b/Add-ons/UmbracoForms/Editor/Attaching-Workflows/Workflow-Types/index.md index 96ebfa053fd..1c377c6b700 100644 --- a/Add-ons/UmbracoForms/Editor/Attaching-Workflows/Workflow-Types/index.md +++ b/Add-ons/UmbracoForms/Editor/Attaching-Workflows/Workflow-Types/index.md @@ -1,127 +1,186 @@ --- -versionFrom: 7.0.0 -versionTo: 8.0.0 +versionFrom: 9.0.0 --- -# Overview of the default workflow types +# Workflow Types -There are a couple of default workflow types that can be used to extend the functionality of your form. Here is an overview: +There are a couple of default workflow types that can be used to extend the functionality of your Form. -## Change Record State +- [Change Record State](#change-record-state) +- [Post as XML](#post-as-xml) +- [Save as an XML file](#save-as-an-xml-file) +- [Save as Umbraco Content Node](#save-as-umbraco-content-node) +- [Send Email](#send-email) +- [Send Email with Template (Razor)](#send-email-with-template-razor) +- [Send Form to URL](#send-form-to-url) +- [Send XSLT Transformed Email](#send-xslt-transformed-email) +- [Slack](#slack) -![Change Record state](images/change-record-state.png) +## **Change Record State** -This workflow is used to automatically **approve** or **delete** a record once it is submitted. +![Change Record state](images/change-record-state.png) -Configure some words that you want to check for, and select whether these words should trigger a deletion or an approval. +Used to automatically **Approve Record** or **Delete Record** once it is submitted. Configure words that you want to match and select whether these words should trigger an approval or deletion of the record. -## Post as XML +## **Post as XML** ![Post as XML](images/post-as-xml.png) -This workflow is used to post the form as XML to a specified URL. - -Besides a name, the following configuration can be set: +Used to post the Form as an XML to a specified URL. The following configuration can be set: -* URL (required) -* Method -* XsltFile - used to transform the XML -* Headers - map the needed files -* User and password +- Workflow Name +- URL (required) +- Method +- XsltFile - used to transform the XML +- Headers - map the needed files +- User +- Password -## Save as an XML file +## **Save as an XML file** ![Save as XML](images/save-as-an-xml-file.png) -This workflow will save the result of the form as an XML file by using XSLT. -In the configuration you can configure the following settings: +Saves the result of the Form as an XML file by using XSLT. The following configuration can be set: -* Path - where to save the XML file (required) -* File extension (required) -* XsltFile - used to transform the XML +- Workflow Name +- Path (required) - where to save the XML file +- File extension (required) +- XsltFile - used to transform the XML -## Save as Umbraco Content Node +## **Save as Umbraco Content Node** ![Save as content node](images/save-as-content-node.png) -This workflow gives you the option to save a forms submission as a new content node. +Saves a submitted Form as a new content node. You need to choose a Document type and match the fields in the Form with the properties on the selected Document Type. -First of all, you need to choose a Document type and match the fields in the form with the properties on the selected Document Type. You can also choose to set a static value to fill in the properties. +You can also choose to set a static value to fill in the properties: ![Save as content node](images/create-new-node.png) -In the example above, a Document Type called 'Blogpost' is selected to be used for creating the new content node. Furthermore, the value from the 'Name' field will be added as the 'Node Name' property in the new content node, and the value from the 'Email' field will be used for the 'Content' property. +In the example above, a Document Type called **Blogpost** is selected for creating the new Content node. -Other configuration: +The value from the **Name** field will be added as the **Node Name** property in the new Content node and the value from the **Email** field will be used as the **Content** property. -* Publish - choose whether to publish the node on submission -* Where to save - choose a section in the content tree where this new node should be added +The following configuration can be set: -## Send email +- Workflow Name +- Publish - choose whether to publish the node on submission +- Where to save - choose a section in the content tree where this new node should be added + +## **Send Email** ![Send email](images/send-email.png) -Send the result of the form to a specified email address. +Sends the result of the Form to the specified email address. The following configuration can be set: + +- Workflow Name +- Message (required) +- Attachment - specify whether file uploads should be attached to the email +- Recipient Email (required) +- CC Email +- BCC Email +- SenderEmail - also configurable in `appsettings.json` under `Umbraco:CMS:Global:Smtp`. For more information, see the [Global Settings](../../../../../Reference/V9-Config/GlobalSettings/index.md) article. + +```json + "Umbraco": { + "CMS": { + "Global": { + "Smtp": { + "From": "person@umbraco.dk" + } + } + } + } +``` + +- Reply To Email +- Subject of the email (required) + +## **Send Email with Template (Razor)** -The following settings can be configured: +![Send email with template](images/send-email-razor.png) -* Email (required) -* SenderEmail - also configurable in `Config/umbracosettings.config` -* Subject of the email -* Message (required) -* Attachment - specify whether file uploads should be attached to the email +Uses a template to send the results of the Form to a specified email address. -## Send email with template (Razor) +You can create your own custom Razor templates to be used to send out emails upon Forms submission. Read more about how to create these templates in the [Email Templates](../../../Developer/Email-Templates) article. -![Send email with template](images/send-email-razor.png) +The following configuration can be set: -This workflow will use a template to send the results of the form to a specified email address. +- Workflow Name +- Email Template (required) - specify which template you want to use +- Attachment - specify whether file uploads should be attached to the email +- Recipient Email (required) +- CC Email +- BCC Email +- SenderEmail - also configurable in `appsettings.json` under `Umbraco:CMS:Global:Smtp`. For more information, see the [Global Settings](../../../../../Reference/V9-Config/GlobalSettings/index.md) article. -You can create your own custom Razor templates to be used to send out emails upon forms submission. Read more about how to create these templates in the [Email Templates](../../../Developer/Email-Templates) article. +```json -The following settings can be configured: + "Umbraco": { + "CMS": { + "Global": { + "Smtp": { + "From": "person@umbraco.dk" + } + } + } + } +``` -* Email (required) -* SenderEmail - also configurable in `Config/umbracosettings.config` -* Subject (required) -* Email Template - specify which template you want to use (required) -* Attachment - specify whether file uploads should be attached to the email +- Reply To Email +- Subject of the email (required) -## Send form to URL +## **Send Form to URL** ![Send to URL](images/send-to-URL.png) -This workflow sends the form to a url, either as a HTTP POST or GET. +Sends the Form to a URL either as a HTTP POST or GET. The following configuration can be set: -The following settings can be configured: +- Workflow Name +- URL (required) +- Method (required) - POST, GET, PUT or DELETE +- Fields - map the needed fields +- User +- Password -* URL (required) -* Method - POST, GET, PUT or DELETE (required) -* Fields - map the needed fields -* User and password - -## Send XSLT transformed email +## **Send XSLT Transformed Email** ![Send XSLT Email](images/xslt-email.png) -Send the result of the form to an email address and have full control over the email contents by supplying an xslt file. - -The following settings can be configured: - -* Email (required) -* SenderEmail - also configurable in `Config/umbracosettings.config` -* Subject (required) -* XSLT File - specify which file should be used to transform the content - -## Slack +Sends the result of the Form to an email address with full control over the email contents by providing an xslt file. The following configuration can be set: + +- Workflow Name +- XSLT File - specify which file should be used to transform the content +- Recipient Email (required) +- CC Email +- BCC Email +- SenderEmail - also configurable in `appsettings.json` under `Umbraco:CMS:Global:Smtp`. For more information, see the [Global Settings](../../../../../Reference/V9-Config/GlobalSettings/index.md) article. + +```json + + "Umbraco": { + "CMS": { + "Global": { + "Smtp": { + "From": "person@umbraco.dk" + } + } + } + } +``` + +- Reply To Email +- Subject of the email (required) + +## **Slack** ![Send to Slack](images/email-slack.png) -This workflow lets you post the form data to a specific channel on Slack. +Allows to post the Form data to a specific channel on Slack. The following configuration can be set: + +- Workflow Name +- Webhook URL (required) -The following settings can be configured: +--- -* API Token (required) -* Channel (required) -* Username (required) -* Avatar URL (required) +Prev: [Attaching Workflows](../index.md)                 Next: [Viewing and Exporting Entries](../../Viewing-and-Exporting-Entries/index.md) diff --git a/Add-ons/UmbracoForms/Editor/Viewing-and-Exporting-Entries/index.md b/Add-ons/UmbracoForms/Editor/Viewing-and-Exporting-Entries/index.md index 400368c031a..474fb0aceb1 100644 --- a/Add-ons/UmbracoForms/Editor/Viewing-and-Exporting-Entries/index.md +++ b/Add-ons/UmbracoForms/Editor/Viewing-and-Exporting-Entries/index.md @@ -52,4 +52,4 @@ Select at least 1 record to see the available actions in the top-right corner. B --- -Prev: [Workflow Types](../Attaching-Workflows/Workflow-Types/index-v9.md)                 Next: [Defining and Attaching Prevalue Sources](../Defining-and-Attaching-Prevaluesources/index.md) +Prev: [Workflow Types](../Attaching-Workflows/Workflow-Types/index.md)                 Next: [Defining and Attaching Prevalue Sources](../Defining-and-Attaching-Prevaluesources/index.md) diff --git a/Add-ons/UmbracoForms/Installation/Install/index-v7.md b/Add-ons/UmbracoForms/Installation/Install/index-v7.md new file mode 100644 index 00000000000..48430bae6d5 --- /dev/null +++ b/Add-ons/UmbracoForms/Installation/Install/index-v7.md @@ -0,0 +1,31 @@ +--- +versionFrom: 7.0.0 + +meta.Title: "Installing Umbraco Forms" +meta.Description: "Installing Umbraco Forms" +--- + +# Extending Umbraco with the full Forms section + +Since Umbraco v7.2, Umbraco contains the Forms section by default. From there it's a few steps to install and get started using Umbraco Forms. + +## Installing Umbraco Forms + +It only takes a few steps to install Umbraco Forms. + +1. Navigate to the Forms section in the Umbraco backoffice +2. The Forms dashboard will contain a *Install* button +3. Hit the *Install* button +4. Wait for the installation to complete +5. Your browser will automagically refresh +6. You are now ready to start building your very first form with Umbraco Forms! + +![Installing Umbraco Forms](images/InstallingForms.gif) + +## Start building forms + +Once the installation is successful you will be able to start using Umbraco Forms. + +For details on how to proceed, check out the [editor documentation](../../Editor) + +![Create form](images/start-with-forms.png) diff --git a/Add-ons/UmbracoForms/Installation/Install/index-v9.md b/Add-ons/UmbracoForms/Installation/Install/index-v9.md deleted file mode 100644 index ddec88ad952..00000000000 --- a/Add-ons/UmbracoForms/Installation/Install/index-v9.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -versionFrom: 9.0.0 - -meta.Title: "Installing Umbraco Forms" -meta.Description: "Installing Umbraco Forms" ---- - -# Extending Umbraco with the Forms Section - -Since Umbraco v7.2, Umbraco contains the **Forms** section, by default. You will see a similar interface, when you click on the **Forms** section in the **Umbraco Backoffice**. - -![Form section in backoffice](images/Forms_Section_Backoffice.png) - -## Installing Umbraco Forms - -To install the Umbraco Forms package (**Umbraco.Forms**), follow these steps: - -1. Run the following command on a command prompt of your choice: - - ```cli - dotnet add package Umbraco.Forms - ``` - -2. Restart the web application using the following command: - - ```cli - dotnet run - ``` - -## Start Building Forms - -Once the installation is successful, you will see a similar screen in the Forms section: - -![Create form](images/start-with-forms-v9.png) - -## Using Forms - -For details on using Forms, see the [Editor Documentation](../../Editor). diff --git a/Add-ons/UmbracoForms/Installation/Install/index.md b/Add-ons/UmbracoForms/Installation/Install/index.md index 48430bae6d5..ddec88ad952 100644 --- a/Add-ons/UmbracoForms/Installation/Install/index.md +++ b/Add-ons/UmbracoForms/Installation/Install/index.md @@ -1,31 +1,38 @@ --- -versionFrom: 7.0.0 +versionFrom: 9.0.0 meta.Title: "Installing Umbraco Forms" meta.Description: "Installing Umbraco Forms" --- -# Extending Umbraco with the full Forms section +# Extending Umbraco with the Forms Section -Since Umbraco v7.2, Umbraco contains the Forms section by default. From there it's a few steps to install and get started using Umbraco Forms. +Since Umbraco v7.2, Umbraco contains the **Forms** section, by default. You will see a similar interface, when you click on the **Forms** section in the **Umbraco Backoffice**. + +![Form section in backoffice](images/Forms_Section_Backoffice.png) ## Installing Umbraco Forms -It only takes a few steps to install Umbraco Forms. +To install the Umbraco Forms package (**Umbraco.Forms**), follow these steps: + +1. Run the following command on a command prompt of your choice: + + ```cli + dotnet add package Umbraco.Forms + ``` + +2. Restart the web application using the following command: -1. Navigate to the Forms section in the Umbraco backoffice -2. The Forms dashboard will contain a *Install* button -3. Hit the *Install* button -4. Wait for the installation to complete -5. Your browser will automagically refresh -6. You are now ready to start building your very first form with Umbraco Forms! + ```cli + dotnet run + ``` -![Installing Umbraco Forms](images/InstallingForms.gif) +## Start Building Forms -## Start building forms +Once the installation is successful, you will see a similar screen in the Forms section: -Once the installation is successful you will be able to start using Umbraco Forms. +![Create form](images/start-with-forms-v9.png) -For details on how to proceed, check out the [editor documentation](../../Editor) +## Using Forms -![Create form](images/start-with-forms.png) +For details on using Forms, see the [Editor Documentation](../../Editor). diff --git a/Add-ons/UmbracoForms/Installation/ManualUpgrade.md b/Add-ons/UmbracoForms/Installation/ManualUpgrade.md index 316a57bf14b..88f9c969df2 100644 --- a/Add-ons/UmbracoForms/Installation/ManualUpgrade.md +++ b/Add-ons/UmbracoForms/Installation/ManualUpgrade.md @@ -2,6 +2,7 @@ versionFrom: 8.0.0 meta.Title: "Manually Upgrading Umbraco Forms" meta.Description: "Documentation on how to upgrade Umbraco Forms" +needsV9Update: "true" --- # Manually upgrading forms diff --git a/Add-ons/UmbracoForms/Installation/Version-Specific.md b/Add-ons/UmbracoForms/Installation/Version-Specific.md index 5f1e5103237..5983f608289 100644 --- a/Add-ons/UmbracoForms/Installation/Version-Specific.md +++ b/Add-ons/UmbracoForms/Installation/Version-Specific.md @@ -1,5 +1,6 @@ --- versionFrom: 7.0.0 +needsV9Update: "true" --- # Upgrading - version specific diff --git a/Add-ons/UmbracoForms/Installation/index.md b/Add-ons/UmbracoForms/Installation/index.md index f1fddfce559..0bf608f36e4 100644 --- a/Add-ons/UmbracoForms/Installation/index.md +++ b/Add-ons/UmbracoForms/Installation/index.md @@ -1,5 +1,6 @@ --- versionFrom: 8.0.0 +versionTo: 9.0.0 --- # Installation and upgrading diff --git a/Fundamentals/Backoffice/LogViewer/index.md b/Fundamentals/Backoffice/LogViewer/index.md index 97f7942857c..4e9e056cb89 100644 --- a/Fundamentals/Backoffice/LogViewer/index.md +++ b/Fundamentals/Backoffice/LogViewer/index.md @@ -3,6 +3,7 @@ meta.Title: "Log Viewer" meta.Description: "Information on using the Umbraco log viewer in version 8" keywords: logging logviewer logs serilog messagetemplates logs v8 version8 versionFrom: 8.0.0 +needsV9Update: "true" --- # Log Viewer diff --git a/Fundamentals/Backoffice/Login/index-v8.md b/Fundamentals/Backoffice/Login/index-v8.md new file mode 100644 index 00000000000..f489eca5313 --- /dev/null +++ b/Fundamentals/Backoffice/Login/index-v8.md @@ -0,0 +1,72 @@ +--- +meta.Title: "Configure and customize the Login screen" +meta.Description: "In this article you can learn the various ways of customizing the Umbraco backoffice login screen and form." +versionFrom: 8.0.0 +versionTo: 9.0.0 +--- + +# Login screen + +To access the backoffice, you will need to login. You can do this by adding `/umbraco` to the end of your website URL, e.g. . + +You will be presented with a login form similar to this: + +![Login screen](images/backoffice-login.png "The login screen has a greeting, username/password field and optionally a 'Forgotten password' link.") +*The login screen has a greeting, username/password field and optionally a 'Forgotten password' link* + +Below, you will find instructions on how to customise the login screen. + +## Greeting + +The login screen features a greeting, which you can personalize by changing the language file of your choice. For example for en-US you would add the following keys to: `~/Config/Lang/en-US.user.xml` + +```xml + + Sunday greeting + Monday greeting + Tuesday greeting + Wednesday greeting + Thursday greeting + Friday greeting + Saturday greeting + +``` + +You can customize other text in the login screen as well, grab the default values from `~/Umbraco/Config/Lang/en.xml` and copy the keys you want to translate into your `~/Config/Lang/MYLANGUAGE.user.xml` file. + +## Password reset + +The "Forgot password?" link allows your backoffice users to reset their password. To setup this feature you will need to add the following key to the `` section in the `~Config/umbracoSettings.config`: + +```xml +true +``` + +Set it to `true` to enable the password reset feature, and to `false` to disable the feature. + +You will also need to configure an SMTP server in your `web.config` file. When you get a successful result on the SMTP configuration when running a health check in the backoffice, you are good to go! + +An example: + +```xml + + + + + + + +``` + +## Background image + +It is possible to customize the background image for the backoffice login screen. In [`~/Config/umbracoSettings.config`](../../../Reference/Config/umbracoSettings/) find the `loginBackgroundImage` and change the path to the image you want to use. + +```xml + + + ... + /images/myCustomImage.jpg + + +``` diff --git a/Fundamentals/Backoffice/Login/index-v9.md b/Fundamentals/Backoffice/Login/index-v9.md deleted file mode 100644 index 072bce4fac1..00000000000 --- a/Fundamentals/Backoffice/Login/index-v9.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -meta.Title: "Configure and customize the Login screen" -meta.Description: "In this article you can learn the various ways of customizing the Umbraco backoffice login screen and form." -versionFrom: 9.0.0 ---- - -# Login screen - -To access the backoffice, you will need to login. You can do this by adding `/umbraco` at the end of your website URL, e.g. http://mywebsite.com/umbraco. - -You will be presented with a login form similar to this: - -![Login screen](images/backoffice-login.png "The login screen has a greeting, username/password field and optionally a 'Forgotten password' link.") - -The **login** screen contains a **Greeting**, **Email**, **Password** field and optionally a **Forgotten password** link - -Below, you will find instructions on how to customise the login screen. - -## Greeting - -The login screen features a greeting, which you can personalize by changing the language file of your choice. For example for en-US you would add the following keys to: `~/umbraco/config/lang/en_us.xml` - -```xml - - Happy super Sunday - Happy manic Monday - Happy tubular Tuesday - Happy wonderful Wednesday - Happy thunderous Thursday - Happy funky Friday - Happy Caturday - -``` - -You can customize other text in the login screen as well, grab the default values from `~/umbraco/config/lang/en.xml` and copy the keys you want to translate into your `~/umbraco/config/lang/MYLANGUAGE.xml` file. - -## Password reset - -The **Forgotten password?** link allows your backoffice users to reset their password. To use this feature, you will need to add the following key to the `Umbraco.Cms.Security` section in the `appsettings.json` file: - -```json -"Umbraco": { - "CMS": { - "Security": { - "AllowPasswordReset": true - } - } -} -``` - -Set it to `true` to enable the password reset feature, and `false` to disable the feature. - -You will also need to configure an SMTP server in your `appsettings.json` file. When you get a successful result on the SMTP configuration when running a health check in the backoffice, you are good to go! - -An example: - -```json -"Umbraco": { - "CMS": { - "Global": { - "Id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "Smtp": { - "From": "noreply@test.com", - "Host": "127.0.0.1", - "Username": "username", - "Password": "password" - } - } - } -} -``` - -## Background image - -It is possible to customize the background image for the backoffice login screen by adding the `"Content"` section in the `appsettings.json` file: - -```json -"Umbraco": { - "CMS": { - "Content": { - "LoginBackgroundImage": "assets/img/login.jpg" - } - } -} -``` diff --git a/Fundamentals/Backoffice/Login/index.md b/Fundamentals/Backoffice/Login/index.md index f489eca5313..072bce4fac1 100644 --- a/Fundamentals/Backoffice/Login/index.md +++ b/Fundamentals/Backoffice/Login/index.md @@ -1,72 +1,85 @@ --- meta.Title: "Configure and customize the Login screen" meta.Description: "In this article you can learn the various ways of customizing the Umbraco backoffice login screen and form." -versionFrom: 8.0.0 -versionTo: 9.0.0 +versionFrom: 9.0.0 --- # Login screen -To access the backoffice, you will need to login. You can do this by adding `/umbraco` to the end of your website URL, e.g. . +To access the backoffice, you will need to login. You can do this by adding `/umbraco` at the end of your website URL, e.g. http://mywebsite.com/umbraco. You will be presented with a login form similar to this: ![Login screen](images/backoffice-login.png "The login screen has a greeting, username/password field and optionally a 'Forgotten password' link.") -*The login screen has a greeting, username/password field and optionally a 'Forgotten password' link* + +The **login** screen contains a **Greeting**, **Email**, **Password** field and optionally a **Forgotten password** link Below, you will find instructions on how to customise the login screen. ## Greeting -The login screen features a greeting, which you can personalize by changing the language file of your choice. For example for en-US you would add the following keys to: `~/Config/Lang/en-US.user.xml` +The login screen features a greeting, which you can personalize by changing the language file of your choice. For example for en-US you would add the following keys to: `~/umbraco/config/lang/en_us.xml` ```xml - Sunday greeting - Monday greeting - Tuesday greeting - Wednesday greeting - Thursday greeting - Friday greeting - Saturday greeting + Happy super Sunday + Happy manic Monday + Happy tubular Tuesday + Happy wonderful Wednesday + Happy thunderous Thursday + Happy funky Friday + Happy Caturday ``` -You can customize other text in the login screen as well, grab the default values from `~/Umbraco/Config/Lang/en.xml` and copy the keys you want to translate into your `~/Config/Lang/MYLANGUAGE.user.xml` file. +You can customize other text in the login screen as well, grab the default values from `~/umbraco/config/lang/en.xml` and copy the keys you want to translate into your `~/umbraco/config/lang/MYLANGUAGE.xml` file. ## Password reset -The "Forgot password?" link allows your backoffice users to reset their password. To setup this feature you will need to add the following key to the `` section in the `~Config/umbracoSettings.config`: +The **Forgotten password?** link allows your backoffice users to reset their password. To use this feature, you will need to add the following key to the `Umbraco.Cms.Security` section in the `appsettings.json` file: -```xml -true +```json +"Umbraco": { + "CMS": { + "Security": { + "AllowPasswordReset": true + } + } +} ``` -Set it to `true` to enable the password reset feature, and to `false` to disable the feature. +Set it to `true` to enable the password reset feature, and `false` to disable the feature. -You will also need to configure an SMTP server in your `web.config` file. When you get a successful result on the SMTP configuration when running a health check in the backoffice, you are good to go! +You will also need to configure an SMTP server in your `appsettings.json` file. When you get a successful result on the SMTP configuration when running a health check in the backoffice, you are good to go! An example: -```xml - - - - - - - +```json +"Umbraco": { + "CMS": { + "Global": { + "Id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "Smtp": { + "From": "noreply@test.com", + "Host": "127.0.0.1", + "Username": "username", + "Password": "password" + } + } + } +} ``` ## Background image -It is possible to customize the background image for the backoffice login screen. In [`~/Config/umbracoSettings.config`](../../../Reference/Config/umbracoSettings/) find the `loginBackgroundImage` and change the path to the image you want to use. +It is possible to customize the background image for the backoffice login screen by adding the `"Content"` section in the `appsettings.json` file: -```xml - - - ... - /images/myCustomImage.jpg - - +```json +"Umbraco": { + "CMS": { + "Content": { + "LoginBackgroundImage": "assets/img/login.jpg" + } + } +} ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Block-List-Editor/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Block-List-Editor/index-v8.md similarity index 96% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Block-List-Editor/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Block-List-Editor/index-v8.md index b4980c96c05..d457fdcad5a 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Block-List-Editor/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Block-List-Editor/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.7.0 --- # Block List @@ -146,8 +146,8 @@ In the following example of a Partial view for a Block Type, please note that th Example: ```csharp -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage; -@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; +@inherits Umbraco.Web.Mvc.UmbracoViewPage +@using ContentModels = Umbraco.Web.PublishedModels; @{ var content = (ContentModels.MyElementTypeAliasOfContent)Model.Content; var settings = (ContentModels.MyElementTypeAliasOfSettings)Model.Settings; @@ -174,8 +174,8 @@ A built-in value converter is available to use the data as you like. Call the `V Example: ```csharp -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage; -@using Umbraco.Cms.Core.Models.Blocks; +@inherits Umbraco.Web.Mvc.UmbracoViewPage +@using Umbraco.Core.Models.Blocks; @{ var blocks = Model.Value>("myBlocksProperty"); foreach (var block in blocks) @@ -192,9 +192,9 @@ Each item is a `BlockListItem` entity that contains two main properties `Content Example: ```csharp -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage; -@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; -@using Umbraco.Cms.Core.Models.Blocks; +@inherits Umbraco.Web.Mvc.UmbracoViewPage +@using Umbraco.Core.Models.Blocks; +@using ContentModels = Umbraco.Web.PublishedModels; @{ var blocks = Model.Value>("myBlocksProperty"); foreach (var block in blocks) @@ -217,9 +217,9 @@ In this case, you can extract the variant's data using the following, which retu Example: ```csharp -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage; -@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; -@using Umbraco.Cms.Core.Models.Blocks; +@inherits Umbraco.Web.Mvc.UmbracoViewPage +@using Umbraco.Core.Models.Blocks; +@using ContentModels = Umbraco.Web.PublishedModels; @{ var variants = Model.Value>("variants").Select(x => x.Content); foreach (var variant in variants) @@ -314,7 +314,6 @@ angular.module("umbraco").controller("customBlockController", function ($scope) ``` #### Example: Displaying an image from a Media Picker - Your block may enable you to 'pick' an image for use as the background for a particular block or to display as part of the block layout. If you try to display this image directly in the view from the property `block.data.image` you'll see the unique id and not the image. We'll need to use the Id in our custom angularJS controller to get the ImageUrl to display in our Backoffice Block Editor View. @@ -461,14 +460,10 @@ public class Person After injecting [ContentService](../../../../../Reference/Management/Services/ContentService/) and [ContentTypeService](../../../../../Reference/Management/Services/ContentTypeService/), we can do the following: ```csharp - - @inject IContentService Services; - @inject IContentTypeService _contentTypeService; - //if the class containing our code inherits SurfaceController, UmbracoApiController, or UmbracoAuthorizedApiController, we can get ContentService from Services namespace - var contentService = Services; + var contentService = Services.ContentService; //not to be confused with ContentService, this service will be useful for getting some Document Type IDs - IContentTypeService contentTypeService = _contentTypeService; + IContentTypeService contentTypeService = Services.ContentTypeService; //we are creating two people to be added to Blocklist, which means we need two new Guids GuidUdi contentUdi1 = new GuidUdi("element", System.Guid.NewGuid()); //Since these will be BLock List objects the Guids need to mention the "element" keyword diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Block-List-Editor/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Block-List-Editor/index.md index d457fdcad5a..b4980c96c05 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Block-List-Editor/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Block-List-Editor/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.7.0 +versionFrom: 9.0.0 --- # Block List @@ -146,8 +146,8 @@ In the following example of a Partial view for a Block Type, please note that th Example: ```csharp -@inherits Umbraco.Web.Mvc.UmbracoViewPage -@using ContentModels = Umbraco.Web.PublishedModels; +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage; +@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; @{ var content = (ContentModels.MyElementTypeAliasOfContent)Model.Content; var settings = (ContentModels.MyElementTypeAliasOfSettings)Model.Settings; @@ -174,8 +174,8 @@ A built-in value converter is available to use the data as you like. Call the `V Example: ```csharp -@inherits Umbraco.Web.Mvc.UmbracoViewPage -@using Umbraco.Core.Models.Blocks; +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage; +@using Umbraco.Cms.Core.Models.Blocks; @{ var blocks = Model.Value>("myBlocksProperty"); foreach (var block in blocks) @@ -192,9 +192,9 @@ Each item is a `BlockListItem` entity that contains two main properties `Content Example: ```csharp -@inherits Umbraco.Web.Mvc.UmbracoViewPage -@using Umbraco.Core.Models.Blocks; -@using ContentModels = Umbraco.Web.PublishedModels; +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage; +@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; +@using Umbraco.Cms.Core.Models.Blocks; @{ var blocks = Model.Value>("myBlocksProperty"); foreach (var block in blocks) @@ -217,9 +217,9 @@ In this case, you can extract the variant's data using the following, which retu Example: ```csharp -@inherits Umbraco.Web.Mvc.UmbracoViewPage -@using Umbraco.Core.Models.Blocks; -@using ContentModels = Umbraco.Web.PublishedModels; +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage; +@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; +@using Umbraco.Cms.Core.Models.Blocks; @{ var variants = Model.Value>("variants").Select(x => x.Content); foreach (var variant in variants) @@ -314,6 +314,7 @@ angular.module("umbraco").controller("customBlockController", function ($scope) ``` #### Example: Displaying an image from a Media Picker + Your block may enable you to 'pick' an image for use as the background for a particular block or to display as part of the block layout. If you try to display this image directly in the view from the property `block.data.image` you'll see the unique id and not the image. We'll need to use the Id in our custom angularJS controller to get the ImageUrl to display in our Backoffice Block Editor View. @@ -460,10 +461,14 @@ public class Person After injecting [ContentService](../../../../../Reference/Management/Services/ContentService/) and [ContentTypeService](../../../../../Reference/Management/Services/ContentTypeService/), we can do the following: ```csharp + + @inject IContentService Services; + @inject IContentTypeService _contentTypeService; + //if the class containing our code inherits SurfaceController, UmbracoApiController, or UmbracoAuthorizedApiController, we can get ContentService from Services namespace - var contentService = Services.ContentService; + var contentService = Services; //not to be confused with ContentService, this service will be useful for getting some Document Type IDs - IContentTypeService contentTypeService = Services.ContentTypeService; + IContentTypeService contentTypeService = _contentTypeService; //we are creating two people to be added to Blocklist, which means we need two new Guids GuidUdi contentUdi1 = new GuidUdi("element", System.Guid.NewGuid()); //Since these will be BLock List objects the Guids need to mention the "element" keyword diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/CheckBox-List/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/CheckBox-List/index-v8.md similarity index 85% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/CheckBox-List/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/CheckBox-List/index-v8.md index 1087d80c3ee..084afa6b205 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/CheckBox-List/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/CheckBox-List/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Checkbox List @@ -61,11 +61,10 @@ Unlike other property editors, the Prevalue IDs are not directly accessible in R See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @using Newtonsoft.Json @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -93,10 +92,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ - -// Set the value of the property with alias 'superHeros' -content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor,x => x.SuperHeros).Alias, JsonConvert.SerializeObject(new[] { "Umbraco", "CodeGarden"})); + // Set the value of the property with alias 'superHeros' + content.SetValue(Home.GetModelPropertyType(x => x.SuperHeros).Alias, JsonConvert.SerializeObject(new[] { "Umbraco", "CodeGarden"})); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/CheckBox-List/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/CheckBox-List/index.md index 084afa6b205..1087d80c3ee 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/CheckBox-List/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/CheckBox-List/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Checkbox List @@ -61,10 +61,11 @@ Unlike other property editors, the Prevalue IDs are not directly accessible in R See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @using Newtonsoft.Json @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -92,8 +93,10 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ - // Set the value of the property with alias 'superHeros' - content.SetValue(Home.GetModelPropertyType(x => x.SuperHeros).Alias, JsonConvert.SerializeObject(new[] { "Umbraco", "CodeGarden"})); + +// Set the value of the property with alias 'superHeros' +content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor,x => x.SuperHeros).Alias, JsonConvert.SerializeObject(new[] { "Umbraco", "CodeGarden"})); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Color-Picker/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Color-Picker/index-v8.md similarity index 90% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Color-Picker/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Color-Picker/index-v8.md index e223c88eca7..5946bf3090d 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Color-Picker/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Color-Picker/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Color Picker @@ -58,10 +58,9 @@ It's possible to add a label to use with the color. See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -90,9 +89,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'color' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Color).Alias, "38761d"); + content.SetValue(Home.GetModelPropertyType(x => x.Color).Alias, "38761d"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Color-Picker/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Color-Picker/index.md index 5946bf3090d..e223c88eca7 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Color-Picker/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Color-Picker/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Color Picker @@ -58,9 +58,10 @@ It's possible to add a label to use with the color. See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -89,8 +90,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'color' - content.SetValue(Home.GetModelPropertyType(x => x.Color).Alias, "38761d"); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Color).Alias, "38761d"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Content-Picker/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Content-Picker/index-v8.md similarity index 89% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Content-Picker/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Content-Picker/index-v8.md index 01d5d184696..e1daec12d94 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Content-Picker/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Content-Picker/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.1.0 --- # Content Picker @@ -49,10 +49,9 @@ The content picker opens a panel to pick a specific page from the content struct See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -86,9 +85,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'featurePicker' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.FeaturePicker).Alias, udi.ToString()); + content.SetValue(Home.GetModelPropertyType(x => x.FeaturePicker).Alias, udi.ToString()); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Content-Picker/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Content-Picker/index.md index e1daec12d94..01d5d184696 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Content-Picker/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Content-Picker/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.1.0 +versionFrom: 9.0.0 --- # Content Picker @@ -49,9 +49,10 @@ The content picker opens a panel to pick a specific page from the content struct See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -85,8 +86,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'featurePicker' - content.SetValue(Home.GetModelPropertyType(x => x.FeaturePicker).Alias, udi.ToString()); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.FeaturePicker).Alias, udi.ToString()); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Date-Time/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Date-Time/index-v8.md similarity index 87% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Date-Time/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Date-Time/index-v8.md index e25381f52cf..8e0571c8580 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Date-Time/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Date-Time/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # DateTime @@ -43,10 +43,9 @@ The second setting is "Offset time". When enabling this setting the displayed ti See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = new Guid("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -74,10 +73,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'datePicker' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.DatePicker).Alias, DateTime.Now); + content.SetValue(Home.GetModelPropertyType(x => x.DatePicker).Alias, DateTime.Now); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Date-Time/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Date-Time/index.md index 8e0571c8580..e25381f52cf 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Date-Time/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Date-Time/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # DateTime @@ -43,9 +43,10 @@ The second setting is "Offset time". When enabling this setting the displayed ti See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = new Guid("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -73,9 +74,10 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'datePicker' - content.SetValue(Home.GetModelPropertyType(x => x.DatePicker).Alias, DateTime.Now); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.DatePicker).Alias, DateTime.Now); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Decimal/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Decimal/index-v8.md similarity index 87% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Decimal/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Decimal/index-v8.md index ab15b673856..a66626de76d 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Decimal/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Decimal/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Decimal @@ -41,10 +41,9 @@ If the value of **Step Size** is not set then all decimal values between 8 and 1 See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -72,9 +71,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'myDecimal' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MyDecimal).Alias, 3); + content.SetValue(Home.GetModelPropertyType(x => x.MyDecimal).Alias, 3); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Decimal/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Decimal/index.md index a66626de76d..ab15b673856 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Decimal/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Decimal/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Decimal @@ -41,9 +41,10 @@ If the value of **Step Size** is not set then all decimal values between 8 and 1 See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -71,8 +72,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'myDecimal' - content.SetValue(Home.GetModelPropertyType(x => x.MyDecimal).Alias, 3); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MyDecimal).Alias, 3); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/DropDown/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/DropDown/index-v8.md similarity index 90% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/DropDown/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/DropDown/index-v8.md index f99f525ae4b..c03983c8dbb 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/DropDown/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/DropDown/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Dropdown @@ -88,11 +88,10 @@ Prevalues are the options which are shown in the dropdown list. You can add, edi See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @using Newtonsoft.Json @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -120,9 +119,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'categories' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Categories).Alias, JsonConvert.SerializeObject(new[] { "News" })); + content.SetValue(Home.GetModelPropertyType(x => x.Categories).Alias, JsonConvert.SerializeObject(new[] { "News" })); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/DropDown/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/DropDown/index.md index c03983c8dbb..f99f525ae4b 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/DropDown/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/DropDown/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Dropdown @@ -88,10 +88,11 @@ Prevalues are the options which are shown in the dropdown list. You can add, edi See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @using Newtonsoft.Json @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -119,8 +120,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'categories' - content.SetValue(Home.GetModelPropertyType(x => x.Categories).Alias, JsonConvert.SerializeObject(new[] { "News" })); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Categories).Alias, JsonConvert.SerializeObject(new[] { "News" })); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Email-Address/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Email-Address/index-v8.md similarity index 68% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Email-Address/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Email-Address/index-v8.md index e3002bf65b6..e9fc3a16e8a 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Email-Address/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Email-Address/index-v8.md @@ -1,7 +1,5 @@ --- -meta.Title: "Display an email address" -meta.Description: "In this article you can learn how to use the build in email property editor" -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Email Address @@ -14,10 +12,24 @@ Displays an email address. ## Settings +### Enable required checkbox (8.7.0 and below) + +If the property is set to mandatory, Umbraco will display a warning label under the property editor when you publish the page this editor is located on and clean the input. This functionality has been deprecated in `8.8.0` and above. Instead, you select the mandatory checkbox when adding the property editor to a Document Type. + +## Data Type Definition Example + +### 8.8.0 and higher + +![Email Data Type Definition 8.8.0](images/EmailAddress-DataType-v88.png) + ### Mandatory checkbox example ![Mandatory Checkbox Example](images/mandatory-checkbox.png) +### 8.0.0 - 8.7.0 + +![Email Data Type Definition 8.0.0 - 8.7.0](images/EmailAddress-DataType-v8.png) + ## Content Example ![Single email address content example](images/EmailAddress-DataType-Content.png) @@ -49,11 +61,9 @@ Displays an email address. See the example below to learn how a value can be added or changed programmatically to an Email-address property. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; - @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of your page var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); @@ -61,7 +71,7 @@ See the example below to learn how a value can be added or changed programmatica // Get the page using the GUID you've just defined var content = contentService.GetById(guid); // Set the value of the property with alias 'email' - content.SetValue("email", "jpk@umbraco.dk"); + content.SetValue("email", "stk@umbraco.com"); // Save the change contentService.Save(content); diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Email-Address/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Email-Address/index.md index e9fc3a16e8a..e3002bf65b6 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Email-Address/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Email-Address/index.md @@ -1,5 +1,7 @@ --- -versionFrom: 8.0.0 +meta.Title: "Display an email address" +meta.Description: "In this article you can learn how to use the build in email property editor" +versionFrom: 9.0.0 --- # Email Address @@ -12,24 +14,10 @@ Displays an email address. ## Settings -### Enable required checkbox (8.7.0 and below) - -If the property is set to mandatory, Umbraco will display a warning label under the property editor when you publish the page this editor is located on and clean the input. This functionality has been deprecated in `8.8.0` and above. Instead, you select the mandatory checkbox when adding the property editor to a Document Type. - -## Data Type Definition Example - -### 8.8.0 and higher - -![Email Data Type Definition 8.8.0](images/EmailAddress-DataType-v88.png) - ### Mandatory checkbox example ![Mandatory Checkbox Example](images/mandatory-checkbox.png) -### 8.0.0 - 8.7.0 - -![Email Data Type Definition 8.0.0 - 8.7.0](images/EmailAddress-DataType-v8.png) - ## Content Example ![Single email address content example](images/EmailAddress-DataType-Content.png) @@ -61,9 +49,11 @@ If the property is set to mandatory, Umbraco will display a warning label under See the example below to learn how a value can be added or changed programmatically to an Email-address property. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; + @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of your page var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); @@ -71,7 +61,7 @@ See the example below to learn how a value can be added or changed programmatica // Get the page using the GUID you've just defined var content = contentService.GetById(guid); // Set the value of the property with alias 'email' - content.SetValue("email", "stk@umbraco.com"); + content.SetValue("email", "jpk@umbraco.dk"); // Save the change contentService.Save(content); diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Eye-Dropper-Color-Picker/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Eye-Dropper-Color-Picker/index-v8.md similarity index 83% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Eye-Dropper-Color-Picker/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Eye-Dropper-Color-Picker/index-v8.md index 19cca476bae..47f2d5e4c8c 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Eye-Dropper-Color-Picker/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Eye-Dropper-Color-Picker/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.13.0 --- # Eye Dropper Color Picker @@ -49,10 +49,9 @@ The Eye Dropper Color picker allows you to choose a color from the full color sp See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -80,12 +79,11 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'color' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Color).Alias, "#6fa8dc"); + content.SetValue(Home.GetModelPropertyType(x => x.Color).Alias, "#6fa8dc"); // Set the value of the property with alias 'theme' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Theme).Alias, "rgba(111, 168, 220, 0.7)"); + content.SetValue(Home.GetModelPropertyType(x => x.Theme).Alias, "rgba(111, 168, 220, 0.7)"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Eye-Dropper-Color-Picker/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Eye-Dropper-Color-Picker/index.md index 47f2d5e4c8c..19cca476bae 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Eye-Dropper-Color-Picker/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Eye-Dropper-Color-Picker/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.13.0 +versionFrom: 9.0.0 --- # Eye Dropper Color Picker @@ -49,9 +49,10 @@ The Eye Dropper Color picker allows you to choose a color from the full color sp See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -79,11 +80,12 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'color' - content.SetValue(Home.GetModelPropertyType(x => x.Color).Alias, "#6fa8dc"); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Color).Alias, "#6fa8dc"); // Set the value of the property with alias 'theme' - content.SetValue(Home.GetModelPropertyType(x => x.Theme).Alias, "rgba(111, 168, 220, 0.7)"); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Theme).Alias, "rgba(111, 168, 220, 0.7)"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/File-Upload/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/File-Upload/index-v8.md similarity index 74% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/File-Upload/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/File-Upload/index-v8.md index 76e51243fca..0dee07841f7 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/File-Upload/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/File-Upload/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # File upload @@ -28,13 +28,12 @@ Example: `"/media/o01axaqu/guidelines-on-remote-working.pdf"` ### Without Modelsbuilder ```csharp -@using System.IO; @{ if (Model.HasValue("myFile")) { var myFile = Model.Value("myFile"); - @System.IO.Path.GetFileName(myFile) + @Path.GetFileName(myFile) } } @@ -47,7 +46,7 @@ Example: `"/media/o01axaqu/guidelines-on-remote-working.pdf"` @{ if (!string.IsNullOrWhiteSpace(Model.MyFile)) { - @System.IO.Path.GetFileName(Model.MyFile) + @Path.GetFileName(Model.MyFile) } } ``` @@ -57,21 +56,12 @@ Example: `"/media/o01axaqu/guidelines-on-remote-working.pdf"` See the example below to see how a value can be added or changed programmatically. To update a value of this property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md) and the [Media Service](../../../../../Reference/Management/Services/MediaService/index.md). ```csharp - -@using Umbraco.Cms.Core.IO -@using Umbraco.Cms.Core.Serialization -@using Umbraco.Cms.Core.Strings -@inject MediaFileManager _mediaFileManager; -@inject IShortStringHelper _shortStringHelper; -@inject IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; -@inject IContentService Services; -@inject IJsonSerializer _serializer; @{ - // Get access to ContentService - var contentService = Services; + // Get access to ContentService + var contentService = Services.ContentService; // Get access to MediaService - var mediaService = MediaService; + var mediaService = Services.MediaService; // Create a variable for the GUID of the parent where you want to add a child item var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -93,7 +83,7 @@ See the example below to see how a value can be added or changed programmaticall // Create a media file var media = mediaService.CreateMediaWithIdentity("myImage", -1, "File"); - media.SetValue(_mediaFileManager, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, filename, responseStream); + media.SetValue(Services.ContentTypeBaseServices, "umbracoFile", filename, responseStream); // Save the created media mediaService.Save(media); @@ -121,9 +111,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'myFile' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MyFile).Alias, publishedMedia.Url(); + content.SetValue(Home.GetModelPropertyType(x => x.MyFile).Alias, publishedMedia.Url(); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/File-Upload/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/File-Upload/index.md index 0dee07841f7..76e51243fca 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/File-Upload/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/File-Upload/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # File upload @@ -28,12 +28,13 @@ Example: `"/media/o01axaqu/guidelines-on-remote-working.pdf"` ### Without Modelsbuilder ```csharp +@using System.IO; @{ if (Model.HasValue("myFile")) { var myFile = Model.Value("myFile"); - @Path.GetFileName(myFile) + @System.IO.Path.GetFileName(myFile) } } @@ -46,7 +47,7 @@ Example: `"/media/o01axaqu/guidelines-on-remote-working.pdf"` @{ if (!string.IsNullOrWhiteSpace(Model.MyFile)) { - @Path.GetFileName(Model.MyFile) + @System.IO.Path.GetFileName(Model.MyFile) } } ``` @@ -56,12 +57,21 @@ Example: `"/media/o01axaqu/guidelines-on-remote-working.pdf"` See the example below to see how a value can be added or changed programmatically. To update a value of this property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md) and the [Media Service](../../../../../Reference/Management/Services/MediaService/index.md). ```csharp + +@using Umbraco.Cms.Core.IO +@using Umbraco.Cms.Core.Serialization +@using Umbraco.Cms.Core.Strings +@inject MediaFileManager _mediaFileManager; +@inject IShortStringHelper _shortStringHelper; +@inject IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; +@inject IContentService Services; +@inject IJsonSerializer _serializer; @{ - // Get access to ContentService - var contentService = Services.ContentService; + // Get access to ContentService + var contentService = Services; // Get access to MediaService - var mediaService = Services.MediaService; + var mediaService = MediaService; // Create a variable for the GUID of the parent where you want to add a child item var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -83,7 +93,7 @@ See the example below to see how a value can be added or changed programmaticall // Create a media file var media = mediaService.CreateMediaWithIdentity("myImage", -1, "File"); - media.SetValue(Services.ContentTypeBaseServices, "umbracoFile", filename, responseStream); + media.SetValue(_mediaFileManager, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, filename, responseStream); // Save the created media mediaService.Save(media); @@ -111,8 +121,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'myFile' - content.SetValue(Home.GetModelPropertyType(x => x.MyFile).Alias, publishedMedia.Url(); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MyFile).Alias, publishedMedia.Url(); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Add-Value-Programmatically-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Add-Value-Programmatically-v8.md similarity index 88% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Add-Value-Programmatically-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Add-Value-Programmatically-v8.md index 8fef768d166..4f91a836580 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Add-Value-Programmatically-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Add-Value-Programmatically-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Add values programmatically @@ -16,10 +16,9 @@ See the example below to see how a value can be added or changed programmaticall ```csharp @using Newtonsoft.Json -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -89,9 +88,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'body' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Body).Alias, serializedGridValue); + content.SetValue(Home.GetModelPropertyType(x => x.Body).Alias, serializedGridValue); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Add-Value-Programmatically.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Add-Value-Programmatically.md index 4f91a836580..8fef768d166 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Add-Value-Programmatically.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Add-Value-Programmatically.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Add values programmatically @@ -16,9 +16,10 @@ See the example below to see how a value can be added or changed programmaticall ```csharp @using Newtonsoft.Json +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -88,8 +89,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'body' - content.SetValue(Home.GetModelPropertyType(x => x.Body).Alias, serializedGridValue); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Body).Alias, serializedGridValue); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/index.md index eec97335c66..976f1272ea6 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/index.md @@ -1,5 +1,6 @@ --- versionFrom: 7.0.0 +versionTo: 9.0.0 --- # Grid Layout diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Image-Cropper/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Image-Cropper/index.md index e573ac3dfcb..9778c09f7b3 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Image-Cropper/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Image-Cropper/index.md @@ -1,5 +1,6 @@ --- versionFrom: 8.7.0 +needsV9Update: "true" --- # Image Cropper diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Label-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Label-v8.md similarity index 87% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Label-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Label-v8.md index 8ecc140495c..41e63ede8f5 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Label-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Label-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Label @@ -53,10 +53,8 @@ See the example below to see how a value can be added or changed programmaticall ```csharp @{ - @inject IContentService Services; - // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -85,10 +83,8 @@ If Modelsbuilder is enabled you can get the alias of the desired property withou ```csharp @{ - @inject IPublishedSnapshotAccessor _publishedSnapshotAccessor - // Set the value of the property with alias 'pageLabel' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MyLabel).Alias, "A Preset string"); + content.SetValue(Home.GetModelPropertyType(x => x.PageLabel).Alias, "A pre-set string value"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Label.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Label.md index 41e63ede8f5..8ecc140495c 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Label.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Label.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Label @@ -53,8 +53,10 @@ See the example below to see how a value can be added or changed programmaticall ```csharp @{ + @inject IContentService Services; + // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -83,8 +85,10 @@ If Modelsbuilder is enabled you can get the alias of the desired property withou ```csharp @{ + @inject IPublishedSnapshotAccessor _publishedSnapshotAccessor + // Set the value of the property with alias 'pageLabel' - content.SetValue(Home.GetModelPropertyType(x => x.PageLabel).Alias, "A pre-set string value"); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MyLabel).Alias, "A Preset string"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Markdown-Editor/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Markdown-Editor/index-v8.md similarity index 91% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Markdown-Editor/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Markdown-Editor/index-v8.md index 573a94a7f7a..91583ca22fb 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Markdown-Editor/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Markdown-Editor/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Markdown editor @@ -67,10 +67,9 @@ paste | Ctrl + V See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -101,9 +100,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'myMarkdownEditor' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MyMarkdownEditor).Alias, markdownValue); + content.SetValue(Home.GetModelPropertyType(x => x.MyMarkdownEditor).Alias, markdownValue); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Markdown-Editor/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Markdown-Editor/index.md index 91583ca22fb..573a94a7f7a 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Markdown-Editor/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Markdown-Editor/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Markdown editor @@ -67,9 +67,10 @@ paste | Ctrl + V See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -100,8 +101,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'myMarkdownEditor' - content.SetValue(Home.GetModelPropertyType(x => x.MyMarkdownEditor).Alias, markdownValue); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MyMarkdownEditor).Alias, markdownValue); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker-3/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker-3/index-v8.md similarity index 86% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker-3/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker-3/index-v8.md index 3c11816ffc2..3c2121a23bf 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker-3/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker-3/index-v8.md @@ -1,7 +1,5 @@ --- -versionFrom: 9.0.0 -state: partial -updated-links: false +versionFrom: 8.14.0 --- # Media Picker @@ -63,12 +61,11 @@ Global crops are configured on the Image Cropper property of the Image Media Typ ### Multiple enabled without Modelsbuilder ```csharp -@using Umbraco.Cms.Core.Models @{ var typedMultiMediaPicker = Model.Value>("medias"); foreach (var entry in typedMultiMediaPicker) { - + } } ``` @@ -80,7 +77,7 @@ Global crops are configured on the Image Cropper property of the Image Media Typ var typedMultiMediaPicker = Model.Medias; foreach (var entry in typedMultiMediaPicker) { - + } } ``` @@ -88,12 +85,11 @@ Global crops are configured on the Image Cropper property of the Image Media Typ ### Multiple disabled without Modelsbuilder ```csharp -@using Umbraco.Cms.Core.Models @{ var typedMediaPickerSingle = Model.Value("media"); if (typedMediaPickerSingle != null) { - @typedMediaPickerSingle.Value( + @typedMediaPickerSingle.MediaItem.Value( } } ``` @@ -101,12 +97,11 @@ Global crops are configured on the Image Cropper property of the Image Media Typ ### Multiple disabled with Modelsbuilder ```csharp -@using Umbraco.Cms.Core.Models @{ var typedMediaPickerSingle = Model.Media; if (typedMediaPickerSingle is MediaWithCrops mediaEntry) { - + } } ``` @@ -151,7 +146,7 @@ The global crops are configured on the DataType of the `umbracoFile` property on @{ foreach (var entry in Model.Medias) { - + } } ``` @@ -160,23 +155,16 @@ The global crops are configured on the DataType of the `umbracoFile` property on ```csharp @{ - foreach (var entry in Model.Images) + foreach (var entry in Model.Medias) { - + } } ``` - - ### Add values programmatically -:::warning -Adding values programmatically for media picker 3 have not been verified for V9 yet. -The concept and code examples might not work if you are running Umbraco 9.0. -::: - This solution can be applied to both Media Picker 3 and Multi Media Picker 3 ```csharp diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker-3/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker-3/index.md index 3c2121a23bf..3c11816ffc2 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker-3/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker-3/index.md @@ -1,5 +1,7 @@ --- -versionFrom: 8.14.0 +versionFrom: 9.0.0 +state: partial +updated-links: false --- # Media Picker @@ -61,11 +63,12 @@ Global crops are configured on the Image Cropper property of the Image Media Typ ### Multiple enabled without Modelsbuilder ```csharp +@using Umbraco.Cms.Core.Models @{ var typedMultiMediaPicker = Model.Value>("medias"); foreach (var entry in typedMultiMediaPicker) { - + } } ``` @@ -77,7 +80,7 @@ Global crops are configured on the Image Cropper property of the Image Media Typ var typedMultiMediaPicker = Model.Medias; foreach (var entry in typedMultiMediaPicker) { - + } } ``` @@ -85,11 +88,12 @@ Global crops are configured on the Image Cropper property of the Image Media Typ ### Multiple disabled without Modelsbuilder ```csharp +@using Umbraco.Cms.Core.Models @{ var typedMediaPickerSingle = Model.Value("media"); if (typedMediaPickerSingle != null) { - @typedMediaPickerSingle.MediaItem.Value( + @typedMediaPickerSingle.Value( } } ``` @@ -97,11 +101,12 @@ Global crops are configured on the Image Cropper property of the Image Media Typ ### Multiple disabled with Modelsbuilder ```csharp +@using Umbraco.Cms.Core.Models @{ var typedMediaPickerSingle = Model.Media; if (typedMediaPickerSingle is MediaWithCrops mediaEntry) { - + } } ``` @@ -146,7 +151,7 @@ The global crops are configured on the DataType of the `umbracoFile` property on @{ foreach (var entry in Model.Medias) { - + } } ``` @@ -155,16 +160,23 @@ The global crops are configured on the DataType of the `umbracoFile` property on ```csharp @{ - foreach (var entry in Model.Medias) + foreach (var entry in Model.Images) { - + } } ``` + + ### Add values programmatically +:::warning +Adding values programmatically for media picker 3 have not been verified for V9 yet. +The concept and code examples might not work if you are running Umbraco 9.0. +::: + This solution can be applied to both Media Picker 3 and Multi Media Picker 3 ```csharp diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index-v8.md similarity index 80% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index-v8.md index 8120121b906..af759e4eabf 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.1.0 --- # Media Picker @@ -11,7 +11,7 @@ versionFrom: 9.0.0 This property editors returns a single item if the "Pick multiple items" data type setting is disabled or a collection if it is enabled. :::note -As of Umbraco 8.14, this Media Picker has been replaced by [Media Picker 3](Media-Picker-3). This updated property contains more customizable features, and we recommend using this over the Media Picker, which is also marked as the *old* version of the picker. +As of Umbraco 8.14, this Media Picker has been replaced by [Media Picker 3](../Media-Picker-3). This updated property contains more customizable features, and we recommend using this over the Media Picker, which is also marked as the *old* version of the picker. ::: ## Data Type Definition Example @@ -37,7 +37,7 @@ When this setting is enabled, a user who doesn't normally have access to the med var typedMultiMediaPicker = Model.Value>("sliders"); foreach (var item in typedMultiMediaPicker) { - + } } ``` @@ -49,7 +49,7 @@ When this setting is enabled, a user who doesn't normally have access to the med var typedMultiMediaPicker = Model.Sliders; foreach (var item in typedMultiMediaPicker) { - + } } ``` @@ -61,8 +61,8 @@ When this setting is enabled, a user who doesn't normally have access to the med var typedMediaPickerSingle = Model.Value("featuredBanner"); if (typedMediaPickerSingle != null) { -

@typedMediaPickerSingle.Url()

- @typedMediaPickerSingle.Value( +

@typedMediaPickerSingle.Url

+ @typedMediaPickerSingle.Value( } } ``` @@ -74,8 +74,8 @@ When this setting is enabled, a user who doesn't normally have access to the med var typedMediaPickerSingle = Model.FeaturedBanner; if (typedMediaPickerSingle is Image image) { -

@image.())

- +

@image.Url

+ } } ``` @@ -85,7 +85,6 @@ When this setting is enabled, a user who doesn't normally have access to the med See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService var contentService = Services.ContentService; @@ -122,9 +121,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'featuredBanner' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.FeaturedBanner).Alias, true); + content.SetValue(Home.GetModelPropertyType(x => x.FeaturedBanner).Alias, true); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index.md index af759e4eabf..8120121b906 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.1.0 +versionFrom: 9.0.0 --- # Media Picker @@ -11,7 +11,7 @@ versionFrom: 8.1.0 This property editors returns a single item if the "Pick multiple items" data type setting is disabled or a collection if it is enabled. :::note -As of Umbraco 8.14, this Media Picker has been replaced by [Media Picker 3](../Media-Picker-3). This updated property contains more customizable features, and we recommend using this over the Media Picker, which is also marked as the *old* version of the picker. +As of Umbraco 8.14, this Media Picker has been replaced by [Media Picker 3](Media-Picker-3). This updated property contains more customizable features, and we recommend using this over the Media Picker, which is also marked as the *old* version of the picker. ::: ## Data Type Definition Example @@ -37,7 +37,7 @@ When this setting is enabled, a user who doesn't normally have access to the med var typedMultiMediaPicker = Model.Value>("sliders"); foreach (var item in typedMultiMediaPicker) { - + } } ``` @@ -49,7 +49,7 @@ When this setting is enabled, a user who doesn't normally have access to the med var typedMultiMediaPicker = Model.Sliders; foreach (var item in typedMultiMediaPicker) { - + } } ``` @@ -61,8 +61,8 @@ When this setting is enabled, a user who doesn't normally have access to the med var typedMediaPickerSingle = Model.Value("featuredBanner"); if (typedMediaPickerSingle != null) { -

@typedMediaPickerSingle.Url

- @typedMediaPickerSingle.Value( +

@typedMediaPickerSingle.Url()

+ @typedMediaPickerSingle.Value( } } ``` @@ -74,8 +74,8 @@ When this setting is enabled, a user who doesn't normally have access to the med var typedMediaPickerSingle = Model.FeaturedBanner; if (typedMediaPickerSingle is Image image) { -

@image.Url

- +

@image.())

+ } } ``` @@ -85,6 +85,7 @@ When this setting is enabled, a user who doesn't normally have access to the med See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService var contentService = Services.ContentService; @@ -121,8 +122,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'featuredBanner' - content.SetValue(Home.GetModelPropertyType(x => x.FeaturedBanner).Alias, true); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.FeaturedBanner).Alias, true); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Group-Picker/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Group-Picker/index-v8.md similarity index 89% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Group-Picker/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Group-Picker/index-v8.md index 1445de73ae9..f62410a5cd9 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Group-Picker/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Group-Picker/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Member Group Picker @@ -44,10 +44,9 @@ The Member Group Picker opens a panel to pick one or more member groups from the See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); @@ -84,9 +83,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'memberGroup' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MemberGroup).Alias, 1067); + content.SetValue(Home.GetModelPropertyType(x => x.MemberGroup).Alias, 1067); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Group-Picker/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Group-Picker/index.md index f62410a5cd9..1445de73ae9 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Group-Picker/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Group-Picker/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Member Group Picker @@ -44,9 +44,10 @@ The Member Group Picker opens a panel to pick one or more member groups from the See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); @@ -83,8 +84,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'memberGroup' - content.SetValue(Home.GetModelPropertyType(x => x.MemberGroup).Alias, 1067); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MemberGroup).Alias, 1067); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Picker/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Picker/index-v8.md similarity index 80% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Picker/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Picker/index-v8.md index 3a27e2a5bd8..a2726daf2fd 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Picker/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Picker/index-v8.md @@ -49,10 +49,9 @@ The member picker opens a panel to pick a specific member from the member sectio See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -62,9 +61,12 @@ See the example below to see how a value can be added or changed programmaticall // Create a variable for the GUID of the member ID var authorId = Guid.Parse("ed944097281e4492bcdf783355219450"); - - // Set the value of the property with alias 'author'. - content.SetValue("author", authorId); + + // Create a variable for the UDI of the member + var authorUdi = Udi.Create(Umbraco.Core.Constants.UdiEntityType.Member, authorId); + + // Set the value of the property with alias 'author' to the memberUdi. + content.SetValue("author", authorUdi); // Save the change contentService.Save(content); @@ -83,11 +85,10 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ - var udi = Udi.Create(Constants.UdiEntityType.Member, authorId); + var udi = Udi.Create(Umbraco.Core.Constants.UdiEntityType.Member, authorId); // Set the value of the property with alias 'author' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Author).Alias, udi); + content.SetValue(Home.GetModelPropertyType(x => x.Author).Alias, udi); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Picker/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Picker/index.md index a2726daf2fd..828a9deb026 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Picker/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Member-Picker/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Member Picker @@ -49,9 +49,10 @@ The member picker opens a panel to pick a specific member from the member sectio See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -61,12 +62,9 @@ See the example below to see how a value can be added or changed programmaticall // Create a variable for the GUID of the member ID var authorId = Guid.Parse("ed944097281e4492bcdf783355219450"); - - // Create a variable for the UDI of the member - var authorUdi = Udi.Create(Umbraco.Core.Constants.UdiEntityType.Member, authorId); - - // Set the value of the property with alias 'author' to the memberUdi. - content.SetValue("author", authorUdi); + + // Set the value of the property with alias 'author'. + content.SetValue("author", authorId); // Save the change contentService.Save(content); @@ -85,10 +83,11 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ - var udi = Udi.Create(Umbraco.Core.Constants.UdiEntityType.Member, authorId); + var udi = Udi.Create(Constants.UdiEntityType.Member, authorId); // Set the value of the property with alias 'author' - content.SetValue(Home.GetModelPropertyType(x => x.Author).Alias, udi); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Author).Alias, udi); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multi-Url-Picker/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multi-Url-Picker/index-v8.md similarity index 92% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multi-Url-Picker/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multi-Url-Picker/index-v8.md index 380e0288b7f..df54cd03a4c 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multi-Url-Picker/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multi-Url-Picker/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.1.0 --- # Multi Url Picker @@ -57,11 +57,10 @@ See the example below to see how a value can be added or changed programmaticall ```csharp @using Newtonsoft.Json -@using Umbraco.Cms.Core.Models; -@inject IContentService Services; +@using Umbraco.Web.Models @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -136,9 +135,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'footerLinks' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.FooterLinks).Alias, links); + content.SetValue(Home.GetModelPropertyType(x => x.FooterLinks).Alias, links); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multi-Url-Picker/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multi-Url-Picker/index.md index df54cd03a4c..380e0288b7f 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multi-Url-Picker/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multi-Url-Picker/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.1.0 +versionFrom: 9.0.0 --- # Multi Url Picker @@ -57,10 +57,11 @@ See the example below to see how a value can be added or changed programmaticall ```csharp @using Newtonsoft.Json -@using Umbraco.Web.Models +@using Umbraco.Cms.Core.Models; +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -135,8 +136,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'footerLinks' - content.SetValue(Home.GetModelPropertyType(x => x.FooterLinks).Alias, links); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.FooterLinks).Alias, links); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multinode-Treepicker/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multinode-Treepicker/index-v8.md similarity index 90% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multinode-Treepicker/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multinode-Treepicker/index-v8.md index 74741954804..b3ccc5eb580 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multinode-Treepicker/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multinode-Treepicker/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.1.0 --- # Multinode Treepicker @@ -60,6 +60,18 @@ Enter `typeAlias,altTypeAlias` to only allow selecting nodes with those alias'. } ``` +### Without Modelsbuilder (Maximum number of items set to 1) + +```csharp +@{ + var typedMultiNodeTreePicker = Model.Value("featuredArticle"); + if (typedMultiNodeTreePicker != null) + { +

@typedMultiNodeTreePicker.Name

+ } +} +``` + ### With Modelsbuilder ```csharp @@ -77,10 +89,9 @@ Enter `typeAlias,altTypeAlias` to only allow selecting nodes with those alias'. See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -119,9 +130,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'featuredArticles' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor ,x => x.FeaturedArticles).Alias, string.Join(",", udis)); + content.SetValue(Home.GetModelPropertyType(x => x.FeaturedArticles).Alias, string.Join(",", udis)); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multinode-Treepicker/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multinode-Treepicker/index.md index b3ccc5eb580..74741954804 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multinode-Treepicker/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multinode-Treepicker/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.1.0 +versionFrom: 9.0.0 --- # Multinode Treepicker @@ -60,18 +60,6 @@ Enter `typeAlias,altTypeAlias` to only allow selecting nodes with those alias'. } ``` -### Without Modelsbuilder (Maximum number of items set to 1) - -```csharp -@{ - var typedMultiNodeTreePicker = Model.Value("featuredArticle"); - if (typedMultiNodeTreePicker != null) - { -

@typedMultiNodeTreePicker.Name

- } -} -``` - ### With Modelsbuilder ```csharp @@ -89,9 +77,10 @@ Enter `typeAlias,altTypeAlias` to only allow selecting nodes with those alias'. See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -130,8 +119,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'featuredArticles' - content.SetValue(Home.GetModelPropertyType(x => x.FeaturedArticles).Alias, string.Join(",", udis)); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor ,x => x.FeaturedArticles).Alias, string.Join(",", udis)); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multiple-Textbox/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multiple-Textbox/index-v8.md similarity index 88% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multiple-Textbox/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multiple-Textbox/index-v8.md index e3fb282b871..3392825531f 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multiple-Textbox/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multiple-Textbox/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Repeatable textstrings @@ -57,10 +57,9 @@ The Repeatable textstrings property editor enables a content editor to make a li See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = new Guid("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -92,9 +91,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'keyFeatureList' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.KeyFeatureList).Alias, "Awesome" + Environment.NewLine + "Super"); + content.SetValue(Home.GetModelPropertyType(x => x.KeyFeatureList).Alias, "Awesome" + Environment.NewLine + "Super"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multiple-Textbox/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multiple-Textbox/index.md index 3392825531f..e3fb282b871 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multiple-Textbox/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Multiple-Textbox/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Repeatable textstrings @@ -57,9 +57,10 @@ The Repeatable textstrings property editor enables a content editor to make a li See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = new Guid("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -91,8 +92,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'keyFeatureList' - content.SetValue(Home.GetModelPropertyType(x => x.KeyFeatureList).Alias, "Awesome" + Environment.NewLine + "Super"); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.KeyFeatureList).Alias, "Awesome" + Environment.NewLine + "Super"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Nested-Content/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Nested-Content/index-v8.md similarity index 89% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Nested-Content/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Nested-Content/index-v8.md index eb4fef0ad95..2b39bbce410 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Nested-Content/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Nested-Content/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Nested Content @@ -85,11 +85,11 @@ To render the stored value of your **Nested Content** property, a built in value Example: ```csharp -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage +@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ - var items = Model.Value>("nest"); + var items = Model.Value>("myPropertyAlias"); - foreach (var item in items) + foreach(var item in items) { // Render your content, e.g. item.Value("heading") } @@ -101,27 +101,25 @@ Each item is treated as a standard `IPublishedElement` entity, which means you c Example: ```csharp -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage -@using Umbraco.Cms.Core.Models.PublishedContent; -@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; +@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ - var items = Model.Value>("nest"); + var items = Model.Value>("myPropertyAlias"); - foreach (var item in items) + foreach(var item in items) { var description = item.Value("description"); var image = item.Value("image"); -

@item.Value("heading")

+

@item.GetProperty("heading").Value()

if (!string.IsNullOrEmpty(description)) { -

@description

+

@Html.Raw(Html.ReplaceLineBreaksForHtml(description))

} if (image != null) { - + } } } @@ -134,9 +132,7 @@ If your **Nested Content** property editor is configured in single item mode, th Example: ```csharp -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage -@using Umbraco.Cms.Core.Models.PublishedContent; -@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; +@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ var item = Model.Value("myPropertyAlias"); } @@ -154,16 +150,11 @@ The first dictionary item property/parameter we should specify for each Nested C Afterwards, the entire list needs to be serialized to Json via JsonConvert. ```csharp -@using Umbraco.Cms.Core.Models; -@using Umbraco.Cms.Core.Services; -@using Newtonsoft.Json; -@inject IContentService _contentService; - //if the class containing our code inherits SurfaceController, UmbracoApiController, //or UmbracoAuthorizedApiController, we can get ContentService from Services namespace - var contentService = _contentService; + var contentService = Services.ContentService; //here we create a new node, and fill out attendeeList afterwards - IContent request = contentService.Create("new node", guid, "mydoctype", -1); + IContent request = ContentService.Create("new node", guid, "mydoctype", -1); //our list which will contain nested content var attendees = new List>(); //participants is our list of attendees - multiple items, good use case for nested content diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Nested-Content/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Nested-Content/index.md index 2b39bbce410..eb4fef0ad95 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Nested-Content/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Nested-Content/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Nested Content @@ -85,11 +85,11 @@ To render the stored value of your **Nested Content** property, a built in value Example: ```csharp -@inherits Umbraco.Web.Mvc.UmbracoViewPage +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @{ - var items = Model.Value>("myPropertyAlias"); + var items = Model.Value>("nest"); - foreach(var item in items) + foreach (var item in items) { // Render your content, e.g. item.Value("heading") } @@ -101,25 +101,27 @@ Each item is treated as a standard `IPublishedElement` entity, which means you c Example: ```csharp -@inherits Umbraco.Web.Mvc.UmbracoViewPage +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage +@using Umbraco.Cms.Core.Models.PublishedContent; +@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; @{ - var items = Model.Value>("myPropertyAlias"); + var items = Model.Value>("nest"); - foreach(var item in items) + foreach (var item in items) { var description = item.Value("description"); var image = item.Value("image"); -

@item.GetProperty("heading").Value()

+

@item.Value("heading")

if (!string.IsNullOrEmpty(description)) { -

@Html.Raw(Html.ReplaceLineBreaksForHtml(description))

+

@description

} if (image != null) { - + } } } @@ -132,7 +134,9 @@ If your **Nested Content** property editor is configured in single item mode, th Example: ```csharp -@inherits Umbraco.Web.Mvc.UmbracoViewPage +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage +@using Umbraco.Cms.Core.Models.PublishedContent; +@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; @{ var item = Model.Value("myPropertyAlias"); } @@ -150,11 +154,16 @@ The first dictionary item property/parameter we should specify for each Nested C Afterwards, the entire list needs to be serialized to Json via JsonConvert. ```csharp +@using Umbraco.Cms.Core.Models; +@using Umbraco.Cms.Core.Services; +@using Newtonsoft.Json; +@inject IContentService _contentService; + //if the class containing our code inherits SurfaceController, UmbracoApiController, //or UmbracoAuthorizedApiController, we can get ContentService from Services namespace - var contentService = Services.ContentService; + var contentService = _contentService; //here we create a new node, and fill out attendeeList afterwards - IContent request = ContentService.Create("new node", guid, "mydoctype", -1); + IContent request = contentService.Create("new node", guid, "mydoctype", -1); //our list which will contain nested content var attendees = new List>(); //participants is our list of attendees - multiple items, good use case for nested content diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Numeric/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Numeric/index-v8.md similarity index 92% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Numeric/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Numeric/index-v8.md index 00207de096f..12e914299c0 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Numeric/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Numeric/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Numeric @@ -77,10 +77,9 @@ You can also render the output by casting it to a string, which means you will n See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = new Guid("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -109,9 +108,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'students' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Students).Alias, 20); + content.SetValue(Home.GetModelPropertyType(x => x.Students).Alias, 20); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Numeric/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Numeric/index.md index 12e914299c0..00207de096f 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Numeric/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Numeric/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Numeric @@ -77,9 +77,10 @@ You can also render the output by casting it to a string, which means you will n See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = new Guid("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -108,8 +109,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'students' - content.SetValue(Home.GetModelPropertyType(x => x.Students).Alias, 20); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.Students).Alias, 20); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/RadioButton-List/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/RadioButton-List/index-v8.md similarity index 92% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/RadioButton-List/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/RadioButton-List/index-v8.md index 709e44b3e9d..59b52949b04 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/RadioButton-List/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/RadioButton-List/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Radiobutton List @@ -47,10 +47,9 @@ Pretty much like the name indicates this Data type enables editors to choose fro See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); @@ -78,7 +77,6 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'colorTheme' content.SetValue(Home.GetModelPropertyType(x => x.ColorTheme).Alias, "water"); diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/RadioButton-List/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/RadioButton-List/index.md index 59b52949b04..709e44b3e9d 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/RadioButton-List/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/RadioButton-List/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Radiobutton List @@ -47,9 +47,10 @@ Pretty much like the name indicates this Data type enables editors to choose fro See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); @@ -77,6 +78,7 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'colorTheme' content.SetValue(Home.GetModelPropertyType(x => x.ColorTheme).Alias, "water"); diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index-v8.md similarity index 88% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index-v8.md index a0b38831f85..626339c6f55 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Rich Text Editor @@ -54,10 +54,9 @@ Use CSS to define specific editor styles and add them to the RTE. See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -88,9 +87,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'richText' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.RichText).Alias, "Add some text here"); + content.SetValue(Home.GetModelPropertyType(x => x.RichText).Alias, "Add some text here"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index.md index 626339c6f55..a0b38831f85 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Rich Text Editor @@ -54,9 +54,10 @@ Use CSS to define specific editor styles and add them to the RTE. See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -87,8 +88,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'richText' - content.SetValue(Home.GetModelPropertyType(x => x.RichText).Alias, "Add some text here"); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.RichText).Alias, "Add some text here"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Slider/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Slider/index-v8.md similarity index 86% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Slider/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Slider/index-v8.md index ad85b187b18..af0b1302003 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Slider/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Slider/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Slider @@ -65,10 +65,9 @@ See the example below to see how a value can be added or changed programmaticall ### With a range off ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -87,10 +86,9 @@ See the example below to see how a value can be added or changed programmaticall ### With a range on ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -121,12 +119,11 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'singleValueSlider' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.SingleValueSlider).Alias, 10); + content.SetValue(Home.GetModelPropertyType(x => x.SingleValueSlider).Alias, 10); // Set the value of the property with alias 'multiValueSlider' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MultiValueSlider).Alias, new Range {Minimum = 10, Maximum = 12}); + content.SetValue(Home.GetModelPropertyType(x => x.MultiValueSlider).Alias, new Range {Minimum = 10, Maximum = 12}); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Slider/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Slider/index.md index af0b1302003..ad85b187b18 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Slider/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Slider/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Slider @@ -65,9 +65,10 @@ See the example below to see how a value can be added or changed programmaticall ### With a range off ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -86,9 +87,10 @@ See the example below to see how a value can be added or changed programmaticall ### With a range on ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services.; // Create a variable for the GUID of the page you want to update var guid = Guid.Parse("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -119,11 +121,12 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'singleValueSlider' - content.SetValue(Home.GetModelPropertyType(x => x.SingleValueSlider).Alias, 10); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.SingleValueSlider).Alias, 10); // Set the value of the property with alias 'multiValueSlider' - content.SetValue(Home.GetModelPropertyType(x => x.MultiValueSlider).Alias, new Range {Minimum = 10, Maximum = 12}); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.MultiValueSlider).Alias, new Range {Minimum = 10, Maximum = 12}); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Tags/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Tags/index.md index 001327f4a99..f403d6d34f4 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Tags/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Tags/index.md @@ -1,5 +1,6 @@ --- versionFrom: 8.0.0 +needsV9Update: "true" --- # Tags diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/index-v8.md similarity index 95% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/index-v8.md index f142a99bed4..90ae46dd271 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Textarea @@ -60,11 +60,9 @@ Textarea is an HTML textarea control for multiple lines of text. It can be confi See the example below to learn how a value can be added or changed programmatically to a Textarea property. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; - @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of your page var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/index.md index 90ae46dd271..f142a99bed4 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Textarea @@ -60,9 +60,11 @@ Textarea is an HTML textarea control for multiple lines of text. It can be confi See the example below to learn how a value can be added or changed programmatically to a Textarea property. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; + @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of your page var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textbox/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textbox/index-v8.md similarity index 89% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textbox/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textbox/index-v8.md index fa32310b26b..e719e921804 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textbox/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textbox/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Textbox @@ -56,10 +56,9 @@ Textbox is an HTML input control for text. It can be configured to have a fixed See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = new Guid("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -87,10 +86,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'pageTitle' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.PageTitle).Alias, "Umbraco Demo"); + content.SetValue(Home.GetModelPropertyType(x => x.PageTitle).Alias, "Umbraco Demo"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textbox/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textbox/index.md index e719e921804..fa32310b26b 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textbox/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/Textbox/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Textbox @@ -56,9 +56,10 @@ Textbox is an HTML input control for text. It can be configured to have a fixed See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = new Guid("32e60db4-1283-4caa-9645-f2153f9888ef"); @@ -86,9 +87,10 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'pageTitle' - content.SetValue(Home.GetModelPropertyType(x => x.PageTitle).Alias, "Umbraco Demo"); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.PageTitle).Alias, "Umbraco Demo"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/True-False/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/True-False/index-v8.md similarity index 88% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/True-False/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/True-False/index-v8.md index aa94d668012..75dc1b63b63 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/True-False/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/True-False/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Toggle @@ -49,10 +49,9 @@ It is also possible to define a label, that will be displayed next to the checkb See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); @@ -81,10 +80,7 @@ If Modelsbuilder is enabled you can get the alias of the desired property withou ```csharp @{ - @inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; - // Set the value of the property with alias 'myCheckBox' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor,x => x.MyCheckBox).Alias, true); - + content.SetValue(Home.GetModelPropertyType(x => x.MyCheckBox).Alias, true); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/True-False/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/True-False/index.md index 75dc1b63b63..aa94d668012 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/True-False/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/True-False/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Toggle @@ -49,9 +49,10 @@ It is also possible to define a label, that will be displayed next to the checkb See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); @@ -80,7 +81,10 @@ If Modelsbuilder is enabled you can get the alias of the desired property withou ```csharp @{ + @inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; + // Set the value of the property with alias 'myCheckBox' - content.SetValue(Home.GetModelPropertyType(x => x.MyCheckBox).Alias, true); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor,x => x.MyCheckBox).Alias, true); + } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/User-Picker/index-v9.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/User-Picker/index-v8.md similarity index 89% rename from Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/User-Picker/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/User-Picker/index-v8.md index 9d4e4861322..c2b04770864 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/User-Picker/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/User-Picker/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # User Picker @@ -58,10 +58,9 @@ Please note that getting the Value of the property will return the user ID - pro See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp -@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services; + var contentService = Services.ContentService; // Create a variable for the GUID of the page you want to update var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); @@ -89,9 +88,8 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp -@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'userPicker' - content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.UserPicker).Alias, "Umbraco Demo"); + content.SetValue(Home.GetModelPropertyType(x => x.UserPicker).Alias, "Umbraco Demo"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/User-Picker/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/User-Picker/index.md index c2b04770864..9d4e4861322 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/User-Picker/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/User-Picker/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # User Picker @@ -58,9 +58,10 @@ Please note that getting the Value of the property will return the user ID - pro See the example below to see how a value can be added or changed programmatically. To update a value of a property editor you need the [Content Service](../../../../../Reference/Management/Services/ContentService/index.md). ```csharp +@inject IContentService Services; @{ // Get access to ContentService - var contentService = Services.ContentService; + var contentService = Services; // Create a variable for the GUID of the page you want to update var guid = new Guid("796a8d5c-b7bb-46d9-bc57-ab834d0d1248"); @@ -88,8 +89,9 @@ Although the use of a GUID is preferable, you can also use the numeric ID to get If Modelsbuilder is enabled you can get the alias of the desired property without using a magic string: ```csharp +@inject IPublishedSnapshotAccessor _publishedSnapshotAccessor; @{ // Set the value of the property with alias 'userPicker' - content.SetValue(Home.GetModelPropertyType(x => x.UserPicker).Alias, "Umbraco Demo"); + content.SetValue(Home.GetModelPropertyType(_publishedSnapshotAccessor, x => x.UserPicker).Alias, "Umbraco Demo"); } ``` diff --git a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/index.md b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/index.md index 36d79d08f06..b0d81576715 100644 --- a/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/index.md +++ b/Fundamentals/Backoffice/Property-Editors/Built-in-Property-Editors/index.md @@ -1,5 +1,6 @@ --- versionFrom: 8.0.0 +versionTo: 9.0.0 --- # Built-in Umbraco Property Editors diff --git a/Fundamentals/Backoffice/Property-Editors/index-v9.md b/Fundamentals/Backoffice/Property-Editors/index-v8.md similarity index 90% rename from Fundamentals/Backoffice/Property-Editors/index-v9.md rename to Fundamentals/Backoffice/Property-Editors/index-v8.md index 71b0a1f6520..6dee2bb2038 100644 --- a/Fundamentals/Backoffice/Property-Editors/index-v9.md +++ b/Fundamentals/Backoffice/Property-Editors/index-v8.md @@ -2,7 +2,6 @@ meta.Title: "Property editors" meta.Description: "A Property editor is the editor that a Data Type references, and it's defined in a JSON manifest file and an associated JavaScript file." versionFrom: 8.0.0 -versionTo: 9.0.0 --- # Property Editors @@ -11,7 +10,7 @@ A Property Editor is the editor that a Data Type references. A Data Type is defi When creating a Data Type, you specify the property editor for the Data Type to use by selecting from the "Property editor" list (as shown below). -![Data Type Definition](images/Media-picker-dataType-v9.png) +![Data Type Definition](Built-in-Property-Editors/Media-Picker/images/Media-Picker-DataType.jpg) ## [Built-in Property Editors in Umbraco](Built-in-Property-Editors) diff --git a/Fundamentals/Backoffice/Property-Editors/index.md b/Fundamentals/Backoffice/Property-Editors/index.md index 6dee2bb2038..a2d86d1d357 100644 --- a/Fundamentals/Backoffice/Property-Editors/index.md +++ b/Fundamentals/Backoffice/Property-Editors/index.md @@ -1,7 +1,7 @@ --- meta.Title: "Property editors" meta.Description: "A Property editor is the editor that a Data Type references, and it's defined in a JSON manifest file and an associated JavaScript file." -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Property Editors @@ -10,7 +10,7 @@ A Property Editor is the editor that a Data Type references. A Data Type is defi When creating a Data Type, you specify the property editor for the Data Type to use by selecting from the "Property editor" list (as shown below). -![Data Type Definition](Built-in-Property-Editors/Media-Picker/images/Media-Picker-DataType.jpg) +![Data Type Definition](images/Media-picker-dataType-v9.png) ## [Built-in Property Editors in Umbraco](Built-in-Property-Editors) diff --git a/Fundamentals/Backoffice/index.md b/Fundamentals/Backoffice/index.md index e0fad044f05..ae4236c389b 100644 --- a/Fundamentals/Backoffice/index.md +++ b/Fundamentals/Backoffice/index.md @@ -2,6 +2,7 @@ meta.Title: "The Umbraco Backoffice" meta.Description: "In this article you can learn more about the common terms and concepts that are used throughout the Umbraco Backoffice." versionFrom: 8.0.0 +versionTo: 9.0.0 --- # Backoffice overview diff --git a/Fundamentals/Code/Creating-Forms/index-v9.md b/Fundamentals/Code/Creating-Forms/index-v8.md similarity index 65% rename from Fundamentals/Code/Creating-Forms/index-v9.md rename to Fundamentals/Code/Creating-Forms/index-v8.md index 8ba28fb4ec7..87d3a791755 100644 --- a/Fundamentals/Code/Creating-Forms/index-v9.md +++ b/Fundamentals/Code/Creating-Forms/index-v8.md @@ -1,28 +1,30 @@ --- meta.Title: "Creating Forms" meta.Description: "Information on creating forms in Umbraco" -versionFrom: 9.0.0 -state: complete -verified-against: beta-3 -update-links: false +versionFrom: 8.0.0 --- # Creating forms -Creating forms requires that you know your way around .NET Core MVC. So if you are familiar with adding view models, views and controllers you are ready to make your first form. +Creating forms requires that you know your way around .NET MVC. So if you are familiar with adding view models, views and controllers you are ready to make your first form. :::note You can also use [Umbraco forms](https://umbraco.com/products/umbraco-forms/). It lets you and/or your editors create and handle forms in the backoffice. This includes setting up validation, redirecting and storing and sending form data. Great UI, extendable and supported by Umbraco HQ. ::: -In this example we'll create a basic contact form containing a name, email and message field. +In this example we'll create a basic contact form contain name, email and message field. ### Creating the view model First, we're going to create the model for the contact form by adding a new class to the `/Models` folder. Let's call it `ContactFormViewModel.cs` ```csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + namespace MyFirstForm.Models { public class ContactFormViewModel @@ -44,64 +46,45 @@ Name your view "ContactForm". The view can be built with standard MVC helpers: ```csharp -@using MyFirstForm.Controllers @model MyFirstForm.Models.ContactFormViewModel -@using (Html.BeginUmbracoForm(nameof(ContactFormController.Submit))) +@using(Html.BeginUmbracoForm("Submit", "ContactForm")) { -
-

Name:

- @Html.TextBoxFor(m => m.Name) +
+ @Html.TextBoxFor(m=>m.Name)
-

Email:

- @Html.TextBoxFor(m => m.Email) + @Html.TextBoxFor(m=>m.Email)
-

Message:

- @Html.TextAreaFor(m => m.Message) + @Html.TextAreaFor(m=>m.Message)
-
} ``` ### Adding the controller -Finally, we're going to add the controller. Create a new empty class in the `/Controllers` folder, name it `ContactController` and make it inherit from `SurfaceController`. Inheriting from `SurfaceController` requires that you call its base constructor, most IDE's can do this automatically for you. +Finally, we're going to add the controller. Add a controller to the `/Controllers` folder, name it `ContactController` and make sure to use an __empty MVC controller__ as the template. ```csharp -using Microsoft.AspNetCore.Mvc; using MyFirstForm.Models; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Web.Website.Controllers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using Umbraco.Web.Mvc; namespace MyFirstForm.Controllers { public class ContactFormController : SurfaceController { - public ContactFormController( - IUmbracoContextAccessor umbracoContextAccessor, - IUmbracoDatabaseFactory databaseFactory, - ServiceContext services, - AppCaches appCaches, - IProfilingLogger profilingLogger, - IPublishedUrlProvider publishedUrlProvider) - : base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger, publishedUrlProvider) - {} - [HttpPost] - public IActionResult Submit(ContactFormViewModel model) + public ActionResult Submit(ContactFormViewModel model) { if (!ModelState.IsValid) - { return CurrentUmbracoPage(); - } - + // Work with form data here return RedirectToCurrentUmbracoPage(); @@ -110,6 +93,8 @@ namespace MyFirstForm.Controllers } ``` +You should note that the controller inherits "SurfaceController" and not from "Controller" as initially added to the file by Visual Studio. + If the model state is invalid, `CurrentUmbracoPage()` will send the user back to the form. If valid, you can work with the form data, e.g. sending an email to site admin and then `RedirectToCurrentUmbracoPage();`. ## Adding the form to a template diff --git a/Fundamentals/Code/Creating-Forms/index.md b/Fundamentals/Code/Creating-Forms/index.md index 87d3a791755..8ba28fb4ec7 100644 --- a/Fundamentals/Code/Creating-Forms/index.md +++ b/Fundamentals/Code/Creating-Forms/index.md @@ -1,30 +1,28 @@ --- meta.Title: "Creating Forms" meta.Description: "Information on creating forms in Umbraco" -versionFrom: 8.0.0 +versionFrom: 9.0.0 +state: complete +verified-against: beta-3 +update-links: false --- # Creating forms -Creating forms requires that you know your way around .NET MVC. So if you are familiar with adding view models, views and controllers you are ready to make your first form. +Creating forms requires that you know your way around .NET Core MVC. So if you are familiar with adding view models, views and controllers you are ready to make your first form. :::note You can also use [Umbraco forms](https://umbraco.com/products/umbraco-forms/). It lets you and/or your editors create and handle forms in the backoffice. This includes setting up validation, redirecting and storing and sending form data. Great UI, extendable and supported by Umbraco HQ. ::: -In this example we'll create a basic contact form contain name, email and message field. +In this example we'll create a basic contact form containing a name, email and message field. ### Creating the view model First, we're going to create the model for the contact form by adding a new class to the `/Models` folder. Let's call it `ContactFormViewModel.cs` ```csharp -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - namespace MyFirstForm.Models { public class ContactFormViewModel @@ -46,45 +44,64 @@ Name your view "ContactForm". The view can be built with standard MVC helpers: ```csharp +@using MyFirstForm.Controllers @model MyFirstForm.Models.ContactFormViewModel -@using(Html.BeginUmbracoForm("Submit", "ContactForm")) +@using (Html.BeginUmbracoForm(nameof(ContactFormController.Submit))) { -
- @Html.TextBoxFor(m=>m.Name) +
+

Name:

+ @Html.TextBoxFor(m => m.Name)
- @Html.TextBoxFor(m=>m.Email) +

Email:

+ @Html.TextBoxFor(m => m.Email)
- @Html.TextAreaFor(m=>m.Message) +

Message:

+ @Html.TextAreaFor(m => m.Message)
+
} ``` ### Adding the controller -Finally, we're going to add the controller. Add a controller to the `/Controllers` folder, name it `ContactController` and make sure to use an __empty MVC controller__ as the template. +Finally, we're going to add the controller. Create a new empty class in the `/Controllers` folder, name it `ContactController` and make it inherit from `SurfaceController`. Inheriting from `SurfaceController` requires that you call its base constructor, most IDE's can do this automatically for you. ```csharp +using Microsoft.AspNetCore.Mvc; using MyFirstForm.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using Umbraco.Web.Mvc; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Web.Website.Controllers; namespace MyFirstForm.Controllers { public class ContactFormController : SurfaceController { + public ContactFormController( + IUmbracoContextAccessor umbracoContextAccessor, + IUmbracoDatabaseFactory databaseFactory, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger profilingLogger, + IPublishedUrlProvider publishedUrlProvider) + : base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger, publishedUrlProvider) + {} + [HttpPost] - public ActionResult Submit(ContactFormViewModel model) + public IActionResult Submit(ContactFormViewModel model) { if (!ModelState.IsValid) + { return CurrentUmbracoPage(); - + } + // Work with form data here return RedirectToCurrentUmbracoPage(); @@ -93,8 +110,6 @@ namespace MyFirstForm.Controllers } ``` -You should note that the controller inherits "SurfaceController" and not from "Controller" as initially added to the file by Visual Studio. - If the model state is invalid, `CurrentUmbracoPage()` will send the user back to the form. If valid, you can work with the form data, e.g. sending an email to site admin and then `RedirectToCurrentUmbracoPage();`. ## Adding the form to a template diff --git a/Fundamentals/Code/Debugging/Logging/index-v8.md b/Fundamentals/Code/Debugging/Logging/index-v8.md new file mode 100644 index 00000000000..40e22ffbaa2 --- /dev/null +++ b/Fundamentals/Code/Debugging/Logging/index-v8.md @@ -0,0 +1,234 @@ +--- +keywords: logging serilog messagetemplates logs v8 version8 +versionFrom: 8.6.2 +--- + +# Logging + +In Umbraco v8.0+ we have changed the underlying logging framework from [Log4Net](https://logging.apache.org/log4net/) to [Serilog](https://serilog.net/). + +Out of the box for v8.0+ we will write a JSON log file that contains a more rich logfile, that allows tools to perform searches & correlation on log patterns a lot easier. + +The default location of this file is written to `App_Data/Logs` and contains the Machine name, along with the date too: + +* `/App_Data/Logs/UmbracoTraceLog.DELLBOOK.20181108.json` + +## Structured logging + +Serilog is a logging framework that allows us to do structured logging or write log messages using the message template format. This allows us to have a more detailed log message, rather than the traditional text message in a long txt file. + +```xml +2018-11-12 08:34:50,419 [P27004/D2/T1] INFO Umbraco.Core.Runtime.CoreRuntime - Booted. (4586ms) [Timing 9e76e5f] +``` + +Here is an example of the same log message represented as JSON, you can see here we have much more information that would allow us to search & filter logs based on these properties with an appropriate logging system. + +```json +{ + "@t": "2018-11-12T08:34:50.4190399Z", + "@mt": "{EndMessage} ({Duration}ms) [Timing {TimingId}]", + "EndMessage": "Booted.", + "Duration": 4586, + "TimingId": "9e76e5f", + "SourceContext": "Umbraco.Core.Runtime.CoreRuntime", + "ProcessId": 27004, + "ProcessName": "iisexpress", + "ThreadId": 1, + "AppDomainId": 2, + "AppDomainAppId": "LMW3SVC2ROOT", + "MachineName": "DELLBOOK", + "Log4NetLevel": "INFO ", + "HttpRequestNumber": 1, + "HttpRequestId": "557f45ba-0888-4216-8723-e226d795a2f7" +} +``` + +To learn more about structured logging and message templates you can read more about it over on the [https://messagetemplates.org](https://messagetemplates.org) website or alternatively watch this video from the Serilog creator - [https://www.youtube.com/watch?v=OhmNp8UPEEg](https://www.youtube.com/watch?v=OhmNp8UPEEg) + +## Writing to the log + +Umbraco writes log messages, but you are also able to use the Umbraco logger to write the log file as needed, so you can get further insights and details about your implementation. + +Here is an example of using the logger to write an Information message to the log which will contain one property of **Name** which will output the name variable that is passed into the method + +```csharp +using Umbraco.Web.WebApi; +using Umbraco.Core.Logging; + +namespace MyNamespace +{ + public class MyApiController : UmbracoApiController + { + public string GetSayHello(string name) + { + Logger.Info("We are saying hello to {Name}", name); + return $"Hello {name}"; + } + } +} +``` + +The incorrect way to log the message would be use string interpolation or string concatenation such as + +```csharp +//GOOD - Do use :) +Logger.Info("We are saying hello to {Name}", name); + +//BAD - Do not use :( +Logger.Info($"We are saying hello to {name}"); + +//BAD - Do not use :( +Logger.Info("We are saying hello to " + name); +``` + +The bad examples above will write to the log file, but we will not get a separate property logged with the message. This means we can't find them by searching for log messages that use the message template `We are saying hello to {Name}` + +If you are writing classes that inherit from one of these special Umbraco base classes: + +* RenderMvcController +* SurfaceController +* UmbracoApiController +* UmbracoAuthorizedApiController + +Then you can access the logging functionality via a special 'Logger' property included in those base classes and use the friendlier syntax of `Logger.Info` to pass the type, if you add a reference to `Umbraco.Core.Logging` as a Using statement. + +Outside of these places, eg a ContentFinder or your own custom code, you can get a reference to the logger via Dependency Injection. While using Dependency Injection is the recommended way, it is possible to use Current.Logger instead, if DI is not an option. + +```csharp +using Umbraco.Core.Logging; +using Umbraco.Web.Routing; + +namespace MyNamespace +{ + public class MyContentFinder : IContentFinder + { + private readonly ILogger _logger; + + public MyContentFinder(ILogger logger) + { + _logger = logger; + } + public bool TryFindContent(PublishedRequest frequest) + { + _logger.Info("Trying to find content for url {RequestUrl}", frequest.Uri); + + //Do Content Finder Logic... + } + } +} +``` + +You will need to register your ContentFinder [using a Composer](../../../../Implementation/Composing/index.md) + +## Log Levels + +Serilog uses levels as the primary means for assigning importance to log events. The levels in increasing order of importance are: + +1. **Verbose** - tracing information and debugging minutiae; generally only switched on in unusual situations +1. **Debug** - internal control flow and diagnostic state dumps to facilitate pinpointing of recognised problems +1. **Information** - events of interest or that have relevance to outside observers; the default enabled minimum logging level +1. **Warning** - indicators of possible issues or service/functionality degradation +1. **Error** - indicating a failure within the application or connected system +1. **Fatal** - critical errors causing complete failure of the application + +The default log levels we ship with in Umbraco v8.0+ are: + +* .txt file **Debug** +* .json file **Verbose** + +## Configuration + +Serilog can be configured and extended by using the two XML configuration files on disk. + +* `/config/serilog.config` is used to modify the main Umbraco logging pipeline +* `/config/serilog.user.config` which is a sublogger and allows you to make modifications without affecting the main Umbraco logger + +Info on the Serilog config [here](../../../../Reference/Config/Serilog/index.md). + +## Advanced + +### Full C# control over Serilog configuration + +If you like using Serilog but prefer to use C# to configure the logging pipeline then you can do so with the following example. This sets the minimum logging level from a web.config AppSetting, allowing you to set different minimum logging levels in different environments using web config transforms. + +```csharp +using System; +using System.Configuration; +using Umbraco.Web; +using Umbraco.Core; +using Umbraco.Web.Runtime; +using Umbraco.Core.Logging.Serilog; + +using Serilog; +using Serilog.Core; +using Serilog.Events; + +namespace MyNamespace +{ + public class FineTuneLoggingApplication : UmbracoApplication + { + protected override IRuntime GetRuntime() + { + var logLevelSetting = ConfigurationManager.AppSettings["YourMinimumLoggingLevel"]; //Warning, Debug, Information, etc + + const bool ignoreCase = true; //this is to clarify the function of the boolean second parameter in the TryParse + if (!Enum.TryParse(logLevelSetting, ignoreCase, out LogEventLevel minimumLevel)) + { + minimumLevel = LogEventLevel.Information;//set to this level if the config setting is missing or doesn't match a valid enumeration + } + + var levelSwitch = new LoggingLevelSwitch { MinimumLevel = minimumLevel }; + + var loggerConfig = new LoggerConfiguration() + .MinimalConfiguration() + .ReadFromConfigFile() + .ReadFromUserConfigFile() + .MinimumLevel.ControlledBy(levelSwitch); + + var logger = new SerilogLogger(loggerConfig); + + var runtime = new WebRuntime(this, logger, GetMainDom(logger)); + + return runtime; + } + } +} +``` + +You will then need to update the `global.asax` file on disk to use our FineTuneLogging class like so + +```xml +<%@ Application Inherits="MyNamespace.FineTuneLoggingApplication" Language="C#" %> +``` + +## The logviewer dashboard + +Learn more about the [logviewer dashboard](../../../Backoffice/LogViewer/) in the backoffice and how it can be extended. + +## The logviewer desktop app + +This is a tool for viewing & querying JSON log files from disk in the same way as the built in log viewer dashboard. + +English badge + +## Serilog project/references shipped + +Umbraco v8.0+ ships with the following Serilog projects, where you can find further information & details with the GitHub readme files as needed. + +* [Serilog](https://github.com/serilog/serilog) +* [Serilog.Enrichers.Process](https://github.com/serilog/serilog-enrichers-process) +* [Serilog.Enrichers.Thread](https://github.com/serilog/serilog-enrichers-thread) +* [Serilog.Filters.Expressions](https://github.com/serilog/serilog-filters-expressions) +* [Serilog.Formatting.Compact](https://github.com/serilog/serilog-formatting-compact) +* [Serilog.Settings.AppSettings](https://github.com/serilog/Serilog-Settings-AppSettings) +* [Serilog.Sinks.File](https://github.com/serilog/serilog-sinks-file) + +## Further Resources + +If you are interested in learning more then the following resources will beneficial: + +* [Serilog](https://serilog.net/) +* [Serilog Community Gitter Chatroom](https://gitter.im/serilog/serilog) +* [Nicholas Blumhardt Blog, creator of Serilog](https://nblumhardt.com/) +* [Serilog Pluralsight Course](https://www.pluralsight.com/courses/modern-structured-logging-serilog-seq) +* [Seq](https://getseq.net/) This is FREE for a single machine such as your own local development computer diff --git a/Fundamentals/Code/Debugging/Logging/index-v9.md b/Fundamentals/Code/Debugging/Logging/index-v9.md deleted file mode 100644 index 4ed1c192659..00000000000 --- a/Fundamentals/Code/Debugging/Logging/index-v9.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -keywords: logging serilog messagetemplates logs v9 version9 -versionFrom: 9.0.0 ---- - -# Logging - -In Umbraco we use the underlying logging framework of [Serilog](https://serilog.net/). - -Out of the box we write a JSON log file that contains a more rich logfile, that allows tools to perform searches & correlation on log patterns a lot easier. - -The default location of this file is written to `umbraco/Logs` and contains the Machine name, along with the date too: - -* `umbraco/Logs/UmbracoTraceLog.DELLBOOK.20210809.json` - -## Structured logging - -Serilog is a logging framework that allows us to do structured logging or write log messages using the message template format. This allows us to have a more detailed log message, rather than the traditional text message in a long txt file. - -``` -2021-08-10 09:33:23,677 [P25776/D1/T22] INFO Umbraco.Cms.Core.Services.Implement.ContentService - Document Home (id=1062) has been published. -``` - -Here is an example of the same log message represented as JSON, you can see here we have much more information that would allow us to search & filter logs based on these properties with an appropriate logging system. - -```json -{ - "@t":"2021-08-10T08:33:23.6778640Z", - "@mt":"Document {ContentName} (id={ContentId}) has been published.", - "ContentName":"Home", - "ContentId":1062, - "SourceContext":"Umbraco.Cms.Core.Services.Implement.ContentService", - "ActionId":"7726d745-d502-4b2d-b55e-97731308041b", - "ActionName":"Umbraco.Cms.Web.BackOffice.Controllers.ContentController.PostSave (Umbraco.Web.BackOffice)", - "RequestId":"8000000c-0012-fb00-b63f-84710c7967bb", - "RequestPath":"/umbraco/backoffice/umbracoapi/content/PostSave", - "ProcessId":25776, - "ProcessName":"iisexpress", - "ThreadId":22, - "AppDomainId":1, - "AppDomainAppId":"2f4961977e5c252fa708f7d83915c269b53a620c", - "MachineName":"DELLBOOK", - "Log4NetLevel":"INFO ", - "HttpRequestId":"318b6dd4-b127-4da3-8339-37701f4d1416", - "HttpRequestNumber":4, - "HttpSessionId":"0cea7395-ba29-e6c6-93ee-7c08a2fd7219" -} -``` - -To learn more about structured logging and message templates you can read more about it over on the [https://messagetemplates.org](https://messagetemplates.org) website or alternatively watch this video from the Serilog creator - [https://www.youtube.com/watch?v=OhmNp8UPEEg](https://www.youtube.com/watch?v=OhmNp8UPEEg) - -## Writing to the log - -Umbraco writes log messages, but you are also able to use the Umbraco logger to write the log file as needed, so you can get further insights and details about your implementation. - -Here is an example of using the logger to write an Information message to the log which will contain one property of **Name** which will output the name variable that is passed into the method - -```csharp -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Web.Common.Controllers; - -namespace Umbraco.Cms.Web.UI.NetCore -{ - public class MyApiController : UmbracoApiController - { - private readonly ILogger _logger; - - public MyApiController(ILogger logger) - { - _logger = logger; - } - - /// /umbraco/api/MyApi/SayHello?name=John - [HttpGet] - public string SayHello(string name) - { - _logger.LogInformation("We are saying hello to {Name}", name); - return $"Hello {name}"; - } - } -} -``` - -The incorrect way to log the message would be use string interpolation or string concatenation such as - -```csharp -//GOOD - Do use :) -_logger.LogInformation("We are saying hello to {Name}", name); - -//BAD - Do not use :( -_logger.LogInformation($"We are saying hello to {name}"); - -//BAD - Do not use :( -_logger.LogInformation("We are saying hello to " + name); -``` - -The bad examples above will write to the log file, but we will not get a separate property logged with the message. This means we can't find them by searching for log messages that use the message template `We are saying hello to {Name}` - -## Log Levels - -Serilog uses levels as the primary means for assigning importance to log events. The levels in increasing order of importance are: - -1. **Verbose** - tracing information and debugging minutiae; generally only switched on in unusual situations -1. **Debug** - internal control flow and diagnostic state dumps to facilitate pinpointing of recognised problems -1. **Information** - events of interest or that have relevance to outside observers; the default enabled minimum logging level -1. **Warning** - indicators of possible issues or service/functionality degradation -1. **Error** - indicating a failure within the application or connected system -1. **Fatal** - critical errors causing complete failure of the application - - -## Configuration - -Serilog can be configured and extended by using the .NETCore configuration such as the AppSetting.json files or environment variables. -Info on the Serilog config [here](../../../../Reference/V9-Config/Serilog/index.md). - -## The logviewer dashboard - -Learn more about the [logviewer dashboard](../../../Backoffice/LogViewer/) in the backoffice and how it can be extended. - -## The logviewer desktop app - -This is a tool for viewing & querying JSON log files from disk in the same way as the built in log viewer dashboard. - -English badge - -## Serilog project/references shipped - -Umbraco ships with the following Serilog projects, where you can find further information & details with the GitHub readme files as needed. - -* [Serilog](https://github.com/serilog/serilog) -* [Serilog.AspNetCore](https://github.com/serilog/serilog-aspnetcore) -* [Serilog.Enrichers.Process](https://github.com/serilog/serilog-enrichers-process) -* [Serilog.Enrichers.Thread](https://github.com/serilog/serilog-enrichers-thread) -* [Serilog.Filters.Expressions](https://github.com/serilog/serilog-filters-expressions) -* [Serilog.Formatting.Compact](https://github.com/serilog/serilog-formatting-compact) -* [Serilog.Formatting.Compact.Reader](https://github.com/serilog/serilog-formatting-compact-reader) -* [Serilog.Settings.Configuration](https://github.com/serilog/serilog-settings-configuration) -* [Serilog.Sinks.Console](https://github.com/serilog/serilog-sinks-console) -* [Serilog.Sinks.File](https://github.com/serilog/serilog-sinks-file) - -## Further Resources - -If you are interested in learning more then the following resources will beneficial: - -* [Serilog](https://serilog.net/) -* [Serilog Community Gitter Chatroom](https://gitter.im/serilog/serilog) -* [Nicholas Blumhardt Blog, creator of Serilog](https://nblumhardt.com/) -* [Serilog Pluralsight Course](https://www.pluralsight.com/courses/modern-structured-logging-serilog-seq) -* [Seq](https://getseq.net/) This is FREE for a single machine such as your own local development computer diff --git a/Fundamentals/Code/Debugging/Logging/index.md b/Fundamentals/Code/Debugging/Logging/index.md index 40e22ffbaa2..4ed1c192659 100644 --- a/Fundamentals/Code/Debugging/Logging/index.md +++ b/Fundamentals/Code/Debugging/Logging/index.md @@ -1,45 +1,49 @@ --- -keywords: logging serilog messagetemplates logs v8 version8 -versionFrom: 8.6.2 +keywords: logging serilog messagetemplates logs v9 version9 +versionFrom: 9.0.0 --- # Logging -In Umbraco v8.0+ we have changed the underlying logging framework from [Log4Net](https://logging.apache.org/log4net/) to [Serilog](https://serilog.net/). +In Umbraco we use the underlying logging framework of [Serilog](https://serilog.net/). -Out of the box for v8.0+ we will write a JSON log file that contains a more rich logfile, that allows tools to perform searches & correlation on log patterns a lot easier. +Out of the box we write a JSON log file that contains a more rich logfile, that allows tools to perform searches & correlation on log patterns a lot easier. -The default location of this file is written to `App_Data/Logs` and contains the Machine name, along with the date too: +The default location of this file is written to `umbraco/Logs` and contains the Machine name, along with the date too: -* `/App_Data/Logs/UmbracoTraceLog.DELLBOOK.20181108.json` +* `umbraco/Logs/UmbracoTraceLog.DELLBOOK.20210809.json` ## Structured logging Serilog is a logging framework that allows us to do structured logging or write log messages using the message template format. This allows us to have a more detailed log message, rather than the traditional text message in a long txt file. -```xml -2018-11-12 08:34:50,419 [P27004/D2/T1] INFO Umbraco.Core.Runtime.CoreRuntime - Booted. (4586ms) [Timing 9e76e5f] +``` +2021-08-10 09:33:23,677 [P25776/D1/T22] INFO Umbraco.Cms.Core.Services.Implement.ContentService - Document Home (id=1062) has been published. ``` Here is an example of the same log message represented as JSON, you can see here we have much more information that would allow us to search & filter logs based on these properties with an appropriate logging system. ```json { - "@t": "2018-11-12T08:34:50.4190399Z", - "@mt": "{EndMessage} ({Duration}ms) [Timing {TimingId}]", - "EndMessage": "Booted.", - "Duration": 4586, - "TimingId": "9e76e5f", - "SourceContext": "Umbraco.Core.Runtime.CoreRuntime", - "ProcessId": 27004, - "ProcessName": "iisexpress", - "ThreadId": 1, - "AppDomainId": 2, - "AppDomainAppId": "LMW3SVC2ROOT", - "MachineName": "DELLBOOK", - "Log4NetLevel": "INFO ", - "HttpRequestNumber": 1, - "HttpRequestId": "557f45ba-0888-4216-8723-e226d795a2f7" + "@t":"2021-08-10T08:33:23.6778640Z", + "@mt":"Document {ContentName} (id={ContentId}) has been published.", + "ContentName":"Home", + "ContentId":1062, + "SourceContext":"Umbraco.Cms.Core.Services.Implement.ContentService", + "ActionId":"7726d745-d502-4b2d-b55e-97731308041b", + "ActionName":"Umbraco.Cms.Web.BackOffice.Controllers.ContentController.PostSave (Umbraco.Web.BackOffice)", + "RequestId":"8000000c-0012-fb00-b63f-84710c7967bb", + "RequestPath":"/umbraco/backoffice/umbracoapi/content/PostSave", + "ProcessId":25776, + "ProcessName":"iisexpress", + "ThreadId":22, + "AppDomainId":1, + "AppDomainAppId":"2f4961977e5c252fa708f7d83915c269b53a620c", + "MachineName":"DELLBOOK", + "Log4NetLevel":"INFO ", + "HttpRequestId":"318b6dd4-b127-4da3-8339-37701f4d1416", + "HttpRequestNumber":4, + "HttpSessionId":"0cea7395-ba29-e6c6-93ee-7c08a2fd7219" } ``` @@ -52,16 +56,26 @@ Umbraco writes log messages, but you are also able to use the Umbraco logger to Here is an example of using the logger to write an Information message to the log which will contain one property of **Name** which will output the name variable that is passed into the method ```csharp -using Umbraco.Web.WebApi; -using Umbraco.Core.Logging; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Web.Common.Controllers; -namespace MyNamespace +namespace Umbraco.Cms.Web.UI.NetCore { public class MyApiController : UmbracoApiController { - public string GetSayHello(string name) + private readonly ILogger _logger; + + public MyApiController(ILogger logger) { - Logger.Info("We are saying hello to {Name}", name); + _logger = logger; + } + + /// /umbraco/api/MyApi/SayHello?name=John + [HttpGet] + public string SayHello(string name) + { + _logger.LogInformation("We are saying hello to {Name}", name); return $"Hello {name}"; } } @@ -72,54 +86,17 @@ The incorrect way to log the message would be use string interpolation or string ```csharp //GOOD - Do use :) -Logger.Info("We are saying hello to {Name}", name); +_logger.LogInformation("We are saying hello to {Name}", name); //BAD - Do not use :( -Logger.Info($"We are saying hello to {name}"); +_logger.LogInformation($"We are saying hello to {name}"); //BAD - Do not use :( -Logger.Info("We are saying hello to " + name); +_logger.LogInformation("We are saying hello to " + name); ``` The bad examples above will write to the log file, but we will not get a separate property logged with the message. This means we can't find them by searching for log messages that use the message template `We are saying hello to {Name}` -If you are writing classes that inherit from one of these special Umbraco base classes: - -* RenderMvcController -* SurfaceController -* UmbracoApiController -* UmbracoAuthorizedApiController - -Then you can access the logging functionality via a special 'Logger' property included in those base classes and use the friendlier syntax of `Logger.Info` to pass the type, if you add a reference to `Umbraco.Core.Logging` as a Using statement. - -Outside of these places, eg a ContentFinder or your own custom code, you can get a reference to the logger via Dependency Injection. While using Dependency Injection is the recommended way, it is possible to use Current.Logger instead, if DI is not an option. - -```csharp -using Umbraco.Core.Logging; -using Umbraco.Web.Routing; - -namespace MyNamespace -{ - public class MyContentFinder : IContentFinder - { - private readonly ILogger _logger; - - public MyContentFinder(ILogger logger) - { - _logger = logger; - } - public bool TryFindContent(PublishedRequest frequest) - { - _logger.Info("Trying to find content for url {RequestUrl}", frequest.Uri); - - //Do Content Finder Logic... - } - } -} -``` - -You will need to register your ContentFinder [using a Composer](../../../../Implementation/Composing/index.md) - ## Log Levels Serilog uses levels as the primary means for assigning importance to log events. The levels in increasing order of importance are: @@ -131,75 +108,11 @@ Serilog uses levels as the primary means for assigning importance to log events. 1. **Error** - indicating a failure within the application or connected system 1. **Fatal** - critical errors causing complete failure of the application -The default log levels we ship with in Umbraco v8.0+ are: - -* .txt file **Debug** -* .json file **Verbose** ## Configuration -Serilog can be configured and extended by using the two XML configuration files on disk. - -* `/config/serilog.config` is used to modify the main Umbraco logging pipeline -* `/config/serilog.user.config` which is a sublogger and allows you to make modifications without affecting the main Umbraco logger - -Info on the Serilog config [here](../../../../Reference/Config/Serilog/index.md). - -## Advanced - -### Full C# control over Serilog configuration - -If you like using Serilog but prefer to use C# to configure the logging pipeline then you can do so with the following example. This sets the minimum logging level from a web.config AppSetting, allowing you to set different minimum logging levels in different environments using web config transforms. - -```csharp -using System; -using System.Configuration; -using Umbraco.Web; -using Umbraco.Core; -using Umbraco.Web.Runtime; -using Umbraco.Core.Logging.Serilog; - -using Serilog; -using Serilog.Core; -using Serilog.Events; - -namespace MyNamespace -{ - public class FineTuneLoggingApplication : UmbracoApplication - { - protected override IRuntime GetRuntime() - { - var logLevelSetting = ConfigurationManager.AppSettings["YourMinimumLoggingLevel"]; //Warning, Debug, Information, etc - - const bool ignoreCase = true; //this is to clarify the function of the boolean second parameter in the TryParse - if (!Enum.TryParse(logLevelSetting, ignoreCase, out LogEventLevel minimumLevel)) - { - minimumLevel = LogEventLevel.Information;//set to this level if the config setting is missing or doesn't match a valid enumeration - } - - var levelSwitch = new LoggingLevelSwitch { MinimumLevel = minimumLevel }; - - var loggerConfig = new LoggerConfiguration() - .MinimalConfiguration() - .ReadFromConfigFile() - .ReadFromUserConfigFile() - .MinimumLevel.ControlledBy(levelSwitch); - - var logger = new SerilogLogger(loggerConfig); - - var runtime = new WebRuntime(this, logger, GetMainDom(logger)); - - return runtime; - } - } -} -``` - -You will then need to update the `global.asax` file on disk to use our FineTuneLogging class like so - -```xml -<%@ Application Inherits="MyNamespace.FineTuneLoggingApplication" Language="C#" %> -``` +Serilog can be configured and extended by using the .NETCore configuration such as the AppSetting.json files or environment variables. +Info on the Serilog config [here](../../../../Reference/V9-Config/Serilog/index.md). ## The logviewer dashboard @@ -209,18 +122,21 @@ Learn more about the [logviewer dashboard](../../../Backoffice/LogViewer/) in th This is a tool for viewing & querying JSON log files from disk in the same way as the built in log viewer dashboard. -English badge +English badge ## Serilog project/references shipped -Umbraco v8.0+ ships with the following Serilog projects, where you can find further information & details with the GitHub readme files as needed. +Umbraco ships with the following Serilog projects, where you can find further information & details with the GitHub readme files as needed. * [Serilog](https://github.com/serilog/serilog) +* [Serilog.AspNetCore](https://github.com/serilog/serilog-aspnetcore) * [Serilog.Enrichers.Process](https://github.com/serilog/serilog-enrichers-process) * [Serilog.Enrichers.Thread](https://github.com/serilog/serilog-enrichers-thread) * [Serilog.Filters.Expressions](https://github.com/serilog/serilog-filters-expressions) * [Serilog.Formatting.Compact](https://github.com/serilog/serilog-formatting-compact) -* [Serilog.Settings.AppSettings](https://github.com/serilog/Serilog-Settings-AppSettings) +* [Serilog.Formatting.Compact.Reader](https://github.com/serilog/serilog-formatting-compact-reader) +* [Serilog.Settings.Configuration](https://github.com/serilog/serilog-settings-configuration) +* [Serilog.Sinks.Console](https://github.com/serilog/serilog-sinks-console) * [Serilog.Sinks.File](https://github.com/serilog/serilog-sinks-file) ## Further Resources diff --git a/Fundamentals/Code/Debugging/index.md b/Fundamentals/Code/Debugging/index.md index 2ddf6a4488a..902632c359b 100644 --- a/Fundamentals/Code/Debugging/index.md +++ b/Fundamentals/Code/Debugging/index.md @@ -2,6 +2,7 @@ meta.Title: "Debugging" meta.Description: "Debugging in Umbraco" versionFrom: 8.0.0 +needsV9Update: "true" --- # Debugging diff --git a/Fundamentals/Code/Subscribing-To-Events/index.md b/Fundamentals/Code/Subscribing-To-Events/index.md index 269504b3483..90803074fe1 100644 --- a/Fundamentals/Code/Subscribing-To-Events/index.md +++ b/Fundamentals/Code/Subscribing-To-Events/index.md @@ -2,6 +2,7 @@ meta.title: "Subscribing to events" meta.description: "Subscribing to events allows you to execute custom code on a number of events both before and after the event occurs" versionFrom: 8.0.0 +versionRemoved: 9.0.0 --- # Subscribing to events diff --git a/Fundamentals/Code/Umbraco-Services/index-v8.md b/Fundamentals/Code/Umbraco-Services/index-v8.md new file mode 100644 index 00000000000..4a465d5e073 --- /dev/null +++ b/Fundamentals/Code/Umbraco-Services/index-v8.md @@ -0,0 +1,197 @@ +--- +meta.title: "Using Umbraco's service APIs" +meta.description: "Using the Umbraco service APIs you can create, update and delete any of the core Umbraco entities directly from your custom code" +versionFrom: 8.0.0 +--- + +# Using Umbraco's service APIs + +_Whenever you need to modify an entity that Umbraco stores in the database, there is a service API available to help you. This means that you can create, update and delete any of the core Umbraco entities directly from your custom code._ + + +## Accessing the Umbraco services +The services live in the `Umbraco.Core.Services` namespace. To use the service APIs you must first access them. This can be achieved via what is known as the `ServiceContext` or by injecting the specific service you require using Umbraco's underlying dependency injection framework. + + +### Access via a Controller +If you are accessing Umbraco services inside your own controller class and your controller inherits from one of the base Umbraco controller classes (eg RenderMvcController, SurfaceController etc) then you can access the `ServiceContext` and all services. This is done through a special `Services` property that is exposed on these base Umbraco controller classes: + +```csharp +public class EventController : Umbraco.Web.Mvc.SurfaceController +{ + public Action PerformAction() + { + IContentService contentService = Services.ContentService; + var someContent = contentService.GetById(1234); + } +} +``` + +### Access via a Razor View Template +Inside a Razor View template, that inherits UmbracoViewPage (or similar eg PartialViewMacroPage), you can access the `ServiceContext` and therefore all services, through a special `Services` property that is exposed in the Umbraco base View models: + +```csharp +@using Umbraco.Core.Services; +@inherits Umbraco.Web.Mvc.UmbracoViewPage +@{ + Layout = "master.cshtml"; + IPublicAccessService publicAccessService = Services.PublicAccessService; + bool isPageProtected = publicAccessService.IsProtected(Model.Path); +} +@if (isPageProtected) +{ +

Secret Page - shhshshsh!

+} +``` + +### Access in a Custom Class via dependency injection + +If for instance we wish to subscribe to an event on one of the services, we'd do so in a Component C# class, where there is no `ServiceContext` available. Instead we would inject the service we need into the public constructor of the Component and Umbraco's underlying dependency injection framework will do the rest. + +In this example we will wire up to the ContentService 'Saved' event, and create a new folder in the Media section whenever a new LandingPage is created in the content section to store associated media. Therefore we will need the MediaService available to create the new folder. + +```csharp +using System; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; + +namespace Umbraco8.Components +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public class SubscribeToContentSavedEventComposer : ComponentComposer + { + } + + public class SubscribeToContentSavedEventComponent : IComponent + { + // access to the MediaService by injection + private readonly IMediaService _mediaService; + public SubscribeToContentSavedEventComponent(IMediaService mediaService) + { + _mediaService = mediaService; + } + + public void Initialize() + { + ContentService.Saved += ContentService_Saved; + } + + private void ContentService_Saved(Umbraco.Core.Services.IContentService sender, Umbraco.Core.Events.ContentSavedEventArgs e) + { + foreach (var contentItem in e.SavedEntities) + { + // if this is a new landing page create a folder for associated media in the media section + if (contentItem.ContentType.Alias == "landingPage") + { + // we have injected in the mediaService in the constructor for the component see above. + bool hasExistingFolder = _mediaService.GetByLevel(1).Any(f => f.Name == contentItem.Name); + if (!hasExistingFolder) + { + // let's create one (-1 indicates the root of the media section) + IMedia newFolder = _mediaService.CreateMedia(contentItem.Name, -1, "Folder"); + _mediaService.Save(newFolder); + } + } + } + } + + public void Terminate() + { + //unsubscribe during shutdown + ContentService.Saved -= ContentService_Saved; + } + } +} +``` +#### Custom Class example +It is the same approach if you are creating your own custom class, as long as your class is registered with the dependency injection framework (via a composer). + +```csharp +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco8.Controllers; +using Umbraco8.Services; + +namespace Umbraco8.Composers +{ + public class RegisterCustomNewsArticleServiceComposer : IUserComposer + { + public void Compose(Composition composition) + { + + composition.Register(Lifetime.Request); + } + } +} +``` + +Then your custom class eg. `CustomNewsArticleService` can take advantage of the same injection to access services eg: + +```csharp +using Umbraco.Core.Logging; +using Umbraco.Core.Services; +using Umbraco.Web; + +namespace Umbraco8.Services +{ + public class CustomNewsArticleService : ICustomNewsArticleService + { + private readonly IMediaService _mediaService; + private readonly ILogger _logger; + private readonly IUmbracoContextFactory _contextFactory; + + public CustomNewsArticleService(ILogger logger, IUmbracoContextFactory contextFactory,IMediaService mediaService) + { + _logger = logger; + _contextFactory = contextFactory; + _mediaService = mediaService; + } + + public void DoSomethingWithNewsArticles() + { + using (var contextReference = _contextFactory.EnsureUmbracoContext()) + { + IPublishedContentCache contentCache = contextReference.UmbracoContext.Content; + IPublishedContent newsSection = contentCache.GetAtRoot().FirstOrDefault().Children.FirstOrDefault(f => f.ContentType.Alias == "newsSection"); + if (newsSection== null) + { + _logger.Debug("News Section Not Found"); + } + } + // etc + } + } +} +``` + +## Services available +There is full API coverage of all Umbraco core entities: + +- [ConsentService](../../../Reference/Management/Services/ConsentService/index.md) +- [ContentService](../../../Reference/Management/Services/ContentService/index.md) +- [ApplicationTreeService](../../../Reference/Management/Services/TreeService/index.md) +- [DataTypeService](../../../Reference/Management/Services/DataTypeService/index.md) +- EntityService +- [FileService](../../../Reference/Management/Services/FileService/index.md) +- [LocalizationService](../../../Reference/Management/Services/LocalizationService/index.md) +- MacroService +- [MediaService](../../../Reference/Management/Services/MediaService/index.md) +- [MemberService](../../../Reference/Management/Services/MemberService/index.md) +- [MemberTypeService](../../../Reference/Management/Services/MemberTypeService/index.md) +- [MemberGroupService](../../../Reference/Management/Services/MemberGroupService/index.md) +- [ContentTypeService](../../../Reference/Management/Services/ContentTypeService/index.md) + + +### More information +- [Umbraco Services API reference](../../../Reference/Management/Services/) +- [Umbraco Events reference](../../../Reference/Events/) +- [Routes and controllers](../../../Reference/Routing/) + +### Umbraco TV +- [Chapter: Content API](https://umbraco.tv/videos/umbraco-v7/developer/fundamentals/content-api/) +- [Chapter: Media API](https://umbraco.tv/videos/umbraco-v7/developer/fundamentals/media-api/) +- [Chapter: Member API](https://umbraco.tv/videos/umbraco-v7/developer/fundamentals/member-api/) diff --git a/Fundamentals/Code/Umbraco-Services/index-v9.md b/Fundamentals/Code/Umbraco-Services/index-v9.md deleted file mode 100644 index 614215e6b89..00000000000 --- a/Fundamentals/Code/Umbraco-Services/index-v9.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -versionFrom: 9.0.0 -verified-against: beta-3 -state: partial -updated-links: false ---- - -# Using Umbraco's service APIs - -_Whenever you need to modify an entity that Umbraco stores in the database, there are a number of service APIs available to help you. This means that you can create, update and delete any of the core Umbraco entities directly from your custom code._ - - -## Accessing the Umbraco services -Services are typically defined using interfaces. Umbraco has them in the `Umbraco.Cms.Core.Services` namespace, while the specific implementations can be found under the `Umbraco.Cms.Core.Services.Implement` namespace. To use the service APIs you must first access them. Owing to the built-in dependency injection (DI) in ASP.NET Core, configured services are made available throughout Umbraco's codebase. This can be achieved via injecting the specific service you require - the service type or an interface. - -### Access via a Controller -If you are accessing Umbraco services inside your own controller class, you can add the Umbraco services that you need as constructor parameters. An instance of every service will be provided at runtime from the service container and by saving each one to a local field, you can make use of them within the scope of your class: - -```csharp -public class CustomController -{ - private readonly IContentService _contentService; - - - public ContentController(IContentService contentService) - { - _contentService = contentService; - } - - - public ActionResult PerformAction() - { - var someContent = _contentService.GetById(1234); - } -} -``` - -### Access via a Razor View Template -Inside a Razor View template, you can make use of a service injection into a view using the `@inject` directive. It works similarly to adding a property to the view, and populating the property using DI: - -```csharp -@using Umbraco.Cms.Core.Services -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage -@inject IPublicAccessService PublicAccessService -@{ - Layout = "master.cshtml"; - bool isPageProtected = PublicAccessService.IsProtected(Model.Path); -} -@if (isPageProtected) -{ -

Secret Page - shhshshsh!

-} -``` - -### Access in a Custom Class via dependency injection - -If for instance we wish to subscribe to notifications on one of the services, we'd do so in a Composer C# class, where you will add a custom `NotificationHandler`. In this custom `NotificationHandler` we would inject the service we need into the public constructor of the class and Umbraco's underlying dependency injection framework will do the rest. - -In this example we will wire up to the ContentService 'Saved' event, and create a new folder in the Media section whenever a new LandingPage is created in the content section to store associated media. Therefore we will need the MediaService available to create the new folder. - -```csharp -public class CustomComposer : IComposer -{ - public void Compose(IUmbracoBuilder builder) - { - builder.AddNotificationHandler(); - } -} -``` - -```csharp -using System.Linq; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Notifications; -using Umbraco.Cms.Core.Services; - -namespace Umbraco.Cms.Core.Events -{ - public class CustomNotificationHandler : INotificationHandler - { - // access to the MediaService by injection - private readonly IMediaService _mediaService; - private readonly IRuntimeState _runtimeState; - - public CustomNotificationHandler(IMediaService mediaService, IRuntimeState runtimeState) - { - _mediaService = mediaService; - _runtimeState = runtimeState; - } - - public void Handle(ContentSavedNotification notification) - { - if (_runtimeState.Level != RuntimeLevel.Run) - { - return; - } - - foreach (var contentItem in notification.SavedEntities) - { - // if this is a new landing page create a folder for associated media in the media section - if (contentItem.ContentType.Alias == "landingPage") - { - // we have injected in the mediaService in the constructor for the component see above. - bool hasExistingFolder = _mediaService.GetByLevel(1).Any(f => f.Name == contentItem.Name); - if (!hasExistingFolder) - { - // let's create one (-1 indicates the root of the media section) - IMedia newFolder = _mediaService.CreateMedia(contentItem.Name, -1, "Folder"); - _mediaService.Save(newFolder); - } - } - } - } - } -} -``` - -#### Custom Class example -When you are creating your own custom class, in order to make use of the dependency injection framework, you need to register the `ICustomNewsArticleService` service with the concrete type `CustomNewsArticleService`. The `AddScoped()` method registers the service with the lifetime of a single request. - -There are several different ways that you can achieve the same outcome: - -Register directly into the **Startup.cs** class. -```csharp -using Microsoft.Extensions.DependencyInjection; - -namespace DefaultNamespace -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } - } -} -``` - -Another approach is to create an extension method to `IUmbracoBuilder` and add it to the startup pipeline. - -```csharp -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Cms.Core.DependencyInjection; - -namespace DefaultNamespace -{ - public static class UmbracoBuilderServiceExtensions - { - public static IUmbracoBuilder AddCustomServices(this IUmbracoBuilder builder) - { - builder.Services.AddScoped(); - - return builder; - } - } -} -``` - -```csharp -using System; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Extensions; - -namespace DefaultNamespace -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - services.AddUmbraco(_env, _config) - .AddBackOffice() - .AddWebsite() - .AddComposers() - .AddCustomServices() - .Build(); - } - } -} -``` - -Especially recommended when creating Umbraco packages as you won't have access to the Startup class, instead you can achieve the same as above by using a custom Composer which gives you access to the `IUmbracoBuilder`. - -```C# -public class CustomComposer : IComposer -{ - public void Compose(IUmbracoBuilder builder) - { - builder.Services.AddScoped(); - } -} -``` - -
- -Then your custom class eg. `CustomNewsArticleService` can take advantage of the same injection to access services eg: - -```csharp -using System.Linq; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; - -namespace Umbraco.Cms.Infrastructure.Services.Implement -{ - public class CustomNewsArticleService: ICustomNewsArticleService - { - private readonly IMediaService _mediaService; - private readonly ILogger _logger; - private readonly IUmbracoContextFactory _contextFactory; - - public CustomNewsArticleService(ILogger logger, IUmbracoContextFactory contextFactory, IMediaService mediaService) - { - _logger = logger; - _contextFactory = contextFactory; - _mediaService = mediaService; - } - - public void DoSomethingWithNewsArticles() - { - using (var contextReference = _contextFactory.EnsureUmbracoContext()) - { - IPublishedContentCache contentCache = contextReference.UmbracoContext.Content; - IPublishedContent newsSection = contentCache.GetAtRoot().FirstOrDefault().Children.FirstOrDefault(f => f.ContentType.Alias == "newsSection"); - if (newsSection == null) - { - _logger.LogDebug("News Section Not Found"); - } - } - // etc - } - } -} -``` - -## Services available -There is full API coverage of all Umbraco core entities: - -- [AuditService](../../../Reference/Management/Services/AuditService/index-v9.md) -- [ConsentService](../../../Reference/Management/Services/ConsentService/index-v9.md) -- [ContentService](../../../Reference/Management/Services/ContentService/index-v9.md) -- [ContentTypeService](../../../Reference/Management/Services/ContentTypeService/index-v9.md) -- [DataTypeService](../../../Reference/Management/Services/DataTypeService/index-v9.md) -- [EntityService](../../../Reference/Management/Services/EntityService/index-v9.md) -- [FileService](../../../Reference/Management/Services/FileService/index-v9.md) -- [LocalizationService](../../../Reference/Management/Services/LocalizationService/index-v9.md) -- [MacroService](../../../Reference/Management/Services/MacroService/index-v9.md) -- [MediaService](../../../Reference/Management/Services/MediaService/index-v9.md) -- [MemberService](../../../Reference/Management/Services/MemberService/index-v9.md) -- [MemberTypeService](../../../Reference/Management/Services/MemberTypeService/index-v9.md) -- [MemberGroupService](../../../Reference/Management/Services/MemberGroupService/index-v9.md) - - -### More information -- [Umbraco Services API reference](../../../Reference/Management/Services/) -- [Umbraco Events reference](../../../Reference/Events/) -- [Routes and controllers](../../../Reference/Routing/) diff --git a/Fundamentals/Code/Umbraco-Services/index.md b/Fundamentals/Code/Umbraco-Services/index.md index 4a465d5e073..5939f961c3d 100644 --- a/Fundamentals/Code/Umbraco-Services/index.md +++ b/Fundamentals/Code/Umbraco-Services/index.md @@ -1,42 +1,50 @@ --- -meta.title: "Using Umbraco's service APIs" -meta.description: "Using the Umbraco service APIs you can create, update and delete any of the core Umbraco entities directly from your custom code" -versionFrom: 8.0.0 +versionFrom: 9.0.0 +verified-against: beta-3 +state: partial +updated-links: false --- # Using Umbraco's service APIs -_Whenever you need to modify an entity that Umbraco stores in the database, there is a service API available to help you. This means that you can create, update and delete any of the core Umbraco entities directly from your custom code._ +_Whenever you need to modify an entity that Umbraco stores in the database, there are a number of service APIs available to help you. This means that you can create, update and delete any of the core Umbraco entities directly from your custom code._ ## Accessing the Umbraco services -The services live in the `Umbraco.Core.Services` namespace. To use the service APIs you must first access them. This can be achieved via what is known as the `ServiceContext` or by injecting the specific service you require using Umbraco's underlying dependency injection framework. - +Services are typically defined using interfaces. Umbraco has them in the `Umbraco.Cms.Core.Services` namespace, while the specific implementations can be found under the `Umbraco.Cms.Core.Services.Implement` namespace. To use the service APIs you must first access them. Owing to the built-in dependency injection (DI) in ASP.NET Core, configured services are made available throughout Umbraco's codebase. This can be achieved via injecting the specific service you require - the service type or an interface. ### Access via a Controller -If you are accessing Umbraco services inside your own controller class and your controller inherits from one of the base Umbraco controller classes (eg RenderMvcController, SurfaceController etc) then you can access the `ServiceContext` and all services. This is done through a special `Services` property that is exposed on these base Umbraco controller classes: +If you are accessing Umbraco services inside your own controller class, you can add the Umbraco services that you need as constructor parameters. An instance of every service will be provided at runtime from the service container and by saving each one to a local field, you can make use of them within the scope of your class: ```csharp -public class EventController : Umbraco.Web.Mvc.SurfaceController +public class CustomController { - public Action PerformAction() + private readonly IContentService _contentService; + + + public ContentController(IContentService contentService) + { + _contentService = contentService; + } + + + public ActionResult PerformAction() { - IContentService contentService = Services.ContentService; - var someContent = contentService.GetById(1234); + var someContent = _contentService.GetById(1234); } } ``` ### Access via a Razor View Template -Inside a Razor View template, that inherits UmbracoViewPage (or similar eg PartialViewMacroPage), you can access the `ServiceContext` and therefore all services, through a special `Services` property that is exposed in the Umbraco base View models: +Inside a Razor View template, you can make use of a service injection into a view using the `@inject` directive. It works similarly to adding a property to the view, and populating the property using DI: ```csharp -@using Umbraco.Core.Services; -@inherits Umbraco.Web.Mvc.UmbracoViewPage +@using Umbraco.Cms.Core.Services +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage +@inject IPublicAccessService PublicAccessService @{ Layout = "master.cshtml"; - IPublicAccessService publicAccessService = Services.PublicAccessService; - bool isPageProtected = publicAccessService.IsProtected(Model.Path); + bool isPageProtected = PublicAccessService.IsProtected(Model.Path); } @if (isPageProtected) { @@ -46,43 +54,48 @@ Inside a Razor View template, that inherits UmbracoViewPage (or similar eg Parti ### Access in a Custom Class via dependency injection -If for instance we wish to subscribe to an event on one of the services, we'd do so in a Component C# class, where there is no `ServiceContext` available. Instead we would inject the service we need into the public constructor of the Component and Umbraco's underlying dependency injection framework will do the rest. +If for instance we wish to subscribe to notifications on one of the services, we'd do so in a Composer C# class, where you will add a custom `NotificationHandler`. In this custom `NotificationHandler` we would inject the service we need into the public constructor of the class and Umbraco's underlying dependency injection framework will do the rest. In this example we will wire up to the ContentService 'Saved' event, and create a new folder in the Media section whenever a new LandingPage is created in the content section to store associated media. Therefore we will need the MediaService available to create the new folder. ```csharp -using System; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; - -namespace Umbraco8.Components +public class CustomComposer : IComposer { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public class SubscribeToContentSavedEventComposer : ComponentComposer + public void Compose(IUmbracoBuilder builder) { + builder.AddNotificationHandler(); } +} +``` + +```csharp +using System.Linq; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; - public class SubscribeToContentSavedEventComponent : IComponent +namespace Umbraco.Cms.Core.Events +{ + public class CustomNotificationHandler : INotificationHandler { // access to the MediaService by injection private readonly IMediaService _mediaService; - public SubscribeToContentSavedEventComponent(IMediaService mediaService) + private readonly IRuntimeState _runtimeState; + + public CustomNotificationHandler(IMediaService mediaService, IRuntimeState runtimeState) { _mediaService = mediaService; + _runtimeState = runtimeState; } - public void Initialize() + public void Handle(ContentSavedNotification notification) { - ContentService.Saved += ContentService_Saved; - } + if (_runtimeState.Level != RuntimeLevel.Run) + { + return; + } - private void ContentService_Saved(Umbraco.Core.Services.IContentService sender, Umbraco.Core.Events.ContentSavedEventArgs e) - { - foreach (var contentItem in e.SavedEntities) + foreach (var contentItem in notification.SavedEntities) { // if this is a new landing page create a folder for associated media in the media section if (contentItem.ContentType.Alias == "landingPage") @@ -98,53 +111,109 @@ namespace Umbraco8.Components } } } + } +} +``` - public void Terminate() - { - //unsubscribe during shutdown - ContentService.Saved -= ContentService_Saved; +#### Custom Class example +When you are creating your own custom class, in order to make use of the dependency injection framework, you need to register the `ICustomNewsArticleService` service with the concrete type `CustomNewsArticleService`. The `AddScoped()` method registers the service with the lifetime of a single request. + +There are several different ways that you can achieve the same outcome: + +Register directly into the **Startup.cs** class. +```csharp +using Microsoft.Extensions.DependencyInjection; + +namespace DefaultNamespace +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); } } } ``` -#### Custom Class example -It is the same approach if you are creating your own custom class, as long as your class is registered with the dependency injection framework (via a composer). + +Another approach is to create an extension method to `IUmbracoBuilder` and add it to the startup pipeline. ```csharp -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco8.Controllers; -using Umbraco8.Services; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.DependencyInjection; -namespace Umbraco8.Composers +namespace DefaultNamespace { - public class RegisterCustomNewsArticleServiceComposer : IUserComposer + public static class UmbracoBuilderServiceExtensions { - public void Compose(Composition composition) + public static IUmbracoBuilder AddCustomServices(this IUmbracoBuilder builder) { + builder.Services.AddScoped(); + + return builder; + } + } +} +``` - composition.Register(Lifetime.Request); +```csharp +using System; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Extensions; + +namespace DefaultNamespace +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddUmbraco(_env, _config) + .AddBackOffice() + .AddWebsite() + .AddComposers() + .AddCustomServices() + .Build(); } } } ``` +Especially recommended when creating Umbraco packages as you won't have access to the Startup class, instead you can achieve the same as above by using a custom Composer which gives you access to the `IUmbracoBuilder`. + +```C# +public class CustomComposer : IComposer +{ + public void Compose(IUmbracoBuilder builder) + { + builder.Services.AddScoped(); + } +} +``` + +
+ Then your custom class eg. `CustomNewsArticleService` can take advantage of the same injection to access services eg: ```csharp -using Umbraco.Core.Logging; -using Umbraco.Core.Services; -using Umbraco.Web; +using System.Linq; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Web; -namespace Umbraco8.Services +namespace Umbraco.Cms.Infrastructure.Services.Implement { - public class CustomNewsArticleService : ICustomNewsArticleService + public class CustomNewsArticleService: ICustomNewsArticleService { private readonly IMediaService _mediaService; - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IUmbracoContextFactory _contextFactory; - public CustomNewsArticleService(ILogger logger, IUmbracoContextFactory contextFactory,IMediaService mediaService) + public CustomNewsArticleService(ILogger logger, IUmbracoContextFactory contextFactory, IMediaService mediaService) { _logger = logger; _contextFactory = contextFactory; @@ -157,9 +226,9 @@ namespace Umbraco8.Services { IPublishedContentCache contentCache = contextReference.UmbracoContext.Content; IPublishedContent newsSection = contentCache.GetAtRoot().FirstOrDefault().Children.FirstOrDefault(f => f.ContentType.Alias == "newsSection"); - if (newsSection== null) + if (newsSection == null) { - _logger.Debug("News Section Not Found"); + _logger.LogDebug("News Section Not Found"); } } // etc @@ -171,27 +240,22 @@ namespace Umbraco8.Services ## Services available There is full API coverage of all Umbraco core entities: +- [AuditService](../../../Reference/Management/Services/AuditService/index.md) - [ConsentService](../../../Reference/Management/Services/ConsentService/index.md) - [ContentService](../../../Reference/Management/Services/ContentService/index.md) -- [ApplicationTreeService](../../../Reference/Management/Services/TreeService/index.md) +- [ContentTypeService](../../../Reference/Management/Services/ContentTypeService/index.md) - [DataTypeService](../../../Reference/Management/Services/DataTypeService/index.md) -- EntityService +- [EntityService](../../../Reference/Management/Services/EntityService/index.md) - [FileService](../../../Reference/Management/Services/FileService/index.md) - [LocalizationService](../../../Reference/Management/Services/LocalizationService/index.md) -- MacroService +- [MacroService](../../../Reference/Management/Services/MacroService/index.md) - [MediaService](../../../Reference/Management/Services/MediaService/index.md) - [MemberService](../../../Reference/Management/Services/MemberService/index.md) - [MemberTypeService](../../../Reference/Management/Services/MemberTypeService/index.md) - [MemberGroupService](../../../Reference/Management/Services/MemberGroupService/index.md) -- [ContentTypeService](../../../Reference/Management/Services/ContentTypeService/index.md) ### More information - [Umbraco Services API reference](../../../Reference/Management/Services/) -- [Umbraco Events reference](../../../Reference/Events/) +- [Umbraco Events reference](../../../Reference/Events/) - [Routes and controllers](../../../Reference/Routing/) - -### Umbraco TV -- [Chapter: Content API](https://umbraco.tv/videos/umbraco-v7/developer/fundamentals/content-api/) -- [Chapter: Media API](https://umbraco.tv/videos/umbraco-v7/developer/fundamentals/media-api/) -- [Chapter: Member API](https://umbraco.tv/videos/umbraco-v7/developer/fundamentals/member-api/) diff --git a/Fundamentals/Code/index.md b/Fundamentals/Code/index.md index f4d0cc69438..e42b3b92c2d 100644 --- a/Fundamentals/Code/index.md +++ b/Fundamentals/Code/index.md @@ -1,5 +1,6 @@ --- versionFrom: 7.0.0 +versionTo: 9.0.0 --- # Code diff --git a/Fundamentals/Data/Data-Types/default-data-types-v8.md b/Fundamentals/Data/Data-Types/default-data-types-v8.md new file mode 100644 index 00000000000..2d439bea10d --- /dev/null +++ b/Fundamentals/Data/Data-Types/default-data-types-v8.md @@ -0,0 +1,113 @@ +--- +versionFrom: 8.0.0 +meta.Title: "Default data types in Umbraco" +meta.Description: "Learn about the default data types in Umbraco." +--- + +# Default Data Types + +Here's a list of the default Data Types that come installed with Umbraco. There are plenty more that you can create based on the installed [Property Editors](../../Backoffice/Property-Editors/index.md). + +![Umbraco 8 Data Type List](images/default-data-types-8.png) + +### Approved Color +Adds a list of approved colors which can be selected by clicking. The approved colors are added as hex values by using the color picker. Optionally you can enable labels to give the colors different names. + +### Checkbox list +Displays a list of preset values as a list of checkbox controls. The preset values are modified in the Settings +section under "Data Types" / checkbox list where new items can be added. The value saved is a comma-separated +string of prevalue IDs. + +### Content picker +The content picker opens a modal to pick a specific page from the content structure. +The value saved is the selected page's ID. + +### Date picker +Displays a calendar UI for selecting dates and time, the value saved is a standard dateTime value, +but with no time information. + +### Date picker with time +Displays a calendar UI for selecting dates and time, the value saved is a standard dateTime value. + +### Dropdown +Displays a list of preset values as a list where only a single value can be selected. The default Data Type does not contain any prevalues. The value saved is the selected value as a string. + +### Dropdown multiple +Displays a list of preset values as a list where multiple values can be selected. The default data type does not contain any prevlaues .The value saved is a comma separated string of prevalue IDs. + +### Image Cropper +Allows for the upload and cropping of images by using a focal point. Specific crop definitions can also be added. This data type is used by default on the Image Media Type. + +### Label +Is a non-editable control, can only be used to display a present text (string). It can also be used in the +media section to load in values related to the node, such as width, height and file size. + +If you want to input something other than a string into a Label, you can use one of the five other Label Data Types: Label (bigint), Label (datetime), Label (decimal), Label (integer) or Label (time). + +If you want to save a long string value for a Label, there is a Value type: Label (Long string) which can be used for that. + +### List View - Content +This data type is used by Document Types that are set to display as list views. + +### List View - Media +This data type is used by Media Types that are set to display as list views. + +### List View - Members +This data type is used by Member Types that are set to display as list views. + +### Media Picker +The picker opens a modal to pick a specific media item from the Media tree. +The value saved is the selected media node UDI. + +### Member Picker +Displays a dropdown with all available members in. A single member can be selected. +The value saved is the ID of the member. + +### Multi URL Picker +This Data Type allows an editor to add an array of links. These can either be internal Umbraco pages external URLs or links to media in the Media section. The Data Type can be configured by limited number of links it is possible to add. + +### Multiple Media Picker +The picker opens a modal to pick multiple media item from the Media tree. +The value saved is a comma separated string of media node UDIs. + +### Numeric +A textbox to input a numeric value. + +### Radiobox +This Data type enables editors to choose from a list of radiobuttons. + +### Richtext Editor +The TinyMCE based WYSIWYG editor. This is the standard editor used to edit larger amount of text. The editor has a lot of settings, which can be changed on Richtext editor Data Type in the Settings section. The editor also supports TinyMCE plugins which can be controlled in the configuration file located at `/config/tinyMce.config`. + +In the default settings some tags such as bullet list can be used. If you want to use other tags like `h1` or `h2`, you need to add stylesheets. + +Create child stylesheets for each tag(`h1` or `h2`) under a base one. +Go to "*Backoffice -> Settings -> Data Types -> Richtext editor*" and associate the Richtext editor with your stylesheet. +Also turn on "Style select" in the toolbar section. + +An example of the stylesheet tree is as follows. + +
+Stylesheets
+-IE7
+-IE8
+-Style
+-RichEdit
+--h1
+--h2
+
+ +### Tags +A textbox that allows you to use multiple tags on a Document Type. You can specify a Tag Group for this data type, in case you need to use Tags on different sections of your site (i.e News, Article, Events). + +### Textarea +A textarea + +### Textstring +A normal HTML input text field + +### True/False +A checkbox which saves either 0 or 1, depending on the checkbox being checked or not. A common use is to create a property with the special alias 'umbracoNaviHide' and the Data Type True/False to enable editors to hide nodes from appearing in a navigation menu. + +### Upload +Adds an upload field, which allows documents or images to be uploaded to Umbraco. This does not add them to the media library, they are added to the document data. diff --git a/Fundamentals/Data/Data-Types/default-data-types-v9.md b/Fundamentals/Data/Data-Types/default-data-types-v9.md deleted file mode 100644 index 3e0ff79e8cd..00000000000 --- a/Fundamentals/Data/Data-Types/default-data-types-v9.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -versionFrom: 9.0.0 -meta.Title: "Default data types in Umbraco" -meta.Description: "Learn about the default data types in Umbraco." ---- - -# Default Data Types - -Here's a list of the default Data Types that come installed with Umbraco. There are plenty more that you can create based on the installed [Property Editors](../../Backoffice/Property-Editors/index.md). - -![Umbraco 9 Data Type List](images/default-data-types-9.png) - -## Approved Color - -Adds a list of approved colors. The approved colors are added as hex values by using the color picker. Optionally, you can enable labels to give the colors different names. - -## Checkbox List - -Displays a list of preset values as a list of checkbox controls. The preset values are modified in the **Settings** section under **Data Types** in **Checkbox List** where new items can be added. The value saved is a comma-separated string of prevalue IDs. - -## Content Picker - -The Content Picker opens a modal to pick a specific page from the content structure. The value saved is the selected page's ID. - -## Date Picker - -Displays a calendar UI for selecting date and time. The value saved is a standard DateTime value but does not contain time information. - -## Date Picker with time - -Displays a calendar UI for selecting date and time. The value saved is a standard DateTime value. - -## Dropdown - -Displays a list of preset values as a list where only a single value can be selected. The default Data Type does not contain any prevalues. The value saved is the selected value as a string. - -## Dropdown multiple - -Displays a list of preset values as a list where multiple values can be selected. The default data type does not contain any prevlaues. The value saved is a comma separated string of prevalue IDs. - -## Image Cropper - -Allows to upload and crop images by using a focal point. Specific crop definitions can also be added. This data type is used by default on the Image Media Type. - -## Image Media Picker - -The Image Media Picker opens a modal to pick images from the **Media** tree or images from your Computer. The value saved is the selected media node UDI. - -## Label - -Is a non-editable control and can be used to *only* display the value. It can also be used in the **Media** section to load in values related to the node, such as width, height and file size. - -There are six Label Data Types: - -- Label (bigint) - Allows to save a big integer value for a Label. -- Label (datetime) - Allows to set a DateTime value for a Label. -- Label (decimal) - Allows to set a decimal value for a Label. -- Label (integer) - Allows to set an integer value for a Label. -- Label (string) - Allows to set a long string value for a Label. -- Label (time) - Allows to set time for a Label - -## List View - Content - -This data type is used by **Document Types** that are set to display as list views. - -## List View - Media - -This data type is used by **Media Types** that are set to display as list views. - -## List View - Members - -This data type is used by **Member Types** that are set to display as list views. - -## Media Picker - -The picker opens a modal to pick a specific media item from the Media tree. The value saved is the selected media node UDI. - -## Member Picker - -Displays a dropdown with all the available members. A single member can be selected. The value saved is the ID of the member. - -## Multi URL Picker - -This Data Type allows an editor to add an array of links. These can either be internal Umbraco pages external URLs or links to media in the Media section. The Data Type can be configured by limited number of links it is possible to add. - -## Multiple Image Media Picker - -The picker opens a modal to pick multiple images from the **Media** tree. The value saved is a comma separated string of media node UDIs. - -## Multiple Media Picker - -The picker opens a modal to pick multiple media items from the **Media** tree. The value saved is a comma separated string of media node UDIs. - -## Numeric - -A textbox to input a numeric value. - -## Radiobox - -This Data type enables editors to choose from a list of radiobuttons. - -## Richtext Editor - -The TinyMCE based WYSIWYG editor. This is the standard editor used to edit larger amount of text. The editor has a lot of settings, which can be changed on Richtext editor Data Type in the Settings section. The editor also supports TinyMCE plugins which can be controlled in the configuration file located at `/config/tinyMce.config`. - -In the default settings some tags such as bullet list can be used. If you want to use other tags like `h1` or `h2`, you need to add stylesheets. - -Create child stylesheets for each tag(`h1` or `h2`) under a base one. -Go to "*Backoffice -> Settings -> Data Types -> Richtext editor*" and associate the Richtext editor with your stylesheet. -Also turn on "Style select" in the toolbar section. - -An example of the stylesheet tree is as follows. - -```json - -Stylesheets --IE7 --IE8 --Style --RichEdit ---h1 ---h2 -``` - -## Tags - -A textbox that allows you to use multiple tags on a **Document Type**. You can specify a Tag Group for the data type, if you need to use Tags on different sections of your site (i.e. News, Article, Events). - -## Textarea - -A textarea provides a multi-line plain-text editing control. You can set the maximum allowed characters for the textarea and the number of rows, if any. - -## Textstring - -A normal HTML input text field. - -## True/False - -A checkbox which saves either 0 or 1, depending on the checkbox being checked or not. A common use is to create a property with the special alias 'umbracoNaviHide' and the Data Type True/False to enable editors to hide nodes from appearing in a navigation menu. - -## Upload - -Adds an upload field, which allows documents or images to be uploaded to Umbraco. This does not add them to the media library, they are added to the document data. - -There are five Upload Data Types: - -- Upload Article - Used for uploading and storing documents. -- Upload Audio - Used for uploading and storing digital audio files. -- Upload File - Used for uploading and storing different types of files in the Media section -- Upload Vector Graphics - Used for uploading and storing Scalable Vector Graphics (svg) files which are text files containing source code to draw the desired image. -- Upload Video - Used for uploading and storing video files. diff --git a/Fundamentals/Data/Data-Types/default-data-types.md b/Fundamentals/Data/Data-Types/default-data-types.md index 2d439bea10d..3e0ff79e8cd 100644 --- a/Fundamentals/Data/Data-Types/default-data-types.md +++ b/Fundamentals/Data/Data-Types/default-data-types.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 meta.Title: "Default data types in Umbraco" meta.Description: "Learn about the default data types in Umbraco." --- @@ -8,75 +8,99 @@ meta.Description: "Learn about the default data types in Umbraco." Here's a list of the default Data Types that come installed with Umbraco. There are plenty more that you can create based on the installed [Property Editors](../../Backoffice/Property-Editors/index.md). -![Umbraco 8 Data Type List](images/default-data-types-8.png) +![Umbraco 9 Data Type List](images/default-data-types-9.png) -### Approved Color -Adds a list of approved colors which can be selected by clicking. The approved colors are added as hex values by using the color picker. Optionally you can enable labels to give the colors different names. +## Approved Color -### Checkbox list -Displays a list of preset values as a list of checkbox controls. The preset values are modified in the Settings -section under "Data Types" / checkbox list where new items can be added. The value saved is a comma-separated -string of prevalue IDs. +Adds a list of approved colors. The approved colors are added as hex values by using the color picker. Optionally, you can enable labels to give the colors different names. -### Content picker -The content picker opens a modal to pick a specific page from the content structure. -The value saved is the selected page's ID. +## Checkbox List -### Date picker -Displays a calendar UI for selecting dates and time, the value saved is a standard dateTime value, -but with no time information. +Displays a list of preset values as a list of checkbox controls. The preset values are modified in the **Settings** section under **Data Types** in **Checkbox List** where new items can be added. The value saved is a comma-separated string of prevalue IDs. -### Date picker with time -Displays a calendar UI for selecting dates and time, the value saved is a standard dateTime value. +## Content Picker + +The Content Picker opens a modal to pick a specific page from the content structure. The value saved is the selected page's ID. + +## Date Picker + +Displays a calendar UI for selecting date and time. The value saved is a standard DateTime value but does not contain time information. + +## Date Picker with time + +Displays a calendar UI for selecting date and time. The value saved is a standard DateTime value. + +## Dropdown -### Dropdown Displays a list of preset values as a list where only a single value can be selected. The default Data Type does not contain any prevalues. The value saved is the selected value as a string. -### Dropdown multiple -Displays a list of preset values as a list where multiple values can be selected. The default data type does not contain any prevlaues .The value saved is a comma separated string of prevalue IDs. +## Dropdown multiple + +Displays a list of preset values as a list where multiple values can be selected. The default data type does not contain any prevlaues. The value saved is a comma separated string of prevalue IDs. + +## Image Cropper + +Allows to upload and crop images by using a focal point. Specific crop definitions can also be added. This data type is used by default on the Image Media Type. + +## Image Media Picker + +The Image Media Picker opens a modal to pick images from the **Media** tree or images from your Computer. The value saved is the selected media node UDI. + +## Label + +Is a non-editable control and can be used to *only* display the value. It can also be used in the **Media** section to load in values related to the node, such as width, height and file size. + +There are six Label Data Types: + +- Label (bigint) - Allows to save a big integer value for a Label. +- Label (datetime) - Allows to set a DateTime value for a Label. +- Label (decimal) - Allows to set a decimal value for a Label. +- Label (integer) - Allows to set an integer value for a Label. +- Label (string) - Allows to set a long string value for a Label. +- Label (time) - Allows to set time for a Label + +## List View - Content -### Image Cropper -Allows for the upload and cropping of images by using a focal point. Specific crop definitions can also be added. This data type is used by default on the Image Media Type. +This data type is used by **Document Types** that are set to display as list views. -### Label -Is a non-editable control, can only be used to display a present text (string). It can also be used in the -media section to load in values related to the node, such as width, height and file size. +## List View - Media -If you want to input something other than a string into a Label, you can use one of the five other Label Data Types: Label (bigint), Label (datetime), Label (decimal), Label (integer) or Label (time). +This data type is used by **Media Types** that are set to display as list views. -If you want to save a long string value for a Label, there is a Value type: Label (Long string) which can be used for that. +## List View - Members -### List View - Content -This data type is used by Document Types that are set to display as list views. +This data type is used by **Member Types** that are set to display as list views. -### List View - Media -This data type is used by Media Types that are set to display as list views. +## Media Picker -### List View - Members -This data type is used by Member Types that are set to display as list views. +The picker opens a modal to pick a specific media item from the Media tree. The value saved is the selected media node UDI. -### Media Picker -The picker opens a modal to pick a specific media item from the Media tree. -The value saved is the selected media node UDI. +## Member Picker -### Member Picker -Displays a dropdown with all available members in. A single member can be selected. -The value saved is the ID of the member. +Displays a dropdown with all the available members. A single member can be selected. The value saved is the ID of the member. + +## Multi URL Picker -### Multi URL Picker This Data Type allows an editor to add an array of links. These can either be internal Umbraco pages external URLs or links to media in the Media section. The Data Type can be configured by limited number of links it is possible to add. -### Multiple Media Picker -The picker opens a modal to pick multiple media item from the Media tree. -The value saved is a comma separated string of media node UDIs. +## Multiple Image Media Picker + +The picker opens a modal to pick multiple images from the **Media** tree. The value saved is a comma separated string of media node UDIs. + +## Multiple Media Picker + +The picker opens a modal to pick multiple media items from the **Media** tree. The value saved is a comma separated string of media node UDIs. + +## Numeric -### Numeric A textbox to input a numeric value. -### Radiobox +## Radiobox + This Data type enables editors to choose from a list of radiobuttons. -### Richtext Editor +## Richtext Editor + The TinyMCE based WYSIWYG editor. This is the standard editor used to edit larger amount of text. The editor has a lot of settings, which can be changed on Richtext editor Data Type in the Settings section. The editor also supports TinyMCE plugins which can be controlled in the configuration file located at `/config/tinyMce.config`. In the default settings some tags such as bullet list can be used. If you want to use other tags like `h1` or `h2`, you need to add stylesheets. @@ -87,7 +111,8 @@ Also turn on "Style select" in the toolbar section. An example of the stylesheet tree is as follows. -
+```json
+
 Stylesheets
 -IE7
 -IE8
@@ -95,19 +120,32 @@ Stylesheets
 -RichEdit
 --h1
 --h2
-
+``` -### Tags -A textbox that allows you to use multiple tags on a Document Type. You can specify a Tag Group for this data type, in case you need to use Tags on different sections of your site (i.e News, Article, Events). +## Tags -### Textarea -A textarea +A textbox that allows you to use multiple tags on a **Document Type**. You can specify a Tag Group for the data type, if you need to use Tags on different sections of your site (i.e. News, Article, Events). -### Textstring -A normal HTML input text field +## Textarea + +A textarea provides a multi-line plain-text editing control. You can set the maximum allowed characters for the textarea and the number of rows, if any. + +## Textstring + +A normal HTML input text field. + +## True/False -### True/False A checkbox which saves either 0 or 1, depending on the checkbox being checked or not. A common use is to create a property with the special alias 'umbracoNaviHide' and the Data Type True/False to enable editors to hide nodes from appearing in a navigation menu. -### Upload +## Upload + Adds an upload field, which allows documents or images to be uploaded to Umbraco. This does not add them to the media library, they are added to the document data. + +There are five Upload Data Types: + +- Upload Article - Used for uploading and storing documents. +- Upload Audio - Used for uploading and storing digital audio files. +- Upload File - Used for uploading and storing different types of files in the Media section +- Upload Vector Graphics - Used for uploading and storing Scalable Vector Graphics (svg) files which are text files containing source code to draw the desired image. +- Upload Video - Used for uploading and storing video files. diff --git a/Fundamentals/Data/Members/index.md b/Fundamentals/Data/Members/index.md index 67c7a1ab8b7..03bc7fdc5be 100644 --- a/Fundamentals/Data/Members/index.md +++ b/Fundamentals/Data/Members/index.md @@ -2,6 +2,7 @@ meta.Title: "Creating Members in Umbraco" meta.Description: "Members are used for registering and authentication external / frontend users of an Umbraco installation. This could be Forum members and Intranet members." versionFrom: 8.0.0 +needsV9Update: "true" --- # Members diff --git a/Fundamentals/Data/index.md b/Fundamentals/Data/index.md index 94511e49105..eee9cfcdcde 100644 --- a/Fundamentals/Data/index.md +++ b/Fundamentals/Data/index.md @@ -2,6 +2,7 @@ meta.Title: "Umbraco Data" meta.Description: "This section focuses on how to create data using the Umbraco backoffice" versionFrom: 7.0.0 +versionTo: 9.0.0 --- # Data diff --git a/Fundamentals/Design/Rendering-Content/index-v8.md b/Fundamentals/Design/Rendering-Content/index-v8.md new file mode 100644 index 00000000000..e1c38447c2c --- /dev/null +++ b/Fundamentals/Design/Rendering-Content/index-v8.md @@ -0,0 +1,116 @@ +--- +meta.Title: "Rendering content" +keywords: content razor v8 version8 +versionFrom: 8.0.0 +--- + +# Rendering content + +_The primary task of any template in Umbraco is to render the values of the current page or the result of a query against the content cache._ + +## Display a value in your template view + +Each property in your [document type](../../Data/Defining-Content/index.md#what-is-a-document-type) has an alias, this is used to specify where in the template view to display the value. + +```html +

@Model.Value("pageTitle")

+
@Model.Value("bodyContent")
+ +``` + +### Specifying types of data + +You can specify the type of data being returned to help you format the value for display, consider the publish date in our example: + +```html +

@(Model.Value("pageTitle"))

+
@(Model.Value("bodyContent"))
+

Article date:

+``` + +### Using ModelsBuilder + +```html +

@Model.PageTitle

+
@Model.BodyContent
+ +``` + +### Working with the grid + +See [Writing out Umbraco Grid Properties](../../Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Render-Grid-In-Template.md#render-grid-in-template).... + +### Using fall-back methods + +The `.Value()` method has a number of optional parameters that support scenarios where we want to "fall-back" to some other content, when the property value does not exist on the current content item. + +We can display a static, default value when a property value is not populated on the current content item: + +```csharp +@Model.Value("pageTitle", fallback: Fallback.ToDefaultValue, defaultValue: "Default page title") +``` + +A second supported method is to traverse up the tree ancestors to try to find a value. If the current content item isn't populated for a property, we can retrieve the value from the parent, grand-parent, or a higher ancestor in the tree. The first ancestor encountered that has a value will be the one returned. + +```csharp +@Model.Value("pageTitle", fallback: Fallback.ToAncestors) +``` + +If developing a multi-lingual site and fall-back languages* have been configured, the third method available is to retrieve a value for a different language, if the language we are requesting does not have content populated. In this way, we could render a field containing French content for a property if it's populated in that language, and if not, default to English. + +```csharp +@Model.Value("pageTitle", "fr", fallback: Fallback.ToLanguage) +``` + +We can also combine these options to create some more sophisticated scenarios. For example we might want to fall-back via language first, and if that doesn't find any populated content, then try to find a value by traversing through the ancestors of the tree. We can do that using the following syntax, with the order of the fall-back options provided determining the order that content will be attempted to be retrieved: + +```csharp +@Model.Value("pageTitle", "fr", fallback: Fallback.To(Fallback.Language, Fallback.Ancestors)) +``` + +In this further example, we are looking for content firstly on the current node for the default language, and if not found we'll search through the ancestors. If failing to find any populated value from them, we'll use the provided default: + +```csharp +@Model.Value("pageTitle", fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: "Default page title") +``` + +We can use similar overloads when working with ModelsBuilder, for example: + +```csharp +// For projects created before January 2020 +@model.Value(x => x.PageTitle, "fr", fallback: Fallback.ToLanguage) +@model.Value(x => x.PageTitle, fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: "Default page title") + +// For projects created after January 2020 +@model.ValueFor(x => x.PageTitle, "fr", fallback: Fallback.ToLanguage) +@model.ValueFor(x => x.PageTitle, fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: "Default page title") +``` + +* Fall-back languages can be configured via the Languages tree within the Settings section. Each language can optionally be provided with a fall-back language, that will be used when content is not populated for the language requested and the appropriate overload parameters are provided. It's possible to chain these language fall-backs, so requesting content for Portuguese, could fall-back to Spanish and then on to English. + +![Configuring fall-back languages](images/language-fallback.png) + +## Query content + +In many cases you want to do more than display values from the current page, like creating a list of pages in a navigation. You can access content relative to the current page using methods such as `Children()`, `Descendants()` & `Ancestors()`. Explore the [full list of methods](../../../Reference/Templating/Mvc/querying.md#traversing). + +You can do this by querying content relative to your current page in template views: + +```csharp +
    + @foreach (var child in Model.Children()) + { +
  • @child.Name
  • + } +
+``` + +You can use the Query Builder in the template editor to build more advanced queries. +![Query button](images/button-v8.png) + +![Query helper](images/query-v8.png) + +### More information + +* [Razor examples](../../../Reference/Templating/Mvc/examples.md) +* [Querying](../../../Reference/Templating/Mvc/querying.md) diff --git a/Fundamentals/Design/Rendering-Content/index-v9.md b/Fundamentals/Design/Rendering-Content/index-v9.md deleted file mode 100644 index 52994933684..00000000000 --- a/Fundamentals/Design/Rendering-Content/index-v9.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -meta.Title: "Rendering content" -keywords: content razor v9 version9 -versionFrom: 9.0.0 ---- - -# Rendering Content - -_The primary task of any template in Umbraco is to render the values of the current page or the result of a query against the content cache._ - -## Display a value in your template view - -Each property in your [document type](../../Data/Defining-Content/index.md#what-is-a-document-type) has an alias, this is used to specify where in the template view to display the value. - -```html -

@Model.Value("pageTitle")

-
@Model.Value("bodyContent")
- -``` - -### Specifying types of data - -You can specify the type of data being returned to help you format the value for display, consider the publish date in our example: - -To use the `` as the data return type, add the `@using Microsoft.AspNetCore.Html;` directive. - -```html -

@(Model.Value("pageTitle"))

-
@(Model.Value("bodyContent"))
-

Article date:

-``` - -### Using ModelsBuilder - -```html -

@Model.PageTitle

-
@Model.BodyContent
- -``` - -### Working with the grid - -To render the content from the Grid, see the [Render Grid in Template](../../Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Render-Grid-In-Template.md#render-grid-in-template) article. - -### Using fall-back methods - -The `.Value()` method has a number of optional parameters that support scenarios where we want to "fall-back" to some other content, when the property value does not exist on the current content item. - -To use the `fallback` type, add the `@using Umbraco.Cms.Core.Models.PublishedContent;` directive. - -- To display a static, default value when a property value is not populated on the current content item: - - ```csharp - @Model.Value("pageTitle", fallback: Fallback.ToDefaultValue, defaultValue: new HtmlString("Default page title")) - ``` - -- A second supported method is to traverse up the tree ancestors to try to find a value. If the current content item isn't populated for a property, we can retrieve the value from the parent, grand-parent, or a higher ancestor in the tree. The first ancestor encountered that has a value will be the one returned. - - ```csharp - @Model.Value("pageTitle", fallback: Fallback.ToAncestors) - ``` - -- If developing a multi-lingual site and fall-back languages* have been configured, the third method available is to retrieve a value for a different language, if the language we are requesting does not have content populated. In this way, we could render a field containing French content for a property if it's populated in that language, and if not, default to English. - - ```csharp - @Model.Value("pageTitle", "fr", fallback: Fallback.ToLanguage) - ``` - -- We can also combine these options to create some more sophisticated scenarios. For example we might want to fall-back via language first, and if that doesn't find any populated content, then try to find a value by traversing through the ancestors of the tree. We can do that using the following syntax, with the order of the fall-back options provided determining the order that content will be attempted to be retrieved: - - ```csharp - @Model.Value("pageTitle", "fr", fallback: Fallback.To(Fallback.Language, Fallback.Ancestors)) - ``` - -- In this example, we are looking for content firstly on the current node for the default language, and if not found we'll search through the ancestors. If failing to find any populated value from them, we'll use the provided default: - - ```csharp - @Model.Value("pageTitle", fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: new HtmlString("Default page title")) - ``` - -- We can use similar overloads when working with ModelsBuilder, for example: - - ```csharp - // For projects created before January 2020 - @Model.Value(x => x.PageTitle, "fr", fallback: Fallback.ToLanguage) - @Model.Value(x => x.PageTitle, fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: new HtmlString("Default page title")) - - // For projects created after January 2020 - @Model.ValueFor(x => x.PageTitle, "fr", fallback: Fallback.ToLanguage) - @Model.ValueFor(x => x.PageTitle, fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: new HtmlString("Default page title")) - ``` - - - Fall-back languages can be configured via the **Languages** tree within the **Settings** section. - - Each language can optionally be provided with a fall-back language, that will be used when content is not populated for the language requested and the appropriate overload parameters are provided. - - It is possible to chain these language fall-backs, so requesting content for Portuguese, could fall-back to Spanish and then on to English. - - ![Configuring fall-back languages](images/language-fallback.png) - -## Query content - -In many cases you want to do more than display values from the current page, like creating a list of pages in a navigation. You can access content relative to the current page using methods such as `Children()`, `Descendants()` & `Ancestors()`. Explore the [full list of methods](../../../Reference/Templating/Mvc/querying.md#traversing). - -You can do this by querying content relative to your current page in template views: - -```csharp -
    - @foreach (var child in Model.Children()) - { -
  • @child.Name()
  • - } -
-``` - -You can use the Query Builder in the template editor to build more advanced queries. -![Query button](images/button-v8.png) - -![Query helper](images/query-v9.png) - -### More information - -- [Razor examples](../../../Reference/Templating/Mvc/examples.md) -- [Querying](../../../Reference/Templating/Mvc/querying.md) diff --git a/Fundamentals/Design/Rendering-Content/index.md b/Fundamentals/Design/Rendering-Content/index.md index e1c38447c2c..52994933684 100644 --- a/Fundamentals/Design/Rendering-Content/index.md +++ b/Fundamentals/Design/Rendering-Content/index.md @@ -1,10 +1,10 @@ --- meta.Title: "Rendering content" -keywords: content razor v8 version8 -versionFrom: 8.0.0 +keywords: content razor v9 version9 +versionFrom: 9.0.0 --- -# Rendering content +# Rendering Content _The primary task of any template in Umbraco is to render the values of the current page or the result of a query against the content cache._ @@ -22,9 +22,11 @@ Each property in your [document type](../../Data/Defining-Content/index.md#what- You can specify the type of data being returned to help you format the value for display, consider the publish date in our example: +To use the `` as the data return type, add the `@using Microsoft.AspNetCore.Html;` directive. + ```html

@(Model.Value("pageTitle"))

-
@(Model.Value("bodyContent"))
+
@(Model.Value("bodyContent"))

Article date:

``` @@ -38,57 +40,61 @@ You can specify the type of data being returned to help you format the value for ### Working with the grid -See [Writing out Umbraco Grid Properties](../../Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Render-Grid-In-Template.md#render-grid-in-template).... +To render the content from the Grid, see the [Render Grid in Template](../../Backoffice/Property-Editors/Built-in-Property-Editors/Grid-Layout/Render-Grid-In-Template.md#render-grid-in-template) article. ### Using fall-back methods The `.Value()` method has a number of optional parameters that support scenarios where we want to "fall-back" to some other content, when the property value does not exist on the current content item. -We can display a static, default value when a property value is not populated on the current content item: +To use the `fallback` type, add the `@using Umbraco.Cms.Core.Models.PublishedContent;` directive. -```csharp -@Model.Value("pageTitle", fallback: Fallback.ToDefaultValue, defaultValue: "Default page title") -``` +- To display a static, default value when a property value is not populated on the current content item: -A second supported method is to traverse up the tree ancestors to try to find a value. If the current content item isn't populated for a property, we can retrieve the value from the parent, grand-parent, or a higher ancestor in the tree. The first ancestor encountered that has a value will be the one returned. + ```csharp + @Model.Value("pageTitle", fallback: Fallback.ToDefaultValue, defaultValue: new HtmlString("Default page title")) + ``` -```csharp -@Model.Value("pageTitle", fallback: Fallback.ToAncestors) -``` +- A second supported method is to traverse up the tree ancestors to try to find a value. If the current content item isn't populated for a property, we can retrieve the value from the parent, grand-parent, or a higher ancestor in the tree. The first ancestor encountered that has a value will be the one returned. -If developing a multi-lingual site and fall-back languages* have been configured, the third method available is to retrieve a value for a different language, if the language we are requesting does not have content populated. In this way, we could render a field containing French content for a property if it's populated in that language, and if not, default to English. + ```csharp + @Model.Value("pageTitle", fallback: Fallback.ToAncestors) + ``` -```csharp -@Model.Value("pageTitle", "fr", fallback: Fallback.ToLanguage) -``` +- If developing a multi-lingual site and fall-back languages* have been configured, the third method available is to retrieve a value for a different language, if the language we are requesting does not have content populated. In this way, we could render a field containing French content for a property if it's populated in that language, and if not, default to English. -We can also combine these options to create some more sophisticated scenarios. For example we might want to fall-back via language first, and if that doesn't find any populated content, then try to find a value by traversing through the ancestors of the tree. We can do that using the following syntax, with the order of the fall-back options provided determining the order that content will be attempted to be retrieved: + ```csharp + @Model.Value("pageTitle", "fr", fallback: Fallback.ToLanguage) + ``` -```csharp -@Model.Value("pageTitle", "fr", fallback: Fallback.To(Fallback.Language, Fallback.Ancestors)) -``` +- We can also combine these options to create some more sophisticated scenarios. For example we might want to fall-back via language first, and if that doesn't find any populated content, then try to find a value by traversing through the ancestors of the tree. We can do that using the following syntax, with the order of the fall-back options provided determining the order that content will be attempted to be retrieved: -In this further example, we are looking for content firstly on the current node for the default language, and if not found we'll search through the ancestors. If failing to find any populated value from them, we'll use the provided default: + ```csharp + @Model.Value("pageTitle", "fr", fallback: Fallback.To(Fallback.Language, Fallback.Ancestors)) + ``` -```csharp -@Model.Value("pageTitle", fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: "Default page title") -``` +- In this example, we are looking for content firstly on the current node for the default language, and if not found we'll search through the ancestors. If failing to find any populated value from them, we'll use the provided default: -We can use similar overloads when working with ModelsBuilder, for example: + ```csharp + @Model.Value("pageTitle", fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: new HtmlString("Default page title")) + ``` -```csharp -// For projects created before January 2020 -@model.Value(x => x.PageTitle, "fr", fallback: Fallback.ToLanguage) -@model.Value(x => x.PageTitle, fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: "Default page title") +- We can use similar overloads when working with ModelsBuilder, for example: -// For projects created after January 2020 -@model.ValueFor(x => x.PageTitle, "fr", fallback: Fallback.ToLanguage) -@model.ValueFor(x => x.PageTitle, fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: "Default page title") -``` + ```csharp + // For projects created before January 2020 + @Model.Value(x => x.PageTitle, "fr", fallback: Fallback.ToLanguage) + @Model.Value(x => x.PageTitle, fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: new HtmlString("Default page title")) + + // For projects created after January 2020 + @Model.ValueFor(x => x.PageTitle, "fr", fallback: Fallback.ToLanguage) + @Model.ValueFor(x => x.PageTitle, fallback: Fallback.To(Fallback.Ancestors, Fallback.DefaultValue), defaultValue: new HtmlString("Default page title")) + ``` -* Fall-back languages can be configured via the Languages tree within the Settings section. Each language can optionally be provided with a fall-back language, that will be used when content is not populated for the language requested and the appropriate overload parameters are provided. It's possible to chain these language fall-backs, so requesting content for Portuguese, could fall-back to Spanish and then on to English. + - Fall-back languages can be configured via the **Languages** tree within the **Settings** section. + - Each language can optionally be provided with a fall-back language, that will be used when content is not populated for the language requested and the appropriate overload parameters are provided. + - It is possible to chain these language fall-backs, so requesting content for Portuguese, could fall-back to Spanish and then on to English. -![Configuring fall-back languages](images/language-fallback.png) + ![Configuring fall-back languages](images/language-fallback.png) ## Query content @@ -100,7 +106,7 @@ You can do this by querying content relative to your current page in template vi ``` @@ -108,9 +114,9 @@ You can do this by querying content relative to your current page in template vi You can use the Query Builder in the template editor to build more advanced queries. ![Query button](images/button-v8.png) -![Query helper](images/query-v8.png) +![Query helper](images/query-v9.png) ### More information -* [Razor examples](../../../Reference/Templating/Mvc/examples.md) -* [Querying](../../../Reference/Templating/Mvc/querying.md) +- [Razor examples](../../../Reference/Templating/Mvc/examples.md) +- [Querying](../../../Reference/Templating/Mvc/querying.md) diff --git a/Fundamentals/Design/Rendering-Media/index-v9.md b/Fundamentals/Design/Rendering-Media/index-v8.md similarity index 93% rename from Fundamentals/Design/Rendering-Media/index-v9.md rename to Fundamentals/Design/Rendering-Media/index-v8.md index 0921bdaa051..1f25b86f5cf 100644 --- a/Fundamentals/Design/Rendering-Media/index-v9.md +++ b/Fundamentals/Design/Rendering-Media/index-v8.md @@ -1,8 +1,8 @@ --- meta.Title: "Rendering Media in Umbraco" meta.Description: "Info on rendering media items and imaging cropping" -keywords: v9 version8 rendering media imagecropper -versionFrom: 9.0.0 +keywords: v8 version8 rendering media imagecropper +versionFrom: 8.0.0 --- # Rendering media @@ -36,12 +36,12 @@ _Assumption: We are going to assume that our media item has an ID of **1234**, a @{ // The Umbraco Helper has a Media method that will retrieve a Media Item by Id in the form of IPublishedContent, in this example the Media Item has a unique id of 1234: - var mediaItem = Umbraco.Media(1055); + var mediaItem = Umbraco.Media(1234); } @if (mediaItem!=null) { // To get the url for your media item, you use the Url property: - var url = mediaItem.Url(); + var url = mediaItem.Url; // to read a property by alias var imageHeight = mediaItem.Value("umbracoHeight"); var imageWidth = mediaItem.Value("umbracoWidth"); @@ -61,14 +61,14 @@ As with example one, we are accessing a MediaType `image` using the same ID assu @using ContentModels = Umbraco.Web.PublishedModels; @{ // Since the Image Model generated by Modelsbuilder is a compatible type to IPublishedContent we can use the 'as' operator to convert into the ModelsBuilder Umbraco.Web.PublishedModels.Image class - var mediaItem = Umbraco.Media(1055) as ContentModels.Image; + var mediaItem = Umbraco.Media(1148) as ContentModels.Image; } @if (mediaItem!=null) { // you could add this as an extension method to the Umbraco.Web.PublishedModels.Image class var orientationCssClass = mediaItem.UmbracoWidth > mediaItem.UmbracoHeight ? "img-landscape" : "img-portrait"; - @mediaItem.Name + @mediaItem.Name } ``` @@ -111,6 +111,6 @@ If you want the original, uncropped image, you can ignore the GetCropUrl extensi ### More information -- [Media Picker](../../Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index-v9.md) -- [Image Cropper](../../Backoffice/Property-Editors/Built-in-Property-Editors/Image-Cropper/index.md) +- [Media Picker](../../Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index.md) +- [Image Cropper](../../Backoffice/Property-Editors/Built-in-Property-Editors/Image-Cropper.md) - [Creating a Media Type](../../Data/Creating-Media/index.md#creating-a-media-type) diff --git a/Fundamentals/Design/Rendering-Media/index.md b/Fundamentals/Design/Rendering-Media/index.md index 1f25b86f5cf..99ae3995b8f 100644 --- a/Fundamentals/Design/Rendering-Media/index.md +++ b/Fundamentals/Design/Rendering-Media/index.md @@ -1,8 +1,8 @@ --- meta.Title: "Rendering Media in Umbraco" meta.Description: "Info on rendering media items and imaging cropping" -keywords: v8 version8 rendering media imagecropper -versionFrom: 8.0.0 +keywords: v9 version8 rendering media imagecropper +versionFrom: 9.0.0 --- # Rendering media @@ -36,12 +36,12 @@ _Assumption: We are going to assume that our media item has an ID of **1234**, a @{ // The Umbraco Helper has a Media method that will retrieve a Media Item by Id in the form of IPublishedContent, in this example the Media Item has a unique id of 1234: - var mediaItem = Umbraco.Media(1234); + var mediaItem = Umbraco.Media(1055); } @if (mediaItem!=null) { // To get the url for your media item, you use the Url property: - var url = mediaItem.Url; + var url = mediaItem.Url(); // to read a property by alias var imageHeight = mediaItem.Value("umbracoHeight"); var imageWidth = mediaItem.Value("umbracoWidth"); @@ -61,14 +61,14 @@ As with example one, we are accessing a MediaType `image` using the same ID assu @using ContentModels = Umbraco.Web.PublishedModels; @{ // Since the Image Model generated by Modelsbuilder is a compatible type to IPublishedContent we can use the 'as' operator to convert into the ModelsBuilder Umbraco.Web.PublishedModels.Image class - var mediaItem = Umbraco.Media(1148) as ContentModels.Image; + var mediaItem = Umbraco.Media(1055) as ContentModels.Image; } @if (mediaItem!=null) { // you could add this as an extension method to the Umbraco.Web.PublishedModels.Image class var orientationCssClass = mediaItem.UmbracoWidth > mediaItem.UmbracoHeight ? "img-landscape" : "img-portrait"; - @mediaItem.Name + @mediaItem.Name } ``` @@ -112,5 +112,5 @@ If you want the original, uncropped image, you can ignore the GetCropUrl extensi ### More information - [Media Picker](../../Backoffice/Property-Editors/Built-in-Property-Editors/Media-Picker/index.md) -- [Image Cropper](../../Backoffice/Property-Editors/Built-in-Property-Editors/Image-Cropper.md) +- [Image Cropper](../../Backoffice/Property-Editors/Built-in-Property-Editors/Image-Cropper/index.md) - [Creating a Media Type](../../Data/Creating-Media/index.md#creating-a-media-type) diff --git a/Fundamentals/Design/Stylesheets-Javascript/index-v7.md b/Fundamentals/Design/Stylesheets-Javascript/index-v7.md new file mode 100644 index 00000000000..a9302f10084 --- /dev/null +++ b/Fundamentals/Design/Stylesheets-Javascript/index-v7.md @@ -0,0 +1,116 @@ +--- +meta.Title: "Working with stylesheets and JavaScript in Umbraco" +meta.Description: "Information on working with stylesheets and JavaScript in Umbraco, including bundling & minification" +versionFrom: 7.0.0 +--- + +# Working with stylesheets and JavaScript + +## Stylesheets in the Backoffice + +You can create and edit stylesheets in the Stylesheets folder in the Settings section of the Backoffice. + +![Creating a new stylesheet](images/1-creating-stylesheet.png) + +In the Create menu, there are several options available: + +* Stylesheet file (for use in templates/views) +* Rich Text Editor stylesheet file (for use in [Rich Text Editor](../../Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/)) +* Folder (for keeping stylesheets organized) + +:::note +It is currently not possible to use any CSS preprocessor (such as SASS) in the backoffice. +::: + +After creating a new stylesheet, you would work with it as you would with templates or javascript files - using the built-in backoffice text editor. +When you're working with stylesheets, you also have access to the Rich Text Editor, which allows you to create CSS styles and get a real-time preview. + +![Stylesheet RTE](images/2-rte-editor.png) + +The rules you create in the Rich Text Editor section will carry over to the Code tab. + +![Stylesheet RTE tab](images/3-rte-editor-p2.png) +![Stylesheet Code tab](images/3-rte-editor-p3.png) + +To reference your newly included stylesheet in a template file, navigate to Templates, pick the template you like (css files are usually referenced in the layout or home templates) and link to it with the `link` tag. + +![Linking CSS in template](images/4-link-css.png) + +By default, the stylesheets will be saved in the `css` folder in the solution. +To reference them you can use either of the methods used in the above screenshot. + +```html + +``` +or +```html + +``` + +With the stylesheet referenced, you will be able to style the template file with the rules and classes defined in the stylesheet. + +Your stylesheets can be used in Rich Text Editors (datatype) as well - please see the [Rich Text Editor](../../Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/RTE-Styles/) documentation for more information. + +:::note +If your RTE is styled differently on the frontend of the site, the backoffice styling might be getting overwritten by other stylesheets you have included. +::: + +## JavaScript files in the Backoffice + +To create and edit JavaScript files in the Backoffice, head on over to the Scripts folder in the Settings section of the Backoffice. + +![Creating a new JavaScript](images/8-create-js.png) + +From here you can add a new JavaScript file, or a new folder. + +Add a new JavaScript file and write your code: + +![Sample JS script](images/9-myscript.png) + +Then, navigate to the template where you would like to include your JS file. +```html + +``` + +![Reference the script in template](images/10-reference-script.png) + +By default all JavaScript files will be stored in the `scripts` folder in the solution. + +:::tip +If you are working locally, you can create CSS and JS files outside of the Backoffice - long as they are placed in appropiate folders (`css` and `scripts`), they will show up in the Backoffice. +::: + +## Bundling & Minification for JavaScript and CSS + +You can use whichever tool you are comfortable with for bundling & minification, though it is worth noting that Umbraco ships with the ClientDependency Framework which offers runtime bundling & minification. + +You can bundle and minify as follows in a view template file. + +```csharp +@using ClientDependency.Core.Mvc +@using ClientDependency.Core +@{ + Html.RequiresJs("~/scripts/Script1.js", 1); + Html.RequiresJs("~/scripts/Script2.js", 2); + + Html.RequiresCss("~/css/style.css"); +} + + + @Html.RenderCssHere() + @Html.RenderJsHere() + +``` + +:::note +If you are running your site locally and the compilation setting for debugging in your `web.config` is set to true, the Client Dependency Framework will not minify or combine your js/CSS. This is by design to help when debugging issues in a local environment. However, it also means if there is a problem minifying a file you will only encounter the issue when you deploy. To test minification and bundling locally, set the debug setting to false for compilation. +::: + +:::note +When adding stylesheet references to `Html.RequiresCss` there is no need to add `?v=1` to specify a specific version of the asset for cache-busting. Doing so will break the minification and combination of your files. The Client Dependency Framework uses the version number from `/config/clientdependency.config` on the querystring of the combined and minified resource. + +To force your updated, minified and combined assets to be refreshed, increment this version number by 1 in the config file to bust any browser caching on the old version of your assets. See the [Healthcheck for Client Dependency Framework](https://our.umbraco.com/packages/developer-tools/health-check-for-client-dependency-framework/) package to do this from the backoffice, or you can automatically increment the version in your build and deploy process. +::: + + +Full details of the ClientDependency Framework can be found here: [https://github.com/Shandem/ClientDependency](https://github.com/Shandem/ClientDependency) diff --git a/Fundamentals/Design/Stylesheets-Javascript/index-v9.md b/Fundamentals/Design/Stylesheets-Javascript/index-v9.md deleted file mode 100644 index 3fa8f059808..00000000000 --- a/Fundamentals/Design/Stylesheets-Javascript/index-v9.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -versionFrom: 9.0.0 -meta.Title: "Working with stylesheets and JavaScript in Umbraco" -meta.Description: "Information on working with stylesheets and JavaScript in Umbraco, including bundling & minification" -verified-against: rc-02 -state: complete -updated-links: true ---- - -# Working with stylesheets and JavaScript - -## Stylesheets in the Backoffice - -You can create and edit stylesheets in the Stylesheets folder in the Settings section of the Backoffice. - -![Creating a new stylesheet](images/1-creating-stylesheet.png) - -In the Create menu, there are several options available: - -* Stylesheet file (for use in templates/views) -* Rich Text Editor stylesheet file (for use in [Rich Text Editor](../../Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index-v9.md)) -* Folder (for keeping stylesheets organized) - -:::note -It is currently not possible to use any CSS preprocessor (such as SASS) in the backoffice. -::: - -After creating a new stylesheet, you would work with it as you would with templates or javascript files - using the built-in backoffice text editor. -When you're working with stylesheets, you also have access to the Rich Text Editor, which allows you to create CSS styles and get a real-time preview. - -![Stylesheet RTE](images/2-rte-editor.png) - -The rules you create in the Rich Text Editor section will carry over to the Code tab. - -![Stylesheet RTE tab](images/3-rte-editor-p2.png) -![Stylesheet Code tab](images/3-rte-editor-p3.png) - -To reference your newly included stylesheet in a template file, navigate to Templates, pick the template you like (css files are usually referenced in the layout or home templates) and link to it with the `link` tag. - -![Linking CSS in template](images/4-link-css-v9.png) - -By default, the stylesheets will be saved in the `wwwroot/css` folder in the solution. -To reference them you can use either of the methods used in the above screenshot. - -```html - -``` -or -```html - -``` - -With the stylesheet referenced, you will be able to style the template file with the rules and classes defined in the stylesheet. - -Your stylesheets can be used in Rich Text Editors (datatype) as well - please see the [Rich Text Editor](../../Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/RTE-Styles/index.md) documentation for more information. - -:::note -If your RTE is styled differently on the frontend of the site, the backoffice styling might be getting overwritten by other stylesheets you have included. -::: - -## JavaScript files in the Backoffice - -To create and edit JavaScript files in the Backoffice, head on over to the Scripts folder in the Settings section of the Backoffice. - -![Creating a new JavaScript](images/8-create-js.png) - -From here you can add a new JavaScript file, or a new folder. - -Add a new JavaScript file and write your code: - -![Sample JS script](images/9-myscript.png) - -Then, navigate to the template where you would like to include your JS file. - -```html - -``` - -![Reference the script in template](images/10-reference-script-v9.png) - -By default all JavaScript files will be stored in the `wwwroot/scripts` folder in the solution. - -:::tip -If you are working locally, you can create CSS and JS files outside of the Backoffice - as long as they are placed in appropriate folders (`css` and `scripts`), they will show up in the Backoffice when you right-click on the folder and then pick reload. -::: - -## Bundling & Minification for JavaScript and CSS - -You can use whichever tool you are comfortable with for bundling & minification by implementing the `IRuntimeMinifier` interface in your custom minifier class, though it is worth noting that Umbraco 9 ships with Smidge which offers lightweight runtime bundling and minification. - -You can create various bundles of your site's CSS or JavaScript files in your code that can be rendered later in your views. There can be a single bundle for the entire site, or a common bundle for the files you want to be loaded on every page, as well as page-specific bundles, just by listing your resources in the order you like. -```csharp -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.WebAssets; - -namespace MyNamespace -{ - public class MyComponent : IComponent - { - private readonly IRuntimeMinifier _runtimeMinifier; - - public MyComponent(IRuntimeMinifier runtimeMinifier) => _runtimeMinifier = runtimeMinifier; - - public void Initialize() - { - _runtimeMinifier.CreateJsBundle("inline-js-bundle", - BundlingOptions.NotOptimizedAndComposite, - new[] { "~/scripts/myScript1.js", "~/scripts/myScript2.js" }); - - _runtimeMinifier.CreateCssBundle("inline-css-bundle", - BundlingOptions.NotOptimizedAndComposite, - new[] { "~/css/mystylesheet.css" }); - } - - public void Terminate() { } - } - - public class MyComposer : ComponentComposer - { } -} -``` - -Then, you can render the bundles by the bundle name in a view template file: - -```csharp - - - - - - -``` - -Or by using our `IRuntimeMinifier`: - -:::note -In case you are in Debug mode, your bundles won't be minified or bundled, so you would need to set `"Debug": false` in your appsettings file. -::: - -```csharp -@using Umbraco.Cms.Core.WebAssets -@inject IRuntimeMinifier runtimeMinifier - - - - @Html.Raw(await runtimeMinifier.RenderJsHereAsync("inline-js-bundle")) - @Html.Raw(await runtimeMinifier.RenderCssHereAsync("inline-css-bundle")) - - -``` - -Another possibility is to declare bundles inline in your views using Smidge directly: - -```csharp -@using Smidge -@{ - SmidgeHelper - .CreateJsBundle("inline-js-bundle") - .RequiresJs("~/scripts/myScript1.js", "~/scripts/myScript2.js"); - - SmidgeHelper - .CreateCssBundle("inline-css-bundle") - .RequiresCss("~/css/mystylesheet.css"); -} - - - @await SmidgeHelper.JsHereAsync("inline-js-bundle") - @await SmidgeHelper.CssHereAsync("inline-css-bundle") - - -``` - -Full details about Smidge can be found here: [https://github.com/Shazwazza/Smidge](https://github.com/Shazwazza/Smidge) \ No newline at end of file diff --git a/Fundamentals/Design/Stylesheets-Javascript/index.md b/Fundamentals/Design/Stylesheets-Javascript/index.md index a9302f10084..d7b785df116 100644 --- a/Fundamentals/Design/Stylesheets-Javascript/index.md +++ b/Fundamentals/Design/Stylesheets-Javascript/index.md @@ -1,7 +1,10 @@ --- +versionFrom: 9.0.0 meta.Title: "Working with stylesheets and JavaScript in Umbraco" meta.Description: "Information on working with stylesheets and JavaScript in Umbraco, including bundling & minification" -versionFrom: 7.0.0 +verified-against: rc-02 +state: complete +updated-links: true --- # Working with stylesheets and JavaScript @@ -15,7 +18,7 @@ You can create and edit stylesheets in the Stylesheets folder in the Settings se In the Create menu, there are several options available: * Stylesheet file (for use in templates/views) -* Rich Text Editor stylesheet file (for use in [Rich Text Editor](../../Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/)) +* Rich Text Editor stylesheet file (for use in [Rich Text Editor](../../Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/index.md)) * Folder (for keeping stylesheets organized) :::note @@ -34,22 +37,22 @@ The rules you create in the Rich Text Editor section will carry over to the Code To reference your newly included stylesheet in a template file, navigate to Templates, pick the template you like (css files are usually referenced in the layout or home templates) and link to it with the `link` tag. -![Linking CSS in template](images/4-link-css.png) +![Linking CSS in template](images/4-link-css-v9.png) -By default, the stylesheets will be saved in the `css` folder in the solution. +By default, the stylesheets will be saved in the `wwwroot/css` folder in the solution. To reference them you can use either of the methods used in the above screenshot. ```html - + ``` or ```html - + ``` With the stylesheet referenced, you will be able to style the template file with the rules and classes defined in the stylesheet. -Your stylesheets can be used in Rich Text Editors (datatype) as well - please see the [Rich Text Editor](../../Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/RTE-Styles/) documentation for more information. +Your stylesheets can be used in Rich Text Editors (datatype) as well - please see the [Rich Text Editor](../../Backoffice/Property-Editors/Built-in-Property-Editors/Rich-Text-Editor/RTE-Styles/index.md) documentation for more information. :::note If your RTE is styled differently on the frontend of the site, the backoffice styling might be getting overwritten by other stylesheets you have included. @@ -68,49 +71,103 @@ Add a new JavaScript file and write your code: ![Sample JS script](images/9-myscript.png) Then, navigate to the template where you would like to include your JS file. + ```html ``` -![Reference the script in template](images/10-reference-script.png) +![Reference the script in template](images/10-reference-script-v9.png) -By default all JavaScript files will be stored in the `scripts` folder in the solution. +By default all JavaScript files will be stored in the `wwwroot/scripts` folder in the solution. :::tip -If you are working locally, you can create CSS and JS files outside of the Backoffice - long as they are placed in appropiate folders (`css` and `scripts`), they will show up in the Backoffice. +If you are working locally, you can create CSS and JS files outside of the Backoffice - as long as they are placed in appropriate folders (`css` and `scripts`), they will show up in the Backoffice when you right-click on the folder and then pick reload. ::: ## Bundling & Minification for JavaScript and CSS -You can use whichever tool you are comfortable with for bundling & minification, though it is worth noting that Umbraco ships with the ClientDependency Framework which offers runtime bundling & minification. - -You can bundle and minify as follows in a view template file. +You can use whichever tool you are comfortable with for bundling & minification by implementing the `IRuntimeMinifier` interface in your custom minifier class, though it is worth noting that Umbraco 9 ships with Smidge which offers lightweight runtime bundling and minification. +You can create various bundles of your site's CSS or JavaScript files in your code that can be rendered later in your views. There can be a single bundle for the entire site, or a common bundle for the files you want to be loaded on every page, as well as page-specific bundles, just by listing your resources in the order you like. ```csharp -@using ClientDependency.Core.Mvc -@using ClientDependency.Core -@{ - Html.RequiresJs("~/scripts/Script1.js", 1); - Html.RequiresJs("~/scripts/Script2.js", 2); +using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.WebAssets; + +namespace MyNamespace +{ + public class MyComponent : IComponent + { + private readonly IRuntimeMinifier _runtimeMinifier; + + public MyComponent(IRuntimeMinifier runtimeMinifier) => _runtimeMinifier = runtimeMinifier; + + public void Initialize() + { + _runtimeMinifier.CreateJsBundle("inline-js-bundle", + BundlingOptions.NotOptimizedAndComposite, + new[] { "~/scripts/myScript1.js", "~/scripts/myScript2.js" }); + + _runtimeMinifier.CreateCssBundle("inline-css-bundle", + BundlingOptions.NotOptimizedAndComposite, + new[] { "~/css/mystylesheet.css" }); + } + + public void Terminate() { } + } - Html.RequiresCss("~/css/style.css"); + public class MyComposer : ComponentComposer + { } } +``` + +Then, you can render the bundles by the bundle name in a view template file: + +```csharp - @Html.RenderCssHere() - @Html.RenderJsHere() + + + ``` +Or by using our `IRuntimeMinifier`: + :::note -If you are running your site locally and the compilation setting for debugging in your `web.config` is set to true, the Client Dependency Framework will not minify or combine your js/CSS. This is by design to help when debugging issues in a local environment. However, it also means if there is a problem minifying a file you will only encounter the issue when you deploy. To test minification and bundling locally, set the debug setting to false for compilation. +In case you are in Debug mode, your bundles won't be minified or bundled, so you would need to set `"Debug": false` in your appsettings file. ::: -:::note -When adding stylesheet references to `Html.RequiresCss` there is no need to add `?v=1` to specify a specific version of the asset for cache-busting. Doing so will break the minification and combination of your files. The Client Dependency Framework uses the version number from `/config/clientdependency.config` on the querystring of the combined and minified resource. +```csharp +@using Umbraco.Cms.Core.WebAssets +@inject IRuntimeMinifier runtimeMinifier -To force your updated, minified and combined assets to be refreshed, increment this version number by 1 in the config file to bust any browser caching on the old version of your assets. See the [Healthcheck for Client Dependency Framework](https://our.umbraco.com/packages/developer-tools/health-check-for-client-dependency-framework/) package to do this from the backoffice, or you can automatically increment the version in your build and deploy process. -::: + + + @Html.Raw(await runtimeMinifier.RenderJsHereAsync("inline-js-bundle")) + @Html.Raw(await runtimeMinifier.RenderCssHereAsync("inline-css-bundle")) + + +``` + +Another possibility is to declare bundles inline in your views using Smidge directly: + +```csharp +@using Smidge +@{ + SmidgeHelper + .CreateJsBundle("inline-js-bundle") + .RequiresJs("~/scripts/myScript1.js", "~/scripts/myScript2.js"); + SmidgeHelper + .CreateCssBundle("inline-css-bundle") + .RequiresCss("~/css/mystylesheet.css"); +} + + + @await SmidgeHelper.JsHereAsync("inline-js-bundle") + @await SmidgeHelper.CssHereAsync("inline-css-bundle") + + +``` -Full details of the ClientDependency Framework can be found here: [https://github.com/Shandem/ClientDependency](https://github.com/Shandem/ClientDependency) +Full details about Smidge can be found here: [https://github.com/Shazwazza/Smidge](https://github.com/Shazwazza/Smidge) \ No newline at end of file diff --git a/Fundamentals/Design/index.md b/Fundamentals/Design/index.md index 9065581cd86..73a6328553d 100644 --- a/Fundamentals/Design/index.md +++ b/Fundamentals/Design/index.md @@ -1,5 +1,6 @@ --- versionFrom: 7.0.0 +versionTo: 9.0.0 --- # Design diff --git a/Fundamentals/Setup/Install/Unattended-Install-v8.md b/Fundamentals/Setup/Install/Unattended-Install-v8.md new file mode 100644 index 00000000000..f2209fa3105 --- /dev/null +++ b/Fundamentals/Setup/Install/Unattended-Install-v8.md @@ -0,0 +1,84 @@ +--- +meta.Title: "Unattended installation of Umbraco CMS" +meta.Description: "A guide on how to install Umbraco unattended including details about the feature." +versionFrom: 8.11.0 +--- + +# Unattended Installs + +In some cases, you might need to install one or multiple Umbraco instances automatically without having to run through the installation wizard to configure the instance. + +You can use the **Unattended installs** feature to allow for quick installation and set up of Umbraco instances on something like Azure Web Apps. + +This article will give you the details you need to install Umbraco unattended. + +:::warning +When installing Umbraco using the unattended install feature, you will not be able to access the Umbraco backoffice once the installation has completed, as no password has been set for the default user. + +Instead, you will need to configure an external login provider or set a password for the user in some other way. + +Support for adding users through the unattended installation process will be added at a later point. +::: + +## Get the correct version of Umbraco + +In order to install Umbraco without having to run through the installation wizard, you need **Umbraco version 8.11.0+**. + +Get a clean instance from either the [NuGet feed](https://www.nuget.org/packages/UmbracoCms/) or download a zip file directly from [Downloads](https://our.umbraco.com/download). + +## Configure your database + +As you will not be running through the installation wizard when using the unattended installs feature, you need to manually tell Umbraco which database to use. + +* Set up and configure a new database - see [Requirements](../Requirements/#hosting) for details. +* Add the connectionstring to the `web.config` file. + +Example: + +```xml + + + + +``` + +## Define the correct Umbraco version + +In order for Umbraco to be installed correctly, you will need to specify the exact version number before initializing the installation. + +The version needs to be specified using the `Umbraco.Core.ConfigurationStatus` key in the `` section of the `web.config` file. + +Example: + +```xml + + +``` + +## Enable the unattended installs feature + +The unattended installs feature is disabled by default and in order to enable it, you need to add the following key to the `appSettings` section of the `web.config` file. + +```xml + + +``` + +Remember to set the value to `true`. + +## Initialize the unattended install + +After completing the 3 steps above you can now initialize the installation by booting up the Umbraco instance. + +Once it has completed, you should see the following when visiting the frontend of the site. + +![Frontend of Umbraco site installed using the unattended installs feature](images/unattended/final-screen.png) + +## Configuration options + +| | Set ConfigurationStatus | Include connectionstring | InstallUnattended value | +|--- |--- |--- |--- | +| Default | false | false | false | +| Unattended (no installer, will install without a user password) | true | true | true | +| Pre configured (will run installer without DB option) | false | true | false | +| Pre configured (will run installer without DB option) | true | true | false | diff --git a/Fundamentals/Setup/Install/Unattended-Install-v9.md b/Fundamentals/Setup/Install/Unattended-Install-v9.md deleted file mode 100644 index 26b9ffdaedc..00000000000 --- a/Fundamentals/Setup/Install/Unattended-Install-v9.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -versionFrom: 9.0.0 -verified-against: alpha-4 -state: partial -updated-links: false ---- - -# Unattended Installs - -In some cases, you might need to install one or multiple Umbraco instances automatically without having to run through the installation wizard to configure the instance. - -You can use the **Unattended installs** feature to allow for quick installation and set up of Umbraco instances on something like Azure Web Apps. - -This article will give you the details you need to install Umbraco unattended. - -## Get clean install of Umbraco - -In order to get a clean instance of Umbraco either follow our installation guide for how to [Install an Umbraco project template](./install-umbraco-with-templates.md/#InstallUmbracowith.NETCLI) or download a zip file directly from [Downloads](https://our.umbraco.com/download). - -## Configure your database - -As you will not be running through the installation wizard when using the unattended installs feature, you need to manually tell Umbraco which database to use. - -* Set up and configure a new database - see [Requirements](../Requirements/#hosting) for details. -* Add the connection string using configuration. - -Example in appsettings.json - -```json -{ - "ConnectionStrings": { - "umbracoDbDSN": "server=localhost;database=UmbracoUnicore;user id=sa;password='P@ssw0rd'" - } -} -``` - -## Enable the unattended installs feature - -The unattended installs feature is disabled by default and in order to enable it, you need to add the following JSON object to a JSON configuration source. - -```json -{ - "Umbraco": { - "CMS": { - "Unattended": { - "InstallUnattended": true, - "UnattendedUserName": "FRIENDLY_NAME", - "UnattendedUserEmail": "EMAIL", - "UnattendedUserPassword": "PASSWORD" - } - } - } -} -``` - -Remember to set the value of `InstallUnattended` to `true`. - -Alternatively you may set your configuration with Environment Variables or other means. Learn more about this in the [Microsoft .Net Core config documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#environment-variables). - -The keys for this would then be as follows: - -```none -Umbraco__CMS__Unattended__InstallUnattended -Umbraco__CMS__Unattended__UnattendedUserName -Umbraco__CMS__Unattended__UnattendedUserEmail -Umbraco__CMS__Unattended__UnattendedUserPassword -``` - -## Initialize the unattended install - -After completing the steps above you can now initialize the installation by booting up the Umbraco instance. - -Once it has completed, you should see the following when visiting the frontend of the site. - -![Frontend of Umbraco site installed using the unattended installs feature](images/Unattended/final-screen.png) - -## Configuration options - -Depending on your preferences, you can use any type of configuration to specify the connection string and login information, as well as enable unattended install. With the extending configuration functionality, it is possible to read from all kinds of sources. One example can be using a JSON file or environment variables. - -**Program.cs** has a condition, which if met, an *appsettings.Local.json* file will be added and configured as a configuration source. - -```c# -#if DEBUG - .ConfigureAppConfiguration(config - => config.AddJsonFile( - "appsettings.Local.json", - optional: true, - reloadOnChange: true)) -#endif -``` - -Having intellisense will help you to easily add your connection string and information needed for the unattended install. - -```json -{ - "ConnectionStrings": { - "umbracoDbDSN": "server=localhost;database=UmbracoUnicore;user id=sa;password='P@ssw0rd'" - }, - "Umbraco": { - "CMS": { - "Unattended": { - "InstallUnattended": true, - "UnattendedUserName": "FRIENDLY_NAME", - "UnattendedUserEmail": "EMAIL", - "UnattendedUserPassword": "PASSWORD" - } - } - } -} -``` - -## More support - -We have added support for unattended installs with Name, Email and Password, and Connection String as CLI params, which are also available in Visual Studio. There you can fill in your information as follows: - -### CLI - -```powershell -dotnet new umbraco -n MyNewProject --FriendlyName "Friendly User" --Email user@email.com --Password password1234 --ConnectionString "Server=(localdb)\\Umbraco;Database=MyDatabase;Integrated Security=true" --version 9.0.0 -``` - -### Visual Studio - -![Set up unattended install through Visual Studio](images/Unattended/VS-unattended-install.png) diff --git a/Fundamentals/Setup/Install/Unattended-Install.md b/Fundamentals/Setup/Install/Unattended-Install.md index f2209fa3105..26b9ffdaedc 100644 --- a/Fundamentals/Setup/Install/Unattended-Install.md +++ b/Fundamentals/Setup/Install/Unattended-Install.md @@ -1,7 +1,8 @@ --- -meta.Title: "Unattended installation of Umbraco CMS" -meta.Description: "A guide on how to install Umbraco unattended including details about the feature." -versionFrom: 8.11.0 +versionFrom: 9.0.0 +verified-against: alpha-4 +state: partial +updated-links: false --- # Unattended Installs @@ -12,73 +13,113 @@ You can use the **Unattended installs** feature to allow for quick installation This article will give you the details you need to install Umbraco unattended. -:::warning -When installing Umbraco using the unattended install feature, you will not be able to access the Umbraco backoffice once the installation has completed, as no password has been set for the default user. +## Get clean install of Umbraco -Instead, you will need to configure an external login provider or set a password for the user in some other way. - -Support for adding users through the unattended installation process will be added at a later point. -::: - -## Get the correct version of Umbraco - -In order to install Umbraco without having to run through the installation wizard, you need **Umbraco version 8.11.0+**. - -Get a clean instance from either the [NuGet feed](https://www.nuget.org/packages/UmbracoCms/) or download a zip file directly from [Downloads](https://our.umbraco.com/download). +In order to get a clean instance of Umbraco either follow our installation guide for how to [Install an Umbraco project template](./install-umbraco-with-templates.md/#InstallUmbracowith.NETCLI) or download a zip file directly from [Downloads](https://our.umbraco.com/download). ## Configure your database As you will not be running through the installation wizard when using the unattended installs feature, you need to manually tell Umbraco which database to use. * Set up and configure a new database - see [Requirements](../Requirements/#hosting) for details. -* Add the connectionstring to the `web.config` file. +* Add the connection string using configuration. -Example: +Example in appsettings.json -```xml - - - - +```json +{ + "ConnectionStrings": { + "umbracoDbDSN": "server=localhost;database=UmbracoUnicore;user id=sa;password='P@ssw0rd'" + } +} ``` -## Define the correct Umbraco version +## Enable the unattended installs feature -In order for Umbraco to be installed correctly, you will need to specify the exact version number before initializing the installation. +The unattended installs feature is disabled by default and in order to enable it, you need to add the following JSON object to a JSON configuration source. + +```json +{ + "Umbraco": { + "CMS": { + "Unattended": { + "InstallUnattended": true, + "UnattendedUserName": "FRIENDLY_NAME", + "UnattendedUserEmail": "EMAIL", + "UnattendedUserPassword": "PASSWORD" + } + } + } +} +``` -The version needs to be specified using the `Umbraco.Core.ConfigurationStatus` key in the `` section of the `web.config` file. +Remember to set the value of `InstallUnattended` to `true`. -Example: +Alternatively you may set your configuration with Environment Variables or other means. Learn more about this in the [Microsoft .Net Core config documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#environment-variables). -```xml - - +The keys for this would then be as follows: + +```none +Umbraco__CMS__Unattended__InstallUnattended +Umbraco__CMS__Unattended__UnattendedUserName +Umbraco__CMS__Unattended__UnattendedUserEmail +Umbraco__CMS__Unattended__UnattendedUserPassword ``` -## Enable the unattended installs feature +## Initialize the unattended install -The unattended installs feature is disabled by default and in order to enable it, you need to add the following key to the `appSettings` section of the `web.config` file. +After completing the steps above you can now initialize the installation by booting up the Umbraco instance. + +Once it has completed, you should see the following when visiting the frontend of the site. -```xml - - +![Frontend of Umbraco site installed using the unattended installs feature](images/Unattended/final-screen.png) + +## Configuration options + +Depending on your preferences, you can use any type of configuration to specify the connection string and login information, as well as enable unattended install. With the extending configuration functionality, it is possible to read from all kinds of sources. One example can be using a JSON file or environment variables. + +**Program.cs** has a condition, which if met, an *appsettings.Local.json* file will be added and configured as a configuration source. + +```c# +#if DEBUG + .ConfigureAppConfiguration(config + => config.AddJsonFile( + "appsettings.Local.json", + optional: true, + reloadOnChange: true)) +#endif ``` -Remember to set the value to `true`. +Having intellisense will help you to easily add your connection string and information needed for the unattended install. + +```json +{ + "ConnectionStrings": { + "umbracoDbDSN": "server=localhost;database=UmbracoUnicore;user id=sa;password='P@ssw0rd'" + }, + "Umbraco": { + "CMS": { + "Unattended": { + "InstallUnattended": true, + "UnattendedUserName": "FRIENDLY_NAME", + "UnattendedUserEmail": "EMAIL", + "UnattendedUserPassword": "PASSWORD" + } + } + } +} +``` -## Initialize the unattended install +## More support -After completing the 3 steps above you can now initialize the installation by booting up the Umbraco instance. +We have added support for unattended installs with Name, Email and Password, and Connection String as CLI params, which are also available in Visual Studio. There you can fill in your information as follows: -Once it has completed, you should see the following when visiting the frontend of the site. +### CLI -![Frontend of Umbraco site installed using the unattended installs feature](images/unattended/final-screen.png) +```powershell +dotnet new umbraco -n MyNewProject --FriendlyName "Friendly User" --Email user@email.com --Password password1234 --ConnectionString "Server=(localdb)\\Umbraco;Database=MyDatabase;Integrated Security=true" --version 9.0.0 +``` -## Configuration options +### Visual Studio -| | Set ConfigurationStatus | Include connectionstring | InstallUnattended value | -|--- |--- |--- |--- | -| Default | false | false | false | -| Unattended (no installer, will install without a user password) | true | true | true | -| Pre configured (will run installer without DB option) | false | true | false | -| Pre configured (will run installer without DB option) | true | true | false | +![Set up unattended install through Visual Studio](images/Unattended/VS-unattended-install.png) diff --git a/Fundamentals/Setup/Install/iis-v9.md b/Fundamentals/Setup/Install/iis.md similarity index 99% rename from Fundamentals/Setup/Install/iis-v9.md rename to Fundamentals/Setup/Install/iis.md index fabfe92ff0b..a51fa258c61 100644 --- a/Fundamentals/Setup/Install/iis-v9.md +++ b/Fundamentals/Setup/Install/iis.md @@ -108,6 +108,6 @@ And finally the site is running from your local IIS: ![Local IIS site](images/voila.png) -## [Alternative IIS Setup with manual Deployment](../Server-Setup/iis/iis-deployment-v9.md) +## [Alternative IIS Setup with manual Deployment](../Server-Setup/iis/iis-deployment.md) If you need to manually deploy you solution to another server like staging or production, it might be helpful to know some further details about IIS configuration. diff --git a/Fundamentals/Setup/Install/index-v7.md b/Fundamentals/Setup/Install/index-v7.md new file mode 100644 index 00000000000..9d7bee6cf58 --- /dev/null +++ b/Fundamentals/Setup/Install/index-v7.md @@ -0,0 +1,37 @@ +--- +versionFrom: 7.0.0 +meta.Title: "Installing Umbraco" +meta.Description: "Instructions on installing Umbraco: in VS code, via NuGet or on a Mac" +--- + +# Installation + +The easiest way to get the latest version of Umbraco up and running is with VS Code. + +1. **Download and install [Visual Studio Code](https://code.visualstudio.com/)** +1. **Download and install [IIS Express](https://www.microsoft.com/en-us/download/details.aspx?id=48264) (Optional as IIS Express for VSCode will install it)** +1. **Download and unzip [Umbraco](https://our.umbraco.com/download)** +1. **Install the [IIS Express Extension for VS Code](https://marketplace.visualstudio.com/items?itemName=warren-buckley.iis-express)** +1. **Open the unzipped root folder in VS Code** +1. **Run the website with the IIS Express Extension (CTRL+F5)** +1. **Follow instructions on installer** + +If you have never used VS Code before, you can check out a more detailed guide below that shows these steps more in depth to run a local instance of Umbraco. +Below you'll find some in-depth tutorials on the different ways to install Umbraco. + +## [VS Code installation](install-umbraco-with-vs-code.md) + +Visual Studio Code is an editor with an embedded webserver (through the IIS Express extension). A fast way to get you up and running with Umbraco. + +## [NuGet installation](install-umbraco-with-nuget.md) + +NuGet is the package manager for the Microsoft development platform, including .NET. The NuGet client tools provide the ability to produce and consume packages. NuGet allows you to install Umbraco without ever having to leave Visual Studio. + +## [Install Umbraco unattended](Unattended-Install.md) + +Use the Unattended installs when spinning up Umbraco instances on something like Azure Web Apps to avoid having to run through the installation wizard. + +## [Running Umbraco on a Mac](running-umbraco-on-a-mac.md) + +Umbraco does not run natively on Mac OS but it's possible to get Windows up and running on your Mac. +Click on the link [tools and information on how to run Umbraco on a mac](running-umbraco-on-a-mac.md) to find out more about how to do this and the tools available to you. diff --git a/Fundamentals/Setup/Install/index-v9.md b/Fundamentals/Setup/Install/index-v9.md deleted file mode 100644 index bb602ce6087..00000000000 --- a/Fundamentals/Setup/Install/index-v9.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -versionFrom: 9.0.0 -verified-against: rc-003 -state: complete -updated-links: true -meta.Title: "Installing Umbraco" -meta.Description: "Instructions on installing Umbraco on various platforms using various tools." ---- - -# Installation - -The easiest way to get the latest version of Umbraco up and running is with the CLI. - -1. Open your command line -2. Install the Umbraco templates with `dotnet new -i Umbraco.Templates` -3. Enter the project folder, it will be the folder containing the `.csproj` file -4. Run and build your project using `dotnet run` -5. The console will output a message similar to: `[10:57:39 INF] Now listening on: https://localhost:44388` -6. Open your browser and navigate to that url -7. Follow instructions on the installer - -Below you'll find some in-depth tutorials on the different ways to install Umbraco. - -## [VS Code installation](install-umbraco-with-vs-code-v9.md) - -Visual Studio Code is an editor with an embedded webserver (through the IIS Express extension). A fast way to get you up and running with Umbraco. - -### [Run Umbraco on IIS](iis-v9.md) - -Learn how to run an already installed local installation of Umbraco 9. - -## [.NET CLI installation](install-umbraco-with-templates.md) - -.NET CLI, included with the .NET SDK, can be used to install or uninstall .NET templates from NuGet using the `dotnet new` command on any OS. The underlying Template Engine enables the creation of custom templates which make new project bootstrapping much faster. With a few steps you can have an Umbraco project running without the need for a code editor. - -## [Visual Studio installation](visual-studio.md) - -Visual Studio is used to write native code and managed code supported by .NET and many others. -Its built-in tools provide the ability to develop and execute applications for any platform. Developers will be able to install Umbraco without ever having to leave Visual Studio. - -## [Install Umbraco unattended](Unattended-Install-v9.md) - -Use the Unattended installs when spinning up Umbraco instances on something like Azure Web Apps to avoid having to run through the installation wizard. diff --git a/Fundamentals/Setup/Install/index.md b/Fundamentals/Setup/Install/index.md index 9d7bee6cf58..102bde039fa 100644 --- a/Fundamentals/Setup/Install/index.md +++ b/Fundamentals/Setup/Install/index.md @@ -1,37 +1,43 @@ --- -versionFrom: 7.0.0 +versionFrom: 9.0.0 +verified-against: rc-003 +state: complete +updated-links: true meta.Title: "Installing Umbraco" -meta.Description: "Instructions on installing Umbraco: in VS code, via NuGet or on a Mac" +meta.Description: "Instructions on installing Umbraco on various platforms using various tools." --- # Installation -The easiest way to get the latest version of Umbraco up and running is with VS Code. +The easiest way to get the latest version of Umbraco up and running is with the CLI. -1. **Download and install [Visual Studio Code](https://code.visualstudio.com/)** -1. **Download and install [IIS Express](https://www.microsoft.com/en-us/download/details.aspx?id=48264) (Optional as IIS Express for VSCode will install it)** -1. **Download and unzip [Umbraco](https://our.umbraco.com/download)** -1. **Install the [IIS Express Extension for VS Code](https://marketplace.visualstudio.com/items?itemName=warren-buckley.iis-express)** -1. **Open the unzipped root folder in VS Code** -1. **Run the website with the IIS Express Extension (CTRL+F5)** -1. **Follow instructions on installer** +1. Open your command line +2. Install the Umbraco templates with `dotnet new -i Umbraco.Templates` +3. Enter the project folder, it will be the folder containing the `.csproj` file +4. Run and build your project using `dotnet run` +5. The console will output a message similar to: `[10:57:39 INF] Now listening on: https://localhost:44388` +6. Open your browser and navigate to that url +7. Follow instructions on the installer -If you have never used VS Code before, you can check out a more detailed guide below that shows these steps more in depth to run a local instance of Umbraco. Below you'll find some in-depth tutorials on the different ways to install Umbraco. ## [VS Code installation](install-umbraco-with-vs-code.md) Visual Studio Code is an editor with an embedded webserver (through the IIS Express extension). A fast way to get you up and running with Umbraco. -## [NuGet installation](install-umbraco-with-nuget.md) +### [Run Umbraco on IIS](iis.md) -NuGet is the package manager for the Microsoft development platform, including .NET. The NuGet client tools provide the ability to produce and consume packages. NuGet allows you to install Umbraco without ever having to leave Visual Studio. +Learn how to run an already installed local installation of Umbraco 9. -## [Install Umbraco unattended](Unattended-Install.md) +## [.NET CLI installation](install-umbraco-with-templates.md) -Use the Unattended installs when spinning up Umbraco instances on something like Azure Web Apps to avoid having to run through the installation wizard. +.NET CLI, included with the .NET SDK, can be used to install or uninstall .NET templates from NuGet using the `dotnet new` command on any OS. The underlying Template Engine enables the creation of custom templates which make new project bootstrapping much faster. With a few steps you can have an Umbraco project running without the need for a code editor. + +## [Visual Studio installation](visual-studio.md) -## [Running Umbraco on a Mac](running-umbraco-on-a-mac.md) +Visual Studio is used to write native code and managed code supported by .NET and many others. +Its built-in tools provide the ability to develop and execute applications for any platform. Developers will be able to install Umbraco without ever having to leave Visual Studio. -Umbraco does not run natively on Mac OS but it's possible to get Windows up and running on your Mac. -Click on the link [tools and information on how to run Umbraco on a mac](running-umbraco-on-a-mac.md) to find out more about how to do this and the tools available to you. +## [Install Umbraco unattended](Unattended-Install.md) + +Use the Unattended installs when spinning up Umbraco instances on something like Azure Web Apps to avoid having to run through the installation wizard. diff --git a/Fundamentals/Setup/Install/install-umbraco-with-vs-code-v7.md b/Fundamentals/Setup/Install/install-umbraco-with-vs-code-v7.md new file mode 100644 index 00000000000..8f0ed3c6a1b --- /dev/null +++ b/Fundamentals/Setup/Install/install-umbraco-with-vs-code-v7.md @@ -0,0 +1,44 @@ +--- +versionFrom: 7.0.0 +--- + +# Install Umbraco with Visual Studio Code + +Follow these steps to be up and running with VS Code quickly. The benefit of using VS Code is that it is super quick to get up and running. + +## Download and Launch VS Code + +1. Go to [https://code.visualstudio.com/](https://code.visualstudio.com/) and download VS Code for free. + +1. Go to [https://www.microsoft.com/en-us/download/details.aspx?id=48264](https://www.microsoft.com/en-us/download/details.aspx?id=48264) and download IIS Express.
*This is optional as the VS Code IIS Express extension will install this automatically for you now* + +1. Once installed, launch VS Code. + +1. Click the extensions menu at the bottom on the left side. Then search for **IIS Express**, install it then press reload. + + ![VS Code install extension](images/VsCode/1.png) + +1. Download a fresh Umbraco installation from [https://our.umbraco.com/download/](https://our.umbraco.com/download/) then unzip and drag it into VS Code. + + ![Fresh Umbraco installation](images/VsCode/2.png) + +1. To launch the site press **CTRL+F5**, this will open a local version of Umbraco in your standard browser. + + +## Umbraco Web Installer +This section continues from where we left off but covers the installation and configuration of Umbraco inside your web browser when you run Umbraco for the first time. + +1. You will see the welcome screen. If you want a fast demo site fill in the information and click **Install!** If you want to decide the database type, or install without a starter site then click **Customize!** + + ![Web Installer - Lets Get Started](images/VsCode/Installer-v8.png) + +1. When the installer is done you will automatically be logged into the backoffice. + + ![Web Installer - Install Complete](images/VsCode/dashboard-v8.png) + +1. Celebrate - you're all done! + +### Congratulations, you have installed an Umbraco site! + +### Note +*You can log into your Umbraco site by entering the following into your browser: http://yoursite.com/umbraco/* diff --git a/Fundamentals/Setup/Install/install-umbraco-with-vs-code-v9.md b/Fundamentals/Setup/Install/install-umbraco-with-vs-code-v9.md deleted file mode 100644 index 1375b0d0a98..00000000000 --- a/Fundamentals/Setup/Install/install-umbraco-with-vs-code-v9.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -versionFrom: 9.0.0 -verified-against: rc-003 -state: complete -updated-links: true ---- - -# Install Umbraco with Visual Studio Code - -Follow these steps to set up an Umbraco project with VS Code. The benefit of using VS Code is that it is super quick to get up and running. - -## Installing and setting up VS Code - -1. Go to [https://code.visualstudio.com/](https://code.visualstudio.com/) and download VS Code for free. - -2. Once installed, launch VS Code. - -3. Click the extensions menu at the bottom on the left side. Then search for **C#**, install it then press reload. - - ![VS Code install extension](images/VsCode/VsCodeExtension.png) - -## Creating your Umbraco project - -Follow the [Templates Guide](install-umbraco-with-templates.md) to create your project folder. - -## Configure VS Code to run the Umbraco project - -Open your project folder in VS Code, your project will look something like this: - -![Fresh Umbraco installation](images/VsCode/netcoreStructure.png) - -Now we need to tell VS Code how to run your project. - -Open the command palette, you can use the shortcut `Ctrl+Shift+P`, and type in `Tasks: Configure` and select the `Tasks: Configure Task` option: - -![Configure task option](images/VsCode/ConfigureTask.png) - -Select "Create task.json from template" - -![Create task from template](images/VsCode/TaskJsonFromTemplate.png) - -Now select ".NET Core" as your template. - -![Create .NET Core Template](images/VsCode/NetcoreTemplate.png) - -After this VS Code will have created a folder called `.vscode` that contains a file called `tasks.json`, it's this file that tells VS Code how to build your project. - -Now that we've told VS Code how to build your project, we need to tell it how to actually launch it, luckily VS Code can also do this for you. First, click the little play button in the left side menu, and then click the "create a launch.json file" link. - -![Creating launch.json file](images/VsCode/creatingLaunchFile.png) - -This will prompt a menu to appear, select the - -![Nectore launch task](images/VSCode/NetcoreTask.png) - -Now you'll see a green play button appear with a dropdown where ".NET Core Launch" is selected. If you navigate to the files section again, you'll see that another file has been created in the `.vscode` folder, this one is called `launch.json`, it's this file that tells VS Code, to build your project, run it, and then open a browser when you press F5. - -With that, you're ready to run! Press F5, or click the little green play button in the "Run and Debug" section to run your brand new Umbraco site locally. - -## Umbraco Web Installer - -This section continues from where we left off but covers the installation and configuration of Umbraco inside your web browser when you run Umbraco for the first time. - -1. You will see the install screen. If you've installed the template with `-ce` flag, to enable SqlCE, you only need to fill out your information and click "Install", the installer will take care of the rest. If you've enabled SqlCE, but still want to change the database type, click "Customize". If you didn't enable SqlCE, you will see a "Next" button instead of "Install", which will take you to an extra step where you can configure your database. - - ![Web Installer - Lets Get Started](images/VsCode/installer-v9.png) - -2. When the installer is done you will automatically be logged into the backoffice. - - ![Web Installer - Install Complete](images/VsCode/dashboard-v8.png) - -3. Celebrate - you're all done! - -Congratulations, you have installed an Umbraco site! - -:::note -You can log into your Umbraco site by entering the following into your browser: http://yoursite.com/umbraco/. -::: diff --git a/Fundamentals/Setup/Install/install-umbraco-with-vs-code.md b/Fundamentals/Setup/Install/install-umbraco-with-vs-code.md index 8f0ed3c6a1b..1375b0d0a98 100644 --- a/Fundamentals/Setup/Install/install-umbraco-with-vs-code.md +++ b/Fundamentals/Setup/Install/install-umbraco-with-vs-code.md @@ -1,44 +1,78 @@ --- -versionFrom: 7.0.0 +versionFrom: 9.0.0 +verified-against: rc-003 +state: complete +updated-links: true --- # Install Umbraco with Visual Studio Code -Follow these steps to be up and running with VS Code quickly. The benefit of using VS Code is that it is super quick to get up and running. +Follow these steps to set up an Umbraco project with VS Code. The benefit of using VS Code is that it is super quick to get up and running. -## Download and Launch VS Code +## Installing and setting up VS Code 1. Go to [https://code.visualstudio.com/](https://code.visualstudio.com/) and download VS Code for free. -1. Go to [https://www.microsoft.com/en-us/download/details.aspx?id=48264](https://www.microsoft.com/en-us/download/details.aspx?id=48264) and download IIS Express.
*This is optional as the VS Code IIS Express extension will install this automatically for you now* +2. Once installed, launch VS Code. -1. Once installed, launch VS Code. +3. Click the extensions menu at the bottom on the left side. Then search for **C#**, install it then press reload. -1. Click the extensions menu at the bottom on the left side. Then search for **IIS Express**, install it then press reload. + ![VS Code install extension](images/VsCode/VsCodeExtension.png) - ![VS Code install extension](images/VsCode/1.png) +## Creating your Umbraco project -1. Download a fresh Umbraco installation from [https://our.umbraco.com/download/](https://our.umbraco.com/download/) then unzip and drag it into VS Code. +Follow the [Templates Guide](install-umbraco-with-templates.md) to create your project folder. - ![Fresh Umbraco installation](images/VsCode/2.png) +## Configure VS Code to run the Umbraco project -1. To launch the site press **CTRL+F5**, this will open a local version of Umbraco in your standard browser. +Open your project folder in VS Code, your project will look something like this: +![Fresh Umbraco installation](images/VsCode/netcoreStructure.png) + +Now we need to tell VS Code how to run your project. + +Open the command palette, you can use the shortcut `Ctrl+Shift+P`, and type in `Tasks: Configure` and select the `Tasks: Configure Task` option: + +![Configure task option](images/VsCode/ConfigureTask.png) + +Select "Create task.json from template" + +![Create task from template](images/VsCode/TaskJsonFromTemplate.png) + +Now select ".NET Core" as your template. + +![Create .NET Core Template](images/VsCode/NetcoreTemplate.png) + +After this VS Code will have created a folder called `.vscode` that contains a file called `tasks.json`, it's this file that tells VS Code how to build your project. + +Now that we've told VS Code how to build your project, we need to tell it how to actually launch it, luckily VS Code can also do this for you. First, click the little play button in the left side menu, and then click the "create a launch.json file" link. + +![Creating launch.json file](images/VsCode/creatingLaunchFile.png) + +This will prompt a menu to appear, select the + +![Nectore launch task](images/VSCode/NetcoreTask.png) + +Now you'll see a green play button appear with a dropdown where ".NET Core Launch" is selected. If you navigate to the files section again, you'll see that another file has been created in the `.vscode` folder, this one is called `launch.json`, it's this file that tells VS Code, to build your project, run it, and then open a browser when you press F5. + +With that, you're ready to run! Press F5, or click the little green play button in the "Run and Debug" section to run your brand new Umbraco site locally. ## Umbraco Web Installer + This section continues from where we left off but covers the installation and configuration of Umbraco inside your web browser when you run Umbraco for the first time. -1. You will see the welcome screen. If you want a fast demo site fill in the information and click **Install!** If you want to decide the database type, or install without a starter site then click **Customize!** +1. You will see the install screen. If you've installed the template with `-ce` flag, to enable SqlCE, you only need to fill out your information and click "Install", the installer will take care of the rest. If you've enabled SqlCE, but still want to change the database type, click "Customize". If you didn't enable SqlCE, you will see a "Next" button instead of "Install", which will take you to an extra step where you can configure your database. - ![Web Installer - Lets Get Started](images/VsCode/Installer-v8.png) + ![Web Installer - Lets Get Started](images/VsCode/installer-v9.png) -1. When the installer is done you will automatically be logged into the backoffice. +2. When the installer is done you will automatically be logged into the backoffice. ![Web Installer - Install Complete](images/VsCode/dashboard-v8.png) -1. Celebrate - you're all done! +3. Celebrate - you're all done! -### Congratulations, you have installed an Umbraco site! +Congratulations, you have installed an Umbraco site! -### Note -*You can log into your Umbraco site by entering the following into your browser: http://yoursite.com/umbraco/* +:::note +You can log into your Umbraco site by entering the following into your browser: http://yoursite.com/umbraco/. +::: diff --git a/Fundamentals/Setup/Requirements/index-v8.md b/Fundamentals/Setup/Requirements/index-v8.md new file mode 100644 index 00000000000..89f5dc33203 --- /dev/null +++ b/Fundamentals/Setup/Requirements/index-v8.md @@ -0,0 +1,31 @@ +--- +versionFrom: 8.0.0 +--- + +# Minimum System Requirements + +## Browsers + +The Umbraco UI should work in all modern browsers: + +* Firefox (Latest) +* Chrome (Latest) +* Safari (Latest) +* Edge + +## Local Development + +* Microsoft Windows 7 SP1 +* [Visual Studio Code](https://code.visualstudio.com/) with the [IISExpress extension](https://marketplace.visualstudio.com/items?itemName=warren-buckley.iis-express) or [Microsoft Visual Studio](https://www.visualstudio.com/) 2017 **version 15.9.6 and higher** +* ASP.NET 4.7.2 + +## Hosting + +* IIS 8 and higher +* SQL CE, SQL Server 2012 and higher +* ASP.NET 4.7.2 +* Ability to set file permissions to include create/read/write (or better) for the user that "owns" the Application Pool for your site (NETWORK SERVICE, typically) + +## Note + +* Umbraco will not run on ASP.NET Core diff --git a/Fundamentals/Setup/Requirements/index-v9.md b/Fundamentals/Setup/Requirements/index-v9.md deleted file mode 100644 index b8b9d62310d..00000000000 --- a/Fundamentals/Setup/Requirements/index-v9.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -versionFrom: 9.0.0 -verified-against: alpha-4 -state: complete -updated-links: true ---- - -# Minimum System Requirements - -## Browsers - -The Umbraco UI should work in all modern browsers: - -* Firefox (Latest) -* Chrome (Latest) -* Safari (Latest) -* Edge - -## Local Development - -* Either OS: - * Microsoft Windows 7 SP1, 8.1 and 10 - * MacOS High Sierra 10.13 - * Linux (Ubuntu, Alpine, CentOS, Debian, Fedora, openSUSE and other major distributions) -* One of the following .NET Tools or Editors: - * [Visual Studio Code](https://code.visualstudio.com/) with the [IISExpress extension](https://marketplace.visualstudio.com/items?itemName=warren-buckley.iis-express) - * [Microsoft Visual Studio](https://www.visualstudio.com/) 2019 **version 16.8 and higher** - * [JetBrains Rider](https://www.jetbrains.com/rider) **version 2020.3 and higher** - * .NET Core CLI - * etc. -* .NET 5.0 -* SQL connection string (SQL Server) - -## Hosting - -* IIS 8 and higher -* SQL Server 2012 and higher -* .NET 5.0 -* Docker -* Ability to set file permissions to include create/read/write (or better) for the user that "owns" the Application Pool for your site (NETWORK SERVICE, typically) - -*For more information, check the official Microsoft documentation for [Hosting and deploying ASP.NET Core applications](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/?view=aspnetcore-5.0)* \ No newline at end of file diff --git a/Fundamentals/Setup/Requirements/index.md b/Fundamentals/Setup/Requirements/index.md index 89f5dc33203..b8b9d62310d 100644 --- a/Fundamentals/Setup/Requirements/index.md +++ b/Fundamentals/Setup/Requirements/index.md @@ -1,5 +1,8 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 +verified-against: alpha-4 +state: complete +updated-links: true --- # Minimum System Requirements @@ -15,17 +18,25 @@ The Umbraco UI should work in all modern browsers: ## Local Development -* Microsoft Windows 7 SP1 -* [Visual Studio Code](https://code.visualstudio.com/) with the [IISExpress extension](https://marketplace.visualstudio.com/items?itemName=warren-buckley.iis-express) or [Microsoft Visual Studio](https://www.visualstudio.com/) 2017 **version 15.9.6 and higher** -* ASP.NET 4.7.2 +* Either OS: + * Microsoft Windows 7 SP1, 8.1 and 10 + * MacOS High Sierra 10.13 + * Linux (Ubuntu, Alpine, CentOS, Debian, Fedora, openSUSE and other major distributions) +* One of the following .NET Tools or Editors: + * [Visual Studio Code](https://code.visualstudio.com/) with the [IISExpress extension](https://marketplace.visualstudio.com/items?itemName=warren-buckley.iis-express) + * [Microsoft Visual Studio](https://www.visualstudio.com/) 2019 **version 16.8 and higher** + * [JetBrains Rider](https://www.jetbrains.com/rider) **version 2020.3 and higher** + * .NET Core CLI + * etc. +* .NET 5.0 +* SQL connection string (SQL Server) ## Hosting * IIS 8 and higher -* SQL CE, SQL Server 2012 and higher -* ASP.NET 4.7.2 +* SQL Server 2012 and higher +* .NET 5.0 +* Docker * Ability to set file permissions to include create/read/write (or better) for the user that "owns" the Application Pool for your site (NETWORK SERVICE, typically) -## Note - -* Umbraco will not run on ASP.NET Core +*For more information, check the official Microsoft documentation for [Hosting and deploying ASP.NET Core applications](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/?view=aspnetcore-5.0)* \ No newline at end of file diff --git a/Fundamentals/Setup/Server-Setup/Load-Balancing/azure-web-apps-v8.md b/Fundamentals/Setup/Server-Setup/Load-Balancing/azure-web-apps-v8.md new file mode 100644 index 00000000000..2f9b1c9b946 --- /dev/null +++ b/Fundamentals/Setup/Server-Setup/Load-Balancing/azure-web-apps-v8.md @@ -0,0 +1,109 @@ +--- +versionFrom: 8.6.0 +--- + +## Load Balancing Azure Web Apps + +Ensure you read the [overview](index.md) before you begin - you will need to ensure that your ASP.NET & logging configurations are correct. + +### Azure Requirements + +* You will need to setup 2 x Azure Web Apps - one for the backoffice (Administrative) environment and another for your scalable public facing environment (Public) +* You will need 1 x SQL server that is shared with these 2 web apps + +### Lucene/Examine configuration + +The single instance Backoffice Administrative Web App should be set to use [SyncTempEnvDirectoryFactory](file-system-replication.md#examine-directory-factory-options). + +The multi instance Scalable Public Web App should be set to use [TempEnvDirectoryFactory](file-system-replication.md#examine-directory-factory-options). + +### Umbraco TEMP files + +When an instance of Umbraco starts up it generates some 'temporary' files on disk... in a normal IIS environment these would be created within the folders of the Web Application. In an Azure Web App we want these to be created in the local storage of the actual server that Azure happens to be using for the Web App. So we set this configuration setting to 'true' and the temporary files will be located in the environment temporary folder. This is required for both the performance of the website as well as to prevent file locks from occurring due to the nature of Azure Web Apps shared files system. + +```xml + +``` + +### AppDomain synchronization + +Each ASP.Net application runs inside an [AppDomain](https://docs.microsoft.com/en-us/dotnet/framework/app-domains/application-domains) which is like a subprocess within the web app process. When an ASP.Net application restarts, the current AppDomain 'winds down' while another AppDomain is started; meaning there can be more than 1 live AppDomain during a restart. Restarts can occur in many scenarios including when an Azure Web App auto transitions between hosts, you scale the instances or you utilise slot swapping. + +##### v8.6.4+ + +Several file system based services in Umbraco such as the Published Cache and Lucene files can only be accessed by a single AppDomain at once. Umbraco manages this synchronization by an object called `IMainDom`. By default this uses a system-wide locking mechanism but this default mechanism doesn't work in Azure Web Apps so we need to swap it out for an alternative database locking mechanism by using the following appSetting _(in either web.config or the Azure Portal)_: + +```xml + +``` +Apply this setting to both the __MASTER__ Administrative server and the __REPLICA__ scalable public-facing servers. +##### v8.6.0 - v8.6.3 + +The `Umbraco.Core.MainDom.Lock` should be applied to your __MASTER__ Administrative server only. + +Your __REPLICA__ scalable public facing servers should be configured with: + +[Disable overlapping recycling](https://github.com/projectkudu/kudu/wiki/Configurable-settings#disable-overlapped-recycling) by adding the `WEBSITE_DISABLE_OVERLAPPED_RECYCLING` setting to application settings with a value of `1`. This setting must be set in the Application Settings part of the Azure portal _(setting it in your `web.config` file is not supported.)_ + +In some cases, if locking issues are continuing to occur on the REPLICA Web App even with `WEBSITE_DISABLE_OVERLAPPED_RECYCLING` +successfully configured in the Azure Portal - then a further approach would be to set the Published Cache to completely ignore the local database at start up, do this **only** for the REPLICA WebApp. + +A composer is required to configure this option + +```csharp +composition.Register(factory => new PublishedSnapshotServiceOptions +{ + IgnoreLocalDb = true +}); +``` + +Or if you want to control this via the Azure Portal along with the other options you could add an Application Setting. + +e.g. + +```csharp +var appSettingIgnoreLocalDb = ConfigurationManager.AppSettings["PublishedSnapshotServiceOptions.IgnoreLocalDb"]; + +if (appSettingIgnoreLocalDb == "true") +{ + composition.Register(factory => new PublishedSnapshotServiceOptions + { + IgnoreLocalDb = true + }); +} +``` +The downside of this approach is it will take slightly longer to build the published cache when a new server is intialilized, therefore consider ensuring new servers are fully 'warmed up' before swapping a slot, or enabling [Application Intialization](https://docs.microsoft.com/en-us/iis/configuration/system.webserver/applicationinitialization/) to allow Azure to warm up the server before any scaling or auto transitions occur. + +```xml + + + +``` + +##### v8.0 - v8.5.x + +The SQLDomainLock option was added in V8.6, for previous versions of Umbraco V8 you should configure both your __MASTER__ and __REPLICA__ servers 'as if they were all replica servers' based upon the [v8.6.0 - v8.6.1](#v860---v861) configuration above. + +### Steps to set-up an environment + +1. Create an Azure SQL database +2. Install Umbraco on your backoffice administrative environment and ensure to use your Azure SQL Database +3. Install Umbraco on your scalable public facing environment and ensure to use your Azure SQL Database +4. Test: Perform some content updates on the administrative environment, ensure they work successfully on that environment, then verify that those changes appear on the scalable public facing environment +5. Fix the backoffice environment to be the MASTER scheduling server and the scalable public facing environment to be REPLICAs - see [Explicit Master Scheduling](flexible-advanced.md#explicit-master-scheduling-server) + +:::note +Ensure all Azure resources are located in the same region to avoid connection lag +::: + +### Scaling + +**Do not scale your backoffice administrative environment** this is not supported and can cause issues. + +The public facing replica Azure Web Apps can be manually or automatically scaled up or down and is supported by Umbraco's load balancing. + +### Deployment considerations + +Since you have 2 x web apps, when you deploy you will need to deploy to both places - There are various automation techniques you can use to simplify the process. That is outside the scope of this article. + +**Important note:** This also means that you should not be editing templates or views on a live server as master and replica environments do not share the same file system. Changes should be made in a development environment and then pushed to each live environment. diff --git a/Fundamentals/Setup/Server-Setup/Load-Balancing/azure-web-apps-v9.md b/Fundamentals/Setup/Server-Setup/Load-Balancing/azure-web-apps-v9.md deleted file mode 100644 index 42acc6d4401..00000000000 --- a/Fundamentals/Setup/Server-Setup/Load-Balancing/azure-web-apps-v9.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -versionFrom: 9.0.0 ---- - -## Load Balancing Azure Web Apps - -Ensure you read the [overview](index-v9.md) before you begin - you will need to ensure that your ASP.NET Core & logging configurations are correct. - -### Azure Requirements - -* You will need to setup 2 x Azure Web Apps - one for the backoffice (Administrative) environment and another for your scalable public facing environment (Public) -* You will need 1 x SQL server that is shared with these 2 web apps - -### Lucene/Examine configuration - -The single instance Backoffice Administrative Web App should be set to use [SyncTempEnvDirectoryFactory](file-system-replication-v9.md#examine-directory-factory-options). - -The multi instance Scalable Public Web App should be set to use [TempEnvDirectoryFactory](file-system-replication-v9.md#examine-directory-factory-options). - -### Umbraco TEMP files - -When an instance of Umbraco starts up it generates some 'temporary' files on disk... in a normal IIS environment these would be created within the folders of the Web Application. In an Azure Web App we want these to be created in the local storage of the actual server that Azure happens to be using for the Web App. So we set this configuration setting to 'true' and the temporary files will be located in the environment temporary folder. This is required for both the performance of the website as well as to prevent file locks from occurring due to the nature of Azure Web Apps shared files system. - -```json -{ - "Umbraco": { - "CMS": { - "Hosting": { - "LocalTempStorageLocation" : "EnvironmentTemp" - } - } - } -} -``` - -### AppDomain synchronization - -Each application runs inside an [AppDomain](https://docs.microsoft.com/en-us/dotnet/framework/app-domains/application-domains) which is like a subprocess within the web app process. When an ASP.Net application restarts, the current AppDomain 'winds down' while another AppDomain is started; meaning there can be more than 1 live AppDomain during a restart. Restarts can occur in many scenarios including when an Azure Web App auto transitions between hosts, you scale the instances or you utilise slot swapping. - -Several file system based services in Umbraco such as the Published Cache and Lucene files can only be accessed by a single AppDomain at once. Umbraco manages this synchronization by an object called `IMainDom`. By default this uses a system-wide locking mechanism but this default mechanism doesn't work in Azure Web Apps so we need to swap it out for an alternative database locking mechanism by using the following appSetting: - -```json -{ - "Umbraco": { - "CMS": { - "Global": { - "MainDomLock" : "SqlMainDomLock" - } - } - } -} -``` -Apply this setting to both the __MASTER__ Administrative server and the __REPLICA__ scalable public-facing servers. - -### Steps to set-up an environment - -1. Create an Azure SQL database -2. Install Umbraco on your backoffice administrative environment and ensure to use your Azure SQL Database -3. Install Umbraco on your scalable public facing environment and ensure to use your Azure SQL Database -4. Test: Perform some content updates on the administrative environment, ensure they work successfully on that environment, then verify that those changes appear on the scalable public facing environment -5. Fix the backoffice environment to be the MASTER scheduling server and the scalable public facing environment to be REPLICAs - see [Explicit Master Scheduling](flexible-advanced-v9.md#explicit-master-scheduling-server) - -:::note -Ensure all Azure resources are located in the same region to avoid connection lag -::: - -### Scaling - -**Do not scale your backoffice administrative environment** this is not supported and can cause issues. - -The public facing replica Azure Web Apps can be manually or automatically scaled up or down and is supported by Umbraco's load balancing. - -### Deployment considerations - -Since you have 2 x web apps, when you deploy you will need to deploy to both places - There are various automation techniques you can use to simplify the process. That is outside the scope of this article. - -**Important note:** This also means that you should not be editing templates or views on a live server as master and replica environments do not share the same file system. Changes should be made in a development environment and then pushed to each live environment. diff --git a/Fundamentals/Setup/Server-Setup/Load-Balancing/azure-web-apps.md b/Fundamentals/Setup/Server-Setup/Load-Balancing/azure-web-apps.md index 2f9b1c9b946..933ee976d06 100644 --- a/Fundamentals/Setup/Server-Setup/Load-Balancing/azure-web-apps.md +++ b/Fundamentals/Setup/Server-Setup/Load-Balancing/azure-web-apps.md @@ -1,10 +1,10 @@ --- -versionFrom: 8.6.0 +versionFrom: 9.0.0 --- ## Load Balancing Azure Web Apps -Ensure you read the [overview](index.md) before you begin - you will need to ensure that your ASP.NET & logging configurations are correct. +Ensure you read the [overview](index.md) before you begin - you will need to ensure that your ASP.NET Core & logging configurations are correct. ### Azure Requirements @@ -21,68 +21,36 @@ The multi instance Scalable Public Web App should be set to use [TempEnvDirector When an instance of Umbraco starts up it generates some 'temporary' files on disk... in a normal IIS environment these would be created within the folders of the Web Application. In an Azure Web App we want these to be created in the local storage of the actual server that Azure happens to be using for the Web App. So we set this configuration setting to 'true' and the temporary files will be located in the environment temporary folder. This is required for both the performance of the website as well as to prevent file locks from occurring due to the nature of Azure Web Apps shared files system. -```xml - -``` - -### AppDomain synchronization - -Each ASP.Net application runs inside an [AppDomain](https://docs.microsoft.com/en-us/dotnet/framework/app-domains/application-domains) which is like a subprocess within the web app process. When an ASP.Net application restarts, the current AppDomain 'winds down' while another AppDomain is started; meaning there can be more than 1 live AppDomain during a restart. Restarts can occur in many scenarios including when an Azure Web App auto transitions between hosts, you scale the instances or you utilise slot swapping. - -##### v8.6.4+ - -Several file system based services in Umbraco such as the Published Cache and Lucene files can only be accessed by a single AppDomain at once. Umbraco manages this synchronization by an object called `IMainDom`. By default this uses a system-wide locking mechanism but this default mechanism doesn't work in Azure Web Apps so we need to swap it out for an alternative database locking mechanism by using the following appSetting _(in either web.config or the Azure Portal)_: - -```xml - -``` -Apply this setting to both the __MASTER__ Administrative server and the __REPLICA__ scalable public-facing servers. -##### v8.6.0 - v8.6.3 - -The `Umbraco.Core.MainDom.Lock` should be applied to your __MASTER__ Administrative server only. - -Your __REPLICA__ scalable public facing servers should be configured with: - -[Disable overlapping recycling](https://github.com/projectkudu/kudu/wiki/Configurable-settings#disable-overlapped-recycling) by adding the `WEBSITE_DISABLE_OVERLAPPED_RECYCLING` setting to application settings with a value of `1`. This setting must be set in the Application Settings part of the Azure portal _(setting it in your `web.config` file is not supported.)_ - -In some cases, if locking issues are continuing to occur on the REPLICA Web App even with `WEBSITE_DISABLE_OVERLAPPED_RECYCLING` -successfully configured in the Azure Portal - then a further approach would be to set the Published Cache to completely ignore the local database at start up, do this **only** for the REPLICA WebApp. - -A composer is required to configure this option - -```csharp -composition.Register(factory => new PublishedSnapshotServiceOptions +```json { - IgnoreLocalDb = true -}); + "Umbraco": { + "CMS": { + "Hosting": { + "LocalTempStorageLocation" : "EnvironmentTemp" + } + } + } +} ``` -Or if you want to control this via the Azure Portal along with the other options you could add an Application Setting. +### AppDomain synchronization -e.g. +Each application runs inside an [AppDomain](https://docs.microsoft.com/en-us/dotnet/framework/app-domains/application-domains) which is like a subprocess within the web app process. When an ASP.Net application restarts, the current AppDomain 'winds down' while another AppDomain is started; meaning there can be more than 1 live AppDomain during a restart. Restarts can occur in many scenarios including when an Azure Web App auto transitions between hosts, you scale the instances or you utilise slot swapping. -```csharp -var appSettingIgnoreLocalDb = ConfigurationManager.AppSettings["PublishedSnapshotServiceOptions.IgnoreLocalDb"]; +Several file system based services in Umbraco such as the Published Cache and Lucene files can only be accessed by a single AppDomain at once. Umbraco manages this synchronization by an object called `IMainDom`. By default this uses a system-wide locking mechanism but this default mechanism doesn't work in Azure Web Apps so we need to swap it out for an alternative database locking mechanism by using the following appSetting: -if (appSettingIgnoreLocalDb == "true") +```json { - composition.Register(factory => new PublishedSnapshotServiceOptions - { - IgnoreLocalDb = true - }); + "Umbraco": { + "CMS": { + "Global": { + "MainDomLock" : "SqlMainDomLock" + } + } + } } ``` -The downside of this approach is it will take slightly longer to build the published cache when a new server is intialilized, therefore consider ensuring new servers are fully 'warmed up' before swapping a slot, or enabling [Application Intialization](https://docs.microsoft.com/en-us/iis/configuration/system.webserver/applicationinitialization/) to allow Azure to warm up the server before any scaling or auto transitions occur. - -```xml - - - -``` - -##### v8.0 - v8.5.x - -The SQLDomainLock option was added in V8.6, for previous versions of Umbraco V8 you should configure both your __MASTER__ and __REPLICA__ servers 'as if they were all replica servers' based upon the [v8.6.0 - v8.6.1](#v860---v861) configuration above. +Apply this setting to both the __MASTER__ Administrative server and the __REPLICA__ scalable public-facing servers. ### Steps to set-up an environment diff --git a/Fundamentals/Setup/Server-Setup/Load-Balancing/file-system-replication-v9.md b/Fundamentals/Setup/Server-Setup/Load-Balancing/file-system-replication-v8.md similarity index 56% rename from Fundamentals/Setup/Server-Setup/Load-Balancing/file-system-replication-v9.md rename to Fundamentals/Setup/Server-Setup/Load-Balancing/file-system-replication-v8.md index 87d759e2392..9e7fe249aee 100644 --- a/Fundamentals/Setup/Server-Setup/Load-Balancing/file-system-replication-v9.md +++ b/Fundamentals/Setup/Server-Setup/Load-Balancing/file-system-replication-v8.md @@ -1,22 +1,22 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- -## Standalone File System -:::note +## Standalone File System +:::note (No file replication is configured, deployment handles updating files on the different servers) ::: If the file system on your servers isn't performing any file replication then no _Umbraco_ configuration file changes are necessary. However Media will need to be configured to use a shared location such as Blob storage or S3. -Depending on the configuration and performance of the environment's local storage you might need to consider [Examine Directory Factory Options](#examine-directory-factory-options) and the [Umbraco temporary storage location](../../../../Reference/Config/webconfig/index.md#umbracocorelocaltempstorage). +Depending on the configuration and performance of the environment's local storage you might need to consider [Examine Directory Factory Options](#examine-directory-factory-options) and the [Umbraco temporary storage location](../../../../Reference/Config/webconfig/index.md#umbracocorelocaltempstorage). -## Synchronised File System -:::note +## Synchronised File System +:::note (the servers are performing file replication, updates to a file on one server, updates the corresponding file on any other servers) ::: -If the file system on your servers is performing file replication then the Umbraco temporary folder (`~/umbraco/Data/TEMP`) must be excluded from replication. +If the file system on your servers is performing file replication then the Umbraco temporary folder (`App_Data/TEMP`) must be excluded from replication. If the file system on your servers is located on shared storage you will need to configure Umbraco to locate the Umbraco temporary folder outside of the shared storage. @@ -36,33 +36,23 @@ There are other alternatives for file replication out there, some free and some When deploying Umbraco in a load balanced scenario using file replication, it is important to ensure that not all files are replicated - otherwise you will experience file locking issues. Here are the folders and files that should not be replicated: -* `~/umbraco/Data/TEMP/*` +* ~/App_Data/TEMP/* :::tip -Alternatively store the Umbraco temporary files in the local server's 'temp' folder and set Examine to use a [Directory Factory](#examine-directory-factory-options). -Achieve this by changing this configuration setting to 'true' in the web.config. The downside is that if you need to view temporary files you'll have to find it in the temp files. Locating the file this way isn't always clear. -Below is shown how to do this in a Json configuration source. -```json -{ - "Umbraco": { - "CMS": { - "Examine": { - "LuceneDirectoryFactory" : "TempFileSystemDirectoryFactory" - } - } - } -} - +Alternatively store the Umbraco temporary files in the local server's 'temp' folder and set Examine to use a [Directory Factory](#examine-directory-factory-options). Achieve this by changing this configuration setting to 'true' in the web.config. The downside is that if you need to view temporary files you'll have to find it in the temp files. Locating the file this way isn't always clear. + +```xml + ``` -::: -* `~/umbraco/Logs/*` - * This is **optional** and depends on how you want your logs configured (see below) +::: +* ~/App_Data/Logs/* + * This is **optional** and depends on how you want your logs configured (see below) If for some reason your file replication solution doesn't allow you to not replicate specific files folders (which it should!!) then you can use an alternative approach by using virtual directories. *This is not the recommended setup but it is a viable alternative:* -* Copy the `~/umbraco/Data/TEMP` directory to each server, outside of any replication areas or to a unique folder for each server. -* Create a virtual directory (not a virtual application) in the `~/umbraco/Data/` folder, and name it `TEMP`. Point the virtual directory to the folder you created in step 2. -* You may delete the `~/umbraco/Data/TEMP` folder from the file system - not IIS as this may delete the virtual directory - if you wish. +* Copy the /App_Data/TEMP directory to each server, outside of any replication areas or to a unique folder for each server. +* Create a virtual directory (not a virtual application) in the /App_Data folder, and name it TEMP. Point the virtual directory to the folder you created in step 2. +* You may delete the /App_Data/TEMP folder from the file system - not IIS as this may delete the virtual directory - if you wish. ### IIS Setup @@ -72,39 +62,23 @@ IIS configuration is pretty straightforward with file replication. IIS is only r In some scenarios you have a mixture of standalone and synchronised file systems. An example of this is Azure Web Apps where the file system isn't replicated between backoffice and front end servers but is replicated between all front end servers, in this configuration you should follow the steps for synchronised file systems. -There is a specific documentation for load balancing with [Azure Web Apps](azure-web-apps-v9.md) +There is a specific documentation for load balancing with [Azure Web Apps](azure-web-apps.md) ## Examine Directory Factory Options - -- The `TempFileSystemDirectoryFactory` allows Examine to store indexes directly in the environment temporary storage directory, and should be used instead of `SyncTempEnvDirectoryFactory` mentioned above. -```json -{ - "Umbraco": { - "CMS": { - "Examine": { - "LuceneDirectoryFactory" : "TempFileSystemDirectoryFactory" - } - } - } -} -``` -- The `SyncedTempFileSystemDirectoryFactory` enables Examine to sync indexes between the remote file system and the local environment temporary storage directory, the indexes will be accessed from the temporary storage directory. This setting is needed because Lucene has issues when working from a remote file share so the files need to be read/accessed locally. Any time the index is updated, this setting will ensure that both the locally created indexes and the normal indexes are written to. This will ensure that when the app is restarted or the local environment temp files are cleared out that the index files can be restored from the centrally stored index files. -```json -{ - "Umbraco": { - "CMS": { - "Examine": { - "LuceneDirectoryFactory" : "SyncedTempFileSystemDirectoryFactory" - } - } - } -} -``` + +- The `TempEnvDirectoryFactory` allows Examine to store indexes directly in the environment temporary storage directory, and should be used instead of `SyncTempEnvDirectoryFactory` mentioned above. +```xml + +``` +- The `SyncTempEnvDirectoryFactory` enables Examine to sync indexes between the remote file system and the local environment temporary storage directory, the indexes will be accessed from the temporary storage directory. This setting is needed because Lucene has issues when working from a remote file share so the files need to be read/accessed locally. Any time the index is updated, this setting will ensure that both the locally created indexes and the normal indexes are written to. This will ensure that when the app is restarted or the local environment temp files are cleared out that the index files can be restored from the centrally stored index files. +```xml + +``` :::tip -If you are load balancing with [Azure Web Apps](azure-web-apps-v9.md) make sure to check out the article we have for that specific set-up. +If you are load balancing with [Azure Web Apps](azure-web-apps.md) make sure to check out the article we have for that specific set-up. ::: ## Advanced techniques -Once you are familiar with how flexible load balancing works, you might be interested in some [advanced techniques](flexible-advanced-v9.md). +Once you are familiar with how flexible load balancing works, you might be interested in some [advanced techniques](flexible-advanced.md). diff --git a/Fundamentals/Setup/Server-Setup/Load-Balancing/file-system-replication.md b/Fundamentals/Setup/Server-Setup/Load-Balancing/file-system-replication.md index 9e7fe249aee..c1e92c4c756 100644 --- a/Fundamentals/Setup/Server-Setup/Load-Balancing/file-system-replication.md +++ b/Fundamentals/Setup/Server-Setup/Load-Balancing/file-system-replication.md @@ -1,22 +1,22 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- -## Standalone File System -:::note +## Standalone File System +:::note (No file replication is configured, deployment handles updating files on the different servers) ::: If the file system on your servers isn't performing any file replication then no _Umbraco_ configuration file changes are necessary. However Media will need to be configured to use a shared location such as Blob storage or S3. -Depending on the configuration and performance of the environment's local storage you might need to consider [Examine Directory Factory Options](#examine-directory-factory-options) and the [Umbraco temporary storage location](../../../../Reference/Config/webconfig/index.md#umbracocorelocaltempstorage). +Depending on the configuration and performance of the environment's local storage you might need to consider [Examine Directory Factory Options](#examine-directory-factory-options) and the [Umbraco temporary storage location](../../../../Reference/Config/webconfig/index.md#umbracocorelocaltempstorage). -## Synchronised File System -:::note +## Synchronised File System +:::note (the servers are performing file replication, updates to a file on one server, updates the corresponding file on any other servers) ::: -If the file system on your servers is performing file replication then the Umbraco temporary folder (`App_Data/TEMP`) must be excluded from replication. +If the file system on your servers is performing file replication then the Umbraco temporary folder (`~/umbraco/Data/TEMP`) must be excluded from replication. If the file system on your servers is located on shared storage you will need to configure Umbraco to locate the Umbraco temporary folder outside of the shared storage. @@ -36,23 +36,33 @@ There are other alternatives for file replication out there, some free and some When deploying Umbraco in a load balanced scenario using file replication, it is important to ensure that not all files are replicated - otherwise you will experience file locking issues. Here are the folders and files that should not be replicated: -* ~/App_Data/TEMP/* +* `~/umbraco/Data/TEMP/*` :::tip -Alternatively store the Umbraco temporary files in the local server's 'temp' folder and set Examine to use a [Directory Factory](#examine-directory-factory-options). Achieve this by changing this configuration setting to 'true' in the web.config. The downside is that if you need to view temporary files you'll have to find it in the temp files. Locating the file this way isn't always clear. - -```xml - +Alternatively store the Umbraco temporary files in the local server's 'temp' folder and set Examine to use a [Directory Factory](#examine-directory-factory-options). +Achieve this by changing this configuration setting to 'true' in the web.config. The downside is that if you need to view temporary files you'll have to find it in the temp files. Locating the file this way isn't always clear. +Below is shown how to do this in a Json configuration source. +```json +{ + "Umbraco": { + "CMS": { + "Examine": { + "LuceneDirectoryFactory" : "TempFileSystemDirectoryFactory" + } + } + } +} + ``` -::: -* ~/App_Data/Logs/* - * This is **optional** and depends on how you want your logs configured (see below) +::: +* `~/umbraco/Logs/*` + * This is **optional** and depends on how you want your logs configured (see below) If for some reason your file replication solution doesn't allow you to not replicate specific files folders (which it should!!) then you can use an alternative approach by using virtual directories. *This is not the recommended setup but it is a viable alternative:* -* Copy the /App_Data/TEMP directory to each server, outside of any replication areas or to a unique folder for each server. -* Create a virtual directory (not a virtual application) in the /App_Data folder, and name it TEMP. Point the virtual directory to the folder you created in step 2. -* You may delete the /App_Data/TEMP folder from the file system - not IIS as this may delete the virtual directory - if you wish. +* Copy the `~/umbraco/Data/TEMP` directory to each server, outside of any replication areas or to a unique folder for each server. +* Create a virtual directory (not a virtual application) in the `~/umbraco/Data/` folder, and name it `TEMP`. Point the virtual directory to the folder you created in step 2. +* You may delete the `~/umbraco/Data/TEMP` folder from the file system - not IIS as this may delete the virtual directory - if you wish. ### IIS Setup @@ -65,15 +75,31 @@ In some scenarios you have a mixture of standalone and synchronised file systems There is a specific documentation for load balancing with [Azure Web Apps](azure-web-apps.md) ## Examine Directory Factory Options - -- The `TempEnvDirectoryFactory` allows Examine to store indexes directly in the environment temporary storage directory, and should be used instead of `SyncTempEnvDirectoryFactory` mentioned above. -```xml - -``` -- The `SyncTempEnvDirectoryFactory` enables Examine to sync indexes between the remote file system and the local environment temporary storage directory, the indexes will be accessed from the temporary storage directory. This setting is needed because Lucene has issues when working from a remote file share so the files need to be read/accessed locally. Any time the index is updated, this setting will ensure that both the locally created indexes and the normal indexes are written to. This will ensure that when the app is restarted or the local environment temp files are cleared out that the index files can be restored from the centrally stored index files. -```xml - -``` + +- The `TempFileSystemDirectoryFactory` allows Examine to store indexes directly in the environment temporary storage directory, and should be used instead of `SyncTempEnvDirectoryFactory` mentioned above. +```json +{ + "Umbraco": { + "CMS": { + "Examine": { + "LuceneDirectoryFactory" : "TempFileSystemDirectoryFactory" + } + } + } +} +``` +- The `SyncedTempFileSystemDirectoryFactory` enables Examine to sync indexes between the remote file system and the local environment temporary storage directory, the indexes will be accessed from the temporary storage directory. This setting is needed because Lucene has issues when working from a remote file share so the files need to be read/accessed locally. Any time the index is updated, this setting will ensure that both the locally created indexes and the normal indexes are written to. This will ensure that when the app is restarted or the local environment temp files are cleared out that the index files can be restored from the centrally stored index files. +```json +{ + "Umbraco": { + "CMS": { + "Examine": { + "LuceneDirectoryFactory" : "SyncedTempFileSystemDirectoryFactory" + } + } + } +} +``` :::tip If you are load balancing with [Azure Web Apps](azure-web-apps.md) make sure to check out the article we have for that specific set-up. diff --git a/Fundamentals/Setup/Server-Setup/Load-Balancing/flexible-advanced-v8.md b/Fundamentals/Setup/Server-Setup/Load-Balancing/flexible-advanced-v8.md new file mode 100644 index 00000000000..8fa498cf2eb --- /dev/null +++ b/Fundamentals/Setup/Server-Setup/Load-Balancing/flexible-advanced-v8.md @@ -0,0 +1,133 @@ +--- +versionFrom: 8.0.2 +--- + +# Advanced techniques with Flexible Load Balancing + +_This describes some more advanced techniques that you could achieve with flexible load balancing_ + +## Explicit Master Scheduling server + +It is recommended to configure an explicit master scheduling server since this reduces the amount +complexity that the [master election](flexible.md#scheduling-and-master-election) process performs. + +The first thing to do is create a couple classes for your front-end servers and master server to use: + +```csharp +public class MasterServerRegistrar : IServerRegistrar +{ + public IEnumerable Registrations + { + get { return Enumerable.Empty(); } + } + public ServerRole GetCurrentServerRole() + { + return ServerRole.Master; + } + public string GetCurrentServerUmbracoApplicationUrl() + { + // NOTE: If you want to explicitly define the URL that your application is running on, + // this will be used for the server to communicate with itself, you can return the + // custom path here and it needs to be in this format: + // http://www.mysite.com/umbraco + + return null; + } +} + +public class FrontEndReadOnlyServerRegistrar : IServerRegistrar +{ + public IEnumerable Registrations + { + get { return Enumerable.Empty(); } + } + public ServerRole GetCurrentServerRole() + { + return ServerRole.Replica; + } + public string GetCurrentServerUmbracoApplicationUrl() + { + return null; + } +} +``` + +then you'll need to replace the default `DatabaseServerRegistrar` for the your custom registrars. +You'll need to create an [Composer](../../../../Implementation/Composing/index.md) to set the server registrar + +```csharp +// This should be executed on your master server +public void Compose(Composition composition) +{ + composition.SetServerRegistrar(new MasterServerRegistrar()); +} + +// This should be executed on your replica servers +public void Compose(Composition composition) +{ + composition.SetServerRegistrar(new FrontEndReadOnlyServerRegistrar()); +} +``` + +Now that your front-end servers are using your custom `FrontEndReadOnlyServerRegistrar` class, they will always be deemed 'Replica' servers and will not attempt any master election or task scheduling. + +By setting your master server to use your custom `MasterServerRegistrar` class, it will always be deemed the 'Master' and will always be the one that executes all task scheduling. + +## Front-end servers - Read-only database access + +:::note +This description pertains ONLY to Umbraco database tables +::: + +In some cases infrastructure admins will not want their front-end servers to have write access to the database. +By default front-end servers will require write full access to the following tables: + +* `umbracoServer` +* `umbracoNode` + +This is because by default each server will inform the database that they are active and more importantly it is +used for task scheduling. Only a single server can execute task scheduling and these tables are used for servers +to use a master server election process without the need for any configuration. So in the case that a front-end +server becomes the master task scheduler, **it will require write access to all of the Umbraco tables**. + +In order to have read-only database access configured for your front-end servers, you need to implement +the [Explicit master scheduling server](#explicit-master-scheduling-server) configuration mentioned above. + +Now that your front-end servers are using your custom `FrontEndReadOnlyServerRegistrar` class, they will always be deemed 'Replica' servers and will not attempt any master election or task scheduling. Because you are no longer using the default `DatabaseServerRegistrar` they will not try to ping the umbracoServer table. + +:::note +If using [SqlMainDomLock](azure-web-apps.md#appdomain-synchronization) on Azure WebApps then write-permissions are required for the following tables for all server roles including 'Replica'. + +* `umbracoLock` +* `umbracoKeyValue` + +SQL Server Replica databases cannot be used as they are read-only without replacing the default MainDomLock with a custom provider. +::: + +## Controlling how often the load balancing instructions from the database are processed and pruned + +During start up the `DatabaseServerMessengerOptions` can be adjusted to control how often the load balancing instructions from the database are processed and pruned. + +You'll need to create an [Composer](../../../../Implementation/Composing/index.md) to set the messenger options + +```csharp +public void Compose(Composition composition) +{ + composition.SetDatabaseServerMessengerOptions(factory => + { + var options = DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions(factory); + options.DaysToRetainInstructions = 10; + options.MaxProcessingInstructionCount = 1000; + options.ThrottleSeconds = 25; + options.PruneThrottleSeconds = 60; + return options; + }); +} +``` + +Options: + +* DaysToRetainInstructions - The number of days to keep instructions in the database; records older than this number will be pruned. +* MaxProcessingInstructionCount - The maximum number of instructions that can be processed at startup; otherwise the server cold-boots (rebuilds its caches) +* ThrottleSeconds - The number of seconds to wait between each sync operations +* PruneThrottleSeconds - The number of seconds to wait between each prune operation diff --git a/Fundamentals/Setup/Server-Setup/Load-Balancing/flexible-advanced-v9.md b/Fundamentals/Setup/Server-Setup/Load-Balancing/flexible-advanced-v9.md deleted file mode 100644 index 5c1266b2b00..00000000000 --- a/Fundamentals/Setup/Server-Setup/Load-Balancing/flexible-advanced-v9.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -versionFrom: 9.0.0 ---- - -# Advanced techniques with Flexible Load Balancing - -_This describes some more advanced techniques that you could achieve with flexible load balancing_ - -## Explicit Master Scheduling server - -It is recommended to configure an explicit master scheduling server since this reduces the amount -complexity that the master election process performs. - -The first thing to do is create a couple small classes for your front-end servers and master server to use: - -```csharp -public class MasterServerServerRoleAccessor : IServerRoleAccessor -{ - public ServerRole CurrentServerRole => ServerRole.Master; -} - -public class FrontEndReadOnlyServerRoleAccessor : IServerRoleAccessor -{ - public ServerRole CurrentServerRole => ServerRole.Replica; -} -``` - -then you'll need to replace the default `IServerRoleAccessor` for the your custom registrars. -You'll can do this by using the `SetServerRegistrar()` extension method on `IUmbracoBuilder` from a [Composer](../../../../Implementation/Composing/index.md) or directly in your `startup.cs`. - -```csharp -// This should be executed on your master server -builder.SetServerRegistrar(); - -// This should be executed on your replica servers -builder.SetServerRegistrar(); -``` - -Now that your front-end servers are using your custom `FrontEndReadOnlyServerRoleAccessor` class, they will always be deemed 'Replica' servers and will not attempt any master election or task scheduling. - -By setting your master server to use your custom `MasterServerRoleAccessor` class, it will always be deemed the 'Master' and will always be the one that executes all task scheduling. - -## Front-end servers - Read-only database access - -:::note -This description pertains ONLY to Umbraco database tables -::: - -In some cases infrastructure admins will not want their front-end servers to have write access to the database. -By default front-end servers will require write full access to the following tables: - -* `umbracoServer` -* `umbracoNode` - -This is because by default each server will inform the database that they are active and more importantly it is -used for task scheduling. Only a single server can execute task scheduling and these tables are used for servers -to use a master server election process without the need for any configuration. So in the case that a front-end -server becomes the master task scheduler, **it will require write access to all of the Umbraco tables**. - -In order to have read-only database access configured for your front-end servers, you need to implement -the [Explicit master scheduling server](#explicit-master-scheduling-server) configuration mentioned above. - -Now that your front-end servers are using your custom `FrontEndReadOnlyServerRoleAccessor` class, they will always be deemed 'Replica' servers and will not attempt any master election or task scheduling. -Because you are no longer using the default `ElectedServerRoleAccessor` they will not try to ping the umbracoServer table. - -:::note -If using [SqlMainDomLock](azure-web-apps.md#appdomain-synchronization) on Azure WebApps then write-permissions are required for the following tables for all server roles including 'Replica'. - -* `umbracoLock` -* `umbracoKeyValue` - -SQL Server Replica databases cannot be used as they are read-only without replacing the default MainDomLock with a custom provider. -::: - -## Controlling how often the load balancing instructions from the database are processed and pruned - -The configurations can be adjusted to control how often the load balancing instructions from the database are processed and pruned. -Below is shown how to do this from a JSON configuration source. -```json -{ - "Umbraco": { - "CMS": { - "Global": { - "DatabaseServerMessenger": { - "MaxProcessingInstructionCount": 1000, - "TimeBetweenPruneOperations": "00:01:00", - "TimeBetweenSyncOperations": "00:00:05", - "TimeToRetainInstructions": "2.00:00:00" - } - } - } - } -} - -``` - -Options: - -* `TimeToRetainInstructions` - The timespan to keep instructions in the database; records older than this number will be pruned. -* `MaxProcessingInstructionCount` - The maximum number of instructions that can be processed at startup; otherwise the server cold-boots (rebuilds its caches) -* `TimeBetweenSyncOperations` - The timespan to wait between each sync operations -* `TimeBetweenPruneOperations` - The timespan to wait between each prune operation diff --git a/Fundamentals/Setup/Server-Setup/Load-Balancing/flexible-advanced.md b/Fundamentals/Setup/Server-Setup/Load-Balancing/flexible-advanced.md index 8fa498cf2eb..5c1266b2b00 100644 --- a/Fundamentals/Setup/Server-Setup/Load-Balancing/flexible-advanced.md +++ b/Fundamentals/Setup/Server-Setup/Load-Balancing/flexible-advanced.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.2 +versionFrom: 9.0.0 --- # Advanced techniques with Flexible Load Balancing @@ -9,69 +9,36 @@ _This describes some more advanced techniques that you could achieve with flexib ## Explicit Master Scheduling server It is recommended to configure an explicit master scheduling server since this reduces the amount -complexity that the [master election](flexible.md#scheduling-and-master-election) process performs. +complexity that the master election process performs. -The first thing to do is create a couple classes for your front-end servers and master server to use: +The first thing to do is create a couple small classes for your front-end servers and master server to use: ```csharp -public class MasterServerRegistrar : IServerRegistrar +public class MasterServerServerRoleAccessor : IServerRoleAccessor { - public IEnumerable Registrations - { - get { return Enumerable.Empty(); } - } - public ServerRole GetCurrentServerRole() - { - return ServerRole.Master; - } - public string GetCurrentServerUmbracoApplicationUrl() - { - // NOTE: If you want to explicitly define the URL that your application is running on, - // this will be used for the server to communicate with itself, you can return the - // custom path here and it needs to be in this format: - // http://www.mysite.com/umbraco - - return null; - } + public ServerRole CurrentServerRole => ServerRole.Master; } -public class FrontEndReadOnlyServerRegistrar : IServerRegistrar +public class FrontEndReadOnlyServerRoleAccessor : IServerRoleAccessor { - public IEnumerable Registrations - { - get { return Enumerable.Empty(); } - } - public ServerRole GetCurrentServerRole() - { - return ServerRole.Replica; - } - public string GetCurrentServerUmbracoApplicationUrl() - { - return null; - } + public ServerRole CurrentServerRole => ServerRole.Replica; } ``` -then you'll need to replace the default `DatabaseServerRegistrar` for the your custom registrars. -You'll need to create an [Composer](../../../../Implementation/Composing/index.md) to set the server registrar +then you'll need to replace the default `IServerRoleAccessor` for the your custom registrars. +You'll can do this by using the `SetServerRegistrar()` extension method on `IUmbracoBuilder` from a [Composer](../../../../Implementation/Composing/index.md) or directly in your `startup.cs`. ```csharp // This should be executed on your master server -public void Compose(Composition composition) -{ - composition.SetServerRegistrar(new MasterServerRegistrar()); -} +builder.SetServerRegistrar(); // This should be executed on your replica servers -public void Compose(Composition composition) -{ - composition.SetServerRegistrar(new FrontEndReadOnlyServerRegistrar()); -} +builder.SetServerRegistrar(); ``` -Now that your front-end servers are using your custom `FrontEndReadOnlyServerRegistrar` class, they will always be deemed 'Replica' servers and will not attempt any master election or task scheduling. +Now that your front-end servers are using your custom `FrontEndReadOnlyServerRoleAccessor` class, they will always be deemed 'Replica' servers and will not attempt any master election or task scheduling. -By setting your master server to use your custom `MasterServerRegistrar` class, it will always be deemed the 'Master' and will always be the one that executes all task scheduling. +By setting your master server to use your custom `MasterServerRoleAccessor` class, it will always be deemed the 'Master' and will always be the one that executes all task scheduling. ## Front-end servers - Read-only database access @@ -93,7 +60,8 @@ server becomes the master task scheduler, **it will require write access to all In order to have read-only database access configured for your front-end servers, you need to implement the [Explicit master scheduling server](#explicit-master-scheduling-server) configuration mentioned above. -Now that your front-end servers are using your custom `FrontEndReadOnlyServerRegistrar` class, they will always be deemed 'Replica' servers and will not attempt any master election or task scheduling. Because you are no longer using the default `DatabaseServerRegistrar` they will not try to ping the umbracoServer table. +Now that your front-end servers are using your custom `FrontEndReadOnlyServerRoleAccessor` class, they will always be deemed 'Replica' servers and will not attempt any master election or task scheduling. +Because you are no longer using the default `ElectedServerRoleAccessor` they will not try to ping the umbracoServer table. :::note If using [SqlMainDomLock](azure-web-apps.md#appdomain-synchronization) on Azure WebApps then write-permissions are required for the following tables for all server roles including 'Replica'. @@ -101,33 +69,34 @@ If using [SqlMainDomLock](azure-web-apps.md#appdomain-synchronization) on Azure * `umbracoLock` * `umbracoKeyValue` -SQL Server Replica databases cannot be used as they are read-only without replacing the default MainDomLock with a custom provider. +SQL Server Replica databases cannot be used as they are read-only without replacing the default MainDomLock with a custom provider. ::: ## Controlling how often the load balancing instructions from the database are processed and pruned -During start up the `DatabaseServerMessengerOptions` can be adjusted to control how often the load balancing instructions from the database are processed and pruned. - -You'll need to create an [Composer](../../../../Implementation/Composing/index.md) to set the messenger options - -```csharp -public void Compose(Composition composition) +The configurations can be adjusted to control how often the load balancing instructions from the database are processed and pruned. +Below is shown how to do this from a JSON configuration source. +```json { - composition.SetDatabaseServerMessengerOptions(factory => - { - var options = DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions(factory); - options.DaysToRetainInstructions = 10; - options.MaxProcessingInstructionCount = 1000; - options.ThrottleSeconds = 25; - options.PruneThrottleSeconds = 60; - return options; - }); + "Umbraco": { + "CMS": { + "Global": { + "DatabaseServerMessenger": { + "MaxProcessingInstructionCount": 1000, + "TimeBetweenPruneOperations": "00:01:00", + "TimeBetweenSyncOperations": "00:00:05", + "TimeToRetainInstructions": "2.00:00:00" + } + } + } + } } + ``` Options: -* DaysToRetainInstructions - The number of days to keep instructions in the database; records older than this number will be pruned. -* MaxProcessingInstructionCount - The maximum number of instructions that can be processed at startup; otherwise the server cold-boots (rebuilds its caches) -* ThrottleSeconds - The number of seconds to wait between each sync operations -* PruneThrottleSeconds - The number of seconds to wait between each prune operation +* `TimeToRetainInstructions` - The timespan to keep instructions in the database; records older than this number will be pruned. +* `MaxProcessingInstructionCount` - The maximum number of instructions that can be processed at startup; otherwise the server cold-boots (rebuilds its caches) +* `TimeBetweenSyncOperations` - The timespan to wait between each sync operations +* `TimeBetweenPruneOperations` - The timespan to wait between each prune operation diff --git a/Fundamentals/Setup/Server-Setup/Load-Balancing/index-v9.md b/Fundamentals/Setup/Server-Setup/Load-Balancing/index-v8.md similarity index 73% rename from Fundamentals/Setup/Server-Setup/Load-Balancing/index-v9.md rename to Fundamentals/Setup/Server-Setup/Load-Balancing/index-v8.md index 50c90ea7c2d..091efb17f37 100644 --- a/Fundamentals/Setup/Server-Setup/Load-Balancing/index-v9.md +++ b/Fundamentals/Setup/Server-Setup/Load-Balancing/index-v8.md @@ -1,7 +1,7 @@ --- meta.Title: "Umbraco in Load Balanced Environments" meta.Description: "Information on how to deploy Umbraco in a Load Balanced scenario and other details to consider when setting up Umbraco for load balancing" -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Umbraco in Load Balanced Environments @@ -15,10 +15,10 @@ Configuring and setting up a load balanced server environment requires planning, This document assumes that you have a fair amount of knowledge about: * Umbraco -* IIS 10+ +* IIS 7+ * Networking & DNS * Windows Server -* .NET5+ +* .NET Framework v4.7.2+ :::note It is highly recommended that you setup your staging environment to also be load balanced so that you can run all of your testing on a similar environment to your live environment. @@ -29,7 +29,7 @@ It is highly recommended that you setup your staging environment to also be load These instructions make the following assumptions: * All web servers can communicate with the database where Umbraco data is stored -* You are running Umbraco 9.0.0 or above +* You are running Umbraco 8.1.0 or above * _**You will designate a single server to be the backoffice server for which your editors will log into for editing content.**_ Umbraco will not work correctly if the backoffice is behind the load balancer. There are three design alternatives you can use to effectively load balance servers: @@ -81,7 +81,7 @@ by default would mean the "umbracoApplicationUrl" is "f02.mysite.local". In any In many scenarios this is fine, but in case this is not adequate there's a few of options you can use: -* __Recommended__: [set your front-end(s) (non-admin server) to be explicit replica servers](flexible-advanced-v9.md#explicit-master-scheduling-server) by creating a custom `IServerRegistrar`, this means the front end servers will never be used as the master scheduler +* __Recommended__: [set your front-end(s) (non-admin server) to be explicit replica servers](flexible-advanced.md#explicit-master-scheduling-server) by creating a custom `IServerRegistrar`, this means the front end servers will never be used as the master scheduler * Set the `umbracoApplicationUrl` property in the [Web.Routing section of /Config/umbracoSettings.config](../../../../Reference/Config/umbracoSettings/index.md) ## Common load balancing setup information @@ -92,27 +92,35 @@ _The below section applies to all ASP.NET load balancing configurations._ This section describes the various configuration options depending on your hosting setup: -1. [Azure Web Apps](file-system-replication-v9.md#mixture-of-standalone--synchronised) - _You use cloud based auto-scaling appliances like [Microsoft's Azure Web Apps](https://azure.microsoft.com/en-us/services/app-service/web/)_ -2. [File Replication](file-system-replication-v9.md#synchronised-file-system) - _Each server hosts copies of the load balanced website files and a file replication service is running to ensure that all files on all servers are up to date_ -3. [Centralized file share](file-system-replication-v9.md#synchronised-file-system) - _The load balanced website files are located on a centralized file share (SAN/NAS/Clustered File Server/Network Share)_ +1. [Azure Web Apps](file-system-replication.md#mixture-of-standalone--synchronised) - _You use cloud based auto-scaling appliances like [Microsoft's Azure Web Apps](https://azure.microsoft.com/en-us/services/app-service/web/)_ +2. [File Replication](file-system-replication.md#synchronised-file-system) - _Each server hosts copies of the load balanced website files and a file replication service is running to ensure that all files on all servers are up to date_ +3. [Centralized file share](file-system-replication.md#synchronised-file-system) - _The load balanced website files are located on a centralized file share (SAN/NAS/Clustered File Server/Network Share)_ -[Full documentation is available here](file-system-replication-v9.md) +[Full documentation is available here](file-system-replication.md) -### Data Protection -The replacement for Machine Keys in ASP.NET Core are called Data Protection. -You will need to setup data protection to the same keys on all servers, -without this you will end up with view state errors, validation errors and encryption/decryption errors since each server will have its own generated key. +### Machine Key -ASP.NET Core supports multiple ways to share keys. Use the [official docs](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview) to find a description that fits your setup the best. +* You will need to use a custom machine key so that all your machine key level encryption values are the same on all servers, without this you will end up with view state errors, validation errors and encryption/decryption errors since each server will have its own generated key. + * Here are a couple of tools that can be used to generate machine keys: + * [Machine key generator on betterbuilt.com](http://www.betterbuilt.com/machinekey/) + * [Machine key generator on developerfusion.com](https://www.developerfusion.com/tools/generatemachinekey/) +* You need to update your web.config accordingly, note that the validation/decryption types may be different for your environment depending on how you've generated your keys. +* Umbraco offers the opportunity to auto generate a machine key during the installation steps so this may already exist +```xml + + + + + +``` -### Session State and Distributed Cache -It is required to setup a distributed cache, like `DistributedSqlServerCache` or an alternative provider (see [https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed](https://msdn.microsoft.com/en-us/library/aa478952.aspx) for more details). -The distributed cache is used by the session in your application, which is used by the default TempDataProvider in MVC. - -Because Umbraco in some cases uses TempData, your setup needs to be configured with a distributed cache. - +### Session State +* If you use SessionState in your application, or are using the default TempDataProvider in MVC (which uses SessionState) then you will need to configure your application to use the SqlSessionStateStore or an alternative provider (see [https://msdn.microsoft.com/en-us/library/aa478952.aspx](https://msdn.microsoft.com/en-us/library/aa478952.aspx) for more details). ### Logging @@ -130,9 +138,9 @@ Ensure to analyze logs from all servers and check for any warnings and errors. ## Unattended upgrades -When upgrading it is possible to run the upgrades unattended. +When upgrading to Umbraco 8.12+ it is possible to run the upgrades unattended. -Find steps on how to enable the feature for a load balanced setup in the [General Upgrades](../../Upgrading/general-v9#unattended-upgrades-in-a-load-balanced-setup) article. +Find steps on how to enable the feature for a load balanced setup in the [General Upgrades](../../Upgrading/general.md#unattended-upgrades-in-a-load-balanced-setup) article. ## FAQs diff --git a/Fundamentals/Setup/Server-Setup/Load-Balancing/index.md b/Fundamentals/Setup/Server-Setup/Load-Balancing/index.md index 091efb17f37..f7aed8ee7c2 100644 --- a/Fundamentals/Setup/Server-Setup/Load-Balancing/index.md +++ b/Fundamentals/Setup/Server-Setup/Load-Balancing/index.md @@ -1,7 +1,7 @@ --- meta.Title: "Umbraco in Load Balanced Environments" meta.Description: "Information on how to deploy Umbraco in a Load Balanced scenario and other details to consider when setting up Umbraco for load balancing" -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Umbraco in Load Balanced Environments @@ -15,10 +15,10 @@ Configuring and setting up a load balanced server environment requires planning, This document assumes that you have a fair amount of knowledge about: * Umbraco -* IIS 7+ +* IIS 10+ * Networking & DNS * Windows Server -* .NET Framework v4.7.2+ +* .NET5+ :::note It is highly recommended that you setup your staging environment to also be load balanced so that you can run all of your testing on a similar environment to your live environment. @@ -29,7 +29,7 @@ It is highly recommended that you setup your staging environment to also be load These instructions make the following assumptions: * All web servers can communicate with the database where Umbraco data is stored -* You are running Umbraco 8.1.0 or above +* You are running Umbraco 9.0.0 or above * _**You will designate a single server to be the backoffice server for which your editors will log into for editing content.**_ Umbraco will not work correctly if the backoffice is behind the load balancer. There are three design alternatives you can use to effectively load balance servers: @@ -98,29 +98,21 @@ This section describes the various configuration options depending on your hosti [Full documentation is available here](file-system-replication.md) -### Machine Key +### Data Protection +The replacement for Machine Keys in ASP.NET Core are called Data Protection. +You will need to setup data protection to the same keys on all servers, +without this you will end up with view state errors, validation errors and encryption/decryption errors since each server will have its own generated key. -* You will need to use a custom machine key so that all your machine key level encryption values are the same on all servers, without this you will end up with view state errors, validation errors and encryption/decryption errors since each server will have its own generated key. - * Here are a couple of tools that can be used to generate machine keys: - * [Machine key generator on betterbuilt.com](http://www.betterbuilt.com/machinekey/) - * [Machine key generator on developerfusion.com](https://www.developerfusion.com/tools/generatemachinekey/) -* You need to update your web.config accordingly, note that the validation/decryption types may be different for your environment depending on how you've generated your keys. -* Umbraco offers the opportunity to auto generate a machine key during the installation steps so this may already exist +ASP.NET Core supports multiple ways to share keys. Use the [official docs](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview) to find a description that fits your setup the best. -```xml - - - - - -``` -### Session State +### Session State and Distributed Cache +It is required to setup a distributed cache, like `DistributedSqlServerCache` or an alternative provider (see [https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed](https://msdn.microsoft.com/en-us/library/aa478952.aspx) for more details). +The distributed cache is used by the session in your application, which is used by the default TempDataProvider in MVC. + +Because Umbraco in some cases uses TempData, your setup needs to be configured with a distributed cache. + -* If you use SessionState in your application, or are using the default TempDataProvider in MVC (which uses SessionState) then you will need to configure your application to use the SqlSessionStateStore or an alternative provider (see [https://msdn.microsoft.com/en-us/library/aa478952.aspx](https://msdn.microsoft.com/en-us/library/aa478952.aspx) for more details). ### Logging @@ -138,9 +130,9 @@ Ensure to analyze logs from all servers and check for any warnings and errors. ## Unattended upgrades -When upgrading to Umbraco 8.12+ it is possible to run the upgrades unattended. +When upgrading it is possible to run the upgrades unattended. -Find steps on how to enable the feature for a load balanced setup in the [General Upgrades](../../Upgrading/general.md#unattended-upgrades-in-a-load-balanced-setup) article. +Find steps on how to enable the feature for a load balanced setup in the [General Upgrades](../../Upgrading/general-v9#unattended-upgrades-in-a-load-balanced-setup) article. ## FAQs diff --git a/Fundamentals/Setup/Server-Setup/azure-web-apps-v9.md b/Fundamentals/Setup/Server-Setup/azure-web-apps-v8.md similarity index 77% rename from Fundamentals/Setup/Server-Setup/azure-web-apps-v9.md rename to Fundamentals/Setup/Server-Setup/azure-web-apps-v8.md index 51d1e3614c5..90c8a715883 100644 --- a/Fundamentals/Setup/Server-Setup/azure-web-apps-v9.md +++ b/Fundamentals/Setup/Server-Setup/azure-web-apps-v8.md @@ -1,5 +1,5 @@ ---- -versionFrom: 9.0.0 +--- +versionFrom: 8.0.0 --- # Running Umbraco on Azure Web Apps @@ -18,26 +18,24 @@ Umbraco will run on Azure Web Apps but there are some configuration options and ## Recommended configuration -You need to add these configuration values. E.g in a json configuration source like `appSettings.json`: - -```json -{ - "Umbraco": { - "CMS": { - "Global": { - "MainDomLock" : "SqlMainDomLock" - }, - "Hosting": { - "LocalTempStorageLocation": "EnvironmentTemp" - }, - "Examine": { - "LuceneDirectoryFactory": "SyncedTempFileSystemDirectoryFactory" - } - } - } -} +You need to add these `appSettings`: + +```xml + + + ``` +:::note +The `Umbraco.Core.MainDom.Lock` setting is for Umbraco 8.6+. Having this setting for versions between 8.0-8.5 will not have any affect. It is recommended to use 8.6+ when running Umbraco on Azure Web Apps since this setting will prevent file locking issues. +::: + __The minimum recommended Azure SQL Tier is "S2"__, however noticeable performance improvements are seen in higher Tiers __If you are load balancing or require the scaling ("scale out") ability of Azure Web Apps then you need to consult the @@ -58,6 +56,7 @@ to be configured to support scaling/auto-scaling. It's important to know that Azure Web Apps may move your website between their 'workers' at any given time. This is normally a transparent operation but in some cases you may be affected by it if any of your code or libraries use the following variables: * `Environment.MachineName` (or equivalent) +* `HttpRuntime.AppDomainAppId` (or equivalent) When your site is migrated to another worker, these variables will change. You cannot rely on these variables remaining static for the lifetime of your website. diff --git a/Fundamentals/Setup/Server-Setup/azure-web-apps.md b/Fundamentals/Setup/Server-Setup/azure-web-apps.md index 90c8a715883..51d1e3614c5 100644 --- a/Fundamentals/Setup/Server-Setup/azure-web-apps.md +++ b/Fundamentals/Setup/Server-Setup/azure-web-apps.md @@ -1,5 +1,5 @@ ---- -versionFrom: 8.0.0 +--- +versionFrom: 9.0.0 --- # Running Umbraco on Azure Web Apps @@ -18,24 +18,26 @@ Umbraco will run on Azure Web Apps but there are some configuration options and ## Recommended configuration -You need to add these `appSettings`: - -```xml - - - +You need to add these configuration values. E.g in a json configuration source like `appSettings.json`: + +```json +{ + "Umbraco": { + "CMS": { + "Global": { + "MainDomLock" : "SqlMainDomLock" + }, + "Hosting": { + "LocalTempStorageLocation": "EnvironmentTemp" + }, + "Examine": { + "LuceneDirectoryFactory": "SyncedTempFileSystemDirectoryFactory" + } + } + } +} ``` -:::note -The `Umbraco.Core.MainDom.Lock` setting is for Umbraco 8.6+. Having this setting for versions between 8.0-8.5 will not have any affect. It is recommended to use 8.6+ when running Umbraco on Azure Web Apps since this setting will prevent file locking issues. -::: - __The minimum recommended Azure SQL Tier is "S2"__, however noticeable performance improvements are seen in higher Tiers __If you are load balancing or require the scaling ("scale out") ability of Azure Web Apps then you need to consult the @@ -56,7 +58,6 @@ to be configured to support scaling/auto-scaling. It's important to know that Azure Web Apps may move your website between their 'workers' at any given time. This is normally a transparent operation but in some cases you may be affected by it if any of your code or libraries use the following variables: * `Environment.MachineName` (or equivalent) -* `HttpRuntime.AppDomainAppId` (or equivalent) When your site is migrated to another worker, these variables will change. You cannot rely on these variables remaining static for the lifetime of your website. diff --git a/Fundamentals/Setup/Server-Setup/iis/index-v9.md b/Fundamentals/Setup/Server-Setup/iis/index.md similarity index 100% rename from Fundamentals/Setup/Server-Setup/iis/index-v9.md rename to Fundamentals/Setup/Server-Setup/iis/index.md diff --git a/Fundamentals/Setup/Server-Setup/index-v9.md b/Fundamentals/Setup/Server-Setup/index-v7.md similarity index 75% rename from Fundamentals/Setup/Server-Setup/index-v9.md rename to Fundamentals/Setup/Server-Setup/index-v7.md index a8635b3a970..39f84ace587 100644 --- a/Fundamentals/Setup/Server-Setup/index-v9.md +++ b/Fundamentals/Setup/Server-Setup/index-v7.md @@ -1,7 +1,7 @@ --- meta.Title: "Information on server setup for Umbraco hosting" meta.Description: "This section describes different ways of setting up servers for use with Umbraco" -versionFrom: 9.0.0 +versionFrom: 7.0.0 --- # Server setup @@ -12,18 +12,14 @@ versionFrom: 9.0.0 We strongly encourage the use of HTTPS with Umbraco installations especially in production environments. Using HTTPS will greatly enhance the security of your website, see the [Security reference](../../../Reference/Security/index.md) for more information. -## [File & folder permissions](permissions-v9.md) +## [File & folder permissions](permissions.md) To ensure a stable and smoothly running Umbraco installation, these permissions need to be set correctly. -## [Hosting v9+ in IIS](iis/index-v9.md) - -Information about hosting a v9 application using IIS. - -## [Load Balanced setup](Load-Balancing/index-v9.md) +## [Load Balanced setup](Load-Balancing/index.md) Information on how to deploy Umbraco in a Load Balanced scenario and other details to consider when setting up Umbraco for load balancing. -## [Running Umbraco on Azure Web Apps](azure-web-apps-v9.md) +## [Running Umbraco on Azure Web Apps](azure-web-apps.md) Best practices for running Umbraco on Azure Web Apps. diff --git a/Fundamentals/Setup/Server-Setup/index.md b/Fundamentals/Setup/Server-Setup/index.md index 39f84ace587..a449efa5705 100644 --- a/Fundamentals/Setup/Server-Setup/index.md +++ b/Fundamentals/Setup/Server-Setup/index.md @@ -1,7 +1,7 @@ --- meta.Title: "Information on server setup for Umbraco hosting" meta.Description: "This section describes different ways of setting up servers for use with Umbraco" -versionFrom: 7.0.0 +versionFrom: 9.0.0 --- # Server setup @@ -16,6 +16,10 @@ We strongly encourage the use of HTTPS with Umbraco installations especially in To ensure a stable and smoothly running Umbraco installation, these permissions need to be set correctly. +## [Hosting v9+ in IIS](iis/index.md) + +Information about hosting a v9 application using IIS. + ## [Load Balanced setup](Load-Balancing/index.md) Information on how to deploy Umbraco in a Load Balanced scenario and other details to consider when setting up Umbraco for load balancing. diff --git a/Fundamentals/Setup/Server-Setup/permissions-v9.md b/Fundamentals/Setup/Server-Setup/permissions-v8.md similarity index 63% rename from Fundamentals/Setup/Server-Setup/permissions-v9.md rename to Fundamentals/Setup/Server-Setup/permissions-v8.md index df5377765aa..a687b676ad7 100644 --- a/Fundamentals/Setup/Server-Setup/permissions-v9.md +++ b/Fundamentals/Setup/Server-Setup/permissions-v8.md @@ -1,7 +1,7 @@ --- meta.Title: "Umbraco file and folder permissions" meta.Description: "Information on file and folder permissions required for Umbraco sites" -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # File and folder permissions @@ -9,9 +9,9 @@ versionFrom: 9.0.0 :::note To ensure a stable and smoothly running Umbraco installation, these permissions need to be set correctly. These permissions should be setup before or during the installation of Umbraco. -The main account that requires 'modify' file permissions to be set on the folders below, is the account used start Umbraco. If Umbraco is hosted in IIS this will be the Application Pool Identity for the IIS website. Usually IIS APPPOOL\appPoolName or a specific local account or in some circumstances Network Service - If in doubt, ask your server admin / hosting company. Additionally the IUSR account and IIS_IUSRS account only require 'read only' access to the site's folders. +The main account that requires 'modify' file permissions to be set on the folders below, is the account used by the Application Pool Identity for the IIS website. Usually IIS APPPOOL\appPoolName or a specific local account or in some circumstances Network Service - If in doubt, ask your server admin / hosting company. Additionally the IUSR account and IIS_IUSRS account only require 'read only' access to the site's folders. -Generally when developing locally with Visual Studio or Rider, permissions do not need to be strictly applied. +Generally when developing locally with Visual Studio or WebMatrix, permissions do not need to be strictly applied. ::: :::note @@ -31,24 +31,24 @@ The permissions documentation as it is should allow you to run a plain Umbraco i -/appSettings*.json +/Web.config Modify / Full control -

Only needed for setting database and a global identifier during +

Only needed for setting database and version Information during installation. So can be set to read-only afterwards for enhanced security

-/App_Plugins +/App_Code Modify / Full control

Should always have modify rights as the folder and its files -are used by packages

+are used for dynamically loading in and generating dlls

-/umbraco +/App_Data Modify / Full control

Should always have modify rights as the folder and its files @@ -56,15 +56,32 @@ are used for cache and storage

-/Views +/App_Plugins Modify / Full control

Should always have modify rights as the folder and its files -are used for template, partial view and macro files

+are used by packages

+ + + +/Bin +Modify / Full control + +

Needed for installing packages, if no packages are installed, +this can be set to read access only

-/wwwroot/css +/Config +Modify / Full control + +

Only needed for setting database and version Information during +installation. So can be set to read-only afterwards for enhanced +security

+ + + +/Css Modify / Full control

Should always have modify rights as the folder and its files @@ -72,7 +89,7 @@ are used for css files

-/wwwroot/media +/Media Modify / Full control

Should always have modify rights as the folder and its files @@ -80,7 +97,7 @@ are used for media files uploaded via Umbraco CMS interface

-/wwwroot/scripts +/Scripts Modify / Full control

Should always have modify rights as the folder and its files @@ -88,13 +105,20 @@ are used for script files

-/wwwroot/umbraco +/Umbraco Modify / Full control

For upgrades and package installation, it should have modify rights, but can be set to read-only afterwards

- + +/Views +Modify / Full control + +

Should always have modify rights as the folder and its files +are used for template, partial view and macro files

+ + diff --git a/Fundamentals/Setup/Server-Setup/permissions.md b/Fundamentals/Setup/Server-Setup/permissions.md index a687b676ad7..df5377765aa 100644 --- a/Fundamentals/Setup/Server-Setup/permissions.md +++ b/Fundamentals/Setup/Server-Setup/permissions.md @@ -1,7 +1,7 @@ --- meta.Title: "Umbraco file and folder permissions" meta.Description: "Information on file and folder permissions required for Umbraco sites" -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # File and folder permissions @@ -9,9 +9,9 @@ versionFrom: 8.0.0 :::note To ensure a stable and smoothly running Umbraco installation, these permissions need to be set correctly. These permissions should be setup before or during the installation of Umbraco. -The main account that requires 'modify' file permissions to be set on the folders below, is the account used by the Application Pool Identity for the IIS website. Usually IIS APPPOOL\appPoolName or a specific local account or in some circumstances Network Service - If in doubt, ask your server admin / hosting company. Additionally the IUSR account and IIS_IUSRS account only require 'read only' access to the site's folders. +The main account that requires 'modify' file permissions to be set on the folders below, is the account used start Umbraco. If Umbraco is hosted in IIS this will be the Application Pool Identity for the IIS website. Usually IIS APPPOOL\appPoolName or a specific local account or in some circumstances Network Service - If in doubt, ask your server admin / hosting company. Additionally the IUSR account and IIS_IUSRS account only require 'read only' access to the site's folders. -Generally when developing locally with Visual Studio or WebMatrix, permissions do not need to be strictly applied. +Generally when developing locally with Visual Studio or Rider, permissions do not need to be strictly applied. ::: :::note @@ -31,24 +31,24 @@ The permissions documentation as it is should allow you to run a plain Umbraco i -/Web.config +/appSettings*.json Modify / Full control -

Only needed for setting database and version Information during +

Only needed for setting database and a global identifier during installation. So can be set to read-only afterwards for enhanced security

-/App_Code +/App_Plugins Modify / Full control

Should always have modify rights as the folder and its files -are used for dynamically loading in and generating dlls

+are used by packages

-/App_Data +/umbraco Modify / Full control

Should always have modify rights as the folder and its files @@ -56,32 +56,15 @@ are used for cache and storage

-/App_Plugins +/Views Modify / Full control

Should always have modify rights as the folder and its files -are used by packages

- - - -/Bin -Modify / Full control - -

Needed for installing packages, if no packages are installed, -this can be set to read access only

- - - -/Config -Modify / Full control - -

Only needed for setting database and version Information during -installation. So can be set to read-only afterwards for enhanced -security

+are used for template, partial view and macro files

-/Css +/wwwroot/css Modify / Full control

Should always have modify rights as the folder and its files @@ -89,7 +72,7 @@ are used for css files

-/Media +/wwwroot/media Modify / Full control

Should always have modify rights as the folder and its files @@ -97,7 +80,7 @@ are used for media files uploaded via Umbraco CMS interface

-/Scripts +/wwwroot/scripts Modify / Full control

Should always have modify rights as the folder and its files @@ -105,20 +88,13 @@ are used for script files

-/Umbraco +/wwwroot/umbraco Modify / Full control

For upgrades and package installation, it should have modify rights, but can be set to read-only afterwards

- -/Views -Modify / Full control - -

Should always have modify rights as the folder and its files -are used for template, partial view and macro files

- - + diff --git a/Fundamentals/Setup/Upgrading/general-v8.md b/Fundamentals/Setup/Upgrading/general-v8.md new file mode 100644 index 00000000000..774e3895c3e --- /dev/null +++ b/Fundamentals/Setup/Upgrading/general-v8.md @@ -0,0 +1,171 @@ +--- +versionFrom: 8.0.0 +--- + +# Upgrades in general + +_This is the guide for upgrading in general._ + +:::warning +**Important**: If you are upgrading to a new major version, like from Umbraco 7 to Umbraco 8, make sure to check out the **[version-specific documentation.](version-specific.md)** + +Things may go wrong for various reasons. Make sure to **ALWAYS** make a backup of both your site's files and the database so that you can return to a version that you know works. You will need the backed up files for merging later so this step is not optional. + +Before upgrading to a new major version (like v6 to v7), check if the packages you're using are compatible with the version you're upgrading to. On the package's download page, in the **Project compatibility** area, click **View details** to check version-specific compatibility. +::: + +Sometimes there are exceptions to these guidelines, which are listed in the **[version-specific guide](version-specific.md)**. + +## Note + +It is necessary to run the upgrade installer on each environment of your Umbraco site. So if you want to update your staging and your live site then you need to repeat the steps below and make sure that you click through the install screens so that your upgrade is complete. + +## Contents + +In this article you will find instruction of 3 different ways of upgrading: + +* [Upgrade using NuGet](#upgrade-using-nuget) +* [Upgrade manually from a Zip file](#upgrade-manually-from-a-zip-file) +* [Run an unattended upgrade (v8.12+)](#run-an-unattended-upgrade) + +## Upgrade using NuGet + +You can open up the **Package Console** and type: +`Update-Package UmbracoCms` + +You will be prompted to overwrite files, you should choose **"No to All"** by pressing the **"L"** . If there are any specific configuration changes required for the version you are upgrading to then they will be noted in the **[version-specific guide](version-specific.md)**. + +Or you can open the **NuGet Package Manager** and select the **Updates** pane to get a list of available updates. Choose the package called **UmbracoCms** and click update. This will run through all the files and make sure you have the latest changes while leaving files you have updated. + +### Upgrades to versions lower than 7.2.0 + +If you're not upgrading to 7.2.0 or higher then you should follow these extra instructions, if you are upgrading to 7.2.0+ then you can skip this and go to [Merge UI.xml and language files](#MergeUIxmlandlanguagefiles) + +**The following only applies to upgrading to versions lower than 7.2.0** +You will be asked to overwrite your web.config file and the files in /config, make sure to answer **No** to those questions. + +For some inexplicable reason, the installation will fail if you click "No to All" (in the GUI) or answer "L" (in the package manager console) to the question: "File 'Web.config' already exists in project 'MySite'. Do you want to overwrite it?" So make sure to only answer "**No**" (in the GUI) or "**N**" (in the package manager console). + +![](images/nuget-overwrite-dialog.png) +![](images/nuget-upgrade-overwrite.png) + +Now here comes the tricky bit: We'll be mean and overwrite your web.config file anyway. But we'll back it up so don't worry. (Plus you already had your own backup, right?) You can find the backup in `App_Data\NuGetBackup\20140320-165450\`. (The `20140320-165450` bit is the date and time when the backup occurred, which varies.) You can then merge your config files and make sure they're completely up to date. + +## Upgrade manually from a zip file + +Download the .zip file for the new version you are upgrading to from [https://our.umbraco.com/download](https://our.umbraco.com/download) + +Copy the following folders from inside the .zip file over the existing folders in your site: + +- /bin +- /Umbraco + +:::note +There are hosting providers (we know of one: RackSpace Cloud) that require proper casing of file and folder names. Normally on Windows this is not a problem. If your hosting provider however forces proper casing, you will need to verify that the folder and file names are in the same casing as in the newest version you're upgrading to. +::: + +### Merge configuration files + +You can expect some changes to the following configuration files: + +* Any file in the /Config folder +* The /Global.asax file +* The web.config file in the root of your site **(Important: make sure to copy back the version number, and the connection string as they were.)** +* In rare cases, the Web.config file in the Views folder + +Use a tool like [WinMerge](http://winmerge.org/ "WinMerge") to check changes between all of the config files. Depending on when you last did this there may have been updates to quite a few of them. + +There's also the possibility that some files in the /Config folder are new or some have been removed (we do make a note of this in the release notes). WinMerge (and other diff tools) is able to compare folders as well so you can spot these differences. + +Up until version 6.0.0 it was necessary to change the version number in ClientDependency.config. This was to clear the cached html/css/js files in the backoffice. Change the current version number to one that's higher than that. Make sure not to skip this step as you might get strange behaviour in the backoffice otherwise. + +### Merge UI.xml and language files + +Some packages (like Contour and Umbraco Forms) add dialogs to the UI.xml. Make sure to merge those changes back in from your backup during the upgrade so that the packages continue to work. This file can be found in: /Umbraco/Config/Create/UI.xml. + +Packages like Contour, Umbraco Forms, and Courier also make changes to the language files located in: /Umbraco/Config/Lang/*.xml (typically en.xml). + +### Finalize + +After copying the files and making the config changes, you can open your site. You should see the installer which will guide you through the upgrade. + +The installer will do two things: + +* Update the version number in the Web.config +* Upgrade your database in case there are any changes + +We are aware that, currently, the installer is asking you for the database details of a **blank database** while upgrading. In the near future this will be pre-filled with your existing details and the wording will be updated. So no need to be scared. Enter the details of your existing database and Umbraco will upgrade it to the latest version when necessary. + +## Run an unattended upgrade + +When upgrading your Umbraco project to Umbraco v8.12+ it is possible to enable the upgrade to run unattended. This means that you will not need to run through the installation wizard when upgrading. + +Below you will find the steps you need to take in order to upgrade your project unattended. + +:::tip +Are you running a load balanced setup with multiple servers and environments? + +Check out the section about [Unattended upgrades in a load balanced setup](#unattended-upgrades-in-a-load-balanced-setup). +::: + +### Enable the feature + +1. Add the `Umbraco.Core.RuntimeState.UpgradeUnattended` key to `appSettings` in your web.config file. +2. Set the value of the key to `true`. + +```xml + + +``` + +### Check the `ConfigurationStatus` + +In order to trigger the actual upgrade, the correct version number needs to be set. + +It is important to use the version number of the version that you are upgrading to. If this is not set, the upgrade will not run even if the `UpgradeUnattended` key has been set to `true`. + +1. Locate the `ConfigurationStatus` key in the `appSettings` section in your web.config file. +2. Update the value to match the Umbraco version that you are upgrading to. + +```xml + +``` + +### Run the upgrade + +With the correct configuration applied, the project will be upgraded on the next boot. + +:::note +While the upgrade processes are running, any requests made to the site will be "put on hold", meaning that no content will be returned before the upgrade is complete. +::: + +#### Boot order + +The Runtime level will use `Run` instead of `Upgrade` in order to allow the website to continue to boot up directly after the migration is run, instead of initiating the otherwise required restart. + +:::note +The upgrade is run after Composers but before Components. This is because the migration requires services that are registered in Composers and Components requires that Umbraco and the database is ready. +::: + +### Unattended upgrades in a load balanced setup + +Follow the steps outlined below to use run unattended upgrades in a load balanced setup. + +1. Upgrade Umbraco via NuGet in Visual Studio. Make sure the `Umbraco.Core.ConfigurationStatus` key in `appSetting` in the `web.config` file is updated to match the **target version**. +2. Deploy to all environments, including the updated `appSetting` for `Umbraco.Core.ConfigurationStatus`. +3. Set the `Umbraco.Core.RuntimeState.UpgradeUnattended` key in `appSetting` in the `web.config` to `true` for **the Main server only**. +4. Request a page on the Main server and the upgrade will run automatically. +5. Wait for the upgrade to complete. +6. Browse the Read-Only servers and make sure they do not show the “upgrade required” screen. + +## Post installation + +One important recommendation is to always remove the `install` folder immediately after upgrading Umbraco and never to upload it to a live server. + +## Potential issues and gotchas + +### Browser cache + +Google Chrome has notoriously aggressive caching, so if something doesn't seem to work well in the backoffice, make sure to clear cache and cookies thoroughly (for other browsers as well). Normally the browser cache problem is automatically handled in an Umbraco upgrade by modifying the config/ClientDependency.config version number. If you however wish to re-force this update you can increment this version number which will ensure that any server-side cache of JavaScript and stylesheets gets cleared as well. + +One way to nudge the cache in Chrome is to open the developer tools (F12) and go to the settings (the cog icon). There will be a checkbox that says "Disable cache (while DevTools is open)". Once this checkbox is on you can refresh the page and the cache should be invalidated. To force it even more, the "reload" button next to your address bar now has extra options when you right-click it. It should have "Normal reload", "Hard reload" and "Empty cache and hard reload" now. The last option is the most thorough and you might want to try that. diff --git a/Fundamentals/Setup/Upgrading/general-v9.md b/Fundamentals/Setup/Upgrading/general-v9.md deleted file mode 100644 index 740a9044fc5..00000000000 --- a/Fundamentals/Setup/Upgrading/general-v9.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -versionFrom: 9.0.0 -verified-against: alpha-4 -state: partial -updated-links: false ---- - -# Upgrades in general - -_This is the guide for upgrading in general._ - -:::warning -**Important**: If you are upgrading to a new major version, like from Umbraco 8 to Umbraco 9, make sure to check out the **[version-specific documentation.](version-specific.md)** - -Things may go wrong for various reasons. Make sure to **ALWAYS** make a backup of both your site's files and the database so that you can return to a version that you know works. You will need the backed up files for merging later so this step is not optional. - -Before upgrading to a new major version, check if the packages you're using are compatible with the version you're upgrading to. On the package's download page, in the **Project compatibility** area, click **View details** to check version-specific compatibility. -::: - -Sometimes there are exceptions to these guidelines, which are listed in the **[version-specific guide](version-specific.md)**. - -## Note - -It is necessary to run the upgrade installer on each environment of your Umbraco site. So if you want to update your staging and your live site then you need to repeat the steps below and make sure that you click through the install screens so that your upgrade is complete. - -## Contents - -In this article you will find instruction on the following ways of upgrading an Umbraco project: - -* [Upgrade using NuGet](#upgrade-using-nuget) -* [Run an unattended upgrade](#run-an-unattended-upgrade) -## Upgrade using NuGet - -NuGet installs the latest version of the package when you use the `dotnet add package` command unless you specify a package version: - -`dotnet add package Umbraco.Cms --version ` - -After you have added a package reference to your project by executing the `dotnet add package Umbraco.Cms` command in the directory that contains your project file, run `dotnet restore` to install the package. - -When the command completes, open the **.csproj** file to make sure the package reference was updated: - -```xml - - - -``` - -## Run an unattended upgrade - -When upgrading your Umbraco project it is possible to enable the upgrade to run unattended. This means that you will not need to run through the installation wizard when upgrading. - -Below you will find the steps you need to take in order to upgrade your project unattended. - -:::tip -Are you running a load balanced setup with multiple servers and environments? - -Check out the section about [Unattended upgrades in a load balanced setup](#unattended-upgrades-in-a-load-balanced-setup). -::: - -### Enable the unattended upgrade feature - -1. Add the `Umbraco:Cms:Unattended:UpgradeUnattended` configuration key. -2. Set the value of the key to `true`. - -#### Example from an appsettings json configuration file -```json -{ - "Umbraco": { - "CMS": { - "Unattended": { - "UpgradeUnattended": true - } - } - } -} -``` - -### Run the upgrade - -With the correct configuration applied, the project will be upgraded on the next boot. - -#### Boot order - -The Runtime level will use `Run` instead of `Upgrade` in order to allow the website to continue to boot up directly after the migration is run, instead of initiating the otherwise required restart. - -:::note -The upgrade is run after Composers but before Components and the `UmbracoApplicationStartingNotification`. This is because the migration requires services that are registered in Composers and Components requires that Umbraco and the database is ready. -::: - -### Unattended upgrades in a load balanced setup - -Follow the steps outlined below to use run unattended upgrades in a load balanced setup. - -1. Upgrade Umbraco via NuGet. -2. Deploy to all environments. -3. Set the `Umbraco:Cms:Unattended:UpgradeUnattended` configuration key to `true` for **the Main server only**. -4. Boot the Main server and the upgrade will run automatically. -5. Wait for the upgrade to complete. -6. Boot the Read-Only servers and make sure they do not show the “upgrade required” screen. diff --git a/Fundamentals/Setup/Upgrading/general.md b/Fundamentals/Setup/Upgrading/general.md index 774e3895c3e..740a9044fc5 100644 --- a/Fundamentals/Setup/Upgrading/general.md +++ b/Fundamentals/Setup/Upgrading/general.md @@ -1,5 +1,8 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 +verified-against: alpha-4 +state: partial +updated-links: false --- # Upgrades in general @@ -7,11 +10,11 @@ versionFrom: 8.0.0 _This is the guide for upgrading in general._ :::warning -**Important**: If you are upgrading to a new major version, like from Umbraco 7 to Umbraco 8, make sure to check out the **[version-specific documentation.](version-specific.md)** +**Important**: If you are upgrading to a new major version, like from Umbraco 8 to Umbraco 9, make sure to check out the **[version-specific documentation.](version-specific.md)** Things may go wrong for various reasons. Make sure to **ALWAYS** make a backup of both your site's files and the database so that you can return to a version that you know works. You will need the backed up files for merging later so this step is not optional. -Before upgrading to a new major version (like v6 to v7), check if the packages you're using are compatible with the version you're upgrading to. On the package's download page, in the **Project compatibility** area, click **View details** to check version-specific compatibility. +Before upgrading to a new major version, check if the packages you're using are compatible with the version you're upgrading to. On the package's download page, in the **Project compatibility** area, click **View details** to check version-specific compatibility. ::: Sometimes there are exceptions to these guidelines, which are listed in the **[version-specific guide](version-specific.md)**. @@ -22,83 +25,29 @@ It is necessary to run the upgrade installer on each environment of your Umbraco ## Contents -In this article you will find instruction of 3 different ways of upgrading: +In this article you will find instruction on the following ways of upgrading an Umbraco project: * [Upgrade using NuGet](#upgrade-using-nuget) -* [Upgrade manually from a Zip file](#upgrade-manually-from-a-zip-file) -* [Run an unattended upgrade (v8.12+)](#run-an-unattended-upgrade) - +* [Run an unattended upgrade](#run-an-unattended-upgrade) ## Upgrade using NuGet -You can open up the **Package Console** and type: -`Update-Package UmbracoCms` - -You will be prompted to overwrite files, you should choose **"No to All"** by pressing the **"L"** . If there are any specific configuration changes required for the version you are upgrading to then they will be noted in the **[version-specific guide](version-specific.md)**. - -Or you can open the **NuGet Package Manager** and select the **Updates** pane to get a list of available updates. Choose the package called **UmbracoCms** and click update. This will run through all the files and make sure you have the latest changes while leaving files you have updated. - -### Upgrades to versions lower than 7.2.0 - -If you're not upgrading to 7.2.0 or higher then you should follow these extra instructions, if you are upgrading to 7.2.0+ then you can skip this and go to [Merge UI.xml and language files](#MergeUIxmlandlanguagefiles) - -**The following only applies to upgrading to versions lower than 7.2.0** -You will be asked to overwrite your web.config file and the files in /config, make sure to answer **No** to those questions. - -For some inexplicable reason, the installation will fail if you click "No to All" (in the GUI) or answer "L" (in the package manager console) to the question: "File 'Web.config' already exists in project 'MySite'. Do you want to overwrite it?" So make sure to only answer "**No**" (in the GUI) or "**N**" (in the package manager console). - -![](images/nuget-overwrite-dialog.png) -![](images/nuget-upgrade-overwrite.png) - -Now here comes the tricky bit: We'll be mean and overwrite your web.config file anyway. But we'll back it up so don't worry. (Plus you already had your own backup, right?) You can find the backup in `App_Data\NuGetBackup\20140320-165450\`. (The `20140320-165450` bit is the date and time when the backup occurred, which varies.) You can then merge your config files and make sure they're completely up to date. - -## Upgrade manually from a zip file - -Download the .zip file for the new version you are upgrading to from [https://our.umbraco.com/download](https://our.umbraco.com/download) - -Copy the following folders from inside the .zip file over the existing folders in your site: - -- /bin -- /Umbraco - -:::note -There are hosting providers (we know of one: RackSpace Cloud) that require proper casing of file and folder names. Normally on Windows this is not a problem. If your hosting provider however forces proper casing, you will need to verify that the folder and file names are in the same casing as in the newest version you're upgrading to. -::: - -### Merge configuration files - -You can expect some changes to the following configuration files: - -* Any file in the /Config folder -* The /Global.asax file -* The web.config file in the root of your site **(Important: make sure to copy back the version number, and the connection string as they were.)** -* In rare cases, the Web.config file in the Views folder - -Use a tool like [WinMerge](http://winmerge.org/ "WinMerge") to check changes between all of the config files. Depending on when you last did this there may have been updates to quite a few of them. - -There's also the possibility that some files in the /Config folder are new or some have been removed (we do make a note of this in the release notes). WinMerge (and other diff tools) is able to compare folders as well so you can spot these differences. - -Up until version 6.0.0 it was necessary to change the version number in ClientDependency.config. This was to clear the cached html/css/js files in the backoffice. Change the current version number to one that's higher than that. Make sure not to skip this step as you might get strange behaviour in the backoffice otherwise. - -### Merge UI.xml and language files +NuGet installs the latest version of the package when you use the `dotnet add package` command unless you specify a package version: -Some packages (like Contour and Umbraco Forms) add dialogs to the UI.xml. Make sure to merge those changes back in from your backup during the upgrade so that the packages continue to work. This file can be found in: /Umbraco/Config/Create/UI.xml. +`dotnet add package Umbraco.Cms --version ` -Packages like Contour, Umbraco Forms, and Courier also make changes to the language files located in: /Umbraco/Config/Lang/*.xml (typically en.xml). +After you have added a package reference to your project by executing the `dotnet add package Umbraco.Cms` command in the directory that contains your project file, run `dotnet restore` to install the package. -### Finalize +When the command completes, open the **.csproj** file to make sure the package reference was updated: -After copying the files and making the config changes, you can open your site. You should see the installer which will guide you through the upgrade. - -The installer will do two things: - -* Update the version number in the Web.config -* Upgrade your database in case there are any changes - -We are aware that, currently, the installer is asking you for the database details of a **blank database** while upgrading. In the near future this will be pre-filled with your existing details and the wording will be updated. So no need to be scared. Enter the details of your existing database and Umbraco will upgrade it to the latest version when necessary. +```xml + + + +``` ## Run an unattended upgrade -When upgrading your Umbraco project to Umbraco v8.12+ it is possible to enable the upgrade to run unattended. This means that you will not need to run through the installation wizard when upgrading. +When upgrading your Umbraco project it is possible to enable the upgrade to run unattended. This means that you will not need to run through the installation wizard when upgrading. Below you will find the steps you need to take in order to upgrade your project unattended. @@ -108,64 +57,43 @@ Are you running a load balanced setup with multiple servers and environments? Check out the section about [Unattended upgrades in a load balanced setup](#unattended-upgrades-in-a-load-balanced-setup). ::: -### Enable the feature +### Enable the unattended upgrade feature -1. Add the `Umbraco.Core.RuntimeState.UpgradeUnattended` key to `appSettings` in your web.config file. +1. Add the `Umbraco:Cms:Unattended:UpgradeUnattended` configuration key. 2. Set the value of the key to `true`. -```xml - - -``` - -### Check the `ConfigurationStatus` - -In order to trigger the actual upgrade, the correct version number needs to be set. - -It is important to use the version number of the version that you are upgrading to. If this is not set, the upgrade will not run even if the `UpgradeUnattended` key has been set to `true`. - -1. Locate the `ConfigurationStatus` key in the `appSettings` section in your web.config file. -2. Update the value to match the Umbraco version that you are upgrading to. - -```xml - +#### Example from an appsettings json configuration file +```json +{ + "Umbraco": { + "CMS": { + "Unattended": { + "UpgradeUnattended": true + } + } + } +} ``` ### Run the upgrade With the correct configuration applied, the project will be upgraded on the next boot. -:::note -While the upgrade processes are running, any requests made to the site will be "put on hold", meaning that no content will be returned before the upgrade is complete. -::: - #### Boot order The Runtime level will use `Run` instead of `Upgrade` in order to allow the website to continue to boot up directly after the migration is run, instead of initiating the otherwise required restart. :::note -The upgrade is run after Composers but before Components. This is because the migration requires services that are registered in Composers and Components requires that Umbraco and the database is ready. +The upgrade is run after Composers but before Components and the `UmbracoApplicationStartingNotification`. This is because the migration requires services that are registered in Composers and Components requires that Umbraco and the database is ready. ::: ### Unattended upgrades in a load balanced setup Follow the steps outlined below to use run unattended upgrades in a load balanced setup. -1. Upgrade Umbraco via NuGet in Visual Studio. Make sure the `Umbraco.Core.ConfigurationStatus` key in `appSetting` in the `web.config` file is updated to match the **target version**. -2. Deploy to all environments, including the updated `appSetting` for `Umbraco.Core.ConfigurationStatus`. -3. Set the `Umbraco.Core.RuntimeState.UpgradeUnattended` key in `appSetting` in the `web.config` to `true` for **the Main server only**. -4. Request a page on the Main server and the upgrade will run automatically. +1. Upgrade Umbraco via NuGet. +2. Deploy to all environments. +3. Set the `Umbraco:Cms:Unattended:UpgradeUnattended` configuration key to `true` for **the Main server only**. +4. Boot the Main server and the upgrade will run automatically. 5. Wait for the upgrade to complete. -6. Browse the Read-Only servers and make sure they do not show the “upgrade required” screen. - -## Post installation - -One important recommendation is to always remove the `install` folder immediately after upgrading Umbraco and never to upload it to a live server. - -## Potential issues and gotchas - -### Browser cache - -Google Chrome has notoriously aggressive caching, so if something doesn't seem to work well in the backoffice, make sure to clear cache and cookies thoroughly (for other browsers as well). Normally the browser cache problem is automatically handled in an Umbraco upgrade by modifying the config/ClientDependency.config version number. If you however wish to re-force this update you can increment this version number which will ensure that any server-side cache of JavaScript and stylesheets gets cleared as well. - -One way to nudge the cache in Chrome is to open the developer tools (F12) and go to the settings (the cog icon). There will be a checkbox that says "Disable cache (while DevTools is open)". Once this checkbox is on you can refresh the page and the cache should be invalidated. To force it even more, the "reload" button next to your address bar now has extra options when you right-click it. It should have "Normal reload", "Hard reload" and "Empty cache and hard reload" now. The last option is the most thorough and you might want to try that. +6. Boot the Read-Only servers and make sure they do not show the “upgrade required” screen. diff --git a/Fundamentals/Setup/Upgrading/index-v9.md b/Fundamentals/Setup/Upgrading/index-v7.md similarity index 88% rename from Fundamentals/Setup/Upgrading/index-v9.md rename to Fundamentals/Setup/Upgrading/index-v7.md index b1f2bc656be..829f9a0602b 100644 --- a/Fundamentals/Setup/Upgrading/index-v9.md +++ b/Fundamentals/Setup/Upgrading/index-v7.md @@ -1,14 +1,14 @@ --- meta.Title: "Upgrading existing installs" meta.Description: "Info on the steps to upgrade your copy of Umbraco to a newer version" -versionFrom: 9.0.0 +versionFrom: 7.0.0 --- # Upgrading existing installs _Covers the steps to upgrade your copy of Umbraco to a newer version._ -## [General upgrade instructions](general-v9.md) +## [General upgrade instructions](general.md) General upgrade instructions which must be followed for each upgrade. Usually, upgrading involves copying a few files and updating config files. diff --git a/Fundamentals/Setup/Upgrading/index.md b/Fundamentals/Setup/Upgrading/index.md index 829f9a0602b..b9a43a93488 100644 --- a/Fundamentals/Setup/Upgrading/index.md +++ b/Fundamentals/Setup/Upgrading/index.md @@ -1,7 +1,7 @@ --- meta.Title: "Upgrading existing installs" meta.Description: "Info on the steps to upgrade your copy of Umbraco to a newer version" -versionFrom: 7.0.0 +versionFrom: 9.0.0 --- # Upgrading existing installs diff --git a/Fundamentals/Setup/index-v9.md b/Fundamentals/Setup/index-v7.md similarity index 62% rename from Fundamentals/Setup/index-v9.md rename to Fundamentals/Setup/index-v7.md index 316e7cdbd2f..b114c56d70c 100644 --- a/Fundamentals/Setup/index-v9.md +++ b/Fundamentals/Setup/index-v7.md @@ -1,37 +1,29 @@ --- -versionFrom: 9.0.0 -verified-against: alpha-4 -state: partial -updated-links: false meta.Title: "How to install and configure your Umbraco installation" meta.Description: "Information on the requirements to setup, install & upgrade Umbraco" +versionFrom: 7.0.0 --- # Setup How to install and configure your Umbraco installation. -## [Requirements](Requirements/index-v9.md) +## [Requirements](Requirements/) Defines the system requirements to run Umbraco. -## [Install](Install/index-v9.md) +## [Install](Install/) Umbraco installation steps and guidelines. -## [Upgrading](Upgrading/index-v9.md) +## [Upgrading](Upgrading/) Covers the steps to upgrade your copy of Umbraco to a newer version. -## [Server Setup](Server-Setup/index-v9.md) - +## [Server Setup](Server-Setup/) Information about server setup for Umbraco including information about permissions and load balancing. -## [Config](../../Reference/Config/index.md) - -:::note -Please note that this section is currently not available for Umbraco 9. -::: +## [Config](../../Reference/Config/) How to configure your Umbraco installation. Includes information about all of Umbraco's configuration files and options. diff --git a/Fundamentals/Setup/index.md b/Fundamentals/Setup/index.md index b114c56d70c..206921f78a9 100644 --- a/Fundamentals/Setup/index.md +++ b/Fundamentals/Setup/index.md @@ -1,29 +1,37 @@ --- +versionFrom: 9.0.0 +verified-against: alpha-4 +state: partial +updated-links: false meta.Title: "How to install and configure your Umbraco installation" meta.Description: "Information on the requirements to setup, install & upgrade Umbraco" -versionFrom: 7.0.0 --- # Setup How to install and configure your Umbraco installation. -## [Requirements](Requirements/) +## [Requirements](Requirements/index.md) Defines the system requirements to run Umbraco. -## [Install](Install/) +## [Install](Install/index.md) Umbraco installation steps and guidelines. -## [Upgrading](Upgrading/) +## [Upgrading](Upgrading/index.md) Covers the steps to upgrade your copy of Umbraco to a newer version. -## [Server Setup](Server-Setup/) +## [Server Setup](Server-Setup/index.md) + Information about server setup for Umbraco including information about permissions and load balancing. -## [Config](../../Reference/Config/) +## [Config](../../Reference/Config/index.md) + +:::note +Please note that this section is currently not available for Umbraco 9. +::: How to configure your Umbraco installation. Includes information about all of Umbraco's configuration files and options. diff --git a/Tutorials/Add-Google-Authentication/index.md b/Tutorials/Add-Google-Authentication/index.md index 721c6cdbae0..8293b5c7146 100644 --- a/Tutorials/Add-Google-Authentication/index.md +++ b/Tutorials/Add-Google-Authentication/index.md @@ -3,6 +3,7 @@ versionFrom: 7.0.0 needsV8Update: "false" meta.Title: "Add Google Authentication" meta.Description: "A guide to setup a Google login for the Umbraco backoffice." +needsV9Update: "true" --- diff --git a/Tutorials/Creating-Basic-Site/Articles-Parent-and-Article-Items/index-v9.md b/Tutorials/Creating-Basic-Site/Articles-Parent-and-Article-Items/index-v8.md similarity index 74% rename from Tutorials/Creating-Basic-Site/Articles-Parent-and-Article-Items/index-v9.md rename to Tutorials/Creating-Basic-Site/Articles-Parent-and-Article-Items/index-v8.md index 2edc29acd42..b8b5a842fe0 100644 --- a/Tutorials/Creating-Basic-Site/Articles-Parent-and-Article-Items/index-v9.md +++ b/Tutorials/Creating-Basic-Site/Articles-Parent-and-Article-Items/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Articles and Article Items - A Parent Page with Infinite Children @@ -22,7 +22,7 @@ To create **Articles Main** Document Type, follow these steps: | Group | Field Name | Alias | Data Type | |-------|--------------------|------------------|------------------| - | Intro | Articles Title | articlesTitle | Textstring | + | Intro | Articles Title | articlesTitle | TextBox | | Intro | Articles Body Text | articlesBodyText | Rich Text Editor | ![Articles Main Document Type Data Properties](images/figure-38-articles-main-v8.png) @@ -40,7 +40,7 @@ To create **Articles Item** Document Type, follow these steps: | Group | Field Name | Alias | Data Type | |---------|-----------------|----------------|------------------| - | Content | Article Title | articleTitle | Textstring | + | Content | Article Title | articleTitle | TextBox | | Content | Article Content | articleContent | Rich Text Editor | ![Article Item Document Type Data Properties](images/figure-39-articles-item-v8.png) @@ -74,14 +74,15 @@ To add a content node: 1. Go to **Content**. 2. Select **...** next to the **HomePage** and select **Articles Main**. 3. Enter the name for the article. We are going to call it _Articles_. -4. Enter the **Article Title**, **Article Content**, and click **Save and Publish**. When you click on Save and Publish, you will notice an empty list view is created. +4. Enter the **Article Title**, **Article Content**, and click **Save**. + When you click on Save, you will notice an empty list view is created. We still need to add the child nodes which will be displayed in the list view making it easier to view them. You can create new nodes from this section. :::tip If you do not see the list view, try refreshing the page. ::: -5. Click **Create Articles Item** to add two child nodes called **Article 1**, **Article 2**, and click **Save and Publish**. +5. Let's add two child nodes called **Article 1**, **Article 2**, and click **Save and Publish**. ![Content Tree With Articles](images/figure-40-articles-created-v8.png) @@ -96,17 +97,17 @@ To update the **Articles Main** template, follow these steps: 5. Copy the contents of **Blog.html** and paste the content into **Articles Main** below the closing curly brace "}". :::warning - Take care when pasting the template not to overwrite the first line `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage`. If you get an error when loading the page ensure the last part in <> brackets matches your Document Type alias. + Take care when pasting the template not to overwrite the first line `@inherits Umbraco.Web.Mvc.UmbracoViewPage`. If you get an error when loading the page ensure the last part in <> brackets matches your Document Type alias. ::: -6. Remove everything from the `` (around line 9) to the end of the `
` tag (around line 44) which is the `header` and `navigation` of the site since it is already mentioned in the master template. -7. Remove everything from the `` tag (around line 84) to the end of the `` tag (around line 131) -8. Replace the static text within the `

` tags (around line 13) with the Model.Value reference to **_articlesTitle_**. -9. Replace the static text within the `
` tags (from line 24 to 30) with the Model.Value reference to **_articlesBodyText_**. +6. Remove everything from the `` (around line 8) to the end of the `
` tag (around line 43) which is the `header` and `navigation` of the site since it is already mentioned in the master template. +7. Remove everything from the `` tag (around line 92) to the end of the `` tag (around line 139) +8. Replace the static text within the `

` tags (around line 12) with the Model.Value reference to **_articlesTitle_**. +9. Replace the static text within the `
` tags (from line 22 to 28) with the Model.Value reference to **_articlesBodyText_**. - ![Articles Main Template](images/articles-main-template-v9.png) -10. Define a query for all articles, just below the `

` tag (around line 32) of the `` section. + ![Articles Main Template](images/articles-main-template.png) +10. Define a query for all articles, just below the `

` tag (around line 30) of the `` section. - ![Query Builder](images/query-builder-v9.png) + ![Query Builder](images/query-builder.png) 11. You can set conditions to get specific articles or decide the order of the articles. For the purpose of this guide, we'll use the following parameters: ![Query parameters](images/query-parameters.png) @@ -114,9 +115,8 @@ To update the **Articles Main** template, follow these steps: It will look similar to this: ```csharp - - @{ - var selection = Umbraco.Content(Guid.Parse("e0a4f1ff-122e-41bd-941c-f9686f03019f")) + @{ + var selection = Umbraco.Content(Guid.Parse("c5ed4ea3-f9ea-4c37-a798-fe7892c98b95")) .ChildrenOfType("articlesItem") .Where(x => x.IsVisible()) .OrderByDescending(x => x.CreateDate); @@ -125,7 +125,7 @@ To update the **Articles Main** template, follow these steps: @foreach (var item in selection) {
  • - @item.Name() + @item.Name
  • } @@ -134,11 +134,10 @@ To update the **Articles Main** template, follow these steps: 13. The above code will output a list of all the **_Article Items_** as links using the name. We will modify the template a little, to add more information about the articles. Replace the `HTML` in the *foreach* loop with this snippet: ```csharp -
    - - -
    @Html.Truncate(item.Value("articleContent").ToString(), 20, true)Read More..
    + + +
    @Html.Truncate(item.Value("articleContent").ToString(), 20, true)Read More..
    ``` @@ -153,14 +152,14 @@ To update the **Articles Item** template, follow these steps: 5. Copy the contents of **Blogpost.html** and paste the content into **Articles Item** below the closing curly brace "}". :::warning - Take care when pasting the template not to overwrite the first line `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage`. If you get an error when loading the page ensure the last part in <> brackets matches your Document Type alias. + Take care when pasting the template not to overwrite the first line `@inherits Umbraco.Web.Mvc.UmbracoViewPage`. If you get an error when loading the page ensure the last part in <> brackets matches your Document Type alias. ::: -6. Remove everything from the `` (around line 9) to the end of the `

    ` tag (around line 44) which is the `header` and `navigation` of the site since it is already mentioned in the master template. -7. Remove everything from the `` tag (around line 114) to the end of the `` tag (around line 161) -8. Replace the static text within the `

    ` tags (around line 14) with the Model.Value reference to **_articleTitle_**. -9. Replace the static text within the `
    ` tags (from line 26 to 41) with the Model.Value reference to **_articleContent_**. +6. Remove everything from the `` (around line 8) to the end of the `
    ` tag (around line 43) which is the `header` and `navigation` of the site since it is already mentioned in the master template. +7. Remove everything from the `` tag (around line 104) to the end of the `` tag (around line 151) +8. Replace the static text within the `

    ` tags (around line 12) with the Model.Value reference to **_articleTitle_**. +9. Replace the static text within the `
    ` tags (from line 23 to 38) with the Model.Value reference to **_articleContent_**. - ![Articles Item Template](images/articles-item-template-v9.png) + ![Articles Item Template](images/articles-item-template.png) 10. Click **Save**. Check your browser, you should now see something similar to the screen below. diff --git a/Tutorials/Creating-Basic-Site/Articles-Parent-and-Article-Items/index.md b/Tutorials/Creating-Basic-Site/Articles-Parent-and-Article-Items/index.md index b8b5a842fe0..2edc29acd42 100644 --- a/Tutorials/Creating-Basic-Site/Articles-Parent-and-Article-Items/index.md +++ b/Tutorials/Creating-Basic-Site/Articles-Parent-and-Article-Items/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Articles and Article Items - A Parent Page with Infinite Children @@ -22,7 +22,7 @@ To create **Articles Main** Document Type, follow these steps: | Group | Field Name | Alias | Data Type | |-------|--------------------|------------------|------------------| - | Intro | Articles Title | articlesTitle | TextBox | + | Intro | Articles Title | articlesTitle | Textstring | | Intro | Articles Body Text | articlesBodyText | Rich Text Editor | ![Articles Main Document Type Data Properties](images/figure-38-articles-main-v8.png) @@ -40,7 +40,7 @@ To create **Articles Item** Document Type, follow these steps: | Group | Field Name | Alias | Data Type | |---------|-----------------|----------------|------------------| - | Content | Article Title | articleTitle | TextBox | + | Content | Article Title | articleTitle | Textstring | | Content | Article Content | articleContent | Rich Text Editor | ![Article Item Document Type Data Properties](images/figure-39-articles-item-v8.png) @@ -74,15 +74,14 @@ To add a content node: 1. Go to **Content**. 2. Select **...** next to the **HomePage** and select **Articles Main**. 3. Enter the name for the article. We are going to call it _Articles_. -4. Enter the **Article Title**, **Article Content**, and click **Save**. - When you click on Save, you will notice an empty list view is created. +4. Enter the **Article Title**, **Article Content**, and click **Save and Publish**. When you click on Save and Publish, you will notice an empty list view is created. We still need to add the child nodes which will be displayed in the list view making it easier to view them. You can create new nodes from this section. :::tip If you do not see the list view, try refreshing the page. ::: -5. Let's add two child nodes called **Article 1**, **Article 2**, and click **Save and Publish**. +5. Click **Create Articles Item** to add two child nodes called **Article 1**, **Article 2**, and click **Save and Publish**. ![Content Tree With Articles](images/figure-40-articles-created-v8.png) @@ -97,17 +96,17 @@ To update the **Articles Main** template, follow these steps: 5. Copy the contents of **Blog.html** and paste the content into **Articles Main** below the closing curly brace "}". :::warning - Take care when pasting the template not to overwrite the first line `@inherits Umbraco.Web.Mvc.UmbracoViewPage`. If you get an error when loading the page ensure the last part in <> brackets matches your Document Type alias. + Take care when pasting the template not to overwrite the first line `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage`. If you get an error when loading the page ensure the last part in <> brackets matches your Document Type alias. ::: -6. Remove everything from the `` (around line 8) to the end of the `
    ` tag (around line 43) which is the `header` and `navigation` of the site since it is already mentioned in the master template. -7. Remove everything from the `` tag (around line 92) to the end of the `` tag (around line 139) -8. Replace the static text within the `

    ` tags (around line 12) with the Model.Value reference to **_articlesTitle_**. -9. Replace the static text within the `
    ` tags (from line 22 to 28) with the Model.Value reference to **_articlesBodyText_**. +6. Remove everything from the `` (around line 9) to the end of the `
    ` tag (around line 44) which is the `header` and `navigation` of the site since it is already mentioned in the master template. +7. Remove everything from the `` tag (around line 84) to the end of the `` tag (around line 131) +8. Replace the static text within the `

    ` tags (around line 13) with the Model.Value reference to **_articlesTitle_**. +9. Replace the static text within the `
    ` tags (from line 24 to 30) with the Model.Value reference to **_articlesBodyText_**. - ![Articles Main Template](images/articles-main-template.png) -10. Define a query for all articles, just below the `

    ` tag (around line 30) of the `` section. + ![Articles Main Template](images/articles-main-template-v9.png) +10. Define a query for all articles, just below the `

    ` tag (around line 32) of the `` section. - ![Query Builder](images/query-builder.png) + ![Query Builder](images/query-builder-v9.png) 11. You can set conditions to get specific articles or decide the order of the articles. For the purpose of this guide, we'll use the following parameters: ![Query parameters](images/query-parameters.png) @@ -115,8 +114,9 @@ To update the **Articles Main** template, follow these steps: It will look similar to this: ```csharp - @{ - var selection = Umbraco.Content(Guid.Parse("c5ed4ea3-f9ea-4c37-a798-fe7892c98b95")) + + @{ + var selection = Umbraco.Content(Guid.Parse("e0a4f1ff-122e-41bd-941c-f9686f03019f")) .ChildrenOfType("articlesItem") .Where(x => x.IsVisible()) .OrderByDescending(x => x.CreateDate); @@ -125,7 +125,7 @@ To update the **Articles Main** template, follow these steps: @foreach (var item in selection) {
  • - @item.Name + @item.Name()
  • } @@ -134,10 +134,11 @@ To update the **Articles Main** template, follow these steps: 13. The above code will output a list of all the **_Article Items_** as links using the name. We will modify the template a little, to add more information about the articles. Replace the `HTML` in the *foreach* loop with this snippet: ```csharp +
    - - -
    @Html.Truncate(item.Value("articleContent").ToString(), 20, true)Read More..
    + + +
    @Html.Truncate(item.Value("articleContent").ToString(), 20, true)Read More..
    ``` @@ -152,14 +153,14 @@ To update the **Articles Item** template, follow these steps: 5. Copy the contents of **Blogpost.html** and paste the content into **Articles Item** below the closing curly brace "}". :::warning - Take care when pasting the template not to overwrite the first line `@inherits Umbraco.Web.Mvc.UmbracoViewPage`. If you get an error when loading the page ensure the last part in <> brackets matches your Document Type alias. + Take care when pasting the template not to overwrite the first line `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage`. If you get an error when loading the page ensure the last part in <> brackets matches your Document Type alias. ::: -6. Remove everything from the `` (around line 8) to the end of the `

    ` tag (around line 43) which is the `header` and `navigation` of the site since it is already mentioned in the master template. -7. Remove everything from the `` tag (around line 104) to the end of the `` tag (around line 151) -8. Replace the static text within the `

    ` tags (around line 12) with the Model.Value reference to **_articleTitle_**. -9. Replace the static text within the `
    ` tags (from line 23 to 38) with the Model.Value reference to **_articleContent_**. +6. Remove everything from the `` (around line 9) to the end of the `
    ` tag (around line 44) which is the `header` and `navigation` of the site since it is already mentioned in the master template. +7. Remove everything from the `` tag (around line 114) to the end of the `` tag (around line 161) +8. Replace the static text within the `

    ` tags (around line 14) with the Model.Value reference to **_articleTitle_**. +9. Replace the static text within the `
    ` tags (from line 26 to 41) with the Model.Value reference to **_articleContent_**. - ![Articles Item Template](images/articles-item-template.png) + ![Articles Item Template](images/articles-item-template-v9.png) 10. Click **Save**. Check your browser, you should now see something similar to the screen below. diff --git a/Tutorials/Creating-Basic-Site/CSS-And-Images/index-v8.md b/Tutorials/Creating-Basic-Site/CSS-And-Images/index-v8.md new file mode 100644 index 00000000000..91fd5caf901 --- /dev/null +++ b/Tutorials/Creating-Basic-Site/CSS-And-Images/index-v8.md @@ -0,0 +1,16 @@ +--- +versionFrom: 8.0.0 +--- +# CSS and Images + +Our homepage is currently missing the CSS and image files. To include these files: + +1. Navigate to the **Umbraco Installation** folder and the **Custom Umbraco Template** folder in File Explorer. +2. Copy the **css** and **images** folders from the Custom Umbraco template folder to the Umbraco Installation folder. +3. Using Chrome/Firefox/Edge Developer Tools, start or refresh your `http://localhost:xxxx.` + + The network tab should not report any missing css or images files. If the network tab reports any error, check for typos and if the folders are in the right places. + +--- + +Prev: [Creating Your First Template and Content Node](../Creating-Your-First-Template-and-Content-Node)         Next: [Displaying the Document Type Properties](../Displaying-the-Document-Type-Properties) diff --git a/Tutorials/Creating-Basic-Site/CSS-And-Images/index-v9.md b/Tutorials/Creating-Basic-Site/CSS-And-Images/index-v9.md deleted file mode 100644 index b4e9963f5ca..00000000000 --- a/Tutorials/Creating-Basic-Site/CSS-And-Images/index-v9.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -versionFrom: 9.0.0 ---- -# CSS and Images - -Our homepage is currently missing the CSS and image files. To include these files: - -1. Navigate to the **MyCustomUmbracoProject** folder and the **Custom Umbraco Template** folder in File Explorer. -2. Copy the **css** and **images** folders from the *Custom Umbraco template* folder and place it in the **wwwroot** folder inside the *MyCustomUmbracoProject* folder. -3. Navigate to the **Master** template in the **Settings** section and update the stylesheet reference link to `/css/main.css`. - ![Master Template](images/Master-Template.png) -4. Using Chrome/Firefox/Edge Developer Tools, start or refresh your `http://localhost:xxxx.` - - The network tab should not report any missing css or images files. If the network tab reports any error, check for typos and if the folders are in the right places. - ---- - -Prev: [Creating Your First Template and Content Node](../Creating-Your-First-Template-and-Content-Node)         Next: [Displaying the Document Type Properties](../Displaying-the-Document-Type-Properties) diff --git a/Tutorials/Creating-Basic-Site/CSS-And-Images/index.md b/Tutorials/Creating-Basic-Site/CSS-And-Images/index.md index 91fd5caf901..b4e9963f5ca 100644 --- a/Tutorials/Creating-Basic-Site/CSS-And-Images/index.md +++ b/Tutorials/Creating-Basic-Site/CSS-And-Images/index.md @@ -1,13 +1,15 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # CSS and Images Our homepage is currently missing the CSS and image files. To include these files: -1. Navigate to the **Umbraco Installation** folder and the **Custom Umbraco Template** folder in File Explorer. -2. Copy the **css** and **images** folders from the Custom Umbraco template folder to the Umbraco Installation folder. -3. Using Chrome/Firefox/Edge Developer Tools, start or refresh your `http://localhost:xxxx.` +1. Navigate to the **MyCustomUmbracoProject** folder and the **Custom Umbraco Template** folder in File Explorer. +2. Copy the **css** and **images** folders from the *Custom Umbraco template* folder and place it in the **wwwroot** folder inside the *MyCustomUmbracoProject* folder. +3. Navigate to the **Master** template in the **Settings** section and update the stylesheet reference link to `/css/main.css`. + ![Master Template](images/Master-Template.png) +4. Using Chrome/Firefox/Edge Developer Tools, start or refresh your `http://localhost:xxxx.` The network tab should not report any missing css or images files. If the network tab reports any error, check for typos and if the folders are in the right places. diff --git a/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-1/index-v9.md b/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-1/index-v8.md similarity index 90% rename from Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-1/index-v9.md rename to Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-1/index-v8.md index 881ffaaa23f..98eaf8134ce 100644 --- a/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-1/index-v9.md +++ b/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-1/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Creating a Master Template @@ -13,7 +13,7 @@ To create a new master template: 2. Select **Templates** from the **Templating** section. 3. Select the **...** next to the **Templates** folder and click **Create**. 4. A template opens up in the content editor. Enter a **Name** for the master template. Let's call it _Master_. - ![Master Template](images/figure-22-master-template-v9.png) + ![Master Template](images/figure-22-master-template-v8.png) 5. Click **Save**. ## Using the Master Template @@ -24,7 +24,7 @@ To use the master template: 2. Select **Templates** from the **Templating** section and open the **Homepage** template. 3. Select `Master Template: No Master`. The Master template dialog opens on the right-side of the browser. 4. Select the template called **Master**. This will update the Razor code section from `Layout = null;` to `Layout = "Master.cshtml";` - ![Homepage Template now sits under the Master](images/figure-23-homepage-has-master-template-v9.png) + ![Homepage Template now sits under the Master](images/figure-23-homepage-has-master-template-v8.png) 5. Click **Save**. ## Updating Templates With the New Master Template @@ -35,18 +35,18 @@ To update templates with the new master template, follow these steps: 1. Go to **Settings**. 2. Select **Templates** from the **Templating** section and open the **Homepage** template. -3. For this tutorial, we will cut everything from the `` (around line 9) to the end of the `
    ` tag (around line 44) which is the `header` and `navigation` of the site to the master template. - ![Homepage Template After Cutting the Header](images/figure-24-homepage-after-cutting-the-header-v9.png) +3. For this tutorial, we will cut everything from the `` (around line 8) to the end of the `

    ` tag (around line 43) which is the `header` and `navigation` of the site to the master template. + ![Homepage Template After Cutting the Header](images/figure-24-homepage-after-cutting-the-header-v8.png) 4. Click **Save**. -5. Go to the **Master** template and paste this HTML markup after the closing curly brace (around line 9). - ![Master Template after Pasting the Header](images/figure-25-master-template-with-header-v9.png) +5. Go to the **Master** template and paste this HTML markup after the closing curly brace (around line 8). + ![Master Template after Pasting the Header](images/figure-25-master-template-with-header-v8.png) 6. At the end of this markup, we need to tell Umbraco to insert the child template's content. To do so, add the code **_@RenderBody()_** at the end. - ![Adding RenderBody() to the Master Template](images/figure-26-adding-renderbody-v9.png) + ![Adding RenderBody() to the Master Template](images/figure-26-adding-renderbody-v8.png) 7. Click **Save**. 8. Repeat the same process for the footer content: - 1. Go to **Settings > Templates > Homepage template** and cut everything from the `` tag (around line 110) to the end of the `` tag (around line 124) and click **Save**. + 1. Go to **Settings > Templates > Homepage template** and cut everything from the `` tag (around line 108) to the end of the `` tag (around line 122) and click **Save**. 2. Go to the **Master** template and paste this HTML markup after the **_@RenderBody_** field we've added. - ![Completed Master Template](images/figure-27-master-template-complete-v9.png) + ![Completed Master Template](images/figure-27-master-template-complete-v8.png) 3. Click **Save**. Now we've done a lot of work. When we refresh our localhost page, nothing has changed. If you have a compilation error you have perhaps mistyped **@RenderBody()**. @@ -56,8 +56,7 @@ If you are missing any content (header or footer), check that the templates matc ### Master Template ```csharp -@using Umbraco.Cms.Web.Common.PublishedModels; -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage +@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = null; } @@ -71,7 +70,7 @@ If you are missing any content (header or footer), check that the templates matc - + @@ -103,16 +102,15 @@ If you are missing any content (header or footer), check that the templates matc @RenderBody() - + - - + + @@ -123,9 +121,8 @@ If you are missing any content (header or footer), check that the templates matc ### Homepage Template ```csharp -@using Umbraco.Cms.Web.Common.PublishedModels; -@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage -@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; +@inherits Umbraco.Web.Mvc.UmbracoViewPage +@using ContentModels = Umbraco.Web.PublishedModels; @{ Layout = "Master.cshtml"; } @@ -148,8 +145,7 @@ If you are missing any content (header or footer), check that the templates matc
    - @Model.Value("bodyText") - + @Model.Value("bodyText")
    diff --git a/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-1/index.md b/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-1/index.md index 98eaf8134ce..881ffaaa23f 100644 --- a/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-1/index.md +++ b/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-1/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Creating a Master Template @@ -13,7 +13,7 @@ To create a new master template: 2. Select **Templates** from the **Templating** section. 3. Select the **...** next to the **Templates** folder and click **Create**. 4. A template opens up in the content editor. Enter a **Name** for the master template. Let's call it _Master_. - ![Master Template](images/figure-22-master-template-v8.png) + ![Master Template](images/figure-22-master-template-v9.png) 5. Click **Save**. ## Using the Master Template @@ -24,7 +24,7 @@ To use the master template: 2. Select **Templates** from the **Templating** section and open the **Homepage** template. 3. Select `Master Template: No Master`. The Master template dialog opens on the right-side of the browser. 4. Select the template called **Master**. This will update the Razor code section from `Layout = null;` to `Layout = "Master.cshtml";` - ![Homepage Template now sits under the Master](images/figure-23-homepage-has-master-template-v8.png) + ![Homepage Template now sits under the Master](images/figure-23-homepage-has-master-template-v9.png) 5. Click **Save**. ## Updating Templates With the New Master Template @@ -35,18 +35,18 @@ To update templates with the new master template, follow these steps: 1. Go to **Settings**. 2. Select **Templates** from the **Templating** section and open the **Homepage** template. -3. For this tutorial, we will cut everything from the `` (around line 8) to the end of the `` tag (around line 43) which is the `header` and `navigation` of the site to the master template. - ![Homepage Template After Cutting the Header](images/figure-24-homepage-after-cutting-the-header-v8.png) +3. For this tutorial, we will cut everything from the `` (around line 9) to the end of the `` tag (around line 44) which is the `header` and `navigation` of the site to the master template. + ![Homepage Template After Cutting the Header](images/figure-24-homepage-after-cutting-the-header-v9.png) 4. Click **Save**. -5. Go to the **Master** template and paste this HTML markup after the closing curly brace (around line 8). - ![Master Template after Pasting the Header](images/figure-25-master-template-with-header-v8.png) +5. Go to the **Master** template and paste this HTML markup after the closing curly brace (around line 9). + ![Master Template after Pasting the Header](images/figure-25-master-template-with-header-v9.png) 6. At the end of this markup, we need to tell Umbraco to insert the child template's content. To do so, add the code **_@RenderBody()_** at the end. - ![Adding RenderBody() to the Master Template](images/figure-26-adding-renderbody-v8.png) + ![Adding RenderBody() to the Master Template](images/figure-26-adding-renderbody-v9.png) 7. Click **Save**. 8. Repeat the same process for the footer content: - 1. Go to **Settings > Templates > Homepage template** and cut everything from the `` tag (around line 108) to the end of the `` tag (around line 122) and click **Save**. + 1. Go to **Settings > Templates > Homepage template** and cut everything from the `` tag (around line 110) to the end of the `` tag (around line 124) and click **Save**. 2. Go to the **Master** template and paste this HTML markup after the **_@RenderBody_** field we've added. - ![Completed Master Template](images/figure-27-master-template-complete-v8.png) + ![Completed Master Template](images/figure-27-master-template-complete-v9.png) 3. Click **Save**. Now we've done a lot of work. When we refresh our localhost page, nothing has changed. If you have a compilation error you have perhaps mistyped **@RenderBody()**. @@ -56,7 +56,8 @@ If you are missing any content (header or footer), check that the templates matc ### Master Template ```csharp -@inherits Umbraco.Web.Mvc.UmbracoViewPage +@using Umbraco.Cms.Web.Common.PublishedModels; +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @{ Layout = null; } @@ -70,7 +71,7 @@ If you are missing any content (header or footer), check that the templates matc - + @@ -102,15 +103,16 @@ If you are missing any content (header or footer), check that the templates matc @RenderBody() - + - - + + @@ -121,8 +123,9 @@ If you are missing any content (header or footer), check that the templates matc ### Homepage Template ```csharp -@inherits Umbraco.Web.Mvc.UmbracoViewPage -@using ContentModels = Umbraco.Web.PublishedModels; +@using Umbraco.Cms.Web.Common.PublishedModels; +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage +@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels; @{ Layout = "Master.cshtml"; } @@ -145,7 +148,8 @@ If you are missing any content (header or footer), check that the templates matc
    - @Model.Value("bodyText") + @Model.Value("bodyText") +
    diff --git a/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-2/index-v9.md b/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-2/index-v8.md similarity index 88% rename from Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-2/index-v9.md rename to Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-2/index-v8.md index 656b0f692db..7356498fb36 100644 --- a/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-2/index-v9.md +++ b/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-2/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Creating Pages and Using the Master Template - Part 2 @@ -85,11 +85,11 @@ To add the Document Type properties: 1. Go to **Settings**. 2. Select **Templates** from the **Templating** section, and open the **Simple Content Page** template. -3. Scroll to the `` (around line 8) section and highlight the text `“Umbraco Support”` (around line 11). +3. Scroll to the `` (around line 7) section and highlight the text `“Umbraco Support”`. 4. Click **Insert** and select **Value**. 5. Select the **pageTitle** field from the drop-down list and click **Submit**. 6. Repeat the same process for the `
    ` tag: - 1. Highlight the content from the `

    ` tag (around line 19) to the end of the `

    ` tag (around line 22). + 1. Highlight the content from the `

    ` tag (around line 17) to the end of the `

    ` tag (around line 20). 2. Click **Insert** and select **Value**. 3. Select **bodyText** field from the drop-down list. 4. Click **Submit**. @@ -113,19 +113,13 @@ To use the Document Type properties from the homepage, do the following: 1. Go to **Settings**. 2. Select **Templates** from the **Templating** section, and open the **Master** template. -3. Highlight `@Model.Value("footerText")` in the footer (around line 52) and click **Insert**. -4. Select **Value** and choose the **footerText** again from the **Choose field** dropdown. +3. Highlight `@Model.Value("footerText")` in the footer (around line 51) and click **Insert**. +4. Select **Value** and choose the footerText again from the **Choose field** dropdown. 5. Select **Yes, make it recursive** checkbox. This notifies Umbraco to look up the content tree if the field doesn't exist at the node level for the page we're requesting. - - :::note - To use the `fallback` type, add the `@using Umbraco.Cms.Core.Models.PublishedContent;` directive. - ::: - - ![Adding directive for Fallback](images/fallback-directive-v9.png) 6. Click **Submit**. 7. Click **Save**. -Reload the *Contact Us* page to view the content with the footer. +Reload the Contact Us page to view the content with the footer. --- diff --git a/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-2/index.md b/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-2/index.md index 7356498fb36..656b0f692db 100644 --- a/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-2/index.md +++ b/Tutorials/Creating-Basic-Site/Creating-Master-Template-Part-2/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Creating Pages and Using the Master Template - Part 2 @@ -85,11 +85,11 @@ To add the Document Type properties: 1. Go to **Settings**. 2. Select **Templates** from the **Templating** section, and open the **Simple Content Page** template. -3. Scroll to the `` (around line 7) section and highlight the text `“Umbraco Support”`. +3. Scroll to the `` (around line 8) section and highlight the text `“Umbraco Support”` (around line 11). 4. Click **Insert** and select **Value**. 5. Select the **pageTitle** field from the drop-down list and click **Submit**. 6. Repeat the same process for the `
    ` tag: - 1. Highlight the content from the `

    ` tag (around line 17) to the end of the `

    ` tag (around line 20). + 1. Highlight the content from the `

    ` tag (around line 19) to the end of the `

    ` tag (around line 22). 2. Click **Insert** and select **Value**. 3. Select **bodyText** field from the drop-down list. 4. Click **Submit**. @@ -113,13 +113,19 @@ To use the Document Type properties from the homepage, do the following: 1. Go to **Settings**. 2. Select **Templates** from the **Templating** section, and open the **Master** template. -3. Highlight `@Model.Value("footerText")` in the footer (around line 51) and click **Insert**. -4. Select **Value** and choose the footerText again from the **Choose field** dropdown. +3. Highlight `@Model.Value("footerText")` in the footer (around line 52) and click **Insert**. +4. Select **Value** and choose the **footerText** again from the **Choose field** dropdown. 5. Select **Yes, make it recursive** checkbox. This notifies Umbraco to look up the content tree if the field doesn't exist at the node level for the page we're requesting. + + :::note + To use the `fallback` type, add the `@using Umbraco.Cms.Core.Models.PublishedContent;` directive. + ::: + + ![Adding directive for Fallback](images/fallback-directive-v9.png) 6. Click **Submit**. 7. Click **Save**. -Reload the Contact Us page to view the content with the footer. +Reload the *Contact Us* page to view the content with the footer. --- diff --git a/Tutorials/Creating-Basic-Site/Creating-Your-First-Template-and-Content-Node/index-v9.md b/Tutorials/Creating-Basic-Site/Creating-Your-First-Template-and-Content-Node/index-v8.md similarity index 99% rename from Tutorials/Creating-Basic-Site/Creating-Your-First-Template-and-Content-Node/index-v9.md rename to Tutorials/Creating-Basic-Site/Creating-Your-First-Template-and-Content-Node/index-v8.md index 20c47af4d0b..18abd1a146c 100644 --- a/Tutorials/Creating-Basic-Site/Creating-Your-First-Template-and-Content-Node/index-v9.md +++ b/Tutorials/Creating-Basic-Site/Creating-Your-First-Template-and-Content-Node/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Creating Your First Template @@ -11,7 +11,7 @@ To edit the template: 1. Go to **Settings**. 2. Expand the **Templates** folder in the **Templating** section of the tree. You should see a template titled **_HomePage_**. 3. Open the template. It will contain a little bit of **_Razor code_**. - ![Empty Homepage Template](images/figure-13-empty-homepage-template-v9.png) + ![Empty Homepage Template](images/figure-13-empty-homepage-template-v8.png) 4. Leaving the code that's there (if you don't understand it, don't worry!) let's copy our template code in. :::note diff --git a/Tutorials/Creating-Basic-Site/Creating-Your-First-Template-and-Content-Node/index.md b/Tutorials/Creating-Basic-Site/Creating-Your-First-Template-and-Content-Node/index.md index 18abd1a146c..20c47af4d0b 100644 --- a/Tutorials/Creating-Basic-Site/Creating-Your-First-Template-and-Content-Node/index.md +++ b/Tutorials/Creating-Basic-Site/Creating-Your-First-Template-and-Content-Node/index.md @@ -1,5 +1,5 @@ --- -versionFrom: 8.0.0 +versionFrom: 9.0.0 --- # Creating Your First Template @@ -11,7 +11,7 @@ To edit the template: 1. Go to **Settings**. 2. Expand the **Templates** folder in the **Templating** section of the tree. You should see a template titled **_HomePage_**. 3. Open the template. It will contain a little bit of **_Razor code_**. - ![Empty Homepage Template](images/figure-13-empty-homepage-template-v8.png) + ![Empty Homepage Template](images/figure-13-empty-homepage-template-v9.png) 4. Leaving the code that's there (if you don't understand it, don't worry!) let's copy our template code in. :::note diff --git a/Tutorials/Creating-Basic-Site/Displaying-the-Document-Type-Properties/index-v9.md b/Tutorials/Creating-Basic-Site/Displaying-the-Document-Type-Properties/index-v8.md similarity index 85% rename from Tutorials/Creating-Basic-Site/Displaying-the-Document-Type-Properties/index-v9.md rename to Tutorials/Creating-Basic-Site/Displaying-the-Document-Type-Properties/index-v8.md index f3b4f77a58c..9b63190ed24 100644 --- a/Tutorials/Creating-Basic-Site/Displaying-the-Document-Type-Properties/index-v9.md +++ b/Tutorials/Creating-Basic-Site/Displaying-the-Document-Type-Properties/index-v8.md @@ -1,5 +1,5 @@ --- -versionFrom: 9.0.0 +versionFrom: 8.0.0 --- # Displaying the Document Type Properties @@ -17,21 +17,21 @@ To set the Document Type properties: 1. Go to **Settings**. 2. Select **Templates** in the **Templating** section, and open the **Homepage** template. -3. Scroll down to the `` section (around line 46) and highlight the text `“Welcome - UmbracoTV”` (around line 49). - ![Preparing to replace the hardcoded text with an Umbraco Page Field](images/figure-18-replace-hardcoded-text-with-umbraco-page-field-v9.png) +3. Scroll down to the `` section (around line 48) and highlight the text `“Welcome - UmbracoTV”`. + ![Preparing to replace the hardcoded text with an Umbraco Page Field](images/figure-18-replace-hardcoded-text-with-umbraco-page-field-v8.png) 4. Click **Insert** and select **Value**. 5. Select **pageTitle** field from the drop-down list. - ![Umbraco Page Field](images/figure-19-umbraco-page-field-v9.png) + ![Umbraco Page Field](images/figure-19-umbraco-page-field-v8.png) 6. Click **Submit**. -7. Repeat the same process for the content between the `
    ` tags (around line 61 to 78): +7. Repeat the same process for the content between the `
    ` tags (around line 60 to 77): 1. Highlight the content as shown in the figure. - ![Replacing the bodyText with the Umbraco Page Field](images/figure-20-replace-bodytext-with-page-field-v9.png) + ![Replacing the bodyText with the Umbraco Page Field](images/figure-20-replace-bodytext-with-page-field-v8.png) 2. Click **Insert** and select **Value**. 3. Select **bodyText** field from the drop-down list. 4. Click **Submit**. -8. Repeat the same process for the content in the `