fix(templates): reuse ProjectDuplicate.Create for template creation and fix is_template filter semantics

This commit is contained in:
kolaente 2026-03-24 20:23:55 +01:00
parent 3de5206dd4
commit 00645f50b6
3 changed files with 19 additions and 42 deletions

View File

@ -42,6 +42,8 @@ type ProjectDuplicate struct {
SkipPermissions bool `json:"-"`
// If true, skip copying task assignees and comments
SkipAssigneesAndComments bool `json:"-"`
// If true, the duplicated project will be marked as a template
IsTemplate bool `json:"-"`
web.Permissions `json:"-"`
web.CRUDable `json:"-"`
@ -90,7 +92,15 @@ func (pd *ProjectDuplicate) Create(s *xorm.Session, doer web.Auth) (err error) {
pd.Project.ParentProjectID = pd.ParentProjectID
// Set the owner to the current user
pd.Project.OwnerID = doer.GetID()
pd.Project.Title += " - duplicate"
if pd.IsTemplate {
pd.Project.IsTemplate = true
pd.Project.ParentProjectID = 0
pd.SkipPermissions = true
pd.SkipAssigneesAndComments = true
} else {
pd.Project.Title += " - duplicate"
}
err = CreateProject(s, pd.Project, doer, false, false)
if err != nil {
// If there is no available unique project identifier, just reset it.

View File

@ -35,7 +35,7 @@ type ProjectTemplate struct {
web.CRUDable `json:"-"`
}
// CanCreate checks if a user has the right to create a template from a project
// CanCreate checks if a user has the permission to create a template from a project
func (pt *ProjectTemplate) CanCreate(s *xorm.Session, a web.Auth) (canCreate bool, err error) {
p := &Project{ID: pt.ProjectID}
canCreate, _, err = p.CanRead(s, a)
@ -57,56 +57,23 @@ func (pt *ProjectTemplate) CanCreate(s *xorm.Session, a web.Auth) (canCreate boo
func (pt *ProjectTemplate) Create(s *xorm.Session, doer web.Auth) (err error) {
log.Debugf("Creating template from project %d", pt.ProjectID)
// Use ProjectDuplicate to copy the project
pd := &ProjectDuplicate{
ProjectID: pt.ProjectID,
SkipPermissions: true,
SkipAssigneesAndComments: true,
ProjectID: pt.ProjectID,
IsTemplate: true,
}
// Read the source project
// Read the source project so the duplicate has something to work with
pd.Project = &Project{ID: pt.ProjectID}
err = pd.Project.ReadOne(s, doer)
if err != nil {
return err
}
// Reset and mark as template
pd.Project.ID = 0
pd.Project.Identifier = ""
pd.Project.ParentProjectID = 0
pd.Project.OwnerID = doer.GetID()
pd.Project.IsTemplate = true
err = CreateProject(s, pd.Project, doer, false, false)
err = pd.Create(s, doer)
if err != nil {
if IsErrProjectIdentifierIsNotUnique(err) {
pd.Project.Identifier = ""
err = CreateProject(s, pd.Project, doer, false, false)
}
if err != nil {
return err
}
}
log.Debugf("Created template project %d from project %d", pd.Project.ID, pt.ProjectID)
newTaskIDs, err := duplicateTasks(s, doer, pd)
if err != nil {
return
}
err = duplicateViews(s, pd, doer, newTaskIDs)
if err != nil {
return
}
err = duplicateProjectBackground(s, pd, doer)
if err != nil {
return
return err
}
pt.Project = pd.Project
err = pt.Project.ReadOne(s, doer)
return
}

View File

@ -101,12 +101,12 @@ func TestProject(t *testing.T) {
assert.NotContains(t, rec.Body.String(), `Template Project`)
assert.NotContains(t, rec.Body.String(), `Shared Template`)
})
t.Run("Templates only", func(t *testing.T) {
t.Run("Including templates", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(url.Values{"is_template": []string{"true"}}, nil)
require.NoError(t, err)
assert.Contains(t, rec.Body.String(), `Template Project`)
assert.Contains(t, rec.Body.String(), `Shared Template`)
assert.NotContains(t, rec.Body.String(), `"title":"Test1"`)
assert.Contains(t, rec.Body.String(), `Test1`)
})
})
t.Run("ReadOne", func(t *testing.T) {