diff --git a/pkg/migration/20200516123847.go b/pkg/migration/20200516123847.go new file mode 100644 index 00000000..da632d2c --- /dev/null +++ b/pkg/migration/20200516123847.go @@ -0,0 +1,57 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-2020 Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package migration + +import ( + "code.vikunja.io/api/pkg/models" + "src.techknowlogick.com/xormigrate" + "xorm.io/xorm" +) + +func init() { + migrations = append(migrations, &xormigrate.Migration{ + ID: "20200516123847", + Description: "Generate a list identifier for each list", + Migrate: func(tx *xorm.Engine) error { + lists := []*models.List{} + err := tx.Find(&lists) + if err != nil { + return err + } + + for _, l := range lists { + if l.Identifier != "" { + continue + } + + err := models.GenerateListIdentifier(l, tx) + if err != nil { + return err + } + _, err = tx.Where("id = ?", l.ID).Update(l) + if err != nil { + return err + } + } + + return nil + }, + Rollback: func(tx *xorm.Engine) error { + return nil + }, + }) +} diff --git a/pkg/models/list.go b/pkg/models/list.go index 5ddb1112..719e7c0d 100644 --- a/pkg/models/list.go +++ b/pkg/models/list.go @@ -21,7 +21,9 @@ import ( "code.vikunja.io/api/pkg/timeutil" "code.vikunja.io/api/pkg/user" "code.vikunja.io/web" + "strings" "xorm.io/builder" + "xorm.io/xorm" ) // List represents a list of tasks @@ -351,6 +353,43 @@ func (l *List) CheckIsArchived() (err error) { return nil } +// GenerateListIdentifier generates a unique random list identifier based on the list title. +// If it is not able to find one, the list identifier will be empty. +func GenerateListIdentifier(l *List, sess *xorm.Engine) (err error) { + + // The general idea here is to take the title and slice it into pieces, until we found a unique piece. + + var exists = true + titleSlug := strings.Replace(strings.ToUpper(l.Title), " ", "", -1) + + // We can save at most 10 characters in the db, so we need to ensure it has at most 10 characters + if len(titleSlug) > 10 { + titleSlug = titleSlug[0:9] + } + + var i = 0 + + for exists { + + // Prevent endless looping + if i == len(titleSlug) { + break + } + + // Take a random part of the title slug, starting at the beginning + l.Identifier = titleSlug[i:] + exists, err = sess. + Where("identifier = ?", l.Identifier). + And("id != ?", l.ID). + Exist(&List{}) + if err != nil { + return + } + i++ + } + return nil +} + // CreateOrUpdateList updates a list or creates it if it doesn't exist func CreateOrUpdateList(list *List) (err error) { @@ -376,6 +415,14 @@ func CreateOrUpdateList(list *List) (err error) { } } + // Generate a random list identifier base on the list title + if list.ID == 0 && list.Identifier == "" { + err = GenerateListIdentifier(list, x) + if err != nil { + return + } + } + if list.ID == 0 { _, err = x.Insert(list) metrics.UpdateCount(1, metrics.ListCountKey)