mirror of https://github.com/gogs/gogs.git
repo: support avatars (#5221)
* First code for repository avatars * Last code for repository avatars - add new option for repo avatars location on filesystem - add route catch in web - add new fields to repo model - add migration - update settings handlers - update repo header template * Update locale messages * Add repo avatars to home page * Add repo avatars to organization right panel * Show repo avatars in repo list * Remove AvatarEamil field, remove Gravatar support, use generic locale messages * Fix migration * Fix seed and not used tool * Revert public css changes, add them to less files * Latest lessc (2.6.0) don't put result into file but output to stdout So redirect output to file * Simplify things: - migration don't needed, and table changes too - just upload file to repo avatar storage - or generate random image * Fix repo image seed - name not unique * Get rid of not needed model fields * Class value is enough, remove height attribute * Don't generate random avatar for repository - use html and semantic ui icons if no avatar found * Update styles and templates for repo - use repo icon as default avatar - use globe icon for public repos - add micro style for repo avatars at dashboard * Remvoe redundant empty line * Fix nl2br filter - must return string * Fix css style for micro-repo-avatar in dashboard list * Remove `|len`, works fine w/o it. * Update after review 2: - use static route for repository avatar - format images settings block in settings * Update after review 2: - no random avatar for repo * Update after review 2: - no random avatar for repo 2 - update imports - update UploadAvatar* functions * Update after review 2: - update templates * Fix trace call * Remove unused immport since we use static route for repo avatars.pull/5315/head
parent
ef02414d7e
commit
303fa37b60
2
Makefile
2
Makefile
|
@ -62,7 +62,7 @@ pkg/bindata/bindata.go: $(DATA_FILES)
|
||||||
less: public/css/gogs.css
|
less: public/css/gogs.css
|
||||||
|
|
||||||
public/css/gogs.css: $(LESS_FILES)
|
public/css/gogs.css: $(LESS_FILES)
|
||||||
lessc $< $@
|
lessc $< >$@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
go clean -i ./...
|
go clean -i ./...
|
||||||
|
|
10
cmd/web.go
10
cmd/web.go
|
@ -100,6 +100,13 @@ func newMacaron() *macaron.Macaron {
|
||||||
SkipLogging: setting.DisableRouterLog,
|
SkipLogging: setting.DisableRouterLog,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
m.Use(macaron.Static(
|
||||||
|
setting.RepositoryAvatarUploadPath,
|
||||||
|
macaron.StaticOptions{
|
||||||
|
Prefix: "repo-avatars",
|
||||||
|
SkipLogging: setting.DisableRouterLog,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
funcMap := template.NewFuncMap()
|
funcMap := template.NewFuncMap()
|
||||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||||
|
@ -419,6 +426,9 @@ func runWeb(c *cli.Context) error {
|
||||||
m.Group("/settings", func() {
|
m.Group("/settings", func() {
|
||||||
m.Combo("").Get(repo.Settings).
|
m.Combo("").Get(repo.Settings).
|
||||||
Post(bindIgnErr(form.RepoSetting{}), repo.SettingsPost)
|
Post(bindIgnErr(form.RepoSetting{}), repo.SettingsPost)
|
||||||
|
m.Combo("/avatar").Get(repo.SettingsAvatar).
|
||||||
|
Post(binding.MultipartForm(form.Avatar{}), repo.SettingsAvatarPost)
|
||||||
|
m.Post("/avatar/delete", repo.SettingsDeleteAvatar)
|
||||||
m.Group("/collaboration", func() {
|
m.Group("/collaboration", func() {
|
||||||
m.Combo("").Get(repo.SettingsCollaboration).Post(repo.SettingsCollaborationPost)
|
m.Combo("").Get(repo.SettingsCollaboration).Post(repo.SettingsCollaborationPost)
|
||||||
m.Post("/access_mode", repo.ChangeCollaborationAccessMode)
|
m.Post("/access_mode", repo.ChangeCollaborationAccessMode)
|
||||||
|
|
|
@ -286,6 +286,8 @@ CSRF_COOKIE_NAME = _csrf
|
||||||
[picture]
|
[picture]
|
||||||
; Path to store user uploaded avatars
|
; Path to store user uploaded avatars
|
||||||
AVATAR_UPLOAD_PATH = data/avatars
|
AVATAR_UPLOAD_PATH = data/avatars
|
||||||
|
; Path to store repository uploaded avatars
|
||||||
|
REPOSITORY_AVATAR_UPLOAD_PATH = data/repo-avatars
|
||||||
; Chinese users can choose "duoshuo"
|
; Chinese users can choose "duoshuo"
|
||||||
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
|
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
|
||||||
GRAVATAR_SOURCE = gravatar
|
GRAVATAR_SOURCE = gravatar
|
||||||
|
|
|
@ -15,10 +15,14 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"image"
|
||||||
|
_ "image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
|
||||||
"github.com/Unknwon/cae/zip"
|
"github.com/Unknwon/cae/zip"
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
"github.com/nfnt/resize"
|
||||||
"github.com/mcuadros/go-version"
|
"github.com/mcuadros/go-version"
|
||||||
log "gopkg.in/clog.v1"
|
log "gopkg.in/clog.v1"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
@ -27,6 +31,7 @@ import (
|
||||||
api "github.com/gogs/go-gogs-client"
|
api "github.com/gogs/go-gogs-client"
|
||||||
|
|
||||||
"github.com/gogs/gogs/models/errors"
|
"github.com/gogs/gogs/models/errors"
|
||||||
|
"github.com/gogs/gogs/pkg/avatar"
|
||||||
"github.com/gogs/gogs/pkg/bindata"
|
"github.com/gogs/gogs/pkg/bindata"
|
||||||
"github.com/gogs/gogs/pkg/markup"
|
"github.com/gogs/gogs/pkg/markup"
|
||||||
"github.com/gogs/gogs/pkg/process"
|
"github.com/gogs/gogs/pkg/process"
|
||||||
|
@ -284,6 +289,61 @@ func (repo *Repository) HTMLURL() string {
|
||||||
return setting.AppURL + repo.FullName()
|
return setting.AppURL + repo.FullName()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CustomAvatarPath returns repository custom avatar file path.
|
||||||
|
func (repo *Repository) CustomAvatarPath() string {
|
||||||
|
return filepath.Join(setting.RepositoryAvatarUploadPath, com.ToStr(repo.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RelAvatarLink returns relative avatar link to the site domain,
|
||||||
|
// which includes app sub-url as prefix.
|
||||||
|
// Since Gravatar support not needed here - just check for image path.
|
||||||
|
func (repo *Repository) RelAvatarLink() string {
|
||||||
|
defaultImgUrl := ""
|
||||||
|
if !com.IsExist(repo.CustomAvatarPath()) {
|
||||||
|
return defaultImgUrl
|
||||||
|
}
|
||||||
|
return setting.AppSubURL + "/repo-avatars/" + com.ToStr(repo.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AvatarLink returns user avatar absolute link.
|
||||||
|
func (repo *Repository) AvatarLink() string {
|
||||||
|
link := repo.RelAvatarLink()
|
||||||
|
if link[0] == '/' && link[1] != '/' {
|
||||||
|
return setting.AppURL + strings.TrimPrefix(link, setting.AppSubURL)[1:]
|
||||||
|
}
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadAvatar saves custom avatar for repository.
|
||||||
|
// FIXME: split uploads to different subdirs
|
||||||
|
// in case we have massive number of repositories.
|
||||||
|
func (repo *Repository) UploadAvatar(data []byte) error {
|
||||||
|
img, _, err := image.Decode(bytes.NewReader(data))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Decode: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := resize.Resize(avatar.AVATAR_SIZE, avatar.AVATAR_SIZE, img, resize.NearestNeighbor)
|
||||||
|
os.MkdirAll(setting.RepositoryAvatarUploadPath, os.ModePerm)
|
||||||
|
fw, err := os.Create(repo.CustomAvatarPath())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Create: %v", err)
|
||||||
|
}
|
||||||
|
defer fw.Close()
|
||||||
|
|
||||||
|
if err = png.Encode(fw, m); err != nil {
|
||||||
|
return fmt.Errorf("Encode: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAvatar deletes the repository custom avatar.
|
||||||
|
func (repo *Repository) DeleteAvatar() error {
|
||||||
|
log.Trace("DeleteAvatar [%d]: %s", repo.ID, repo.CustomAvatarPath())
|
||||||
|
return os.Remove(repo.CustomAvatarPath())
|
||||||
|
}
|
||||||
|
|
||||||
// This method assumes following fields have been assigned with valid values:
|
// This method assumes following fields have been assigned with valid values:
|
||||||
// Required - BaseRepo (if fork)
|
// Required - BaseRepo (if fork)
|
||||||
// Arguments that are allowed to be nil: permission
|
// Arguments that are allowed to be nil: permission
|
||||||
|
@ -312,6 +372,8 @@ func (repo *Repository) APIFormat(permission *api.Permission, user ...*User) *ap
|
||||||
Created: repo.Created,
|
Created: repo.Created,
|
||||||
Updated: repo.Updated,
|
Updated: repo.Updated,
|
||||||
Permissions: permission,
|
Permissions: permission,
|
||||||
|
// Reserved for go-gogs-client change
|
||||||
|
// AvatarUrl: repo.AvatarLink(),
|
||||||
}
|
}
|
||||||
if repo.IsFork {
|
if repo.IsFork {
|
||||||
p := &api.Permission{Pull: true}
|
p := &api.Permission{Pull: true}
|
||||||
|
|
|
@ -189,6 +189,7 @@ var (
|
||||||
|
|
||||||
// Picture settings
|
// Picture settings
|
||||||
AvatarUploadPath string
|
AvatarUploadPath string
|
||||||
|
RepositoryAvatarUploadPath string
|
||||||
GravatarSource string
|
GravatarSource string
|
||||||
DisableGravatar bool
|
DisableGravatar bool
|
||||||
EnableFederatedAvatar bool
|
EnableFederatedAvatar bool
|
||||||
|
@ -611,6 +612,11 @@ func NewContext() {
|
||||||
if !filepath.IsAbs(AvatarUploadPath) {
|
if !filepath.IsAbs(AvatarUploadPath) {
|
||||||
AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
|
AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
|
||||||
}
|
}
|
||||||
|
RepositoryAvatarUploadPath = sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "repo-avatars"))
|
||||||
|
forcePathSeparator(RepositoryAvatarUploadPath)
|
||||||
|
if !filepath.IsAbs(RepositoryAvatarUploadPath) {
|
||||||
|
RepositoryAvatarUploadPath = path.Join(workDir, RepositoryAvatarUploadPath)
|
||||||
|
}
|
||||||
switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
|
switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
|
||||||
case "duoshuo":
|
case "duoshuo":
|
||||||
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
|
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
|
||||||
|
|
|
@ -143,17 +143,27 @@
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
margin-bottom: -4px;
|
margin-bottom: -4px;
|
||||||
}
|
}
|
||||||
|
.ui.micro.image {
|
||||||
|
width: 16px;
|
||||||
|
height: auto;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#collaborative-repo-list {
|
#collaborative-repo-list {
|
||||||
.owner-and-repo {
|
.owner-and-repo {
|
||||||
max-width: 80%;
|
max-width: 75%;
|
||||||
margin-bottom: -5px;
|
margin-bottom: -5px;
|
||||||
}
|
}
|
||||||
.owner-name {
|
.owner-name {
|
||||||
max-width: 120px;
|
max-width: 120px;
|
||||||
margin-bottom: -5px;
|
margin-bottom: -5px;
|
||||||
}
|
}
|
||||||
|
.ui.micro.image {
|
||||||
|
width: 16px;
|
||||||
|
height: auto;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
log "gopkg.in/clog.v1"
|
log "gopkg.in/clog.v1"
|
||||||
|
"github.com/Unknwon/com"
|
||||||
"github.com/gogs/git-module"
|
"github.com/gogs/git-module"
|
||||||
|
|
||||||
"github.com/gogs/gogs/models"
|
"github.com/gogs/gogs/models"
|
||||||
|
@ -19,10 +20,12 @@ import (
|
||||||
"github.com/gogs/gogs/pkg/form"
|
"github.com/gogs/gogs/pkg/form"
|
||||||
"github.com/gogs/gogs/pkg/mailer"
|
"github.com/gogs/gogs/pkg/mailer"
|
||||||
"github.com/gogs/gogs/pkg/setting"
|
"github.com/gogs/gogs/pkg/setting"
|
||||||
|
"github.com/gogs/gogs/pkg/tool"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SETTINGS_OPTIONS = "repo/settings/options"
|
SETTINGS_OPTIONS = "repo/settings/options"
|
||||||
|
SETTINGS_REPO_AVATAR = "repo/settings/avatar"
|
||||||
SETTINGS_COLLABORATION = "repo/settings/collaboration"
|
SETTINGS_COLLABORATION = "repo/settings/collaboration"
|
||||||
SETTINGS_BRANCHES = "repo/settings/branches"
|
SETTINGS_BRANCHES = "repo/settings/branches"
|
||||||
SETTINGS_PROTECTED_BRANCH = "repo/settings/protected_branch"
|
SETTINGS_PROTECTED_BRANCH = "repo/settings/protected_branch"
|
||||||
|
@ -632,3 +635,56 @@ func DeleteDeployKey(c *context.Context) {
|
||||||
"redirect": c.Repo.RepoLink + "/settings/keys",
|
"redirect": c.Repo.RepoLink + "/settings/keys",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SettingsAvatar(c *context.Context) {
|
||||||
|
c.Title("settings.avatar")
|
||||||
|
c.PageIs("SettingsAvatar")
|
||||||
|
c.Success(SETTINGS_REPO_AVATAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SettingsAvatarPost(c *context.Context, f form.Avatar) {
|
||||||
|
f.Source = form.AVATAR_LOCAL
|
||||||
|
if err := UpdateAvatarSetting(c, f); err != nil {
|
||||||
|
c.Flash.Error(err.Error())
|
||||||
|
} else {
|
||||||
|
c.Flash.Success(c.Tr("settings.update_avatar_success"))
|
||||||
|
}
|
||||||
|
c.SubURLRedirect(c.Repo.RepoLink + "/settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
func SettingsDeleteAvatar(c *context.Context) {
|
||||||
|
if err := c.Repo.Repository.DeleteAvatar(); err != nil {
|
||||||
|
c.Flash.Error(fmt.Sprintf("DeleteAvatar: %v", err))
|
||||||
|
}
|
||||||
|
c.SubURLRedirect(c.Repo.RepoLink + "/settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: limit size.
|
||||||
|
func UpdateAvatarSetting(c *context.Context, f form.Avatar) error {
|
||||||
|
ctxRepo := c.Repo.Repository;
|
||||||
|
if f.Avatar != nil {
|
||||||
|
r, err := f.Avatar.Open()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Avatar.Open: %v", err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ioutil.ReadAll: %v", err)
|
||||||
|
}
|
||||||
|
if !tool.IsImageFile(data) {
|
||||||
|
return errors.New(c.Tr("settings.uploaded_avatar_not_a_image"))
|
||||||
|
}
|
||||||
|
if err = ctxRepo.UploadAvatar(data); err != nil {
|
||||||
|
return fmt.Errorf("UploadAvatar: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No avatar is uploaded but setting has been changed to enable
|
||||||
|
// No random avatar here.
|
||||||
|
if !com.IsFile(ctxRepo.CustomAvatarPath()) {
|
||||||
|
log.Trace("No avatar was uploaded for repo: %d. Default icon will appear instead.", ctxRepo.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
<div class="ui repository list">
|
<div class="ui repository list">
|
||||||
{{range .Repos}}
|
{{range .Repos}}
|
||||||
<div class="item">
|
<div class="item">
|
||||||
|
<div class="ui grid">
|
||||||
|
<div class="ui two wide column middle aligned">
|
||||||
|
{{if .RelAvatarLink}}<img class="ui tiny image" src="{{.RelAvatarLink}}">{{else}}<i class="mega-octicon octicon-repo"></i>{{end}}
|
||||||
|
</div>
|
||||||
|
<div class="ui fourteen wide column">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
<a class="name" href="{{AppSubURL}}/{{if .Owner}}{{.Owner.Name}}{{else if $.Org}}{{$.Org.Name}}{{else}}{{$.Owner.Name}}{{end}}/{{.Name}}">{{if $.PageIsExplore}}{{.Owner.Name}} / {{end}}{{.Name}}</a>
|
<a class="name" href="{{AppSubURL}}/{{if .Owner}}{{.Owner.Name}}{{else if $.Org}}{{$.Org.Name}}{{else}}{{$.Owner.Name}}{{end}}/{{.Name}}">{{if $.PageIsExplore}}{{.Owner.Name}} / {{end}}{{.Name}}</a>
|
||||||
{{if .IsPrivate}}
|
{{if .IsPrivate}}
|
||||||
|
@ -9,6 +14,8 @@
|
||||||
<span><i class="octicon octicon-repo-forked"></i></span>
|
<span><i class="octicon octicon-repo-forked"></i></span>
|
||||||
{{else if .IsMirror}}
|
{{else if .IsMirror}}
|
||||||
<span><i class="octicon octicon-repo-clone"></i></span>
|
<span><i class="octicon octicon-repo-clone"></i></span>
|
||||||
|
{{else}}
|
||||||
|
<span class="text"><i class="octicon octicon-globe"></i></span>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="ui right metas">
|
<div class="ui right metas">
|
||||||
|
@ -19,5 +26,7 @@
|
||||||
{{if .Description}}<p class="has-emoji">{{.Description | Str2html}}</p>{{end}}
|
{{if .Description}}<p class="has-emoji">{{.Description | Str2html}}</p>{{end}}
|
||||||
<p class="time">{{$.i18n.Tr "org.repo_updated"}} {{TimeSince .Updated $.i18n.Lang}}</p>
|
<p class="time">{{$.i18n.Tr "org.repo_updated"}} {{TimeSince .Updated $.i18n.Lang}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
<a class="ui red small button right" href="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/remove?repoid={{.ID}}">{{$.i18n.Tr "org.teams.remove_repo"}}</a>
|
<a class="ui red small button right" href="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/remove?repoid={{.ID}}">{{$.i18n.Tr "org.teams.remove_repo"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
<a class="member" href="{{AppSubURL}}/{{$.Org.Name}}/{{.Name}}">
|
<a class="member" href="{{AppSubURL}}/{{$.Org.Name}}/{{.Name}}">
|
||||||
|
<img height="16px" class="octicon" src="{{.RelAvatarLink}}" />
|
||||||
<i class="octicon octicon-{{if .IsPrivate}}lock{{else if .IsFork}}repo-forked{{else if .IsMirror}}repo-clone{{else}}repo{{end}}"></i>
|
<i class="octicon octicon-{{if .IsPrivate}}lock{{else if .IsFork}}repo-forked{{else if .IsMirror}}repo-clone{{else}}repo{{end}}"></i>
|
||||||
<strong>{{$.Org.Name}}/{{.Name}}</strong>
|
<strong>{{$.Org.Name}}/{{.Name}}</strong>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
<div class="column"><!-- start column -->
|
<div class="column"><!-- start column -->
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
<div class="ui huge breadcrumb">
|
<div class="ui huge breadcrumb">
|
||||||
<i class="mega-octicon octicon-{{if .IsPrivate}}lock{{else if .IsMirror}}repo-clone{{else if .IsFork}}repo-forked{{else}}repo{{end}}"></i>
|
{{if .RelAvatarLink}}<img class="ui mini spaced image" src="{{.RelAvatarLink}}">{{else}}<i class="mega-octicon octicon-repo"></i>{{end}}
|
||||||
|
<i class="mega-octicon octicon-{{if .IsPrivate}}lock{{else if .IsMirror}}repo-clone{{else if .IsFork}}repo-forked{{else}}globe{{end}}"></i>
|
||||||
<a href="{{AppSubURL}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
|
<a href="{{AppSubURL}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
|
||||||
<div class="divider"> / </div>
|
<div class="divider"> / </div>
|
||||||
<a href="{{$.RepoLink}}">{{.Name}}</a>
|
<a href="{{$.RepoLink}}">{{.Name}}</a>
|
||||||
|
|
|
@ -41,6 +41,21 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button>
|
<button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
|
<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
|
||||||
|
{{.CSRFTokenHTML}}
|
||||||
|
<div class="inline field">
|
||||||
|
<label for="avatar">{{.i18n.Tr "settings.choose_new_avatar"}}</label>
|
||||||
|
<input name="avatar" type="file" >
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<button class="ui green button">{{$.i18n.Tr "settings.update_avatar"}}</button>
|
||||||
|
<a class="ui red button delete-post" data-request-url="{{.Link}}/avatar/delete" data-done-url="{{.Link}}">{{$.i18n.Tr "settings.delete_current_avatar"}}</a>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,8 @@
|
||||||
{{range .Repos}}
|
{{range .Repos}}
|
||||||
<li {{if .IsPrivate}}class="private"{{end}}>
|
<li {{if .IsPrivate}}class="private"{{end}}>
|
||||||
<a href="{{AppSubURL}}/{{$.ContextUser.Name}}/{{.Name}}">
|
<a href="{{AppSubURL}}/{{$.ContextUser.Name}}/{{.Name}}">
|
||||||
<i class="octicon octicon-{{if .IsFork}}repo-forked{{else if .IsPrivate}}lock{{else if .IsMirror}}repo-clone{{else}}repo{{end}}"></i>
|
{{if .RelAvatarLink}}<img class="ui micro image" src="{{.RelAvatarLink}}" />{{else}}<i class="octicon octicon-repo"></i>{{end}}
|
||||||
|
<i class="octicon octicon-{{if .IsFork}}repo-forked{{else if .IsPrivate}}lock{{else if .IsMirror}}repo-clone{{else}}globe{{end}}"></i>
|
||||||
<strong class="text truncate item-name">{{.Name}}</strong>
|
<strong class="text truncate item-name">{{.Name}}</strong>
|
||||||
<span class="ui right text light grey">
|
<span class="ui right text light grey">
|
||||||
{{.NumStars}} <i class="octicon octicon-star rear"></i>
|
{{.NumStars}} <i class="octicon octicon-star rear"></i>
|
||||||
|
@ -57,7 +58,8 @@
|
||||||
{{range .CollaborativeRepos}}
|
{{range .CollaborativeRepos}}
|
||||||
<li {{if .IsPrivate}}class="private"{{end}}>
|
<li {{if .IsPrivate}}class="private"{{end}}>
|
||||||
<a href="{{AppSubURL}}/{{.Owner.Name}}/{{.Name}}">
|
<a href="{{AppSubURL}}/{{.Owner.Name}}/{{.Name}}">
|
||||||
<i class="octicon octicon-{{if .IsPrivate}}lock{{else if .IsFork}}repo-forked{{else if .IsMirror}}repo-clone{{else}}repo{{end}}"></i>
|
{{if .RelAvatarLink}}<img class="ui micro image" src="{{.RelAvatarLink}}" />{{else}}<i class="octicon octicon-repo"></i>{{end}}
|
||||||
|
<i class="octicon octicon-{{if .IsPrivate}}lock{{else if .IsFork}}repo-forked{{else if .IsMirror}}repo-clone{{else}}globe{{end}}"></i>
|
||||||
<span class="text truncate owner-and-repo">
|
<span class="text truncate owner-and-repo">
|
||||||
<span class="text truncate owner-name">{{.Owner.Name}}</span> / <strong>{{.Name}}</strong>
|
<span class="text truncate owner-name">{{.Owner.Name}}</span> / <strong>{{.Name}}</strong>
|
||||||
</span>
|
</span>
|
||||||
|
@ -88,6 +90,7 @@
|
||||||
{{range .ContextUser.Orgs}}
|
{{range .ContextUser.Orgs}}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{AppSubURL}}/{{.Name}}">
|
<a href="{{AppSubURL}}/{{.Name}}">
|
||||||
|
{{if .RelAvatarLink}}<img class="ui micro image" src="{{.RelAvatarLink}}" />{{else}}<i class="octicon octicon-repo"></i>{{end}}
|
||||||
<i class="octicon octicon-organization"></i>
|
<i class="octicon octicon-organization"></i>
|
||||||
<strong class="text truncate item-name">{{.Name}}</strong>
|
<strong class="text truncate item-name">{{.Name}}</strong>
|
||||||
<span class="ui right text light grey">
|
<span class="ui right text light grey">
|
||||||
|
@ -116,6 +119,7 @@
|
||||||
{{range .Mirrors}}
|
{{range .Mirrors}}
|
||||||
<li {{if .IsPrivate}}class="private"{{end}}>
|
<li {{if .IsPrivate}}class="private"{{end}}>
|
||||||
<a href="{{AppSubURL}}/{{$.ContextUser.Name}}/{{.Name}}">
|
<a href="{{AppSubURL}}/{{$.ContextUser.Name}}/{{.Name}}">
|
||||||
|
{{if .RelAvatarLink}}<img class="ui micro image" src="{{.RelAvatarLink}}" />{{else}}<i class="octicon octicon-repo"></i>{{end}}
|
||||||
<i class="octicon octicon-repo-clone"></i>
|
<i class="octicon octicon-repo-clone"></i>
|
||||||
<strong class="text truncate item-name">{{.Name}}</strong>
|
<strong class="text truncate item-name">{{.Name}}</strong>
|
||||||
<span class="ui right text light grey">
|
<span class="ui right text light grey">
|
||||||
|
|
Loading…
Reference in New Issue