2021-04-22 14:10:13 -04:00
---
stage: Manage
group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Cascading Settings
> Introduced in [GitLab 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/321724).
2021-04-22 23:09:40 -04:00
The cascading settings framework allows groups to essentially inherit settings
values from ancestors (parent group on up the group hierarchy) and from
2021-04-22 14:10:13 -04:00
instance-level application settings. The framework also allows settings values
2021-04-22 23:09:40 -04:00
to be enforced on groups lower in the hierarchy.
2021-04-22 14:10:13 -04:00
Cascading settings can currently only be defined within `NamespaceSetting` , though
the framework may be extended to other objects in the future.
## Add a new cascading setting
Settings are not cascading by default. To define a cascading setting, take the following steps:
1. In the `NamespaceSetting` model, define the new attribute using the `cascading_attr`
helper method. You can use an array to define multiple attributes on a single line.
2021-04-22 23:09:40 -04:00
2021-04-22 14:10:13 -04:00
```ruby
class NamespaceSetting
include CascadingNamespaceSettingAttribute
2021-04-29 08:09:58 -04:00
2021-04-22 14:10:13 -04:00
cascading_attr :delayed_project_removal
end
```
1. Create the database columns.
2021-04-22 23:09:40 -04:00
You can use the following database migration helper for a completely new setting.
The helper creates four columns, two each in `namespace_settings` and
2021-04-22 14:10:13 -04:00
`application_settings` .
2021-04-22 23:09:40 -04:00
2021-04-22 14:10:13 -04:00
```ruby
2021-09-01 14:08:49 -04:00
class AddDelayedProjectRemovalCascadingSetting < Gitlab::Database::Migration [ 1 . 0 ]
2021-04-22 14:10:13 -04:00
include Gitlab::Database::MigrationHelpers::CascadingNamespaceSettings
2021-04-29 08:09:58 -04:00
2021-09-09 08:09:09 -04:00
enable_lock_retries!
2021-04-22 14:10:13 -04:00
def up
add_cascading_namespace_setting :delayed_project_removal, :boolean, default: false, null: false
end
2021-04-29 08:09:58 -04:00
2021-04-22 14:10:13 -04:00
def down
remove_cascading_namespace_setting :delayed_project_removal
end
end
```
2021-04-22 23:09:40 -04:00
2021-04-22 14:10:13 -04:00
Existing settings being converted to a cascading setting will require individual
2021-04-22 23:09:40 -04:00
migrations to add columns and change existing columns. Use the specifications
below to create migrations as required:
2021-04-22 14:10:13 -04:00
1. Columns in `namespace_settings` table:
- `delayed_project_removal` : No default value. Null values allowed. Use any column type.
- `lock_delayed_project_removal` : Boolean column. Default value is false. Null values not allowed.
1. Columns in `application_settings` table:
2021-04-22 23:09:40 -04:00
- `delayed_project_removal` : Type matching for the column created in `namespace_settings` .
Set default value as desired. Null values not allowed.
- `lock_delayed_project_removal` : Boolean column. Default value is false. Null values not allowed.
2021-04-22 14:10:13 -04:00
## Convenience methods
By defining an attribute using the `cascading_attr` method, a number of convenience
2021-04-22 23:09:40 -04:00
methods are automatically defined.
2021-04-22 14:10:13 -04:00
**Definition:**
```ruby
cascading_attr :delayed_project_removal
```
**Convenience Methods Available:**
- `delayed_project_removal`
- `delayed_project_removal=`
- `delayed_project_removal_locked?`
- `delayed_project_removal_locked_by_ancestor?`
- `delayed_project_removal_locked_by_application_setting?`
- `delayed_project_removal?` (Boolean attributes only)
2021-06-17 20:10:29 -04:00
- `delayed_project_removal_locked_ancestor` (Returns locked namespace settings object `[namespace_id]` )
2021-04-23 14:10:18 -04:00
### Attribute reader method (`delayed_project_removal`)
2021-04-22 14:10:13 -04:00
The attribute reader method (`delayed_project_removal`) returns the correct
cascaded value using the following criteria:
1. Returns the dirty value, if the attribute has changed. This allows standard
2021-04-22 23:09:40 -04:00
Rails validators to be used on the attribute, though `nil` values *must* be allowed.
2021-04-22 14:10:13 -04:00
1. Return locked ancestor value.
1. Return locked instance-level application settings value.
1. Return this namespace's attribute, if not nil.
1. Return value from nearest ancestor where value is not nil.
1. Return instance-level application setting.
2021-04-23 14:10:18 -04:00
### `_locked?` method
By default, the `_locked?` method (`delayed_project_removal_locked?`) returns
2021-04-29 08:09:58 -04:00
`true` if an ancestor of the group or application setting locks the attribute.
2021-04-23 14:10:18 -04:00
It returns `false` when called from the group that locked the attribute.
When `include_self: true` is specified, it returns `true` when called from the group that locked the attribute.
This would be relevant, for example, when checking if an attribute is locked from a project.
2021-05-13 02:10:25 -04:00
## Display cascading settings on the frontend
There are a few Rails view helpers, HAML partials, and JavaScript functions that can be used to display a cascading setting on the frontend.
### Rails view helpers
2021-06-17 20:10:29 -04:00
[`cascading_namespace_setting_locked?` ](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86 )
2021-05-13 02:10:25 -04:00
2021-06-17 20:10:29 -04:00
Calls through to the [`_locked?` method ](#_locked-method ) to check if the setting is locked.
2021-05-13 02:10:25 -04:00
| Argument | Description | Type | Required (default value) |
|:------------|:---------------------------------------------------------------------------------|:----------------------------------------------------------------------------------|:-------------------------|
| `attribute` | Name of the setting. For example, `:delayed_project_removal` . | `String` or `Symbol` | `true` |
| `group` | Current group. | [`Group` ](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/group.rb ) | `true` |
| `**args` | Additional arguments to pass through to the [`_locked?` method ](#_locked-method ) | | `false` |
### HAML partials
2021-06-17 20:10:29 -04:00
[`_enforcement_checkbox.html.haml` ](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/views/shared/namespaces/cascading_settings/_enforcement_checkbox.html.haml )
2021-05-13 02:10:25 -04:00
2021-06-17 20:10:29 -04:00
Renders the enforcement checkbox.
2021-05-13 02:10:25 -04:00
| Local | Description | Type | Required (default value) |
|:-----------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------|:------------------------------------------------|
| `attribute` | Name of the setting. For example, `:delayed_project_removal` . | `String` or `Symbol` | `true` |
2021-07-05 11:07:38 -04:00
| `group` | Current group. | [`Group` ](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/group.rb ) | `true` |
2021-05-13 02:10:25 -04:00
| `form` | [Rails FormBuilder object ](https://apidock.com/rails/ActionView/Helpers/FormBuilder ). | [`ActionView::Helpers::FormBuilder` ](https://apidock.com/rails/ActionView/Helpers/FormBuilder ) | `true` |
| `setting_locked` | If the setting is locked by an ancestor group or admin setting. Can be calculated with [`cascading_namespace_setting_locked?` ](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86 ). | `Boolean` | `true` |
| `help_text` | Text shown below the checkbox. | `String` | `false` (Subgroups cannot change this setting.) |
2021-06-17 20:10:29 -04:00
[`_setting_label_checkbox.html.haml` ](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/views/shared/namespaces/cascading_settings/_setting_label_checkbox.html.haml )
2021-05-13 02:10:25 -04:00
2021-06-17 20:10:29 -04:00
Renders the label for a checkbox setting.
2021-05-13 02:10:25 -04:00
| Local | Description | Type | Required (default value) |
|:-----------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------|:-------------------------|
| `attribute` | Name of the setting. For example, `:delayed_project_removal` . | `String` or `Symbol` | `true` |
2021-07-05 11:07:38 -04:00
| `group` | Current group. | [`Group` ](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/group.rb ) | `true` |
2021-05-13 02:10:25 -04:00
| `form` | [Rails FormBuilder object ](https://apidock.com/rails/ActionView/Helpers/FormBuilder ). | [`ActionView::Helpers::FormBuilder` ](https://apidock.com/rails/ActionView/Helpers/FormBuilder ) | `true` |
| `setting_locked` | If the setting is locked by an ancestor group or admin setting. Can be calculated with [`cascading_namespace_setting_locked?` ](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86 ). | `Boolean` | `true` |
| `settings_path_helper` | Lambda function that generates a path to the ancestor setting. For example, `settings_path_helper: -> (locked_ancestor) { edit_group_path(locked_ancestor, anchor: 'js-permissions-settings') }` | `Lambda` | `true` |
| `help_text` | Text shown below the checkbox. | `String` | `false` (`nil`) |
2021-06-17 20:10:29 -04:00
[`_setting_label_fieldset.html.haml` ](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/views/shared/namespaces/cascading_settings/_setting_label_fieldset.html.haml )
2021-05-13 02:10:25 -04:00
2021-06-17 20:10:29 -04:00
Renders the label for a fieldset setting.
2021-05-13 02:10:25 -04:00
| Local | Description | Type | Required (default value) |
|:-----------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------|:-------------------------|
| `attribute` | Name of the setting. For example, `:delayed_project_removal` . | `String` or `Symbol` | `true` |
2021-07-05 11:07:38 -04:00
| `group` | Current group. | [`Group` ](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/group.rb ) | `true` |
2021-05-13 02:10:25 -04:00
| `setting_locked` | If the setting is locked. Can be calculated with [`cascading_namespace_setting_locked?` ](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86 ). | `Boolean` | `true` |
| `settings_path_helper` | Lambda function that generates a path to the ancestor setting. For example, `-> (locked_ancestor) { edit_group_path(locked_ancestor, anchor: 'js-permissions-settings') }` | `Lambda` | `true` |
| `help_text` | Text shown below the checkbox. | `String` | `false` (`nil`) |
2021-06-17 20:10:29 -04:00
[`_lock_popovers.html.haml` ](https://gitlab.com/gitlab-org/gitlab/-/blob/b73353e47e283a7d9c9eda5bdedb345dcfb685b6/app/views/shared/namespaces/cascading_settings/_lock_popovers.html.haml )
2021-05-13 02:10:25 -04:00
2021-06-17 20:10:29 -04:00
Renders the mount element needed to initialize the JavaScript used to display the popover when hovering over the lock icon. This partial is only needed once per page.
2021-05-13 02:10:25 -04:00
### JavaScript
2021-06-17 20:10:29 -04:00
[`initCascadingSettingsLockPopovers` ](https://gitlab.com/gitlab-org/gitlab/-/blob/b73353e47e283a7d9c9eda5bdedb345dcfb685b6/app/assets/javascripts/namespaces/cascading_settings/index.js#L4 )
2021-05-13 02:10:25 -04:00
Initializes the JavaScript needed to display the popover when hovering over the lock icon (**{lock}**).
This function should be imported and called in the [page-specific JavaScript ](fe_guide/performance.md#page-specific-javascript ).
### Put it all together
```haml
-# app/views/groups/edit.html.haml
= render 'shared/namespaces/cascading_settings/lock_popovers'
- delayed_project_removal_locked = cascading_namespace_setting_locked?(:delayed_project_removal, @group )
- merge_method_locked = cascading_namespace_setting_locked?(:merge_method, @group )
= form_for @group do |f|
.form-group{ data: { testid: 'delayed-project-removal-form-group' } }
.gl-form-checkbox.custom-control.custom-checkbox
= f.check_box :delayed_project_removal, checked: @group .namespace_settings.delayed_project_removal?, disabled: delayed_project_removal_locked, class: 'custom-control-input'
= render 'shared/namespaces/cascading_settings/setting_label_checkbox', attribute: :delayed_project_removal,
group: @group ,
form: f,
setting_locked: delayed_project_removal_locked,
settings_path_helper: -> (locked_ancestor) { edit_group_path(locked_ancestor, anchor: 'js-permissions-settings') },
help_text: s_('Settings|Projects will be permanently deleted after a 7-day delay. Inherited by subgroups.') do
= s_('Settings|Enable delayed project removal')
= render 'shared/namespaces/cascading_settings/enforcement_checkbox',
attribute: :delayed_project_removal,
group: @group ,
form: f,
setting_locked: delayed_project_removal_locked
%fieldset.form-group
= render 'shared/namespaces/cascading_settings/setting_label_fieldset', attribute: :merge_method,
group: @group ,
setting_locked: merge_method_locked,
settings_path_helper: -> (locked_ancestor) { edit_group_path(locked_ancestor, anchor: 'js-permissions-settings') },
help_text: s_('Settings|Determine what happens to the commit history when you merge a merge request.') do
= s_('Settings|Merge method')
.gl-form-radio.custom-control.custom-radio
= f.radio_button :merge_method, :merge, class: "custom-control-input", disabled: merge_method_locked
= f.label :merge_method_merge, class: 'custom-control-label' do
= s_('Settings|Merge commit')
%p.help-text
= s_('Settings|Every merge creates a merge commit.')
.gl-form-radio.custom-control.custom-radio
= f.radio_button :merge_method, :rebase_merge, class: "custom-control-input", disabled: merge_method_locked
= f.label :merge_method_rebase_merge, class: 'custom-control-label' do
= s_('Settings|Merge commit with semi-linear history')
%p.help-text
= s_('Settings|Every merge creates a merge commit.')
.gl-form-radio.custom-control.custom-radio
= f.radio_button :merge_method, :ff, class: "custom-control-input", disabled: merge_method_locked
= f.label :merge_method_ff, class: 'custom-control-label' do
= s_('Settings|Fast-forward merge')
%p.help-text
= s_('Settings|No merge commits are created.')
= render 'shared/namespaces/cascading_settings/enforcement_checkbox',
attribute: :merge_method,
group: @group ,
form: f,
setting_locked: merge_method_locked
```
```javascript
// app/assets/javascripts/pages/groups/edit/index.js
import { initCascadingSettingsLockPopovers } from '~/namespaces/cascading_settings';
initCascadingSettingsLockPopovers();
```