diff options
-rw-r--r-- | app/assets/javascripts/related_issues/components/related_issues_block.vue | 42 | ||||
-rw-r--r-- | app/assets/javascripts/related_issues/constants.js | 5 | ||||
-rw-r--r-- | app/views/projects/_import_project_pane.html.haml | 2 | ||||
-rw-r--r-- | db/migrate/20221031102916_add_users_foreign_key_to_projects.rb | 15 | ||||
-rw-r--r-- | db/schema_migrations/20221031102916 | 1 | ||||
-rw-r--r-- | db/structure.sql | 3 | ||||
-rw-r--r-- | doc/administration/gitaly/index.md | 4 | ||||
-rw-r--r-- | doc/security/index.md | 2 | ||||
-rw-r--r-- | doc/user/group/manage.md | 49 | ||||
-rw-r--r-- | doc/user/profile/personal_access_tokens.md | 30 | ||||
-rw-r--r-- | locale/gitlab.pot | 24 | ||||
-rw-r--r-- | spec/db/schema_spec.rb | 2 | ||||
-rw-r--r-- | spec/frontend/issuable/related_issues/components/related_issues_block_spec.js | 48 |
13 files changed, 168 insertions, 59 deletions
diff --git a/app/assets/javascripts/related_issues/components/related_issues_block.vue b/app/assets/javascripts/related_issues/components/related_issues_block.vue index c3726ebc14a..4a130ade631 100644 --- a/app/assets/javascripts/related_issues/components/related_issues_block.vue +++ b/app/assets/javascripts/related_issues/components/related_issues_block.vue @@ -1,6 +1,6 @@ <script> import { GlLink, GlIcon, GlButton } from '@gitlab/ui'; -import { __ } from '~/locale'; +import { __, sprintf } from '~/locale'; import { issuableIconMap, linkedIssueTypesMap, @@ -130,9 +130,6 @@ export default { shouldShowTokenBody() { return this.hasRelatedIssues || this.isFetching; }, - hasBody() { - return this.isFormVisible || this.shouldShowTokenBody; - }, headerText() { return issuablesBlockHeaderTextMap[this.issuableType]; }, @@ -157,6 +154,11 @@ export default { toggleLabel() { return this.isOpen ? __('Collapse') : __('Expand'); }, + emptyStateMessage() { + return this.showCategorizedIssues + ? sprintf(this.$options.i18n.emptyItemsPremium, { issuableType: this.issuableType }) + : sprintf(this.$options.i18n.emptyItemsFree, { issuableType: this.issuableType }); + }, }, methods: { handleToggle() { @@ -168,6 +170,12 @@ export default { }, }, linkedIssueTypesTextMap, + i18n: { + emptyItemsFree: __("Link %{issuableType}s together to show that they're related."), + emptyItemsPremium: __( + "Link %{issuableType}s together to show that they're related or that one is blocking others.", + ), + }, }; </script> @@ -176,7 +184,6 @@ export default { <div class="card card-slim gl-overflow-hidden gl-mt-5 gl-mb-0"> <div :class="{ - 'panel-empty-heading border-bottom-0': !hasBody, 'gl-border-b-1': isOpen, 'gl-border-b-0': !isOpen, }" @@ -190,16 +197,6 @@ export default { aria-hidden="true" /> <slot name="header-text">{{ headerText }}</slot> - <gl-link - v-if="hasHelpPath" - :href="helpPath" - target="_blank" - class="gl-display-flex gl-align-items-center gl-ml-2 gl-text-gray-500" - data-testid="help-link" - :aria-label="helpLinkText" - > - <gl-icon name="question" :size="12" /> - </gl-link> <div class="js-related-issues-header-issue-count gl-display-inline-flex gl-mx-3"> <span class="gl-display-inline-flex gl-align-items-center"> @@ -226,7 +223,6 @@ export default { size="small" :icon="toggleIcon" :aria-label="toggleLabel" - :disabled="!hasRelatedIssues" data-testid="toggle-links" @click="handleToggle" /> @@ -281,6 +277,20 @@ export default { @saveReorder="$emit('saveReorder', $event)" /> </template> + <div v-if="!shouldShowTokenBody && !isFormVisible" data-testid="related-items-empty"> + <p class="gl-my-5 gl-px-5"> + {{ emptyStateMessage }} + <gl-link + v-if="hasHelpPath" + :href="helpPath" + target="_blank" + data-testid="help-link" + :aria-label="helpLinkText" + > + {{ __('Learn more.') }} + </gl-link> + </p> + </div> </div> </div> </div> diff --git a/app/assets/javascripts/related_issues/constants.js b/app/assets/javascripts/related_issues/constants.js index 4eb054ccb5c..d1b2d41d7ae 100644 --- a/app/assets/javascripts/related_issues/constants.js +++ b/app/assets/javascripts/related_issues/constants.js @@ -111,8 +111,9 @@ export const issuablesBlockHeaderTextMap = { }; export const issuablesBlockHelpTextMap = { - [issuableTypesMap.ISSUE]: __('Read more about related issues'), - [issuableTypesMap.EPIC]: __('Read more about related epics'), + [issuableTypesMap.ISSUE]: __('Learn more about linking issues'), + [issuableTypesMap.INCIDENT]: __('Learn more about linking issues and incidents'), + [issuableTypesMap.EPIC]: __('Learn more about linking epics'), }; export const issuablesBlockAddButtonTextMap = { diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml index afc7fb3d8b6..cc5271a1cd2 100644 --- a/app/views/projects/_import_project_pane.html.haml +++ b/app/views/projects/_import_project_pane.html.haml @@ -60,6 +60,8 @@ = render Pajamas::ButtonComponent.new(href: new_import_phabricator_path(namespace_id: namespace_id), icon: 'issues', button_options: { class: 'import_phabricator js-import-project-btn', data: { platform: 'phabricator', track_label: "#{track_label}", track_action: "click_button", track_property: "phabricator" } }) do = _('Phabricator tasks') + = render_if_exists "projects/gitee_import_button", namespace_id: namespace_id, track_label: track_label + .js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') } = gitlab_ui_form_for @project, html: { class: 'new_project gl-show-field-errors js-project-import' } do |f| diff --git a/db/migrate/20221031102916_add_users_foreign_key_to_projects.rb b/db/migrate/20221031102916_add_users_foreign_key_to_projects.rb new file mode 100644 index 00000000000..fb37b3b37c2 --- /dev/null +++ b/db/migrate/20221031102916_add_users_foreign_key_to_projects.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddUsersForeignKeyToProjects < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :projects, :users, column: :creator_id, on_delete: :nullify, validate: false + end + + def down + with_lock_retries do + remove_foreign_key_if_exists :projects, column: :creator_id + end + end +end diff --git a/db/schema_migrations/20221031102916 b/db/schema_migrations/20221031102916 new file mode 100644 index 00000000000..53f927cfc32 --- /dev/null +++ b/db/schema_migrations/20221031102916 @@ -0,0 +1 @@ +e0065beaf2e1dc5e5850353244ba2c76477e855733f3683a1901a340a5826ae1
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index e1955b44f97..0b767416d5d 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -32572,6 +32572,9 @@ ALTER TABLE ONLY service_desk_settings ALTER TABLE ONLY design_management_designs_versions ADD CONSTRAINT fk_03c671965c FOREIGN KEY (design_id) REFERENCES design_management_designs(id) ON DELETE CASCADE; +ALTER TABLE ONLY projects + ADD CONSTRAINT fk_03ec10b0d3 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL NOT VALID; + ALTER TABLE ONLY issues ADD CONSTRAINT fk_05f1e72feb FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL; diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md index a1c381cabde..5d9473732e3 100644 --- a/doc/administration/gitaly/index.md +++ b/doc/administration/gitaly/index.md @@ -320,9 +320,7 @@ follow the [hashed storage](../repository_storage_types.md#hashed-storage) schem > - [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/4218) in GitLab 15.0 [with a flag](../feature_flags.md) named `gitaly_praefect_generated_replica_paths`. Disabled by default. > - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitaly/-/issues/4218) in GitLab 15.2. > - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4809) in GitLab 15.3. - -FLAG: -On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../feature_flags.md) named `gitaly_praefect_generated_replica_paths`. On GitLab.com, this feature is available but can be configured by GitLab.com administrators only. +> - [Generally available](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4941) in GitLab 15.6. Feature flag `gitaly_praefect_generated_replica_paths` removed. When Gitaly Cluster creates a repository, it assigns the repository a unique and permanent ID called the _repository ID_. The repository ID is internal to Gitaly Cluster and doesn't relate to any IDs elsewhere in GitLab. If a repository is removed from Gitaly Cluster and later moved diff --git a/doc/security/index.md b/doc/security/index.md index ff0769e0d93..38eb5337f5a 100644 --- a/doc/security/index.md +++ b/doc/security/index.md @@ -6,7 +6,7 @@ comments: false type: index --- -# Security **(FREE)** +# Secure your installation **(FREE)** - [Passwords and OAuth tokens storage](password_storage.md) - [Password length limits](password_length_limits.md) diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md index d05006ba944..ed2bab55f57 100644 --- a/doc/user/group/manage.md +++ b/doc/user/group/manage.md @@ -414,6 +414,55 @@ Group owners can create, edit, and delete compliance frameworks: 1. Expand the **Compliance frameworks** section. 1. Create, edit, or delete compliance frameworks. +### Set a default compliance framework + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375036) in GitLab 15.6. + +Group owners can set a default compliance framework. The default framework is applied to all the new projects +that are created within that group. It does not affect the framework applied to the existing projects. The default +framework cannot be deleted. + +#### Example GraphQL mutations for setting a default compliance framework + +Creating a new compliance framework and setting it as the default framework for the group. + +```graphql +mutation { + createComplianceFramework( + input: {params: {name: "SOX", description: "Sarbanes-Oxley Act", color: "#87CEEB", default: true}, namespacePath: "gitlab-org"} + ) { + framework { + id + name + default + description + color + pipelineConfigurationFullPath + } + errors + } +} +``` + +Setting an existing compliance framework as the default framework the group. + +```graphql +mutation { + updateComplianceFramework( + input: {id: "gid://gitlab/ComplianceManagement::Framework/<id>", params: {default: true}} + ) { + complianceFramework { + id + name + default + description + color + pipelineConfigurationFullPath + } + } +} +``` + ### Configure a compliance pipeline **(ULTIMATE)** > - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3156) in GitLab 13.9, disabled behind `ff_evaluate_group_level_compliance_pipeline` [feature flag](../../administration/feature_flags.md). diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index c5958c4fd11..176bc071c77 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -197,17 +197,29 @@ This code can be shortened into a single-line shell command using the sudo gitlab-rails runner "PersonalAccessToken.find_by_token('token-string-here123').revoke!" ``` -<!-- ## Troubleshooting +## Troubleshooting -Include any troubleshooting steps that you can foresee. If you know beforehand what issues -one might have when setting this up, or when something is changed, or on upgrading, it's -important to describe those, too. Think of things that may go wrong and include them here. -This is important to minimize requests for support, and to avoid doc comments with -questions that you know someone might ask. +### Unrevoke a personal access token **(FREE SELF)** -Each scenario can be a third-level heading, e.g. `### Getting error message X`. -If you have none to add when creating a doc, leave this section in place -but commented out to help encourage others to add to it in the future. --> +If a personal access token is revoked accidentally by any method, administrators can unrevoke that token. + +WARNING: +Running the following commands changes data directly. This could be damaging if not done correctly, or under the right conditions. You should first run these commands in a test environment with a backup of the instance ready to be restored, just in case. + +1. Open a [Rails console](../../administration/operations/rails_console.md#starting-a-rails-console-session). +1. Unrevoke the token: + + ```ruby + token = PersonalAccessToken.find_by_token('<token_string>') + token.update!(revoked:false) + ``` + + For example, to unrevoke a token of `token-string-here123`: + + ```ruby + token = PersonalAccessToken.find_by_token('token-string-here123') + token.update!(revoked:false) + ``` ## Alternatives to personal access tokens diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 7f4fe6795e8..b81b725b5cf 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -20278,9 +20278,6 @@ msgstr "" msgid "IdentityVerification|Step %{stepNumber}: Verify phone number" msgstr "" -msgid "IdentityVerification|Step 1: Verify phone number" -msgstr "" - msgid "IdentityVerification|The code has expired. Send a new code and try again." msgstr "" @@ -23919,6 +23916,15 @@ msgstr "" msgid "Learn more about issues." msgstr "" +msgid "Learn more about linking epics" +msgstr "" + +msgid "Learn more about linking issues" +msgstr "" + +msgid "Learn more about linking issues and incidents" +msgstr "" + msgid "Learn more about max seats used" msgstr "" @@ -24315,6 +24321,12 @@ msgstr "" msgid "Link" msgstr "" +msgid "Link %{issuableType}s together to show that they're related or that one is blocking others." +msgstr "" + +msgid "Link %{issuableType}s together to show that they're related." +msgstr "" + msgid "Link (optional)" msgstr "" @@ -33290,12 +33302,6 @@ msgstr "" msgid "Read more about GitLab at %{link_to_promo}." msgstr "" -msgid "Read more about related epics" -msgstr "" - -msgid "Read more about related issues" -msgstr "" - msgid "Read their documentation." msgstr "" diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index c383c4b6f24..1c24d091649 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -80,7 +80,7 @@ RSpec.describe 'Database schema' do project_error_tracking_settings: %w[sentry_project_id], project_group_links: %w[group_id], project_statistics: %w[namespace_id], - projects: %w[creator_id ci_id mirror_user_id], + projects: %w[ci_id mirror_user_id], redirect_routes: %w[source_id], repository_languages: %w[programming_language_id], routes: %w[source_id], diff --git a/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js b/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js index 1b2935ce5d1..996b2406240 100644 --- a/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js +++ b/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js @@ -40,12 +40,12 @@ describe('RelatedIssuesBlock', () => { }); it.each` - issuableType | pathIdSeparator | titleText | helpLinkText | addButtonText - ${'issue'} | ${PathIdSeparator.Issue} | ${'Linked items'} | ${'Read more about related issues'} | ${'Add a related issue'} - ${'epic'} | ${PathIdSeparator.Epic} | ${'Linked epics'} | ${'Read more about related epics'} | ${'Add a related epic'} + issuableType | pathIdSeparator | titleText | addButtonText + ${'issue'} | ${PathIdSeparator.Issue} | ${'Linked items'} | ${'Add a related issue'} + ${'epic'} | ${PathIdSeparator.Epic} | ${'Linked epics'} | ${'Add a related epic'} `( - 'displays "$titleText" in the header, "$helpLinkText" aria-label for help link, and "$addButtonText" aria-label for add button when issuableType is set to "$issuableType"', - ({ issuableType, pathIdSeparator, titleText, helpLinkText, addButtonText }) => { + 'displays "$titleText" in the header and "$addButtonText" aria-label for add button when issuableType is set to "$issuableType"', + ({ issuableType, pathIdSeparator, titleText, addButtonText }) => { wrapper = mountExtended(RelatedIssuesBlock, { propsData: { pathIdSeparator, @@ -56,9 +56,6 @@ describe('RelatedIssuesBlock', () => { }); expect(wrapper.find('.card-title').text()).toContain(titleText); - expect(wrapper.find('[data-testid="help-link"]').attributes('aria-label')).toBe( - helpLinkText, - ); expect(findIssueCountBadgeAddButton().attributes('aria-label')).toBe(addButtonText); }, ); @@ -100,7 +97,7 @@ describe('RelatedIssuesBlock', () => { slots: { 'header-actions': headerActions }, }); - expect(wrapper.find('[data-testid="custom-button"]').html()).toBe(headerActions); + expect(wrapper.findByTestId('custom-button').html()).toBe(headerActions); }); }); @@ -260,15 +257,30 @@ describe('RelatedIssuesBlock', () => { }); }); - it('toggle button is disabled when issue has no related items', () => { - wrapper = shallowMountExtended(RelatedIssuesBlock, { - propsData: { - pathIdSeparator: PathIdSeparator.Issue, - relatedIssues: [], - issuableType: 'issue', - }, - }); + describe('empty state', () => { + it.each` + issuableType | pathIdSeparator | showCategorizedIssues | emptyText | helpLinkText + ${'issue'} | ${PathIdSeparator.Issue} | ${false} | ${"Link issues together to show that they're related."} | ${'Learn more about linking issues'} + ${'issue'} | ${PathIdSeparator.Issue} | ${true} | ${"Link issues together to show that they're related or that one is blocking others."} | ${'Learn more about linking issues'} + ${'incident'} | ${PathIdSeparator.Issue} | ${false} | ${"Link incidents together to show that they're related."} | ${'Learn more about linking issues and incidents'} + ${'incident'} | ${PathIdSeparator.Issue} | ${true} | ${"Link incidents together to show that they're related or that one is blocking others."} | ${'Learn more about linking issues and incidents'} + ${'epic'} | ${PathIdSeparator.Epic} | ${true} | ${"Link epics together to show that they're related or that one is blocking others."} | ${'Learn more about linking epics'} + `( + 'displays "$emptyText" in the body and "$helpLinkText" aria-label for help link', + ({ issuableType, pathIdSeparator, showCategorizedIssues, emptyText, helpLinkText }) => { + wrapper = mountExtended(RelatedIssuesBlock, { + propsData: { + pathIdSeparator, + issuableType, + canAdmin: true, + helpPath: '/help/user/project/issues/related_issues', + showCategorizedIssues, + }, + }); - expect(findToggleButton().props('disabled')).toBe(true); + expect(wrapper.findByTestId('related-issues-body').text()).toContain(emptyText); + expect(wrapper.findByTestId('help-link').attributes('aria-label')).toBe(helpLinkText); + }, + ); }); }); |