Ver código fonte

#1193 Make organization emails non-mandatory

Unknwon 9 anos atrás
pai
commit
80701d45bb

+ 0 - 1
conf/locale/locale_en-US.ini

@@ -630,7 +630,6 @@ release.tag_name_already_exist = Release with this tag name has already existed.
 [org]
 org_name_holder = Organization Name
 org_name_helper = Great organization names are short and memorable.
-org_email_helper = Organization's E-mail receives all notifications and confirmations.
 create_org = Create Organization
 repo_updated = Updated
 people = People

+ 14 - 37
models/org.go

@@ -9,8 +9,6 @@ import (
 	"fmt"
 	"os"
 	"strings"
-
-	"github.com/gogits/gogs/modules/base"
 )
 
 var (
@@ -93,17 +91,6 @@ func (org *User) RemoveOrgRepo(repoID int64) error {
 	return org.removeOrgRepo(x, repoID)
 }
 
-// IsOrgEmailUsed returns true if the e-mail has been used in organization account.
-func IsOrgEmailUsed(email string) (bool, error) {
-	if len(email) == 0 {
-		return false, nil
-	}
-	return x.Get(&User{
-		Email: email,
-		Type:  ORGANIZATION,
-	})
-}
-
 // CreateOrganization creates record of a new organization.
 func CreateOrganization(org, owner *User) (err error) {
 	if err = IsUsableName(org.Name); err != nil {
@@ -117,18 +104,9 @@ func CreateOrganization(org, owner *User) (err error) {
 		return ErrUserAlreadyExist{org.Name}
 	}
 
-	isExist, err = IsOrgEmailUsed(org.Email)
-	if err != nil {
-		return err
-	} else if isExist {
-		return ErrEmailAlreadyUsed{org.Email}
-	}
-
 	org.LowerName = strings.ToLower(org.Name)
 	org.FullName = org.Name
-	org.Avatar = base.EncodeMd5(org.Email)
-	org.AvatarEmail = org.Email
-	// No password for organization.
+	org.UseCustomAvatar = true
 	org.NumTeams = 1
 	org.NumMembers = 1
 
@@ -141,6 +119,17 @@ func CreateOrganization(org, owner *User) (err error) {
 	if _, err = sess.Insert(org); err != nil {
 		return fmt.Errorf("insert organization: %v", err)
 	}
+	org.GenerateRandomAvatar()
+
+	// Add initial creator to organization and owner team.
+	if _, err = sess.Insert(&OrgUser{
+		Uid:      owner.Id,
+		OrgID:    org.Id,
+		IsOwner:  true,
+		NumTeams: 1,
+	}); err != nil {
+		return fmt.Errorf("insert org-user relation: %v", err)
+	}
 
 	// Create default owner team.
 	t := &Team{
@@ -154,23 +143,11 @@ func CreateOrganization(org, owner *User) (err error) {
 		return fmt.Errorf("insert owner team: %v", err)
 	}
 
-	// Add initial creator to organization and owner team.
-	ou := &OrgUser{
-		Uid:      owner.Id,
-		OrgID:    org.Id,
-		IsOwner:  true,
-		NumTeams: 1,
-	}
-	if _, err = sess.Insert(ou); err != nil {
-		return fmt.Errorf("insert org-user relation: %v", err)
-	}
-
-	tu := &TeamUser{
+	if _, err = sess.Insert(&TeamUser{
 		Uid:    owner.Id,
 		OrgID:  org.Id,
 		TeamID: t.ID,
-	}
-	if _, err = sess.Insert(tu); err != nil {
+	}); err != nil {
 		return fmt.Errorf("insert team-user relation: %v", err)
 	}
 

+ 54 - 42
models/user.go

@@ -55,12 +55,12 @@ type User struct {
 	Name      string `xorm:"UNIQUE NOT NULL"`
 	FullName  string
 	// Email is the primary email address (to be used for communication).
-	Email       string `xorm:"UNIQUE(s) NOT NULL"`
+	Email       string `xorm:"NOT NULL"`
 	Passwd      string `xorm:"NOT NULL"`
 	LoginType   LoginType
 	LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
 	LoginName   string
-	Type        UserType      `xorm:"UNIQUE(s)"`
+	Type        UserType
 	Orgs        []*User       `xorm:"-"`
 	Repos       []*Repository `xorm:"-"`
 	Location    string
@@ -132,42 +132,56 @@ func (u *User) HomeLink() string {
 	return setting.AppSubUrl + "/" + u.Name
 }
 
+// CustomAvatarPath returns user custom avatar file path.
+func (u *User) CustomAvatarPath() string {
+	return filepath.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
+}
+
+// GenerateRandomAvatar generates a random avatar for user.
+func (u *User) GenerateRandomAvatar() error {
+	seed := u.Email
+	if len(seed) == 0 {
+		seed = u.Name
+	}
+
+	img, err := avatar.RandomImage([]byte(seed))
+	if err != nil {
+		return fmt.Errorf("RandomImage: %v", err)
+	}
+	if err = os.MkdirAll(path.Dir(u.CustomAvatarPath()), os.ModePerm); err != nil {
+		return fmt.Errorf("MkdirAll: %v", err)
+	}
+	fw, err := os.Create(u.CustomAvatarPath())
+	if err != nil {
+		return fmt.Errorf("Create: %v", err)
+	}
+	defer fw.Close()
+
+	if err = jpeg.Encode(fw, img, nil); err != nil {
+		return fmt.Errorf("Encode: %v", err)
+	}
+
+	log.Info("New random avatar created: %d", u.Id)
+	return nil
+}
+
 func (u *User) RelAvatarLink() string {
 	defaultImgUrl := "/img/avatar_default.jpg"
 	if u.Id == -1 {
 		return defaultImgUrl
 	}
 
-	imgPath := path.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
 	switch {
 	case u.UseCustomAvatar:
-		if !com.IsExist(imgPath) {
+		if !com.IsExist(u.CustomAvatarPath()) {
 			return defaultImgUrl
 		}
 		return "/avatars/" + com.ToStr(u.Id)
 	case setting.DisableGravatar, setting.OfflineMode:
-		if !com.IsExist(imgPath) {
-			img, err := avatar.RandomImage([]byte(u.Email))
-			if err != nil {
-				log.Error(3, "RandomImage: %v", err)
-				return defaultImgUrl
+		if !com.IsExist(u.CustomAvatarPath()) {
+			if err := u.GenerateRandomAvatar(); err != nil {
+				log.Error(3, "GenerateRandomAvatar: %v", err)
 			}
-			if err = os.MkdirAll(path.Dir(imgPath), os.ModePerm); err != nil {
-				log.Error(3, "MkdirAll: %v", err)
-				return defaultImgUrl
-			}
-			fw, err := os.Create(imgPath)
-			if err != nil {
-				log.Error(3, "Create: %v", err)
-				return defaultImgUrl
-			}
-			defer fw.Close()
-
-			if err = jpeg.Encode(fw, img, nil); err != nil {
-				log.Error(3, "Encode: %v", err)
-				return defaultImgUrl
-			}
-			log.Info("New random avatar created: %d", u.Id)
 		}
 
 		return "/avatars/" + com.ToStr(u.Id)
@@ -208,11 +222,6 @@ func (u *User) ValidatePassword(passwd string) bool {
 	return u.Passwd == newUser.Passwd
 }
 
-// CustomAvatarPath returns user custom avatar file path.
-func (u *User) CustomAvatarPath() string {
-	return filepath.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
-}
-
 // UploadAvatar saves custom avatar for user.
 // FIXME: split uploads to different subdirs in case we have massive users.
 func (u *User) UploadAvatar(data []byte) error {
@@ -494,12 +503,20 @@ func ChangeUserName(u *User, newUserName string) (err error) {
 }
 
 func updateUser(e Engine, u *User) error {
-	u.Email = strings.ToLower(u.Email)
-	has, err := e.Where("id!=?", u.Id).And("type=?", u.Type).And("email=?", u.Email).Get(new(User))
-	if err != nil {
-		return err
-	} else if has {
-		return ErrEmailAlreadyUsed{u.Email}
+	// Organization does not need e-mail.
+	if !u.IsOrganization() {
+		u.Email = strings.ToLower(u.Email)
+		has, err := e.Where("id!=?", u.Id).And("type=?", u.Type).And("email=?", u.Email).Get(new(User))
+		if err != nil {
+			return err
+		} else if has {
+			return ErrEmailAlreadyUsed{u.Email}
+		}
+
+		if len(u.AvatarEmail) == 0 {
+			u.AvatarEmail = u.Email
+		}
+		u.Avatar = avatar.HashEmail(u.AvatarEmail)
 	}
 
 	u.LowerName = strings.ToLower(u.Name)
@@ -514,13 +531,8 @@ func updateUser(e Engine, u *User) error {
 		u.Description = u.Description[:255]
 	}
 
-	if u.AvatarEmail == "" {
-		u.AvatarEmail = u.Email
-	}
-	u.Avatar = avatar.HashEmail(u.AvatarEmail)
-
 	u.FullName = base.Sanitizer.Sanitize(u.FullName)
-	_, err = e.Id(u.Id).AllCols().Update(u)
+	_, err := e.Id(u.Id).AllCols().Update(u)
 	return err
 }
 

+ 1 - 4
modules/auth/org.go

@@ -17,8 +17,7 @@ import (
 //         \/     /_____/     \/     \/         \/     \/                    \/
 
 type CreateOrgForm struct {
-	OrgName string `form:"org_name" binding:"Required;AlphaDashDot;MaxSize(30)"`
-	Email   string `form:"email" binding:"Required;Email;MaxSize(50)"`
+	OrgName string `binding:"Required;AlphaDashDot;MaxSize(30)"`
 }
 
 func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
@@ -28,11 +27,9 @@ func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs binding.Errors) bind
 type UpdateOrgSettingForm struct {
 	OrgUserName string `form:"uname" binding:"Required;AlphaDashDot;MaxSize(30)" locale:"org.org_name_holder"`
 	OrgFullName string `form:"fullname" binding:"MaxSize(100)"`
-	Email       string `form:"email" binding:"Required;Email;MaxSize(50)"`
 	Description string `form:"desc" binding:"MaxSize(255)"`
 	Website     string `form:"website" binding:"Url;MaxSize(100)"`
 	Location    string `form:"location" binding:"MaxSize(50)"`
-	Avatar      string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
 }
 
 func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
public/css/gogs.min.css


+ 31 - 25
public/less/_form.less

@@ -17,28 +17,41 @@
 	}
 }
 
+@create-page-form-input-padding: 250px !important;
+#create-page-form {
+	form {
+		margin: auto;
+		width: 800px!important;
+		.ui.message {
+			text-align: center;
+		}
+		.header {
+			padding-left: @create-page-form-input-padding+30px;
+		}
+		.inline.field > label {
+			text-align: right;
+			width: @create-page-form-input-padding;
+			word-wrap: break-word;
+		}
+		.help {
+			margin-left: @create-page-form-input-padding+15px;
+		}
+		.optional .title {
+			margin-left: @create-page-form-input-padding;
+		}
+		input,
+		textarea {
+			width: 50%!important;
+		}
+	}
+}
+
 .repository {
-	@input-padding: 250px !important;
 	&.new.repo,
 	&.new.migrate,
 	&.new.fork {
+		#create-page-form;
 		form {
-			margin: auto;
-			width: 800px!important;
-			.ui.message {
-				text-align: center;
-			}
-			.header {
-				padding-left: @input-padding+30px;
-			}
-			.inline.field > label {
-				text-align: right;
-				width: @input-padding;
-				word-wrap: break-word;
-			}
-			.help {
-				margin-left: @input-padding+15px;
-			}
 			.dropdown {
 				.dropdown.icon {
 					margin-top: -7px!important;
@@ -50,13 +63,6 @@
 					}
 				}
 			}
-			.optional .title {
-				margin-left: @input-padding;
-			}
-			input,
-			textarea {
-				width: 50%!important;
-			}
 		}
 	}
 
@@ -66,7 +72,7 @@
 				width: 50%!important;
 			}
 			#auto-init {
-				margin-left: @input-padding+15px;
+				margin-left: @create-page-form-input-padding+15px;
 			}
 		}
 	}

+ 4 - 0
public/less/_organization.less

@@ -14,4 +14,8 @@
 			}
 		}
 	}
+
+	&.new.org {
+		#create-page-form;
+	}
 }

+ 2 - 9
routers/org/org.go

@@ -59,25 +59,18 @@ func CreatePost(ctx *middleware.Context, form auth.CreateOrgForm) {
 
 	org := &models.User{
 		Name:     form.OrgName,
-		Email:    form.Email,
 		IsActive: true,
 		Type:     models.ORGANIZATION,
 	}
 
-	var err error
-	if err = models.CreateOrganization(org, ctx.User); err != nil {
+	if err := models.CreateOrganization(org, ctx.User); err != nil {
+		ctx.Data["Err_OrgName"] = true
 		switch {
 		case models.IsErrUserAlreadyExist(err):
-			ctx.Data["Err_OrgName"] = true
 			ctx.RenderWithErr(ctx.Tr("form.org_name_been_taken"), CREATE, &form)
-		case models.IsErrEmailAlreadyUsed(err):
-			ctx.Data["Err_Email"] = true
-			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), CREATE, &form)
 		case models.IsErrNameReserved(err):
-			ctx.Data["Err_OrgName"] = true
 			ctx.RenderWithErr(ctx.Tr("org.form.name_reserved", err.(models.ErrNameReserved).Name), CREATE, &form)
 		case models.IsErrNamePatternNotAllowed(err):
-			ctx.Data["Err_OrgName"] = true
 			ctx.RenderWithErr(ctx.Tr("org.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), CREATE, &form)
 		default:
 			ctx.Handle(500, "CreateOrganization", err)

+ 1 - 9
routers/org/setting.go

@@ -61,19 +61,11 @@ func SettingsPost(ctx *middleware.Context, form auth.UpdateOrgSettingForm) {
 	}
 
 	org.FullName = form.OrgFullName
-	org.Email = form.Email
 	org.Description = form.Description
 	org.Website = form.Website
 	org.Location = form.Location
-	org.Avatar = base.EncodeMd5(form.Avatar)
-	org.AvatarEmail = form.Avatar
 	if err := models.UpdateUser(org); err != nil {
-		if models.IsErrEmailAlreadyUsed(err) {
-			ctx.Data["Err_Email"] = true
-			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), SETTINGS_OPTIONS, &form)
-		} else {
-			ctx.Handle(500, "UpdateUser", err)
-		}
+		ctx.Handle(500, "UpdateUser", err)
 		return
 	}
 	log.Trace("Organization setting updated: %s", org.Name)

+ 27 - 28
templates/org/create.tmpl

@@ -1,31 +1,30 @@
-{{template "ng/base/head" .}}
-{{template "ng/base/header" .}}
-<div id="sign-wrapper">
-    <form class="form-align form panel sign-panel sign-form container panel-radius" id="sign-up-form" action="{{AppSubUrl}}/org/create" method="post">
+{{template "base/head" .}}
+<div class="organization new org">
+  <div class="ui middle very relaxed page grid">
+    <div class="column">
+      <form class="ui form" action="{{.Link}}" method="post">
         {{.CsrfTokenHtml}}
-        <div class="panel-header">
-            <h2>{{.i18n.Tr "new_org"}}</h2>
+        <h3 class="ui top attached header">
+          {{.i18n.Tr "new_org"}}
+        </h3>
+        <div class="ui attached segment">
+          {{template "base/alert" .}}
+          <div class="inline required field {{if .Err_OrgName}}error{{end}}">
+            <label for="org_name">{{.i18n.Tr "org.org_name_holder"}}</label>
+            <input id="org_name" name="org_name" value="{{.org_name}}" autofocus required>
+            <span class="help">{{.i18n.Tr "org.org_name_helper"}}</span>
+          </div>
+
+          <div class="inline field">
+            <label></label>
+            <button class="ui green button">
+              {{.i18n.Tr "org.create_org"}}
+            </button>
+            <a class="ui button" href="{{AppSubUrl}}/">{{.i18n.Tr "cancel"}}</a>
+          </div>
         </div>
-        <div class="panel-content">
-            {{template "ng/base/alert" .}}
-            <div class="field">
-                <label class="req" for="org_name">{{.i18n.Tr "org.org_name_holder"}}</label>
-                <input class="ipt ipt-large ipt-radius {{if .Err_OrgName}}ipt-error{{end}}" id="org_name" name="org_name" type="text" value="{{.org_name}}" required/>
-                <label></label>
-                <span class="help">{{.i18n.Tr "org.org_name_helper"}}</span>
-            </div>
-            <div class="field">
-                <label class="req" for="email">{{.i18n.Tr "email"}}</label>
-                <input class="ipt ipt-large ipt-radius {{if .Err_Email}}ipt-error{{end}}" id="email" name="email" type="email" value="{{.email}}" required/>
-                <label></label>
-                <span class="help">{{.i18n.Tr "org.org_email_helper"}}</span>
-            </div>
-            <div class="field">
-                <span class="form-label"></span>
-                <button class="btn btn-large btn-blue btn-radius">{{.i18n.Tr "org.create_org"}}</button>
-                <a class="btn btn-small btn-gray btn-radius" id="repo-create-cancel" href="{{AppSubUrl}}/"><strong>{{.i18n.Tr "cancel"}}</strong></a>
-            </div>
-        </div>
-    </form>
+      </form>
+    </div>
+  </div>
 </div>
-{{template "ng/base/footer" .}}
+{{template "base/footer" .}}

+ 0 - 8
templates/org/settings/options.tmpl

@@ -30,10 +30,6 @@
                 <label for="full-name">{{.i18n.Tr "org.settings.full_name"}}</label>
                 <input class="ipt ipt-large ipt-radius {{if .Err_FullName}}ipt-error{{end}}" id="full-name" name="fullname" value="{{.Org.FullName}}" />
               </div>
-              <div class="field">
-                <label class="req" for="email">{{.i18n.Tr "email"}}</label>
-                <input class="ipt ipt-large ipt-radius {{if .Err_Email}}ipt-error{{end}}" id="email" name="email" type="email" value="{{.Org.Email}}" required />
-              </div>
               <div class="field clear">
                 <label class="left" for="desc">{{.i18n.Tr "org.org_desc"}}</label>
                 <textarea class="ipt ipt-large ipt-radius {{if .Err_Description}}ipt-error{{end}}" id="desc" name="desc">{{.Org.Description}}</textarea>
@@ -46,10 +42,6 @@
                 <label for="location">{{.i18n.Tr "org.settings.location"}}</label>
                 <input class="ipt ipt-large ipt-radius {{if .Err_Location}}ipt-error{{end}}" id="location" name="location" type="text" value="{{.Org.Location}}" />
               </div>
-              <div class="field {{if DisableGravatar}}hide{{end}}">
-                <label class="req" for="gravatar-email">Gravatar {{.i18n.Tr "email"}}</label>
-                <input class="ipt ipt-large ipt-radius {{if .Err_Avatar}}ipt-error{{end}}" id="gravatar-email" name="avatar" type="text" value="{{.Org.AvatarEmail}}" />
-              </div>
               <div class="field">
                 <span class="form-label"></span>
                 <button class="btn btn-green btn-large btn-radius" id="change-orgname-btn" href="#change-orgname-modal">{{.i18n.Tr "org.settings.update_settings"}}</button>

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff