diff options
-rw-r--r-- | app/assets/javascripts/api.js | 1 | ||||
-rw-r--r-- | app/assets/javascripts/groups_select.js | 7 | ||||
-rw-r--r-- | app/assets/javascripts/pages/groups/edit/index.js | 4 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/callout.scss | 6 | ||||
-rw-r--r-- | app/services/projects/create_service.rb | 2 | ||||
-rw-r--r-- | app/views/groups/edit.html.haml | 1 | ||||
-rw-r--r-- | app/views/projects/new.html.haml | 2 | ||||
-rw-r--r-- | app/views/projects/project_templates/_built_in_templates.html.haml | 6 | ||||
-rw-r--r-- | doc/user/admin_area/custom_project_templates.md | 25 | ||||
-rw-r--r-- | doc/user/group/custom_project_templates.md | 23 | ||||
-rw-r--r-- | doc/user/group/index.md | 5 | ||||
-rw-r--r-- | spec/migrations/clean_up_for_members_spec.rb | 5 | ||||
-rw-r--r-- | spec/migrations/delete_inconsistent_internal_id_records_spec.rb | 15 |
13 files changed, 86 insertions, 16 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 0da7ae1b229..f8dbe412f80 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -5,6 +5,7 @@ import axios from './lib/utils/axios_utils'; const Api = { groupsPath: '/api/:version/groups.json', groupPath: '/api/:version/groups/:id', + subgroupsPath: '/api/:version/groups/:id/subgroups', namespacesPath: '/api/:version/namespaces.json', groupProjectsPath: '/api/:version/groups/:id/projects.json', projectsPath: '/api/:version/projects.json', diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index b4a3037c1b7..2049760fe29 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -10,13 +10,18 @@ export default function groupsSelect() { const $select = $(this); const allAvailable = $select.data('allAvailable'); const skipGroups = $select.data('skipGroups') || []; + const parentGroupID = $select.data('parentId'); + const groupsPath = parentGroupID + ? Api.subgroupsPath.replace(':id', parentGroupID) + : Api.groupsPath; + $select.select2({ placeholder: 'Search for a group', allowClear: $select.hasClass('allowClear'), multiple: $select.hasClass('multiselect'), minimumInputLength: 0, ajax: { - url: Api.buildUrl(Api.groupsPath), + url: Api.buildUrl(groupsPath), dataType: 'json', quietMillis: 250, transport(params) { diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js index 32b55575f95..01ef445c901 100644 --- a/app/assets/javascripts/pages/groups/edit/index.js +++ b/app/assets/javascripts/pages/groups/edit/index.js @@ -5,6 +5,7 @@ import initSettingsPanels from '~/settings_panels'; import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory'; import mountBadgeSettings from '~/pages/shared/mount_badge_settings'; import { GROUP_BADGE } from '~/badges/constants'; +import groupsSelect from '~/groups_select'; import projectSelect from '~/project_select'; document.addEventListener('DOMContentLoaded', () => { @@ -17,5 +18,8 @@ document.addEventListener('DOMContentLoaded', () => { ); mountBadgeSettings(GROUP_BADGE); + // Initialize Subgroups selector + groupsSelect(); + projectSelect(); }); diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss index bdd7f09d926..0d8e4afa76f 100644 --- a/app/assets/stylesheets/framework/callout.scss +++ b/app/assets/stylesheets/framework/callout.scss @@ -33,7 +33,11 @@ .bs-callout-warning { background-color: $orange-100; border-color: $orange-200; - color: $orange-700; + color: $orange-900; + + a { + color: $orange-900; + } } .bs-callout-info { diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 2458f5b308a..9e77a3237e3 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -9,7 +9,7 @@ module Projects end def execute - if @params[:template_name]&.present? + if @params[:template_name].present? return ::Projects::CreateFromTemplateService.new(current_user, params).execute end diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 869c54d89ea..39d0f620283 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -37,6 +37,7 @@ .settings-content = render 'shared/badges/badge_settings' += render_if_exists 'groups/custom_project_templates_setting' = render_if_exists 'groups/templates_setting', expanded: expanded %section.settings.gs-advanced.no-animate#js-advanced-settings{ class: ('expanded' if expanded) } diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 10e3b01096a..a760d02c4c3 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -50,7 +50,7 @@ .project-template .form-group %div - = render 'project_templates', f: f + = render 'project_templates', f: f, project: @project .tab-pane.import-project-pane.js-toggle-container{ id: 'import-project-pane', class: active_when(active_tab == 'import'), role: 'tabpanel' } - if import_sources_enabled? diff --git a/app/views/projects/project_templates/_built_in_templates.html.haml b/app/views/projects/project_templates/_built_in_templates.html.haml index 233c3adba0e..5b4d8927045 100644 --- a/app/views/projects/project_templates/_built_in_templates.html.haml +++ b/app/views/projects/project_templates/_built_in_templates.html.haml @@ -9,9 +9,9 @@ .text-muted = template.description .controls.d-flex.align-items-center - %label.btn.btn-success.template-button.choose-template.append-right-10.append-bottom-0{ for: template.name } + %a.btn.btn-default.append-right-10{ href: template.preview, rel: 'noopener noreferrer', target: '_blank', data: { track_label: "create_from_template", track_property: "template_preview", track_event: "click_button", track_value: template.name } } + = _("Preview") + %label.btn.btn-success.template-button.choose-template.append-bottom-0{ for: template.name } %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name, data: { track_label: "create_from_template", track_property: "template_use", track_event: "click_button" } } %span = _("Use template") - %a.btn.btn-default{ href: template.preview, rel: 'noopener noreferrer', target: '_blank', data: { track_label: "create_from_template", track_property: "template_preview", track_event: "click_button", track_value: template.name } } - = _("Preview") diff --git a/doc/user/admin_area/custom_project_templates.md b/doc/user/admin_area/custom_project_templates.md new file mode 100644 index 00000000000..5afbf9f2934 --- /dev/null +++ b/doc/user/admin_area/custom_project_templates.md @@ -0,0 +1,25 @@ +# Custom instance-level project templates **[PREMIUM ONLY]** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6860) in [GitLab Premium](https://about.gitlab.com/pricing) 11.2. + +When you create a new project, creating it based on custom project templates is +a convenient option to bootstrap from an existing project boilerplate. +The administration setting to configure a GitLab group that serves as template +source can be found under **Admin > Settings > Custom project templates**. + +Within this section, you can configure the group where all the custom project +templates are sourced. Every project directly under the group namespace will be +available to the user if they have access to them. For example, every public +project in the group will be available to every logged user. However, +private projects will be available only if the user has view [permissions](../permissions.md) +in the project: + +- Project Owner, Maintainer, Developer, Reporter or Guest +- Is a member of the Group: Owner, Maintainer, Developer, Reporter or Guest + +Projects below subgroups of the template group are **not** supported. + +Repository and database information that are copied over to each new project are +identical to the data exported with [GitLab's Project Import/Export](../project/settings/import_export.md). + +If you would like to set project templates at a group level, please see [Custom group-level project templates](../group/custom_project_templates.md).
\ No newline at end of file diff --git a/doc/user/group/custom_project_templates.md b/doc/user/group/custom_project_templates.md new file mode 100644 index 00000000000..eaf0273050b --- /dev/null +++ b/doc/user/group/custom_project_templates.md @@ -0,0 +1,23 @@ +# Custom group-level project templates **[PREMIUM ONLY]** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6861) in [GitLab Premium](https://about.gitlab.com/pricing) 11.6. + +When you create a new project, creating it based on custom project templates is +a convenient option to bootstrap from an existing project boilerplate. +The group-level setting to configure a GitLab group that serves as template +source can be found under **Group > Settings > General > Custom project templates**. + +Within this section, you can configure the group where all the custom project +templates are sourced. Every project directly under the group namespace will be +available to the user if they have access to them. For example, every public +project in the group will be available to every logged in user. However, +private projects will be available only if the user has view [permissions](../permissions.md) +in the project. That is, users with Owner, Maintainer, Developer, Reporter or Guest roles for projects, +or for groups to which the project belongs. + +Projects of nested subgroups of a selected template source cannot be used. + +Repository and database information that are copied over to each new project are +identical to the data exported with [GitLab's Project Import/Export](../project/settings/import_export.md). + +If you would like to set project templates at an instance level, please see [Custom instance-level project templates](../admin_area/custom_project_templates.md).
\ No newline at end of file diff --git a/doc/user/group/index.md b/doc/user/group/index.md index d673fa4d21a..36b9318c0e0 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -259,6 +259,11 @@ types with every project in a group. Learn more about [Group-level file templates](https://docs.gitlab.com/ee/user/group/index.html#group-level-file-templates-premium). +#### Group-level project templates **[PREMIUM]** + +Define project templates at a group-level by setting a group as a template source. +[Learn more about group-level project templates](custom_project_templates.md). + ### Advanced settings - **Projects**: view all projects within that group, add members to each project, diff --git a/spec/migrations/clean_up_for_members_spec.rb b/spec/migrations/clean_up_for_members_spec.rb index 0258860d169..7876536cb3e 100644 --- a/spec/migrations/clean_up_for_members_spec.rb +++ b/spec/migrations/clean_up_for_members_spec.rb @@ -3,6 +3,7 @@ require Rails.root.join('db', 'migrate', '20171216111734_clean_up_for_members.rb describe CleanUpForMembers, :migration do let(:migration) { described_class.new } + let(:groups) { table(:namespaces) } let!(:group_member) { create_group_member } let!(:unbinded_group_member) { create_group_member } let!(:invited_group_member) { create_group_member(true) } @@ -25,7 +26,7 @@ describe CleanUpForMembers, :migration do end def create_group_member(invited = false) - fill_member(GroupMember.new(group: create_group), invited) + fill_member(GroupMember.new(source_id: create_group.id, source_type: 'Namespace'), invited) end def create_project_member(invited = false) @@ -54,7 +55,7 @@ describe CleanUpForMembers, :migration do def create_group name = FFaker::Lorem.characters(10) - Group.create(name: name, path: name.downcase.gsub(/\s/, '_')) + groups.create!(type: 'Group', name: name, path: name.downcase.gsub(/\s/, '_')) end def create_project diff --git a/spec/migrations/delete_inconsistent_internal_id_records_spec.rb b/spec/migrations/delete_inconsistent_internal_id_records_spec.rb index 4af51217031..8c55daf0d37 100644 --- a/spec/migrations/delete_inconsistent_internal_id_records_spec.rb +++ b/spec/migrations/delete_inconsistent_internal_id_records_spec.rb @@ -94,17 +94,18 @@ describe DeleteInconsistentInternalIdRecords, :migration do end context 'for milestones (by group)' do - # milestones (by group) is a little different than all of the other models - let!(:group1) { create(:group) } - let!(:group2) { create(:group) } - let!(:group3) { create(:group) } + # milestones (by group) is a little different than most of the other models + let(:groups) { table(:namespaces) } + let(:group1) { groups.create(name: 'Group 1', type: 'Group', path: 'group_1') } + let(:group2) { groups.create(name: 'Group 2', type: 'Group', path: 'group_2') } + let(:group3) { groups.create(name: 'Group 2', type: 'Group', path: 'group_3') } let(:internal_id_query) { ->(group) { InternalId.where(usage: InternalId.usages['milestones'], namespace: group) } } before do - 3.times { create(:milestone, group: group1) } - 3.times { create(:milestone, group: group2) } - 3.times { create(:milestone, group: group3) } + 3.times { create(:milestone, group_id: group1.id) } + 3.times { create(:milestone, group_id: group2.id) } + 3.times { create(:milestone, group_id: group3.id) } internal_id_query.call(group1).first.tap do |iid| iid.last_value = iid.last_value - 2 |