Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'doc/development/cascading_settings.md')
-rw-r--r--doc/development/cascading_settings.md241
1 files changed, 241 insertions, 0 deletions
diff --git a/doc/development/cascading_settings.md b/doc/development/cascading_settings.md
new file mode 100644
index 00000000000..631de544238
--- /dev/null
+++ b/doc/development/cascading_settings.md
@@ -0,0 +1,241 @@
+---
+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).
+
+The cascading settings framework allows groups to essentially inherit settings
+values from ancestors (parent group on up the group hierarchy) and from
+instance-level application settings. The framework also allows settings values
+to be enforced on groups lower in the hierarchy.
+
+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.
+
+ ```ruby
+ class NamespaceSetting
+ include CascadingNamespaceSettingAttribute
+
+ cascading_attr :delayed_project_removal
+ end
+ ```
+
+1. Create the database columns.
+
+ You can use the following database migration helper for a completely new setting.
+ The helper creates four columns, two each in `namespace_settings` and
+ `application_settings`.
+
+ ```ruby
+ class AddDelayedProjectRemovalCascadingSetting < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers::CascadingNamespaceSettings
+
+ def up
+ add_cascading_namespace_setting :delayed_project_removal, :boolean, default: false, null: false
+ end
+
+ def down
+ remove_cascading_namespace_setting :delayed_project_removal
+ end
+ end
+ ```
+
+ Existing settings being converted to a cascading setting will require individual
+ migrations to add columns and change existing columns. Use the specifications
+ below to create migrations as required:
+
+ 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:
+ - `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.
+
+## Convenience methods
+
+By defining an attribute using the `cascading_attr` method, a number of convenience
+methods are automatically defined.
+
+**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)
+- `delayed_project_removal_locked_ancestor` (Returns locked namespace settings object [namespace_id])
+
+### Attribute reader method (`delayed_project_removal`)
+
+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
+ Rails validators to be used on the attribute, though `nil` values *must* be allowed.
+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.
+
+### `_locked?` method
+
+By default, the `_locked?` method (`delayed_project_removal_locked?`) returns
+`true` if an ancestor of the group or application setting locks the attribute.
+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.
+
+## 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
+
+[`cascading_namespace_setting_locked?`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86)
+
+Calls through to the [`_locked?` method](#_locked-method) to check if the setting is locked.
+
+| 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
+
+[`_enforcement_checkbox.html.haml`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/views/shared/namespaces/cascading_settings/_enforcement_checkbox.html.haml)
+
+Renders the enforcement checkbox.
+
+| Local | Description | Type | Required (default value) |
+|:-----------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------|:------------------------------------------------|
+| `attribute` | Name of the setting. For example, `:delayed_project_removal`. | `String` or `Symbol` | `true` |
+| `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.) |
+
+[`_setting_label_checkbox.html.haml`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/views/shared/namespaces/cascading_settings/_setting_label_checkbox.html.haml)
+
+Renders the label for a checkbox setting.
+
+| Local | Description | Type | Required (default value) |
+|:-----------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------|:-------------------------|
+| `attribute` | Name of the setting. For example, `:delayed_project_removal`. | `String` or `Symbol` | `true` |
+| `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`) |
+
+[`_setting_label_fieldset.html.haml`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/views/shared/namespaces/cascading_settings/_setting_label_fieldset.html.haml)
+
+Renders the label for a fieldset setting.
+
+| Local | Description | Type | Required (default value) |
+|:-----------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------|:-------------------------|
+| `attribute` | Name of the setting. For example, `:delayed_project_removal`. | `String` or `Symbol` | `true` |
+| `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`) |
+
+[`_lock_popovers.html.haml`](https://gitlab.com/gitlab-org/gitlab/-/blob/b73353e47e283a7d9c9eda5bdedb345dcfb685b6/app/views/shared/namespaces/cascading_settings/_lock_popovers.html.haml)
+
+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.
+
+### JavaScript
+
+[`initCascadingSettingsLockPopovers`](https://gitlab.com/gitlab-org/gitlab/-/blob/b73353e47e283a7d9c9eda5bdedb345dcfb685b6/app/assets/javascripts/namespaces/cascading_settings/index.js#L4)
+
+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();
+```