Merge branch '6861-group-level-project-templates-ce' into 'master'
Backport from gitlab-org/gitlab-ee!6878 See merge request gitlab-org/gitlab-ce!23391
This commit is contained in:
commit
5f1b71d18a
13 changed files with 86 additions and 16 deletions
|
@ -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',
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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")
|
||||
|
|
25
doc/user/admin_area/custom_project_templates.md
Normal file
25
doc/user/admin_area/custom_project_templates.md
Normal file
|
@ -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).
|
23
doc/user/group/custom_project_templates.md
Normal file
23
doc/user/group/custom_project_templates.md
Normal file
|
@ -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).
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue