From b117befc2b4f5ec1e864e2eeaf99355861be01d7 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Tue, 15 Dec 2015 22:57:18 -0500 Subject: [PATCH] #1692 add user email APIs --- README.md | 2 +- gogs.go | 2 +- models/user.go | 46 ++++++++++++++----- modules/bindata/bindata.go | 4 +- routers/api/v1/api.go | 3 ++ routers/api/v1/user/email.go | 78 +++++++++++++++++++++++++++++++++ routers/api/v1/utils/convert.go | 8 ++++ routers/user/setting.go | 2 +- templates/.VERSION | 2 +- 9 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 routers/api/v1/user/email.go diff --git a/README.md b/README.md index eb6b2d796..8af7e432b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true) -##### Current version: 0.8.6 +##### Current version: 0.8.7 | Web | UI | Preview | |:-------------:|:-------:|:-------:| diff --git a/gogs.go b/gogs.go index 03a98f091..58f0dae63 100644 --- a/gogs.go +++ b/gogs.go @@ -18,7 +18,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.8.6.1215" +const APP_VER = "0.8.7.1215" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/user.go b/models/user.go index 36927730d..71729a4e3 100644 --- a/models/user.go +++ b/models/user.go @@ -888,7 +888,7 @@ func GetEmailAddresses(uid int64) ([]*EmailAddress, error) { } func AddEmailAddress(email *EmailAddress) error { - email.Email = strings.ToLower(email.Email) + email.Email = strings.ToLower(strings.TrimSpace(email.Email)) used, err := IsEmailUsed(email.Email) if err != nil { return err @@ -900,6 +900,29 @@ func AddEmailAddress(email *EmailAddress) error { return err } +func AddEmailAddresses(emails []*EmailAddress) error { + if len(emails) == 0 { + return nil + } + + // Check if any of them has been used + for i := range emails { + emails[i].Email = strings.ToLower(strings.TrimSpace(emails[i].Email)) + used, err := IsEmailUsed(emails[i].Email) + if err != nil { + return err + } else if used { + return ErrEmailAlreadyUsed{emails[i].Email} + } + } + + if _, err := x.Insert(emails); err != nil { + return fmt.Errorf("Insert: %v", err) + } + + return nil +} + func (email *EmailAddress) Activate() error { email.IsActivated = true if _, err := x.Id(email.ID).AllCols().Update(email); err != nil { @@ -914,20 +937,23 @@ func (email *EmailAddress) Activate() error { } } -func DeleteEmailAddress(email *EmailAddress) error { - has, err := x.Get(email) - if err != nil { - return err - } else if !has { - return ErrEmailNotExist +func DeleteEmailAddress(email *EmailAddress) (err error) { + if email.ID > 0 { + _, err = x.Id(email.ID).Delete(new(EmailAddress)) + } else { + _, err = x.Where("email=?", email.Email).Delete(new(EmailAddress)) } + return err +} - if _, err = x.Id(email.ID).Delete(email); err != nil { - return err +func DeleteEmailAddresses(emails []*EmailAddress) (err error) { + for i := range emails { + if err = DeleteEmailAddress(emails[i]); err != nil { + return err + } } return nil - } func MakeEmailPrimary(email *EmailAddress) error { diff --git a/modules/bindata/bindata.go b/modules/bindata/bindata.go index aa23c11d0..755855d00 100644 --- a/modules/bindata/bindata.go +++ b/modules/bindata/bindata.go @@ -4304,7 +4304,7 @@ func confLicenseMozillaPublicLicense20() (*asset, error) { return a, nil } -var _confLocaleTranslators = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x6c\x54\x4d\x6f\xdb\x46\x10\xbd\xf3\x57\x0c\x90\x73\x8d\xa0\x45\xd1\x06\x20\x04\xc8\x76\x93\x58\xb1\xe4\x02\x52\x53\xf8\x38\x22\x97\xdc\x11\xf7\x43\xd9\x5d\x52\xa2\x6e\xfd\x25\x3d\xf7\x98\x4b\xd1\xbb\xe3\xff\xd5\x19\x52\x56\x85\x5a\x07\x89\xcb\xb7\x6f\x76\x66\xe7\xbd\xe1\x1b\x58\x69\x8a\x50\x91\x51\x60\x28\xa6\x08\x68\x0c\xfc\xfa\xdb\xf5\xfd\xdd\x0d\x90\x2b\xa9\xa3\xb2\x45\x13\x41\x63\x47\xae\x86\xc2\xbb\x14\x68\xdd\x26\x55\x0e\x6b\xe5\x12\x24\x0f\x49\x2b\x48\x01\x5d\x34\x98\xc8\xbb\xab\xec\x0d\xfc\x22\x44\xc5\xe7\x05\xc5\x07\xf1\xb1\x5b\x8d\x6b\x95\xa8\x40\x03\x3e\x94\x2a\x5c\x65\xd9\xb4\x21\x4d\xc1\xc3\xe3\xf4\xc3\x74\x39\xfd\x74\x07\x79\x8f\x75\xdd\xa7\xa4\x02\x4c\x57\x60\xbd\x25\xac\x15\xdc\x3e\xac\x38\x99\x9d\x64\x53\xa3\xf6\xe8\x38\x16\x96\x49\x91\xd3\x4f\x7f\x57\xbc\xce\x1b\x2e\x04\x9b\x24\x21\x66\xff\x5d\x1c\xf8\xa5\x3a\xd1\xb9\x80\x39\xd6\xce\x43\x8e\x27\x40\x28\x76\x6d\x25\xa4\xb6\x48\xe6\x2c\x87\xec\xf7\xb0\x50\x41\xee\x9b\xe3\xf8\xca\x3c\x37\x22\x47\xe2\xf0\x5c\x07\xe6\x87\xa4\x5b\x2e\x97\xaf\xee\x7a\x74\x1c\x31\x02\xb2\x3f\xe6\xc1\x97\xad\xd7\xc9\xae\x31\x7c\xfb\xfa\xfc\x27\x4c\x43\x89\xf0\xf8\xed\xab\xb1\x78\x90\x13\x4a\xec\x49\xd6\x35\xda\xb1\x15\xff\x8b\xbb\xd1\x81\xc5\xf2\x5b\x0d\x9f\x28\x56\xca\x94\x90\x17\x27\x48\x58\xcd\x11\xbe\x10\xda\x47\xf2\x5c\x50\x99\xdd\xa2\x23\x65\x60\xb9\x55\x54\x68\x15\x12\xe4\xe5\x88\x70\x50\x3c\x81\x12\xb8\x35\x93\xec\xd6\x12\x0b\xca\x8d\xf1\x35\xf6\x90\x5b\x25\xb4\x02\xd3\x4e\xfb\xc2\x97\xe3\x4d\x6b\x9c\x64\x1f\x82\xaa\x3d\x0b\x84\x2e\x39\x11\xa7\x2e\x55\x37\x28\x43\x9d\x3a\x29\xf3\x11\x2d\x95\xf0\x5e\xd1\x01\xd7\x58\x12\xe4\x5a\x80\xea\x70\x49\x90\x8f\x2d\x59\x36\xd0\xef\x28\x72\xec\xf8\x5f\xdb\xef\xdf\xbe\x7d\x27\x4c\xed\xd3\x19\x77\x78\x6c\xb6\x93\x8c\x4c\xe3\xb3\x3b\xd3\x23\xeb\xde\x60\xf0\x5d\x76\x8f\x55\xa0\x26\x42\x6e\x8e\x8b\xd7\x69\xee\xb1\x0d\x04\x0f\x1b\x36\x31\x75\x2d\xe4\x7b\xe1\xec\x55\xfa\x69\xa0\xf8\x50\x33\xa5\x2d\xc4\x78\x5b\x6e\xd2\x2e\x36\x5c\xb5\x61\x40\x9a\x75\x86\x09\xb9\x62\x5b\xcc\x31\x30\xb9\xd0\x64\x8c\x74\xc1\xca\xab\x50\x5f\x10\xe1\x11\xa7\x9d\x53\xdd\x72\xcb\xb9\x81\x06\xe1\x26\xb4\x2c\xbf\x1d\x21\x19\x80\x22\xec\x47\xab\x72\xcf\xe6\x3e\xf0\xb8\xc1\xf2\xe9\x9f\xa0\x5c\xe4\x55\xde\x18\xb2\x3f\x5f\xd2\x78\xc1\xd7\xf6\xb0\xe2\x7f\x8b\x11\xd9\x50\xe9\xca\x22\x4f\x95\xbb\xe4\xc1\x05\x26\xf1\xa6\x59\xb7\x5f\x5a\x15\xf8\x07\xb9\x13\x08\xff\x43\x7e\xbc\x10\xf6\x50\x92\xf1\x0e\x66\xad\x23\x96\x3b\xf7\xe3\xeb\x20\xc1\x00\xbd\xfb\xe1\x42\xd0\x4a\x7b\xae\x08\xde\xa3\x73\x3c\x4b\x83\x3d\x7c\x3d\x0e\x6c\x1a\xb7\x38\xa6\x3a\xed\x0e\xd3\x93\x38\x4c\x86\x81\x8b\xbf\xc6\x42\x43\x9e\x8e\x6f\x4c\xf5\x6d\x32\xde\x37\x67\x09\xbc\x23\xf8\x2c\x2d\x76\x08\x33\xb2\x4f\x7f\x39\xc5\x2d\x4d\x0c\x77\x9b\x4b\xd7\xf8\x6c\xd8\x7f\x96\x02\x07\xc5\xe8\x5b\x93\xd8\xf5\x1d\xfb\xec\xcb\x4e\xf1\x77\xed\x35\xff\x71\xb6\xf4\x15\x8f\x4a\xbf\x89\xf2\x64\xc2\x71\x35\xcc\xc9\xe8\xf0\x86\xf5\x7f\xfe\xa3\x6d\x30\x1e\x60\xc6\xbd\x5d\x90\xb2\x24\x97\x35\x23\x26\x1f\x93\x23\xf4\x32\x5d\xff\x06\x00\x00\xff\xff\x63\x49\x8c\x8c\x88\x05\x00\x00") +var _confLocaleTranslators = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x6c\x54\x4d\x6f\xdb\x46\x10\xbd\xf3\x57\x0c\x90\x73\x8d\xa0\x45\xd1\x06\x20\x0c\xc8\x76\xe2\xd8\xf1\x47\x51\xab\x29\x7c\x1c\x91\x2b\x72\xc4\xfd\x90\xf7\x83\x16\x75\xeb\x2f\xe9\xb9\xc7\x5c\x8a\xde\x9d\xfc\xaf\xbe\x25\x65\xd5\xa8\x75\x90\x38\xfb\xf6\xcd\xce\xec\xcc\x9b\x7d\x43\xf3\x56\x02\x2d\x45\x2b\xd2\x12\x62\x20\xd6\x9a\x7e\xf9\xed\xe4\xea\xe2\x94\xc4\xd6\xd2\x4b\x9d\x58\x07\x6a\xb9\x17\xdb\x50\xe5\x6c\xf4\xb2\x48\x51\xd5\xa3\xad\x6c\xa4\xe8\x28\xb6\x8a\xa2\x67\x1b\x34\x47\x71\xf6\xa8\x78\x43\xef\x33\x51\xe1\x3c\xaf\x70\x10\x8e\x5d\xb7\xbc\x50\x51\x2a\xd6\xe4\x7c\xad\xfc\x51\x51\xcc\x3a\x69\xc5\x3b\xba\x9f\x9d\xcf\xee\x66\x9f\x2e\xa8\x1c\xb8\x69\x86\x18\x95\xa7\xd9\x9c\x8c\x33\xc2\x8d\xa2\xb3\xdb\x39\x82\x99\xe3\x62\xa6\x55\x17\xd4\x2a\xd0\xb9\x77\x95\xea\x03\x95\xfc\x8c\x80\xde\xec\xc0\x4c\x5f\x7b\x37\xd2\x37\x6c\x11\x8a\xee\xa2\x12\xdb\x3e\xfd\xbd\x84\x5d\x76\xc8\x9b\xbb\x98\x5d\xf4\xe6\xbb\x89\x5f\xab\x3d\x1d\xf9\x5e\x73\x63\xdd\x78\xf8\x0e\xc8\x14\xb3\x30\x63\x14\xc3\xa2\x5f\xa4\x94\xf7\x07\xba\x51\x3e\x97\xa7\xe4\x69\x09\x9e\x9d\x90\x1d\x71\xfc\x2e\x3c\xf8\x3e\xb6\x09\xb7\x43\xa5\xec\xc0\x16\x1e\x13\x90\xf7\xa7\x38\xfc\xbc\xf5\x3a\xd8\x09\xfb\xaf\x5f\xbe\xfd\x49\x33\x5f\x33\xdd\x7f\xfd\xa2\x0d\x6f\xf3\x09\x35\x0f\x92\xed\x86\xcd\x54\xb9\xff\xf9\x9d\xb6\x1e\xbd\x75\xeb\x96\x3e\x49\x58\x2a\x5d\x53\x59\xed\xa1\xcc\xea\x76\xf0\x01\xd7\x21\x88\x43\x42\x75\x71\xc6\x56\x94\xa6\xbb\xb5\x92\xaa\x55\x3e\x52\x59\x4f\x08\x9c\xc2\x1e\x1c\x8b\xaf\x8f\xc1\x86\x72\xe8\x7e\xcb\x4d\x12\x8f\x0a\x96\x75\x5f\x0f\x58\x1d\x08\x71\x66\x04\x52\x41\x0d\x5d\xc3\x03\x95\x46\x65\x4e\xc5\xf1\xb1\x75\x95\xab\xa7\xa2\x34\x7c\x5c\xbc\xdf\xaa\x87\x94\x03\x9e\x3b\xbb\x45\x6f\xb6\xf4\xab\x40\x4c\x65\xe3\xac\xcf\xc6\xeb\x93\xcf\xbd\x6a\x1c\xba\xcf\x36\xda\xdc\xf9\xa6\x56\xfd\xd8\x76\xe9\xd5\xbe\xed\x1f\xd9\x20\xd3\x0f\x4a\xb6\xbc\xe0\x5a\xa8\x6c\x33\xb0\xdc\x1e\xea\xf6\xc7\x24\x06\x62\xfe\x9d\x73\xaf\x1f\xf1\xdf\x9a\xef\xdf\xbe\x7d\x97\x99\xad\x8b\x2f\xb8\xe3\x67\xb5\x3e\x2e\x44\x77\xae\xb8\xd0\x03\x43\x54\x1d\x7b\xd7\x17\x57\xbc\xf4\xd2\x41\xbb\x7a\x67\xbc\x0e\x73\xc5\xc9\x0b\xdd\xae\x30\x50\xd2\x27\x2a\x37\x99\xb3\x51\xf1\xa7\x91\xe2\x7c\x03\x4a\xaa\xb2\xaa\xd7\xe8\xc0\x63\xe8\x90\xb5\x06\x90\x3b\xf1\x02\xcb\xe4\x25\x34\x77\xcd\x1e\xe4\xaa\x15\xad\x73\x15\x4c\x5e\x66\xea\x33\x92\x79\x82\xb0\xd7\xd2\x24\x94\x17\x25\xd7\x4c\xa7\x3e\x41\x5b\x66\x82\xf2\x30\x56\x7e\x33\xcd\x01\x6a\x76\xed\x3c\x46\x9f\xee\x9e\xfe\xf1\xca\x06\x58\x65\xa7\xc5\xfc\x7c\x48\x40\x37\xb8\xb6\xa3\x39\xfe\x0d\x07\x86\x5a\xe3\x91\x61\x4c\xb8\x3d\x24\xf0\x1b\x8e\x59\xf8\x7a\x91\x1e\x92\xf2\xf8\x51\x69\x33\xc4\xff\x21\x3f\x1e\x70\xbb\xad\x45\x3b\x4b\x97\xc9\x0a\xda\x5d\xba\x69\x39\xb6\x60\x84\xde\xfd\x70\xc0\x69\xde\x3a\x64\x44\x1f\xd8\x5a\x0c\xea\x28\x0f\xd7\x4c\xaf\x41\x9c\xb6\xe0\xb3\xdc\xef\x8e\xa3\x19\xe1\x96\x27\x0d\xc9\x9f\x70\xd5\x52\x19\x77\x2b\x50\x5d\x8a\xda\xb9\xee\x45\x00\x67\x85\x3e\xe7\x12\x5b\xa6\x4b\x31\x4f\x7f\x59\x68\xb6\x8c\x80\xfb\xd5\xa1\x6b\x7c\xd6\xd0\x9f\x11\x0f\xa7\x10\x5c\xd2\x11\x23\xd5\x43\x67\x0f\x8f\x0a\x6f\xec\x6b\xfe\xfd\xe5\x9d\x5b\x62\x0e\x87\x55\xc8\x5f\x10\x76\xd6\x38\x84\x93\xc2\x3b\xf4\xff\xdb\x1f\xa9\xe3\xb0\xa5\x4b\xd4\xf6\x46\x94\x91\x7c\x59\x3d\x61\xf9\xa5\xda\x41\xcf\xa3\xfb\x6f\x00\x00\x00\xff\xff\x09\xff\x40\x94\x14\x06\x00\x00") func confLocaleTranslatorsBytes() ([]byte, error) { return bindataRead( @@ -4319,7 +4319,7 @@ func confLocaleTranslators() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/TRANSLATORS", size: 1416, mode: os.FileMode(420), modTime: time.Unix(1450224232, 0)} + info := bindataFileInfo{name: "conf/locale/TRANSLATORS", size: 1556, mode: os.FileMode(420), modTime: time.Unix(1450231021, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 1ac60c26b..eeba71394 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -144,6 +144,9 @@ func RegisterRoutes(m *macaron.Macaron) { m.Combo("/:id").Get(user.GetPublicKey). Delete(user.DeletePublicKey) }) + m.Combo("/emails").Get(user.ListEmails). + Post(bind(api.CreateEmailOption{}), user.AddEmail). + Delete(bind(api.CreateEmailOption{}), user.DeleteEmail) }, ReqToken()) // Repositories diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go new file mode 100644 index 000000000..449560e78 --- /dev/null +++ b/routers/api/v1/user/email.go @@ -0,0 +1,78 @@ +// Copyright 2015 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package user + +import ( + api "github.com/gogits/go-gogs-client" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/middleware" + "github.com/gogits/gogs/modules/setting" + to "github.com/gogits/gogs/routers/api/v1/utils" +) + +func ListEmails(ctx *middleware.Context) { + emails, err := models.GetEmailAddresses(ctx.User.Id) + if err != nil { + ctx.Handle(500, "GetEmailAddresses", err) + return + } + apiEmails := make([]*api.Email, len(emails)) + for i := range emails { + apiEmails[i] = to.ApiEmail(emails[i]) + } + ctx.JSON(200, &apiEmails) +} + +func AddEmail(ctx *middleware.Context, form api.CreateEmailOption) { + if len(form.Emails) == 0 { + ctx.Status(422) + return + } + + emails := make([]*models.EmailAddress, len(form.Emails)) + for i := range form.Emails { + emails[i] = &models.EmailAddress{ + UID: ctx.User.Id, + Email: form.Emails[i], + IsActivated: !setting.Service.RegisterEmailConfirm, + } + } + + if err := models.AddEmailAddresses(emails); err != nil { + if models.IsErrEmailAlreadyUsed(err) { + ctx.APIError(422, "", "Email address has been used: "+err.(models.ErrEmailAlreadyUsed).Email) + } else { + ctx.APIError(500, "AddEmailAddresses", err) + } + return + } + + apiEmails := make([]*api.Email, len(emails)) + for i := range emails { + apiEmails[i] = to.ApiEmail(emails[i]) + } + ctx.JSON(201, &apiEmails) +} + +func DeleteEmail(ctx *middleware.Context, form api.CreateEmailOption) { + if len(form.Emails) == 0 { + ctx.Status(204) + return + } + + emails := make([]*models.EmailAddress, len(form.Emails)) + for i := range form.Emails { + emails[i] = &models.EmailAddress{ + Email: form.Emails[i], + } + } + + if err := models.DeleteEmailAddresses(emails); err != nil { + ctx.APIError(500, "DeleteEmailAddresses", err) + return + } + ctx.Status(204) +} diff --git a/routers/api/v1/utils/convert.go b/routers/api/v1/utils/convert.go index 7ac4edf26..3871c0da1 100644 --- a/routers/api/v1/utils/convert.go +++ b/routers/api/v1/utils/convert.go @@ -26,6 +26,14 @@ func ApiUser(u *models.User) *api.User { } } +func ApiEmail(email *models.EmailAddress) *api.Email { + return &api.Email{ + Email: email.Email, + Verified: email.IsActivated, + Primary: email.IsPrimary, + } +} + // ApiRepository converts repository to API format. func ApiRepository(owner *models.User, repo *models.Repository, permission api.Permission) *api.Repository { cl := repo.CloneLink() diff --git a/routers/user/setting.go b/routers/user/setting.go index b338e112a..56a9cab0a 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -226,7 +226,7 @@ func SettingsEmailPost(ctx *middleware.Context, form auth.AddEmailForm) { e := &models.EmailAddress{ UID: ctx.User.Id, - Email: strings.TrimSpace(form.Email), + Email: form.Email, IsActivated: !setting.Service.RegisterEmailConfirm, } if err := models.AddEmailAddress(e); err != nil { diff --git a/templates/.VERSION b/templates/.VERSION index 5bd084a80..684d3a542 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.8.6.1215 \ No newline at end of file +0.8.7.1215 \ No newline at end of file