| @@ -424,6 +424,15 @@ | |||
| "strictMath": 0, | |||
| "strictUnits": 0 | |||
| }, | |||
| "\/public\/ng\/css\/font-awesome.min.css": { | |||
| "fileType": 16, | |||
| "ignore": 0, | |||
| "ignoreWasSetByUser": 0, | |||
| "inputAbbreviatedPath": "\/public\/ng\/css\/font-awesome.min.css", | |||
| "outputAbbreviatedPath": "No Output Path", | |||
| "outputPathIsOutsideProject": 0, | |||
| "outputPathIsSetByUser": 0 | |||
| }, | |||
| "\/public\/ng\/css\/gogs.css": { | |||
| "fileType": 16, | |||
| "ignore": 1, | |||
| @@ -5,6 +5,7 @@ | |||
| package models | |||
| import ( | |||
| "bytes" | |||
| "errors" | |||
| "fmt" | |||
| "html/template" | |||
| @@ -403,7 +404,12 @@ func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) er | |||
| // MigrateRepository migrates a existing repository from other project hosting. | |||
| func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) { | |||
| repo, err := CreateRepository(u, name, desc, "", "", private, mirror, false) | |||
| repo, err := CreateRepository(u, CreateRepoOptions{ | |||
| Name: name, | |||
| Description: desc, | |||
| IsPrivate: private, | |||
| IsMirror: mirror, | |||
| }) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -488,8 +494,89 @@ func createUpdateHook(repoPath string) error { | |||
| []byte(fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+appPath+"\"", setting.CustomConf)), 0777) | |||
| } | |||
| type CreateRepoOptions struct { | |||
| Name string | |||
| Description string | |||
| Gitignores string | |||
| License string | |||
| Readme string | |||
| IsPrivate bool | |||
| IsMirror bool | |||
| AutoInit bool | |||
| } | |||
| func getRepoInitFile(tp, name string) ([]byte, error) { | |||
| relPath := path.Join("conf", tp, name) | |||
| // Use custom file when available. | |||
| customPath := path.Join(setting.CustomPath, relPath) | |||
| if com.IsFile(customPath) { | |||
| return ioutil.ReadFile(customPath) | |||
| } | |||
| return bindata.Asset(relPath) | |||
| } | |||
| func prepareRepoCommit(repo *Repository, tmpDir, repoPath string, opts CreateRepoOptions) error { | |||
| // Clone to temprory path and do the init commit. | |||
| _, stderr, err := process.Exec( | |||
| fmt.Sprintf("initRepository(git clone): %s", repoPath), "git", "clone", repoPath, tmpDir) | |||
| if err != nil { | |||
| return fmt.Errorf("git clone: %v - %s", err, stderr) | |||
| } | |||
| // README | |||
| data, err := getRepoInitFile("readme", opts.Readme) | |||
| if err != nil { | |||
| return fmt.Errorf("getRepoInitFile[%s]: %v", opts.Readme, err) | |||
| } | |||
| match := map[string]string{ | |||
| "Name": repo.Name, | |||
| "Description": repo.Description, | |||
| } | |||
| if err = ioutil.WriteFile(filepath.Join(tmpDir, "README.md"), | |||
| []byte(com.Expand(string(data), match)), 0644); err != nil { | |||
| return fmt.Errorf("write README.md: %v", err) | |||
| } | |||
| // .gitignore | |||
| if len(opts.Gitignores) > 0 { | |||
| var buf bytes.Buffer | |||
| names := strings.Split(opts.Gitignores, ",") | |||
| for _, name := range names { | |||
| data, err = getRepoInitFile("gitignore", name) | |||
| if err != nil { | |||
| return fmt.Errorf("getRepoInitFile[%s]: %v", name, err) | |||
| } | |||
| buf.WriteString("# ---> " + name + "\n") | |||
| buf.Write(data) | |||
| buf.WriteString("\n") | |||
| } | |||
| if buf.Len() > 0 { | |||
| if err = ioutil.WriteFile(filepath.Join(tmpDir, ".gitignore"), buf.Bytes(), 0644); err != nil { | |||
| return fmt.Errorf("write .gitignore: %v", err) | |||
| } | |||
| } | |||
| } | |||
| // LICENSE | |||
| if len(opts.License) > 0 { | |||
| data, err = getRepoInitFile("license", opts.License) | |||
| if err != nil { | |||
| return fmt.Errorf("getRepoInitFile[%s]: %v", opts.License, err) | |||
| } | |||
| if err = ioutil.WriteFile(filepath.Join(tmpDir, "LICENSE"), data, 0644); err != nil { | |||
| return fmt.Errorf("write LICENSE: %v", err) | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| // InitRepository initializes README and .gitignore if needed. | |||
| func initRepository(e Engine, repoPath string, u *User, repo *Repository, initReadme bool, repoLang, license string) error { | |||
| func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts CreateRepoOptions) error { | |||
| // Somehow the directory could exist. | |||
| if com.IsExist(repoPath) { | |||
| return fmt.Errorf("initRepository: path already exists: %s", repoPath) | |||
| @@ -498,83 +585,32 @@ func initRepository(e Engine, repoPath string, u *User, repo *Repository, initRe | |||
| // Init bare new repository. | |||
| os.MkdirAll(repoPath, os.ModePerm) | |||
| _, stderr, err := process.ExecDir(-1, repoPath, | |||
| fmt.Sprintf("initRepository(git init --bare): %s", repoPath), | |||
| "git", "init", "--bare") | |||
| fmt.Sprintf("initRepository(git init --bare): %s", repoPath), "git", "init", "--bare") | |||
| if err != nil { | |||
| return fmt.Errorf("git init --bare: %s", err) | |||
| return fmt.Errorf("git init --bare: %v - %s", err, stderr) | |||
| } | |||
| if err := createUpdateHook(repoPath); err != nil { | |||
| return err | |||
| } | |||
| // Initialize repository according to user's choice. | |||
| fileName := map[string]string{} | |||
| if initReadme { | |||
| fileName["readme"] = "README.md" | |||
| } | |||
| if repoLang != "" { | |||
| fileName["gitign"] = ".gitignore" | |||
| } | |||
| if license != "" { | |||
| fileName["license"] = "LICENSE" | |||
| } | |||
| // Clone to temprory path and do the init commit. | |||
| tmpDir := filepath.Join(os.TempDir(), com.ToStr(time.Now().Nanosecond())) | |||
| os.MkdirAll(tmpDir, os.ModePerm) | |||
| defer os.RemoveAll(tmpDir) | |||
| _, stderr, err = process.Exec( | |||
| fmt.Sprintf("initRepository(git clone): %s", repoPath), | |||
| "git", "clone", repoPath, tmpDir) | |||
| if err != nil { | |||
| return errors.New("git clone: " + stderr) | |||
| } | |||
| // Initialize repository according to user's choice. | |||
| if opts.AutoInit { | |||
| os.MkdirAll(tmpDir, os.ModePerm) | |||
| defer os.RemoveAll(tmpDir) | |||
| // README | |||
| if initReadme { | |||
| defaultReadme := repo.Name + "\n" + strings.Repeat("=", | |||
| utf8.RuneCountInString(repo.Name)) + "\n\n" + repo.Description | |||
| if err := ioutil.WriteFile(filepath.Join(tmpDir, fileName["readme"]), | |||
| []byte(defaultReadme), 0644); err != nil { | |||
| return err | |||
| if err = prepareRepoCommit(repo, tmpDir, repoPath, opts); err != nil { | |||
| return fmt.Errorf("prepareRepoCommit: %v", err) | |||
| } | |||
| } | |||
| // FIXME: following two can be merged. | |||
| // .gitignore | |||
| // Copy custom file when available. | |||
| customPath := path.Join(setting.CustomPath, "conf/gitignore", repoLang) | |||
| targetPath := path.Join(tmpDir, fileName["gitign"]) | |||
| if com.IsFile(customPath) { | |||
| if err := com.Copy(customPath, targetPath); err != nil { | |||
| return fmt.Errorf("copy gitignore: %v", err) | |||
| } | |||
| } else if com.IsSliceContainsStr(Gitignores, repoLang) { | |||
| if err = ioutil.WriteFile(targetPath, | |||
| bindata.MustAsset(path.Join("conf/gitignore", repoLang)), 0644); err != nil { | |||
| return fmt.Errorf("generate gitignore: %v", err) | |||
| // Apply changes and commit. | |||
| if err = initRepoCommit(tmpDir, u.NewGitSig()); err != nil { | |||
| return fmt.Errorf("initRepoCommit: %v", err) | |||
| } | |||
| } else { | |||
| delete(fileName, "gitign") | |||
| } | |||
| // LICENSE | |||
| customPath = path.Join(setting.CustomPath, "conf/license", license) | |||
| targetPath = path.Join(tmpDir, fileName["license"]) | |||
| if com.IsFile(customPath) { | |||
| if err = com.Copy(customPath, targetPath); err != nil { | |||
| return fmt.Errorf("copy license: %v", err) | |||
| } | |||
| } else if com.IsSliceContainsStr(Licenses, license) { | |||
| if err = ioutil.WriteFile(targetPath, | |||
| bindata.MustAsset(path.Join("conf/license", license)), 0644); err != nil { | |||
| return fmt.Errorf("generate license: %v", err) | |||
| } | |||
| } else { | |||
| delete(fileName, "license") | |||
| repo.IsBare = true | |||
| } | |||
| // Re-fetch the repository from database before updating it (else it would | |||
| @@ -582,21 +618,13 @@ func initRepository(e Engine, repoPath string, u *User, repo *Repository, initRe | |||
| if repo, err = getRepositoryByID(e, repo.ID); err != nil { | |||
| return fmt.Errorf("getRepositoryByID: %v", err) | |||
| } | |||
| if len(fileName) == 0 { | |||
| repo.IsBare = true | |||
| } | |||
| repo.DefaultBranch = "master" | |||
| if err = updateRepository(e, repo, false); err != nil { | |||
| return fmt.Errorf("updateRepository: %v", err) | |||
| } | |||
| // Ignore init process if user choose not to. | |||
| if len(fileName) == 0 { | |||
| return nil | |||
| } | |||
| // Apply changes and commit. | |||
| return initRepoCommit(tmpDir, u.NewGitSig()) | |||
| return nil | |||
| } | |||
| func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) { | |||
| @@ -642,14 +670,14 @@ func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) { | |||
| } | |||
| // CreateRepository creates a repository for given user or organization. | |||
| func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMirror, initReadme bool) (_ *Repository, err error) { | |||
| func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error) { | |||
| repo := &Repository{ | |||
| OwnerID: u.Id, | |||
| Owner: u, | |||
| Name: name, | |||
| LowerName: strings.ToLower(name), | |||
| Description: desc, | |||
| IsPrivate: isPrivate, | |||
| Name: opts.Name, | |||
| LowerName: strings.ToLower(opts.Name), | |||
| Description: opts.Description, | |||
| IsPrivate: opts.IsPrivate, | |||
| } | |||
| sess := x.NewSession() | |||
| @@ -663,9 +691,9 @@ func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMi | |||
| } | |||
| // No need for init mirror. | |||
| if !isMirror { | |||
| if !opts.IsMirror { | |||
| repoPath := RepoPath(u.Name, repo.Name) | |||
| if err = initRepository(sess, repoPath, u, repo, initReadme, lang, license); err != nil { | |||
| if err = initRepository(sess, repoPath, u, repo, opts); err != nil { | |||
| if err2 := os.RemoveAll(repoPath); err2 != nil { | |||
| log.Error(4, "initRepository: %v", err) | |||
| return nil, fmt.Errorf( | |||
| @@ -194,6 +194,12 @@ func GetMaxSize(field reflect.StructField) string { | |||
| return getSize(field, "MaxSize(") | |||
| } | |||
| // FIXME: struct contains a struct | |||
| func validateStruct(obj interface{}) binding.Errors { | |||
| return nil | |||
| } | |||
| func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaron.Locale) binding.Errors { | |||
| if errs.Len() == 0 { | |||
| return errs | |||
| @@ -16,19 +16,15 @@ import ( | |||
| // |____|_ /_______ / |____| \_______ /_______ /|___| |____| \_______ /____|_ // ______| | |||
| // \/ \/ \/ \/ \/ \/ \/ | |||
| type RepoForm struct { | |||
| type CreateRepoForm struct { | |||
| Uid int64 `binding:"Required"` | |||
| RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` | |||
| Private bool | |||
| Description string `binding:"MaxSize(255)"` | |||
| } | |||
| type CreateRepoForm struct { | |||
| RepoForm | |||
| AutoInit bool | |||
| Gitignores string | |||
| License string | |||
| Readme string | |||
| AutoInit bool | |||
| Gitignores string | |||
| License string | |||
| Readme string | |||
| } | |||
| func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
| @@ -39,8 +35,11 @@ type MigrateRepoForm struct { | |||
| CloneAddr string `binding:"Required"` | |||
| AuthUsername string | |||
| AuthPassword string | |||
| RepoForm | |||
| Mirror bool | |||
| Mirror bool | |||
| Uid int64 `binding:"Required"` | |||
| RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` | |||
| Private bool | |||
| Description string `binding:"MaxSize(255)"` | |||
| } | |||
| func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
| @@ -284,7 +284,7 @@ img.avatar-100 { | |||
| font-size: 16px; | |||
| line-height: 1.6; | |||
| word-wrap: break-word; | |||
| padding: 0 2em 2em !important; | |||
| padding: 5px 2em 2em !important; | |||
| } | |||
| .markdown > *:first-child { | |||
| margin-top: 0 !important; | |||
| @@ -4,7 +4,7 @@ | |||
| font-size:16px; | |||
| line-height:1.6; | |||
| word-wrap:break-word; | |||
| padding: 0 2em 2em !important; | |||
| padding: 5px 2em 2em !important; | |||
| >*:first-child { | |||
| margin-top:0 !important; | |||
| @@ -101,8 +101,15 @@ func SearchRepos(ctx *middleware.Context) { | |||
| } | |||
| func createRepo(ctx *middleware.Context, owner *models.User, opt api.CreateRepoOption) { | |||
| repo, err := models.CreateRepository(owner, opt.Name, opt.Description, | |||
| opt.Gitignore, opt.License, opt.Private, false, opt.AutoInit) | |||
| repo, err := models.CreateRepository(owner, models.CreateRepoOptions{ | |||
| Name: opt.Name, | |||
| Description: opt.Description, | |||
| Gitignores: opt.Gitignore, | |||
| License: opt.License, | |||
| // Readme: form.Readme, | |||
| IsPrivate: opt.Private, | |||
| AutoInit: opt.AutoInit, | |||
| }) | |||
| if err != nil { | |||
| if models.IsErrRepoAlreadyExist(err) || | |||
| models.IsErrNameReserved(err) || | |||
| @@ -28,6 +28,12 @@ const ( | |||
| ) | |||
| func checkContextUser(ctx *middleware.Context, uid int64) *models.User { | |||
| if err := ctx.User.GetOrganizations(); err != nil { | |||
| ctx.Handle(500, "GetOrganizations", err) | |||
| return nil | |||
| } | |||
| ctx.Data["Orgs"] = ctx.User.Orgs | |||
| // Not equal means current user is an organization. | |||
| if uid == ctx.User.Id || uid == 0 { | |||
| return ctx.User | |||
| @@ -41,7 +47,10 @@ func checkContextUser(ctx *middleware.Context, uid int64) *models.User { | |||
| if err != nil { | |||
| ctx.Handle(500, "checkContextUser", fmt.Errorf("GetUserById(%d): %v", uid, err)) | |||
| return nil | |||
| } else if !org.IsOrganization() { | |||
| } | |||
| // Check ownership of organization. | |||
| if !org.IsOrganization() || !org.IsOwnedBy(ctx.User.Id) { | |||
| ctx.Error(403) | |||
| return nil | |||
| } | |||
| @@ -63,15 +72,25 @@ func Create(ctx *middleware.Context) { | |||
| } | |||
| ctx.Data["ContextUser"] = ctxUser | |||
| if err := ctx.User.GetOrganizations(); err != nil { | |||
| ctx.Handle(500, "GetOrganizations", err) | |||
| return | |||
| } | |||
| ctx.Data["Orgs"] = ctx.User.Orgs | |||
| ctx.HTML(200, CREATE) | |||
| } | |||
| func handleCreateError(ctx *middleware.Context, err error, name string, tpl base.TplName, form interface{}) { | |||
| switch { | |||
| case models.IsErrRepoAlreadyExist(err): | |||
| ctx.Data["Err_RepoName"] = true | |||
| ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form) | |||
| case models.IsErrNameReserved(err): | |||
| ctx.Data["Err_RepoName"] = true | |||
| ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form) | |||
| case models.IsErrNamePatternNotAllowed(err): | |||
| ctx.Data["Err_RepoName"] = true | |||
| ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form) | |||
| default: | |||
| ctx.Handle(500, name, err) | |||
| } | |||
| } | |||
| func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) { | |||
| ctx.Data["Title"] = ctx.Tr("new_repo") | |||
| @@ -85,27 +104,20 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) { | |||
| } | |||
| ctx.Data["ContextUser"] = ctxUser | |||
| if err := ctx.User.GetOrganizations(); err != nil { | |||
| ctx.Handle(500, "GetOrganizations", err) | |||
| return | |||
| } | |||
| ctx.Data["Orgs"] = ctx.User.Orgs | |||
| if ctx.HasError() { | |||
| ctx.HTML(200, CREATE) | |||
| return | |||
| } | |||
| if ctxUser.IsOrganization() { | |||
| // Check ownership of organization. | |||
| if !ctxUser.IsOwnedBy(ctx.User.Id) { | |||
| ctx.Error(403) | |||
| return | |||
| } | |||
| } | |||
| repo, err := models.CreateRepository(ctxUser, form.RepoName, form.Description, | |||
| form.Gitignores, form.License, form.Private, false, form.AutoInit) | |||
| repo, err := models.CreateRepository(ctxUser, models.CreateRepoOptions{ | |||
| Name: form.RepoName, | |||
| Description: form.Description, | |||
| Gitignores: form.Gitignores, | |||
| License: form.License, | |||
| Readme: form.Readme, | |||
| IsPrivate: form.Private, | |||
| AutoInit: form.AutoInit, | |||
| }) | |||
| if err == nil { | |||
| log.Trace("Repository created: %s/%s", ctxUser.Name, repo.Name) | |||
| ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name) | |||
| @@ -118,19 +130,7 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) { | |||
| } | |||
| } | |||
| switch { | |||
| case models.IsErrRepoAlreadyExist(err): | |||
| ctx.Data["Err_RepoName"] = true | |||
| ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), CREATE, &form) | |||
| case models.IsErrNameReserved(err): | |||
| ctx.Data["Err_RepoName"] = true | |||
| ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), CREATE, &form) | |||
| case models.IsErrNamePatternNotAllowed(err): | |||
| ctx.Data["Err_RepoName"] = true | |||
| ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), CREATE, &form) | |||
| default: | |||
| ctx.Handle(500, "CreatePost", err) | |||
| } | |||
| handleCreateError(ctx, err, "CreatePost", CREATE, &form) | |||
| } | |||
| func Migrate(ctx *middleware.Context) { | |||
| @@ -142,12 +142,6 @@ func Migrate(ctx *middleware.Context) { | |||
| } | |||
| ctx.Data["ContextUser"] = ctxUser | |||
| if err := ctx.User.GetOrganizations(); err != nil { | |||
| ctx.Handle(500, "GetOrganizations", err) | |||
| return | |||
| } | |||
| ctx.Data["Orgs"] = ctx.User.Orgs | |||
| ctx.HTML(200, MIGRATE) | |||
| } | |||
| @@ -160,25 +154,11 @@ func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) { | |||
| } | |||
| ctx.Data["ContextUser"] = ctxUser | |||
| if err := ctx.User.GetOrganizations(); err != nil { | |||
| ctx.Handle(500, "GetOrganizations", err) | |||
| return | |||
| } | |||
| ctx.Data["Orgs"] = ctx.User.Orgs | |||
| if ctx.HasError() { | |||
| ctx.HTML(200, MIGRATE) | |||
| return | |||
| } | |||
| if ctxUser.IsOrganization() { | |||
| // Check ownership of organization. | |||
| if !ctxUser.IsOwnedBy(ctx.User.Id) { | |||
| ctx.Error(403) | |||
| return | |||
| } | |||
| } | |||
| // Remote address can be HTTP/HTTPS/Git URL or local path. | |||
| // Note: remember to change api/v1/repo.go: MigrateRepo | |||
| // FIXME: merge these two functions with better error handling | |||
| @@ -222,19 +202,7 @@ func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) { | |||
| return | |||
| } | |||
| switch { | |||
| case models.IsErrRepoAlreadyExist(err): | |||
| ctx.Data["Err_RepoName"] = true | |||
| ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), MIGRATE, &form) | |||
| case models.IsErrNameReserved(err): | |||
| ctx.Data["Err_RepoName"] = true | |||
| ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), MIGRATE, &form) | |||
| case models.IsErrNamePatternNotAllowed(err): | |||
| ctx.Data["Err_RepoName"] = true | |||
| ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), MIGRATE, &form) | |||
| default: | |||
| ctx.Handle(500, "MigratePost", err) | |||
| } | |||
| handleCreateError(ctx, err, "MigratePost", MIGRATE, &form) | |||
| } | |||
| func Action(ctx *middleware.Context) { | |||
| @@ -13,7 +13,7 @@ | |||
| <link rel="shortcut icon" href="{{AppSubUrl}}/img/favicon.png" /> | |||
| <link rel="stylesheet" href="{{AppSubUrl}}/css/font-awesome.min.css"> | |||
| <link rel="stylesheet" href="{{AppSubUrl}}/ng/css/font-awesome.min.css"> | |||
| <script src="{{AppSubUrl}}/ng/js/lib/jquery-1.11.1.min.js"></script> | |||
| @@ -57,7 +57,7 @@ | |||
| <div class="inline field"> | |||
| <label>{{.i18n.Tr "repo.repo_lang"}}</label> | |||
| <div class="ui multiple search normal selection dropdown"> | |||
| <input type="hidden" name="gitignores" value="{{.gitignoresValue}}"> | |||
| <input type="hidden" name="gitignores" value="{{.gitignores}}"> | |||
| <div class="default text">{{.i18n.Tr "repo.repo_lang_helper"}}</div> | |||
| <div class="menu"> | |||
| {{range .Gitignores}} | |||
| @@ -82,7 +82,7 @@ | |||
| <div class="inline field"> | |||
| <label>{{.i18n.Tr "repo.readme"}}</label> | |||
| <div class="ui selection dropdown"> | |||
| <input type="hidden" name="license" value="{{.readme}}"> | |||
| <input type="hidden" name="readme" value="{{.readme}}"> | |||
| <div class="default text">{{.i18n.Tr "repo.readme_helper"}}</div> | |||
| <div class="menu"> | |||
| {{range .Readmes}} | |||