mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	move templateFuncs to one file, add middleware context.
This commit is contained in:
		
							
								
								
									
										1
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								gogs.go
									
									
									
									
									
								
							| @@ -23,6 +23,7 @@ const go11tag = true | |||||||
| const APP_VER = "0.0.8.0315" | const APP_VER = "0.0.8.0315" | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
|  | 	base.AppVer = APP_VER | ||||||
| 	runtime.GOMAXPROCS(runtime.NumCPU()) | 	runtime.GOMAXPROCS(runtime.NumCPU()) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ import ( | |||||||
| 	"reflect" | 	"reflect" | ||||||
|  |  | ||||||
| 	"github.com/codegangsta/martini" | 	"github.com/codegangsta/martini" | ||||||
| 	"github.com/martini-contrib/render" |  | ||||||
| 	"github.com/martini-contrib/sessions" | 	"github.com/martini-contrib/sessions" | ||||||
|  |  | ||||||
| 	"github.com/gogits/binding" | 	"github.com/gogits/binding" | ||||||
| @@ -62,39 +61,6 @@ func IsSignedIn(session sessions.Session) bool { | |||||||
| 	return SignedInId(session) > 0 | 	return SignedInId(session) > 0 | ||||||
| } | } | ||||||
|  |  | ||||||
| // SignInRequire checks user status from session. |  | ||||||
| // It will assign correspoding values to |  | ||||||
| // template data map if user has signed in. |  | ||||||
| func SignInRequire(redirect bool) martini.Handler { |  | ||||||
| 	return func(r render.Render, data base.TmplData, session sessions.Session) { |  | ||||||
| 		if !IsSignedIn(session) { |  | ||||||
| 			if redirect { |  | ||||||
| 				r.Redirect("/") |  | ||||||
| 			} |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		user := SignedInUser(session) |  | ||||||
| 		if user == nil { |  | ||||||
| 			r.Redirect("/") |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		data["IsSigned"] = true |  | ||||||
| 		data["SignedUser"] = user |  | ||||||
| 		data["SignedUserId"] = user.Id |  | ||||||
| 		data["SignedUserName"] = user.LowerName |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func SignOutRequire() martini.Handler { |  | ||||||
| 	return func(r render.Render, session sessions.Session) { |  | ||||||
| 		if IsSignedIn(session) { |  | ||||||
| 			r.Redirect("/") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type FeedsForm struct { | type FeedsForm struct { | ||||||
| 	UserId int64 `form:"userid" binding:"Required"` | 	UserId int64 `form:"userid" binding:"Required"` | ||||||
| 	Offset int64 `form:"offset"` | 	Offset int64 `form:"offset"` | ||||||
|   | |||||||
| @@ -4,17 +4,9 @@ | |||||||
|  |  | ||||||
| package base | package base | ||||||
|  |  | ||||||
| import ( | import () | ||||||
| 	"github.com/codegangsta/martini" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type ( | type ( | ||||||
| 	// Type TmplData represents data in the templates. | 	// Type TmplData represents data in the templates. | ||||||
| 	TmplData map[string]interface{} | 	TmplData map[string]interface{} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func InitContext() martini.Handler { |  | ||||||
| 	return func(context martini.Context) { |  | ||||||
| 		context.Map(TmplData{}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -15,7 +15,11 @@ import ( | |||||||
| 	"github.com/Unknwon/goconfig" | 	"github.com/Unknwon/goconfig" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var Cfg *goconfig.ConfigFile | var ( | ||||||
|  | 	AppVer  string | ||||||
|  | 	AppName string | ||||||
|  | 	Cfg     *goconfig.ConfigFile | ||||||
|  | ) | ||||||
|  |  | ||||||
| func exeDir() (string, error) { | func exeDir() (string, error) { | ||||||
| 	file, err := exec.LookPath(os.Args[0]) | 	file, err := exec.LookPath(os.Args[0]) | ||||||
| @@ -52,4 +56,6 @@ func init() { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	Cfg.BlockMode = false | 	Cfg.BlockMode = false | ||||||
|  |  | ||||||
|  | 	AppName = Cfg.MustValue("", "APP_NAME") | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								modules/base/template.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								modules/base/template.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | // Copyright 2014 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 base | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"html/template" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func Str2html(raw string) template.HTML { | ||||||
|  | 	return template.HTML(raw) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var TemplateFuncs template.FuncMap = map[string]interface{}{ | ||||||
|  | 	"AppName": func() string { | ||||||
|  | 		return AppName | ||||||
|  | 	}, | ||||||
|  | 	"AppVer": func() string { | ||||||
|  | 		return AppVer | ||||||
|  | 	}, | ||||||
|  | 	"str2html":   Str2html, | ||||||
|  | 	"TimeSince":  TimeSince, | ||||||
|  | 	"Subtract":   Subtract, | ||||||
|  | 	"ActionIcon": ActionIcon, | ||||||
|  | 	"ActionDesc": ActionDesc, | ||||||
|  | 	"DateFormat": DateFormat, | ||||||
|  | } | ||||||
| @@ -8,7 +8,6 @@ import ( | |||||||
| 	"crypto/md5" | 	"crypto/md5" | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"html/template" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
| @@ -30,10 +29,6 @@ const ( | |||||||
| 	Year   = 12 * Month | 	Year   = 12 * Month | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Str2html(raw string) template.HTML { |  | ||||||
| 	return template.HTML(raw) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TimeSince calculates the time interval and generate user-friendly string. | // TimeSince calculates the time interval and generate user-friendly string. | ||||||
| func TimeSince(then time.Time) string { | func TimeSince(then time.Time) string { | ||||||
| 	now := time.Now() | 	now := time.Now() | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								modules/middleware/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								modules/middleware/auth.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | // Copyright 2014 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 middleware | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/codegangsta/martini" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func SignInRequire(redirect bool) martini.Handler { | ||||||
|  | 	return func(ctx *Context) { | ||||||
|  | 		if !ctx.IsSigned { | ||||||
|  | 			if redirect { | ||||||
|  | 				ctx.Render.Redirect("/") | ||||||
|  | 			} | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func SignOutRequire() martini.Handler { | ||||||
|  | 	return func(ctx *Context) { | ||||||
|  | 		if ctx.IsSigned { | ||||||
|  | 			ctx.Render.Redirect("/") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										75
									
								
								modules/middleware/context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								modules/middleware/context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | |||||||
|  | // Copyright 2014 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 middleware | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/codegangsta/martini" | ||||||
|  | 	"github.com/martini-contrib/render" | ||||||
|  | 	"github.com/martini-contrib/sessions" | ||||||
|  |  | ||||||
|  | 	"github.com/gogits/gogs/models" | ||||||
|  | 	"github.com/gogits/gogs/modules/auth" | ||||||
|  | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | 	"github.com/gogits/gogs/modules/log" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Context struct { | ||||||
|  | 	c        martini.Context | ||||||
|  | 	p        martini.Params | ||||||
|  | 	Req      *http.Request | ||||||
|  | 	Res      http.ResponseWriter | ||||||
|  | 	Session  sessions.Session | ||||||
|  | 	Data     base.TmplData | ||||||
|  | 	Render   render.Render | ||||||
|  | 	User     *models.User | ||||||
|  | 	IsSigned bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (ctx *Context) Query(name string) string { | ||||||
|  | 	ctx.Req.ParseForm() | ||||||
|  | 	return ctx.Req.Form.Get(name) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // func (ctx *Context) Param(name string) string { | ||||||
|  | // 	return ctx.p[name] | ||||||
|  | // } | ||||||
|  |  | ||||||
|  | func (ctx *Context) Log(status int, title string, err error) { | ||||||
|  | 	log.Handle(status, title, ctx.Data, ctx.Render, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func InitContext() martini.Handler { | ||||||
|  | 	return func(res http.ResponseWriter, r *http.Request, c martini.Context, | ||||||
|  | 		session sessions.Session, rd render.Render) { | ||||||
|  |  | ||||||
|  | 		data := base.TmplData{} | ||||||
|  |  | ||||||
|  | 		ctx := &Context{ | ||||||
|  | 			c: c, | ||||||
|  | 			// p:      p, | ||||||
|  | 			Req:    r, | ||||||
|  | 			Res:    res, | ||||||
|  | 			Data:   data, | ||||||
|  | 			Render: rd, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Get user from session if logined. | ||||||
|  | 		user := auth.SignedInUser(session) | ||||||
|  | 		ctx.User = user | ||||||
|  | 		ctx.IsSigned = ctx != nil | ||||||
|  |  | ||||||
|  | 		data["IsSigned"] = true | ||||||
|  | 		data["SignedUser"] = user | ||||||
|  | 		data["SignedUserId"] = user.Id | ||||||
|  | 		data["SignedUserName"] = user.LowerName | ||||||
|  |  | ||||||
|  | 		c.Map(ctx) | ||||||
|  | 		c.Map(data) | ||||||
|  |  | ||||||
|  | 		c.Next() | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -15,6 +15,7 @@ import ( | |||||||
| 	"github.com/gogits/gogs/modules/auth" | 	"github.com/gogits/gogs/modules/auth" | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/modules/base" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
|  | 	"github.com/gogits/gogs/modules/middleware" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Dashboard(r render.Render, data base.TmplData, session sessions.Session) { | func Dashboard(r render.Render, data base.TmplData, session sessions.Session) { | ||||||
| @@ -29,35 +30,34 @@ func Dashboard(r render.Render, data base.TmplData, session sessions.Session) { | |||||||
| 	r.HTML(200, "user/dashboard", data) | 	r.HTML(200, "user/dashboard", data) | ||||||
| } | } | ||||||
|  |  | ||||||
| func Profile(params martini.Params, r render.Render, req *http.Request, data base.TmplData, session sessions.Session) { | func Profile(ctx *middleware.Context, params martini.Params) { | ||||||
| 	data["Title"] = "Profile" | 	ctx.Data["Title"] = "Profile" | ||||||
|  |  | ||||||
| 	// TODO: Need to check view self or others. | 	// TODO: Need to check view self or others. | ||||||
| 	user, err := models.GetUserByName(params["username"]) | 	user, err := models.GetUserByName(params["username"]) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Handle(200, "user.Profile", data, r, err) | 		ctx.Log(200, "user.Profile", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	data["Owner"] = user | 	ctx.Data["Owner"] = user | ||||||
|  |  | ||||||
| 	req.ParseForm() | 	tab := ctx.Query("tab") | ||||||
| 	tab := req.Form.Get("tab") | 	ctx.Data["TabName"] = tab | ||||||
| 	data["TabName"] = tab |  | ||||||
|  |  | ||||||
| 	switch tab { | 	switch tab { | ||||||
| 	case "activity": | 	case "activity": | ||||||
| 		feeds, err := models.GetFeeds(user.Id, 0, true) | 		feeds, err := models.GetFeeds(user.Id, 0, true) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Handle(200, "user.Profile", data, r, err) | 			ctx.Log(200, "user.Profile", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		data["Feeds"] = feeds | 		ctx.Data["Feeds"] = feeds | ||||||
| 	default: | 	default: | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	r.HTML(200, "user/profile", data) | 	ctx.Render.HTML(200, "user/profile", ctx.Data) | ||||||
| } | } | ||||||
|  |  | ||||||
| func SignIn(form auth.LogInForm, data base.TmplData, req *http.Request, r render.Render, session sessions.Session) { | func SignIn(form auth.LogInForm, data base.TmplData, req *http.Request, r render.Render, session sessions.Session) { | ||||||
|   | |||||||
							
								
								
									
										59
									
								
								web.go
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								web.go
									
									
									
									
									
								
							| @@ -19,6 +19,7 @@ import ( | |||||||
| 	"github.com/gogits/gogs/modules/auth" | 	"github.com/gogits/gogs/modules/auth" | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/modules/base" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
|  | 	"github.com/gogits/gogs/modules/middleware" | ||||||
| 	"github.com/gogits/gogs/routers" | 	"github.com/gogits/gogs/routers" | ||||||
| 	"github.com/gogits/gogs/routers/repo" | 	"github.com/gogits/gogs/routers/repo" | ||||||
| 	"github.com/gogits/gogs/routers/user" | 	"github.com/gogits/gogs/routers/user" | ||||||
| @@ -33,60 +34,46 @@ gogs web`, | |||||||
| 	Flags:  []cli.Flag{}, | 	Flags:  []cli.Flag{}, | ||||||
| } | } | ||||||
|  |  | ||||||
| var AppHelpers template.FuncMap = map[string]interface{}{ |  | ||||||
| 	"AppName": func() string { |  | ||||||
| 		return base.Cfg.MustValue("", "APP_NAME") |  | ||||||
| 	}, |  | ||||||
| 	"AppVer": func() string { |  | ||||||
| 		return APP_VER |  | ||||||
| 	}, |  | ||||||
| 	"str2html":   base.Str2html, |  | ||||||
| 	"TimeSince":  base.TimeSince, |  | ||||||
| 	"Subtract":   base.Subtract, |  | ||||||
| 	"ActionIcon": base.ActionIcon, |  | ||||||
| 	"ActionDesc": base.ActionDesc, |  | ||||||
| 	"DateFormat": base.DateFormat, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func runWeb(*cli.Context) { | func runWeb(*cli.Context) { | ||||||
| 	log.Info("%s %s", base.Cfg.MustValue("", "APP_NAME"), APP_VER) | 	log.Info("%s %s", base.AppName, base.AppVer) | ||||||
|  |  | ||||||
| 	m := martini.Classic() | 	m := martini.Classic() | ||||||
|  |  | ||||||
| 	// Middlewares. | 	// Middlewares. | ||||||
| 	m.Use(render.Renderer(render.Options{Funcs: []template.FuncMap{AppHelpers}})) | 	m.Use(render.Renderer(render.Options{Funcs: []template.FuncMap{base.TemplateFuncs}})) | ||||||
| 	m.Use(base.InitContext()) |  | ||||||
|  |  | ||||||
| 	// TODO: should use other store because cookie store is not secure. | 	// TODO: should use other store because cookie store is not secure. | ||||||
| 	store := sessions.NewCookieStore([]byte("secret123")) | 	store := sessions.NewCookieStore([]byte("secret123")) | ||||||
| 	m.Use(sessions.Sessions("my_session", store)) | 	m.Use(sessions.Sessions("my_session", store)) | ||||||
|  |  | ||||||
|  | 	m.Use(middleware.InitContext()) | ||||||
|  |  | ||||||
| 	// Routers. | 	// Routers. | ||||||
| 	m.Get("/", auth.SignInRequire(false), routers.Home) | 	m.Get("/", middleware.SignInRequire(false), routers.Home) | ||||||
| 	m.Any("/user/login", auth.SignOutRequire(), binding.BindIgnErr(auth.LogInForm{}), user.SignIn) | 	m.Any("/user/login", middleware.SignOutRequire(), binding.BindIgnErr(auth.LogInForm{}), user.SignIn) | ||||||
| 	m.Any("/user/logout", auth.SignInRequire(true), user.SignOut) | 	m.Any("/user/logout", middleware.SignInRequire(true), user.SignOut) | ||||||
| 	m.Any("/user/sign_up", auth.SignOutRequire(), binding.BindIgnErr(auth.RegisterForm{}), user.SignUp) | 	m.Any("/user/sign_up", middleware.SignOutRequire(), binding.BindIgnErr(auth.RegisterForm{}), user.SignUp) | ||||||
| 	m.Any("/user/delete", auth.SignInRequire(true), user.Delete) | 	m.Any("/user/delete", middleware.SignInRequire(true), user.Delete) | ||||||
| 	m.Get("/user/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | 	m.Get("/user/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | ||||||
|  |  | ||||||
| 	m.Any("/user/setting", auth.SignInRequire(true), binding.BindIgnErr(auth.UpdateProfileForm{}), user.Setting) | 	m.Any("/user/setting", middleware.SignInRequire(true), binding.BindIgnErr(auth.UpdateProfileForm{}), user.Setting) | ||||||
| 	m.Any("/user/setting/password", auth.SignInRequire(true), binding.BindIgnErr(auth.UpdatePasswdForm{}), user.SettingPassword) | 	m.Any("/user/setting/password", middleware.SignInRequire(true), binding.BindIgnErr(auth.UpdatePasswdForm{}), user.SettingPassword) | ||||||
| 	m.Any("/user/setting/ssh", auth.SignInRequire(true), binding.BindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) | 	m.Any("/user/setting/ssh", middleware.SignInRequire(true), binding.BindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) | ||||||
| 	m.Any("/user/setting/notification", auth.SignInRequire(true), user.SettingNotification) | 	m.Any("/user/setting/notification", middleware.SignInRequire(true), user.SettingNotification) | ||||||
| 	m.Any("/user/setting/security", auth.SignInRequire(true), user.SettingSecurity) | 	m.Any("/user/setting/security", middleware.SignInRequire(true), user.SettingSecurity) | ||||||
|  |  | ||||||
| 	m.Get("/user/:username", auth.SignInRequire(false), user.Profile) | 	m.Get("/user/:username", middleware.SignInRequire(false), user.Profile) | ||||||
|  |  | ||||||
| 	m.Any("/repo/create", auth.SignInRequire(true), binding.BindIgnErr(auth.CreateRepoForm{}), repo.Create) | 	m.Any("/repo/create", middleware.SignInRequire(true), binding.BindIgnErr(auth.CreateRepoForm{}), repo.Create) | ||||||
| 	m.Any("/repo/delete", auth.SignInRequire(true), binding.Bind(auth.DeleteRepoForm{}), repo.Delete) | 	m.Any("/repo/delete", middleware.SignInRequire(true), binding.Bind(auth.DeleteRepoForm{}), repo.Delete) | ||||||
| 	m.Any("/repo/list", auth.SignInRequire(false), repo.List) | 	m.Any("/repo/list", middleware.SignInRequire(false), repo.List) | ||||||
|  |  | ||||||
| 	m.Get("/:username/:reponame/settings", auth.SignInRequire(false), auth.RepoAssignment(true), repo.Setting) | 	m.Get("/:username/:reponame/settings", middleware.SignInRequire(false), auth.RepoAssignment(true), repo.Setting) | ||||||
| 	m.Get("/:username/:reponame/tree/:branchname/**", | 	m.Get("/:username/:reponame/tree/:branchname/**", | ||||||
| 		auth.SignInRequire(false), auth.RepoAssignment(true), repo.Single) | 		middleware.SignInRequire(false), auth.RepoAssignment(true), repo.Single) | ||||||
| 	m.Get("/:username/:reponame/tree/:branchname", | 	m.Get("/:username/:reponame/tree/:branchname", | ||||||
| 		auth.SignInRequire(false), auth.RepoAssignment(true), repo.Single) | 		middleware.SignInRequire(false), auth.RepoAssignment(true), repo.Single) | ||||||
| 	m.Get("/:username/:reponame", auth.SignInRequire(false), auth.RepoAssignment(true), repo.Single) | 	m.Get("/:username/:reponame", middleware.SignInRequire(false), auth.RepoAssignment(true), repo.Single) | ||||||
|  |  | ||||||
| 	//m.Get("/:username/:reponame", repo.Repo) | 	//m.Get("/:username/:reponame", repo.Repo) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 slene
					slene