[GITEA] notifies admins on new user registration
Sends email with information on the new user (time of creation and time of last sign-in) and a link to manage the new user from the admin panel closes: https://codeberg.org/forgejo/forgejo/issues/480 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/1371 Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net> Co-committed-by: Aravinth Manivannan <realaravinth@batsense.net> (cherry picked from commitc721aa828b) (cherry picked from commit6487efcb9d) Conflicts: modules/notification/base/notifier.go modules/notification/base/null.go modules/notification/notification.go https://codeberg.org/forgejo/forgejo/pulls/1422 (cherry picked from commit7ea66ee1c5) Conflicts: services/notify/notifier.go services/notify/notify.go services/notify/null.go https://codeberg.org/forgejo/forgejo/pulls/1469
This commit is contained in:
		
							parent
							
								
									589e7d346f
								
							
						
					
					
						commit
						7d2d997011
					
				
					 11 changed files with 218 additions and 1 deletions
				
			
		| 
						 | 
					@ -1455,6 +1455,8 @@ LEVEL = Info
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
 | 
					;; Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
 | 
				
			||||||
;DEFAULT_EMAIL_NOTIFICATIONS = enabled
 | 
					;DEFAULT_EMAIL_NOTIFICATIONS = enabled
 | 
				
			||||||
 | 
					;; Send email notifications to all instance admins on new user sign-ups. Options: enabled, true, false
 | 
				
			||||||
 | 
					;NOTIFY_NEW_SIGN_UPS = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
				
			||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ package setting
 | 
				
			||||||
var Admin struct {
 | 
					var Admin struct {
 | 
				
			||||||
	DisableRegularOrgCreation bool
 | 
						DisableRegularOrgCreation bool
 | 
				
			||||||
	DefaultEmailNotification  string
 | 
						DefaultEmailNotification  string
 | 
				
			||||||
 | 
						NotifyNewSignUps          bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func loadAdminFrom(rootCfg ConfigProvider) {
 | 
					func loadAdminFrom(rootCfg ConfigProvider) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -439,6 +439,10 @@ activate_email = Verify your email address
 | 
				
			||||||
activate_email.title = %s, please verify your email address
 | 
					activate_email.title = %s, please verify your email address
 | 
				
			||||||
activate_email.text = Please click the following link to verify your email address within <b>%s</b>:
 | 
					activate_email.text = Please click the following link to verify your email address within <b>%s</b>:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					admin.new_user.subject = New user %s
 | 
				
			||||||
 | 
					admin.new_user.user_info = User Information
 | 
				
			||||||
 | 
					admin.new_user.text = Please <a href="%s">click here</a> to manage the user from the admin panel.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
register_notify = Welcome to Gitea
 | 
					register_notify = Welcome to Gitea
 | 
				
			||||||
register_notify.title = %[1]s, welcome to %[2]s
 | 
					register_notify.title = %[1]s, welcome to %[2]s
 | 
				
			||||||
register_notify.text_1 = this is your registration confirmation email for %s!
 | 
					register_notify.text_1 = this is your registration confirmation email for %s!
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,7 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/services/externalaccount"
 | 
						"code.gitea.io/gitea/services/externalaccount"
 | 
				
			||||||
	"code.gitea.io/gitea/services/forms"
 | 
						"code.gitea.io/gitea/services/forms"
 | 
				
			||||||
	"code.gitea.io/gitea/services/mailer"
 | 
						"code.gitea.io/gitea/services/mailer"
 | 
				
			||||||
 | 
						notify_service "code.gitea.io/gitea/services/notify"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/markbates/goth"
 | 
						"github.com/markbates/goth"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -586,6 +587,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						notify_service.NewUserSignUp(ctx, u)
 | 
				
			||||||
	// update external user information
 | 
						// update external user information
 | 
				
			||||||
	if gothUser != nil {
 | 
						if gothUser != nil {
 | 
				
			||||||
		if err := externalaccount.UpdateExternalUser(u, *gothUser); err != nil {
 | 
							if err := externalaccount.UpdateExternalUser(u, *gothUser); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -609,7 +611,6 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
 | 
				
			||||||
		ctx.Data["Email"] = u.Email
 | 
							ctx.Data["Email"] = u.Email
 | 
				
			||||||
		ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
 | 
							ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
 | 
				
			||||||
		ctx.HTML(http.StatusOK, TplActivate)
 | 
							ctx.HTML(http.StatusOK, TplActivate)
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if setting.CacheService.Enabled {
 | 
							if setting.CacheService.Enabled {
 | 
				
			||||||
			if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
 | 
								if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
 | 
				
			||||||
				log.Error("Set cache(MailResendLimit) fail: %v", err)
 | 
									log.Error("Set cache(MailResendLimit) fail: %v", err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										82
									
								
								services/mailer/mail_admin_new_user.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								services/mailer/mail_admin_new_user.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,82 @@
 | 
				
			||||||
 | 
					// Copyright 2023 The Forgejo Authors. All rights reserved.
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					package mailer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/templates"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/translation"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						tplNewUserMail base.TplName = "admin_new_user"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var sa = SendAsyncs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MailNewUser sends notification emails on new user registrations to all admins
 | 
				
			||||||
 | 
					func MailNewUser(ctx context.Context, u *user_model.User) {
 | 
				
			||||||
 | 
						if !setting.Admin.NotifyNewSignUps {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if setting.MailService == nil {
 | 
				
			||||||
 | 
							// No mail service configured
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						recipients, err := user_model.GetAllUsers(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Error("user_model.GetAllUsers: %v", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						langMap := make(map[string][]string)
 | 
				
			||||||
 | 
						for _, r := range recipients {
 | 
				
			||||||
 | 
							if r.IsAdmin {
 | 
				
			||||||
 | 
								langMap[r.Language] = append(langMap[r.Language], r.Email)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for lang, tos := range langMap {
 | 
				
			||||||
 | 
							mailNewUser(ctx, u, lang, tos)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func mailNewUser(ctx context.Context, u *user_model.User, lang string, tos []string) {
 | 
				
			||||||
 | 
						locale := translation.NewLocale(lang)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						subject := locale.Tr("mail.admin.new_user.subject", u.Name)
 | 
				
			||||||
 | 
						manageUserURL := setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10)
 | 
				
			||||||
 | 
						body := locale.Tr("mail.admin.new_user.text", manageUserURL)
 | 
				
			||||||
 | 
						mailMeta := map[string]any{
 | 
				
			||||||
 | 
							"NewUser":  u,
 | 
				
			||||||
 | 
							"Subject":  subject,
 | 
				
			||||||
 | 
							"Body":     body,
 | 
				
			||||||
 | 
							"Language": locale.Language(),
 | 
				
			||||||
 | 
							"locale":   locale,
 | 
				
			||||||
 | 
							"Str2html": templates.Str2html,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var mailBody bytes.Buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplNewUserMail), mailMeta); err != nil {
 | 
				
			||||||
 | 
							log.Error("ExecuteTemplate [%s]: %v", string(tplNewUserMail)+"/body", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msgs := make([]*Message, 0, len(tos))
 | 
				
			||||||
 | 
						for _, to := range tos {
 | 
				
			||||||
 | 
							msg := NewMessage(to, subject, mailBody.String())
 | 
				
			||||||
 | 
							msg.Info = subject
 | 
				
			||||||
 | 
							msgs = append(msgs, msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sa(msgs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										88
									
								
								services/mailer/mail_admin_new_user_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								services/mailer/mail_admin_new_user_test.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,88 @@
 | 
				
			||||||
 | 
					// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package mailer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getTestUsers() []*user_model.User {
 | 
				
			||||||
 | 
						admin := new(user_model.User)
 | 
				
			||||||
 | 
						admin.Name = "admin"
 | 
				
			||||||
 | 
						admin.IsAdmin = true
 | 
				
			||||||
 | 
						admin.Language = "en_US"
 | 
				
			||||||
 | 
						admin.Email = "admin@forgejo.org"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newUser := new(user_model.User)
 | 
				
			||||||
 | 
						newUser.Name = "new_user"
 | 
				
			||||||
 | 
						newUser.Language = "en_US"
 | 
				
			||||||
 | 
						newUser.IsAdmin = false
 | 
				
			||||||
 | 
						newUser.Email = "new_user@forgejo.org"
 | 
				
			||||||
 | 
						newUser.LastLoginUnix = 1693648327
 | 
				
			||||||
 | 
						newUser.CreatedUnix = 1693648027
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						user_model.CreateUser(db.DefaultContext, admin)
 | 
				
			||||||
 | 
						user_model.CreateUser(db.DefaultContext, newUser)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						users := make([]*user_model.User, 0)
 | 
				
			||||||
 | 
						users = append(users, admin)
 | 
				
			||||||
 | 
						users = append(users, newUser)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return users
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func cleanUpUsers(ctx context.Context, users []*user_model.User) {
 | 
				
			||||||
 | 
						for _, u := range users {
 | 
				
			||||||
 | 
							db.DeleteByID(ctx, u.ID, new(user_model.User))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAdminNotificationMail_test(t *testing.T) {
 | 
				
			||||||
 | 
						mailService := setting.Mailer{
 | 
				
			||||||
 | 
							From:     "test@forgejo.org",
 | 
				
			||||||
 | 
							Protocol: "dummy",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setting.MailService = &mailService
 | 
				
			||||||
 | 
						setting.Domain = "localhost"
 | 
				
			||||||
 | 
						setting.AppSubURL = "http://localhost"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// test with NOTIFY_NEW_SIGNUPS enabled
 | 
				
			||||||
 | 
						setting.Admin.NotifyNewSignUps = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx := context.Background()
 | 
				
			||||||
 | 
						NewContext(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						users := getTestUsers()
 | 
				
			||||||
 | 
						oldSendAsyncs := sa
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							sa = oldSendAsyncs
 | 
				
			||||||
 | 
							cleanUpUsers(ctx, users)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sa = func(msgs []*Message) {
 | 
				
			||||||
 | 
							assert.Equal(t, len(msgs), 1, "Test provides only one admin user, so only one email must be sent")
 | 
				
			||||||
 | 
							assert.Equal(t, msgs[0].To, users[0].Email, "checks if the recipient is the admin of the instance")
 | 
				
			||||||
 | 
							manageUserURL := "/admin/users/" + strconv.FormatInt(users[1].ID, 10)
 | 
				
			||||||
 | 
							assert.True(t, strings.ContainsAny(msgs[0].Body, manageUserURL), "checks if the message contains the link to manage the newly created user from the admin panel")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						MailNewUser(ctx, users[1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// test with NOTIFY_NEW_SIGNUPS disabled; emails shouldn't be sent
 | 
				
			||||||
 | 
						setting.Admin.NotifyNewSignUps = false
 | 
				
			||||||
 | 
						sa = func(msgs []*Message) {
 | 
				
			||||||
 | 
							assert.Equal(t, 1, 0, "this shouldn't execute. MailNewUser must exit early since NOTIFY_NEW_SIGNUPS is disabled")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MailNewUser(ctx, users[1])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -202,3 +202,7 @@ func (m *mailNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *
 | 
				
			||||||
		log.Error("SendRepoTransferNotifyMail: %v", err)
 | 
							log.Error("SendRepoTransferNotifyMail: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *mailNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
 | 
				
			||||||
 | 
						MailNewUser(ctx, newUser)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,6 +59,8 @@ type Notifier interface {
 | 
				
			||||||
	EditWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page, comment string)
 | 
						EditWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page, comment string)
 | 
				
			||||||
	DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page string)
 | 
						DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NewUserSignUp(ctx context.Context, newUser *user_model.User)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	NewRelease(ctx context.Context, rel *repo_model.Release)
 | 
						NewRelease(ctx context.Context, rel *repo_model.Release)
 | 
				
			||||||
	UpdateRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
 | 
						UpdateRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
 | 
				
			||||||
	DeleteRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
 | 
						DeleteRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -347,6 +347,13 @@ func RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, r
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewUserSignUp notifies deletion of a package to notifiers
 | 
				
			||||||
 | 
					func NewUserSignUp(ctx context.Context, newUser *user_model.User) {
 | 
				
			||||||
 | 
						for _, notifier := range notifiers {
 | 
				
			||||||
 | 
							notifier.NewUserSignUp(ctx, newUser)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PackageCreate notifies creation of a package to notifiers
 | 
					// PackageCreate notifies creation of a package to notifiers
 | 
				
			||||||
func PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
 | 
					func PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
 | 
				
			||||||
	for _, notifier := range notifiers {
 | 
						for _, notifier := range notifiers {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -197,6 +197,10 @@ func (*NullNotifier) SyncDeleteRef(ctx context.Context, doer *user_model.User, r
 | 
				
			||||||
func (*NullNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) {
 | 
					func (*NullNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NotifyNewUserSignUp notifies deletion of a package to notifiers
 | 
				
			||||||
 | 
					func (*NullNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PackageCreate places a place holder function
 | 
					// PackageCreate places a place holder function
 | 
				
			||||||
func (*NullNotifier) PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
 | 
					func (*NullNotifier) PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								templates/mail/admin_new_user.tmpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								templates/mail/admin_new_user.tmpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
						<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 | 
				
			||||||
 | 
						<title>{{.Subject}}</title>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<style>
 | 
				
			||||||
 | 
							blockquote { padding-left: 1em; margin: 1em 0; border-left: 1px solid grey; color: #777}
 | 
				
			||||||
 | 
							.footer { font-size:small; color:#666;}
 | 
				
			||||||
 | 
						</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
						<ul>
 | 
				
			||||||
 | 
							<h3>{{.locale.Tr "mail.admin.new_user.user_info"}}</h3>
 | 
				
			||||||
 | 
							<li>{{.locale.Tr "admin.users.created"}}: {{DateTime "full" .NewUser.LastLoginUnix}}</li>
 | 
				
			||||||
 | 
							<li>{{.locale.Tr "admin.users.last_login"}}: {{DateTime "full" .NewUser.CreatedUnix}}</li>
 | 
				
			||||||
 | 
						</ul>
 | 
				
			||||||
 | 
						<p> {{.Body | Str2html}} </p>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue