feat(repo-creation): added svn bootstrap, and improved repo creation validation

Also added refined UI, ability to pre-validate repo names

fold: repo create

fold repo creation

foldz

feat(repo-creation): added svn bootstrap, and improved repo creation validation

Also added refined UI, ability to pre-validate repo names

fold: repo create

fold repo creation

foldz

fold
This commit is contained in:
RhodeCode Admin 2025-12-23 21:04:15 +01:00
parent ee52770fff
commit cd01bbbddd
23 changed files with 656 additions and 200 deletions

View file

@ -24,6 +24,7 @@
</%def>
<%def name="main()">
<div class="box">
${h.secure_form(h.route_path('repo_group_create'), request=request)}
<div class="form">
@ -34,24 +35,40 @@
<label for="group_name">${_('Group name')}:</label>
</div>
<div class="input">
${h.text('group_name', class_="medium")}
<div class="full-repo-name-container">
% if c.personal_repo_group:
<a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}">
<i class="icon-repo-group tooltip"
title="${_('Select my personal group ({})').format(c.personal_repo_group.group_name)}"></i>
</a>
% endif
${h.select('group_parent_id', request.GET.get('parent_group'), c.repo_groups,class_="medium")}
<div class="repo-create-spacer"> / </div>
<div class="repo-name-input-container">
${h.text('group_name', class_="medium")}
<div class="repo-check-container check-progress" id="group_check" style="display: none"></div>
</div>
</div>
<div class="repo-header-row">
<span class="help-block-repo-create">
${_('Parent Repository Group / Repository Group Name')}.
</span>
</div>
## COPY PERMS
<div id="copy_perms">
<div id="checkbox-span">
${h.checkbox('group_copy_permissions', value="True", checked="checked")}
</div>
<div class="help-block-inline">
${_('Copy permissions from parent repository group.')}
</div>
</div>
</div>
</div>
<div class="field">
<div class="label">
<label for="group_parent_id">${_('Repository group')}:</label>
</div>
<div class="select">
${h.select('group_parent_id', request.GET.get('parent_group'),c.repo_groups,class_="medium")}
% if c.personal_repo_group:
<a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}">
${_('Select my personal group ({})').format(c.personal_repo_group.group_name)}
</a>
% endif
</div>
</div>
<div class="field">
<div class="label">
<label for="group_description">${_('Description')}:</label>
@ -73,16 +90,6 @@
</div>
</div>
<div id="copy_perms" class="field">
<div class="label label-checkbox">
<label for="group_copy_permissions">${_('Copy Parent Group Permissions')}:</label>
</div>
<div class="checkboxes">
${h.checkbox('group_copy_permissions', value="True", checked="checked")}
<span class="help-block">${_('Copy permissions from parent repository group.')}</span>
</div>
</div>
<div class="buttons">
${h.submit('save',_('Create Repository Group'),class_="btn")}
</div>
@ -91,31 +98,90 @@
${h.end_form()}
</div>
<script>
$(document).ready(function(){
var setCopyPermsOption = function(group_val){
if(group_val !== "-1"){
$(document).ready(function () {
let setCopyPermsOption = function (group_val) {
if (group_val !== "-1") {
$('#copy_perms').show()
}
else{
} else {
$('#copy_perms').hide();
}
};
$("#group_parent_id").select2({
'containerCssClass': "drop-menu",
'dropdownCssClass': "drop-menu-dropdown",
'dropdownAutoWidth': true
});
setCopyPermsOption($('#group_parent_id').val());
$("#group_parent_id").on("change", function(e) {
setCopyPermsOption(e.val)
$("#group_parent_id").on("change", function (e) {
setCopyPermsOption(e.val);
$('#group_name').trigger('input');
});
$('#group_name').focus();
$('#select_my_group').on('click', function(e){
$('#select_my_group').on('click', function (e) {
e.preventDefault();
$("#group_parent_id").val($(this).data('personalGroupId')).trigger("change");
})
});
let debounceTimer;
$('#group_name').on('input paste', function () {
let groupName = $(this).val();
if (groupName.length > 0) {
$('#group_check').text(_gettext('checking availability...'))
.show()
.removeClass('check-error check-success')
.addClass('check-progress');
} else {
$('#group_check').hide();
clearTimeout(debounceTimer);
return;
}
clearTimeout(debounceTimer); // Cancel the previous timer
debounceTimer = setTimeout(function () {
performRepoGroupLookup(groupName);
}, 200);
function performRepoGroupLookup(groupName) {
self = this;
let repoGroup = $('#group_parent_id').val();
$.ajax({
url: pyroutes.url('repo_group_name_check'),
data: {"repo_group_name": groupName, "repo_group": repoGroup},
dataType: 'json',
type: 'POST',
success: function (data) {
let result = data.result;
if (result.check_error === true) {
$('#group_check')
.text(result.errors)
.removeClass('check-progress check-success')
.addClass('check-error');
} else {
let text = "Repository group `{0}` is available".format(result.repo_group_name);
$('#group_check')
.text(text)
.removeClass('check-progress check-error')
.addClass('check-success');
}
},
error: function (jqXHR, textStatus, errorThrown) {
var prefix = "Error while checking name.\n"
var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
alert(message)
}
})
}
});
})
</script>
</%def>

View file

@ -7,63 +7,22 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
<div class="label">
<label for="repo_name">${_('Repository')}:</label>
</div>
<style>
.help-block-repo-create {
color: #949494;
}
.repo-header-row {
display: flex; /* Modern way to align elements inline */
align-items: center; /* Keeps text perfectly leveled vertically */
gap: 4px; /* Space between the label and the link */
margin: 10px 0; /* Space below the entire row */
}
#copy_perms {
/* Layout & Alignment */
display: flex;
align-items: center; /* Aligns checkbox with the first line of text */
/* Spacing for the whole row */
margin: 15px 0; /* Space above and below the row */
/* Internal Spacing */
gap: 6px; /* Space specifically between checkbox and text */
}
#checkbox-span {
align-items: center; /* Aligns checkbox with the first line of text */
padding-top: 2px; /* Micro-adjustment to center checkbox with text line */
}
.help-block-inline {
font-size: 14px; /* Standard readable size */
line-height: 1.4; /* Improves readability for multi-line text */
color: #333; /* Professional dark grey */
}
.repo-create-spacer {
margin: 0 4px;
font-weight: bold;
font-size: 16px;
}
</style>
<div class="input">
${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
<span class="repo-create-spacer"> / </span>
${h.text('repo_name', class_="medium", id="repo_name_input")}
% if c.personal_repo_group:
<a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}">
${_('Select my personal group ({})').format(c.personal_repo_group.group_name)}
</a>
% endif
<div class="full-repo-name-container">
% if c.personal_repo_group:
<a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}">
<i class="icon-repo-group tooltip"
title="${_('Select my personal group ({})').format(c.personal_repo_group.group_name)}"></i>
</a>
% endif
${h.select('repo_group', request.GET.get('parent_group'), c.repo_groups, class_="medium")}
<div class="repo-create-spacer"> / </div>
<div class="repo-name-input-container">
${h.text('repo_name', class_="medium", id="repo_name_input")}
<div class="repo-check-container check-progress" id="repo_check" style="display: none"></div>
</div>
</div>
<div class="repo-header-row">
<span class="help-block-repo-create">
@ -85,7 +44,6 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
</div>
</div>
## TODO: what is this ? i don't think we use it anymore
%if not c.rhodecode_user.is_admin:
${h.hidden('user_created',True)}
%endif
@ -118,9 +76,9 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
<label for="repo_type">${_('Type')}:</label>
</div>
<div class="fields repo-type-radio">
% for backend in c.backends:
% if loop.index == 0:
% if backend == c.default_backend:
<input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" checked="checked"/>
% else:
<input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" />
@ -168,7 +126,7 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
</div>
</div>
<div class="field repo-bootstrap-items">
<div class="field repo-bootstrap-items repo-bootstrap-readme">
<div class="label label-checkbox">
<label for="repo_bootstrap_readme">${_('Add Readme')}:</label>
</div>
@ -178,7 +136,7 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
</div>
</div>
<div class="field repo-bootstrap-items">
<div class="field repo-bootstrap-items repo-bootstrap-svn" style="display: none">
<div class="label label-checkbox">
<label for="repo_bootstrap_svn_branch_struct">${_('Add SVN structure')}:</label>
</div>
@ -195,27 +153,26 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
</div>
</div>
<script>
$(document).ready(function(){
var setCopyPermsOption = function(group_val){
if(group_val != "-1"){
$(document).ready(function () {
let setCopyPermsOption = function (group_val) {
if (group_val !== "-1") {
$('#copy_perms').show()
}
else{
} else {
$('#copy_perms').hide();
}
};
$('#remote_clone_toggle').on('click', function(e){
$('#remote_clone_toggle').on('click', function (e) {
$('#remote_clone').show();
e.preventDefault();
$('#remote_clone_toggle').hide();
$('.repo-bootstrap-items').remove();
});
if($('#remote_clone input').hasClass('error')){
if ($('#remote_clone input').hasClass('error')) {
$('#remote_clone').show();
}
if($('#remote_clone input').val()){
if ($('#remote_clone input').val()) {
$('#remote_clone').show();
}
@ -227,29 +184,101 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
});
setCopyPermsOption($('#repo_group').val());
$("#repo_group").on("change", function(e) {
setCopyPermsOption(e.val)
$("#repo_group").on("change", function (e) {
setCopyPermsOption(e.val);
$('#repo_name_input').trigger('input');
});
$('#repo_name').focus();
$('#repo_name_input').focus();
$('#select_my_group').on('click', function(e){
let debounceTimer;
$('#repo_name_input').on('input paste', function () {
let repoName = $(this).val();
if (repoName.length > 0) {
$('#repo_check').text(_gettext('checking availability...'))
.show()
.removeClass('check-error check-success')
.addClass('check-progress');
} else {
$('#repo_check').hide();
clearTimeout(debounceTimer);
return;
}
clearTimeout(debounceTimer); // Cancel the previous timer
debounceTimer = setTimeout(function () {
performRepoLookup(repoName);
}, 200);
function performRepoLookup(repoName) {
self = this;
let repoGroup = $('#repo_group').val();
let repoType = $('input[name="repo_type"]').val();
$.ajax({
url: pyroutes.url('repo_name_check'),
data: {"repo_name": repoName, "repo_group": repoGroup, "repo_type": repoType},
dataType: 'json',
type: 'POST',
success: function (data) {
let result = data.result;
if (result.check_error === true) {
$('#repo_check')
.text(result.errors)
.removeClass('check-progress check-success')
.addClass('check-error');
} else {
let text = _gettext("Repository `{0}` is available").format(result.repo_name);
$('#repo_check')
.text(text)
.removeClass('check-progress check-error')
.addClass('check-success');
}
},
error: function (jqXHR, textStatus, errorThrown) {
var prefix = "Error while checking name.\n"
var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
alert(message)
}
})
}
});
$('#select_my_group').on('click', function (e) {
e.preventDefault();
$("#repo_group").val($(this).data('personalGroupId')).trigger("change");
})
});
$('#clone_uri').on('input paste', function(e) {
var url = $(this).val().trim();
$('#clone_uri').on('input paste', function (e) {
let url = $(this).val().trim();
if (url) {
var parts = url.split('/').filter(Boolean);
var lastPart = parts[parts.length - 1];
var repoName = lastPart.replace(/\.git$/, "");
let parts = url.split('/').filter(Boolean);
let lastPart = parts[parts.length - 1];
let repoName = lastPart.replace(/\.git$/, "");
$('#repo_name_input').val(repoName);
$('#repo_name_input').val(repoName).trigger('input');
} else {
$('#repo_name_input').val("").trigger('input');
}
else {
$('#repo_name_input').val("");
});
let defaultVcsBackend = "${c.default_backend}";
if (defaultVcsBackend === "svn") {
$('.repo-bootstrap-svn').show();
}
$('input[name="repo_type"]').on('change', function (e) {
let selectedVcs = $(this).val();
if (selectedVcs === "svn") {
$('.repo-bootstrap-svn').show();
} else {
$('.repo-bootstrap-svn').hide();
}
})