diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue index 0e0d1e64f4a..bdaed17fd09 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.vue +++ b/app/assets/javascripts/boards/components/issue_card_inner.vue @@ -161,6 +161,14 @@ export default {

+ ** **{settings}** **Settings > Visibility and access controls**. - The exports are stored in a temporary [shared directory](../../development/shared_files.md) and are deleted every 24 hours by a specific worker. diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 125009adc35..c8a91d830a0 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -1986,7 +1986,20 @@ type Epic implements Noteable { """ last: Int ): UserConnection - reference(full: Boolean = false): String! + + """ + Internal reference of the epic. Returned in shortened format by default + """ + reference( + """ + Indicates if the reference should be returned in full + """ + full: Boolean = false + ): String! + + """ + URI path of the epic-issue relationship + """ relationPath: String """ @@ -2043,7 +2056,15 @@ type Epic implements Noteable { Permissions for the current user on the resource """ userPermissions: EpicPermissions! + + """ + Web path of the epic + """ webPath: String! + + """ + Web URL of the epic + """ webUrl: String! } diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index 17a3d8eb2e2..08b84a1ca35 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -5013,11 +5013,11 @@ }, { "name": "reference", - "description": null, + "description": "Internal reference of the epic. Returned in shortened format by default", "args": [ { "name": "full", - "description": null, + "description": "Indicates if the reference should be returned in full", "type": { "kind": "SCALAR", "name": "Boolean", @@ -5040,7 +5040,7 @@ }, { "name": "relationPath", - "description": null, + "description": "URI path of the epic-issue relationship", "args": [ ], @@ -5224,7 +5224,7 @@ }, { "name": "webPath", - "description": null, + "description": "Web path of the epic", "args": [ ], @@ -5242,7 +5242,7 @@ }, { "name": "webUrl", - "description": null, + "description": "Web URL of the epic", "args": [ ], diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index beffd29d884..5e0b7465c20 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -295,8 +295,8 @@ Represents an epic. | `id` | ID! | ID of the epic | | `iid` | ID! | Internal ID of the epic | | `parent` | Epic | Parent epic of the epic | -| `reference` | String! | | -| `relationPath` | String | | +| `reference` | String! | Internal reference of the epic. Returned in shortened format by default | +| `relationPath` | String | URI path of the epic-issue relationship | | `relativePosition` | Int | The relative position of the epic in the epic tree | | `startDate` | Time | Start date of the epic | | `startDateFixed` | Time | Fixed start date of the epic | @@ -308,8 +308,8 @@ Represents an epic. | `updatedAt` | Time | Timestamp of the epic's last activity | | `upvotes` | Int! | Number of upvotes the epic has received | | `userPermissions` | EpicPermissions! | Permissions for the current user on the resource | -| `webPath` | String! | | -| `webUrl` | String! | | +| `webPath` | String! | Web path of the epic | +| `webUrl` | String! | Web URL of the epic | ## EpicDescendantCount diff --git a/doc/user/project/img/issue_boards_blocked_icon_v12_8.png b/doc/user/project/img/issue_boards_blocked_icon_v12_8.png new file mode 100644 index 00000000000..ede57b760ed Binary files /dev/null and b/doc/user/project/img/issue_boards_blocked_icon_v12_8.png differ diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md index 06ecc224f5f..0a5d7805e41 100644 --- a/doc/user/project/issue_board.md +++ b/doc/user/project/issue_board.md @@ -303,6 +303,14 @@ Different issue board features are available in different [GitLab tiers](https:/ | Premium / Silver | Multiple | Multiple | Yes | Yes | | Ultimate / Gold | Multiple | Multiple | Yes | Yes | +## Blocked issues + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/34723) in GitLab 12.8. + +If an issue is blocked by another issue, an icon will display next to its title to differentiate it from unblocked issues. + +![Blocked issues](img/issue_boards_blocked_icon_v12_8.png) + ## Actions you can take on an Issue Board - [Create a new list](#creating-a-new-list). diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index 2266534dc8f..cdf6a789ec2 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -6,28 +6,30 @@ Existing projects running on any GitLab instance or GitLab.com can be exported with all their related data and be moved into a new GitLab instance. +The **GitLab import/export** button is displayed if the project import option is enabled. + See also: -- [Project import/export API](../../../api/project_import_export.md). -- [Project import/export administration rake tasks](../../../administration/raketasks/project_import_export.md). **(CORE ONLY)** +- [Project import/export API](../../../api/project_import_export.md) +- [Project import/export administration rake tasks](../../../administration/raketasks/project_import_export.md) **(CORE ONLY)** + +To set up a project import/export: + + 1. Navigate to **{admin}** **Admin Area >** **{settings}** **Settings > Visibility and access controls**. + 1. Scroll to **Import sources** + 1. Enable desired **Import sources** ## Important notes Note the following: -- Importing is not possible if the import instance version differs from - that of the exporter. -- The project import option must be enabled in application settings - (`/admin/application_settings/general`) under **Import sources**, which is - available under **{admin}** **Admin Area >** **{settings}** **Settings > Visibility and access controls**. - Ask your administrator if you don't see the **GitLab export** button when - creating a new project. -- The exports are stored in a temporary [shared directory](../../../development/shared_files.md) +- Imports will fail unless the import and export GitLab instances are + compatible as described in the [Version history](#version-history). +- Exports are stored in a temporary [shared directory](../../../development/shared_files.md) and are deleted every 24 hours by a specific worker. - Group members are exported as project members, as long as the user has - maintainer or admin access to the group where the exported project lives. An admin - in the import side is required to map the users, based on email. - Otherwise, a supplementary comment is left to mention the original author and + maintainer or admin access to the group where the exported project lives. Import admins should map users by email address. + Otherwise, a supplementary comment is left to mention that the original author and the MRs, notes, or issues will be owned by the importer. - Project members with owner access will be imported as maintainers. - If an imported project contains merge requests originating from forks, @@ -39,7 +41,7 @@ Note the following: The following table lists updates to Import/Export: -| GitLab version | Import/Export version | +| GitLab version | Import/Export schema version | | ---------------- | --------------------- | | 11.1 to current | 0.2.4 | | 10.8 | 0.2.3 | @@ -56,7 +58,9 @@ The following table lists updates to Import/Export: | 8.9.5 | 0.1.1 | | 8.9.0 | 0.1.0 | -For example, 8.10.3 and 8.11 will have the same Import/Export version (0.1.3) +Projects can be exported and imported only between versions of GitLab with matching Import/Export versions. + +For example, 8.10.3 and 8.11 have the same Import/Export version (0.1.3) and the exports between them will be compatible. ## Exported contents diff --git a/lib/api/group_boards.rb b/lib/api/group_boards.rb index f7ef0cfd0d8..88d04e70e11 100644 --- a/lib/api/group_boards.rb +++ b/lib/api/group_boards.rb @@ -28,6 +28,7 @@ module API success ::API::Entities::Board end get '/:board_id' do + authorize!(:read_board, user_group) present board, with: ::API::Entities::Board end @@ -39,6 +40,7 @@ module API use :pagination end get '/' do + authorize!(:read_board, user_group) present paginate(board_parent.boards.with_associations), with: Entities::Board end end @@ -55,6 +57,7 @@ module API use :pagination end get '/lists' do + authorize!(:read_board, user_group) present paginate(board_lists), with: Entities::List end @@ -66,6 +69,7 @@ module API requires :list_id, type: Integer, desc: 'The ID of a list' end get '/lists/:list_id' do + authorize!(:read_board, user_group) present board_lists.find(params[:list_id]), with: Entities::List end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b6ecd5e5926..c64785c7e44 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2768,6 +2768,9 @@ msgstr "" msgid "Blocked" msgstr "" +msgid "Blocked issue" +msgstr "" + msgid "Blocks" msgstr "" diff --git a/spec/controllers/groups/boards_controller_spec.rb b/spec/controllers/groups/boards_controller_spec.rb index 79edfd69429..acfa8bc9354 100644 --- a/spec/controllers/groups/boards_controller_spec.rb +++ b/spec/controllers/groups/boards_controller_spec.rb @@ -27,7 +27,8 @@ describe Groups::BoardsController do context 'with unauthorized user' do before do allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) + allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false) end it 'returns a not found 404 response' do @@ -70,7 +71,8 @@ describe Groups::BoardsController do context 'with unauthorized user' do before do allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) + allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false) end it 'returns a not found 404 response' do @@ -105,7 +107,8 @@ describe Groups::BoardsController do context 'with unauthorized user' do before do allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) + allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false) end it 'returns a not found 404 response' do @@ -142,6 +145,7 @@ describe Groups::BoardsController do context 'with unauthorized user' do before do allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) + allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(false) end diff --git a/spec/frontend/boards/issue_card_spec.js b/spec/frontend/boards/issue_card_spec.js index 526cdb81ac6..1fd2b417aba 100644 --- a/spec/frontend/boards/issue_card_spec.js +++ b/spec/frontend/boards/issue_card_spec.js @@ -66,7 +66,11 @@ describe('Issue card component', () => { }); it('does not render confidential icon', () => { - expect(wrapper.find('.fa-eye-flash').exists()).toBe(false); + expect(wrapper.find('.confidential-icon').exists()).toBe(false); + }); + + it('does not render blocked icon', () => { + expect(wrapper.find('.issue-blocked-icon').exists()).toBe(false); }); it('renders confidential icon', done => { @@ -324,4 +328,20 @@ describe('Issue card component', () => { .catch(done.fail); }); }); + + describe('blocked', () => { + beforeEach(done => { + wrapper.setProps({ + issue: { + ...wrapper.props('issue'), + blocked: true, + }, + }); + wrapper.vm.$nextTick(done); + }); + + it('renders blocked icon if issue is blocked', () => { + expect(wrapper.find('.issue-blocked-icon').exists()).toBe(true); + }); + }); }); diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index ae9d125f970..5a9ca9f7b7e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -438,7 +438,7 @@ describe GroupPolicy do end end - context "create_projects" do + context 'create_projects' do context 'when group has no project creation level set' do before_all do group.update(project_creation_level: nil) @@ -560,7 +560,7 @@ describe GroupPolicy do end end - context "create_subgroup" do + context 'create_subgroup' do context 'when group has subgroup creation level set to owner' do before_all do group.update(subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index c503197a773..63ebbcb93f9 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -16,7 +16,7 @@ RSpec.shared_context 'GroupPolicy context' do read_group_merge_requests ] end - let(:read_group_permissions) { %i[read_label read_list read_milestone] } + let(:read_group_permissions) { %i[read_label read_list read_milestone read_board] } let(:reporter_permissions) { %i[admin_label read_container_image] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do