aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUnknwon <u@gogs.io>2018-12-06 22:58:02 -0500
committerUnknwon <u@gogs.io>2018-12-06 22:58:02 -0500
commitf545faa06d553750b9f4018336e810530389f88c (patch)
treea906d0d46d9f07a093b7eb8faf01e2648b2540b9
parent458aadbb10a50948bbc237c5c2ab62d8710d2b4a (diff)
templates: make state changing routes to POST method (#5541)
- pkg/context: add ParamsUser to unify the injection process
-rw-r--r--cmd/web.go10
-rw-r--r--gogs.go2
-rw-r--r--pkg/context/user.go30
-rw-r--r--public/css/gogs.css6
-rw-r--r--public/js/gogs.js116
-rw-r--r--public/less/_base.less5
-rw-r--r--public/less/_user.less3
-rw-r--r--routes/user/profile.go102
-rw-r--r--templates/.VERSION2
-rw-r--r--templates/base/head.tmpl10
-rw-r--r--templates/repo/header.tmpl38
-rw-r--r--templates/user/profile.tmpl32
12 files changed, 190 insertions, 166 deletions
diff --git a/cmd/web.go b/cmd/web.go
index 6290e924..23628289 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -257,7 +257,7 @@ func runWeb(c *cli.Context) error {
m.Get("/email2user", user.Email2User)
m.Get("/forget_password", user.ForgotPasswd)
m.Post("/forget_password", user.ForgotPasswdPost)
- m.Get("/logout", user.SignOut)
+ m.Post("/logout", user.SignOut)
})
// ***** END: User *****
@@ -308,7 +308,7 @@ func runWeb(c *cli.Context) error {
m.Get("/followers", user.Followers)
m.Get("/following", user.Following)
m.Get("/stars", user.Stars)
- })
+ }, context.InjectParamsUser())
m.Get("/attachments/:uuid", func(c *context.Context) {
attach, err := models.GetAttachmentByUUID(c.Params(":uuid"))
@@ -340,8 +340,8 @@ func runWeb(c *cli.Context) error {
}, ignSignIn)
m.Group("/:username", func() {
- m.Get("/action/:action", user.Action)
- }, reqSignIn)
+ m.Post("/action/:action", user.Action)
+ }, reqSignIn, context.InjectParamsUser())
if macaron.Env == macaron.DEV {
m.Get("/template/*", dev.TemplatePreview)
@@ -484,7 +484,7 @@ func runWeb(c *cli.Context) error {
})
}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef())
- m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
+ m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
m.Group("/:username/:reponame", func() {
m.Get("/issues", repo.RetrieveLabels, repo.Issues)
m.Get("/issues/:index", repo.ViewIssue)
diff --git a/gogs.go b/gogs.go
index 350719e0..d0681aae 100644
--- a/gogs.go
+++ b/gogs.go
@@ -16,7 +16,7 @@ import (
"github.com/gogs/gogs/pkg/setting"
)
-const APP_VER = "0.11.76.1204"
+const APP_VER = "0.11.77.1206"
func init() {
setting.AppVer = APP_VER
diff --git a/pkg/context/user.go b/pkg/context/user.go
new file mode 100644
index 00000000..a2297da8
--- /dev/null
+++ b/pkg/context/user.go
@@ -0,0 +1,30 @@
+// Copyright 2018 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 context
+
+import (
+ "gopkg.in/macaron.v1"
+
+ "github.com/gogs/gogs/models"
+ "github.com/gogs/gogs/models/errors"
+)
+
+// ParamsUser is the wrapper type of the target user defined by URL parameter, namely ':username'.
+type ParamsUser struct {
+ *models.User
+}
+
+// InjectParamsUser returns a handler that retrieves target user based on URL parameter ':username',
+// and injects it as *ParamsUser.
+func InjectParamsUser() macaron.Handler {
+ return func(c *Context) {
+ user, err := models.GetUserByName(c.Params(":username"))
+ if err != nil {
+ c.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err)
+ return
+ }
+ c.Map(&ParamsUser{user})
+ }
+}
diff --git a/public/css/gogs.css b/public/css/gogs.css
index 9e60fff6..2d56467a 100644
--- a/public/css/gogs.css
+++ b/public/css/gogs.css
@@ -347,6 +347,9 @@ footer .ui.language .menu {
.hide {
display: none;
}
+.display.inline {
+ display: inline;
+}
.center {
text-align: center;
}
@@ -2934,6 +2937,9 @@ footer .ui.language .menu {
margin-top: 5px;
margin-right: 8px;
}
+.user.profile .ui.card .profile-avatar {
+ height: 287px;
+}
.user.profile .ui.card .header {
word-break: break-all;
}
diff --git a/public/js/gogs.js b/public/js/gogs.js
index e518bde4..eb161d17 100644
--- a/public/js/gogs.js
+++ b/public/js/gogs.js
@@ -9,11 +9,11 @@ function initCommentPreviewTab($form) {
$tabMenu.find('.item[data-tab="' + $tabMenu.data('preview') + '"]').click(function () {
var $this = $(this);
$.post($this.data('url'), {
- "_csrf": csrf,
- "mode": "gfm",
- "context": $this.data('context'),
- "text": $form.find('.tab.segment[data-tab="' + $tabMenu.data('write') + '"] textarea').val()
- },
+ "_csrf": csrf,
+ "mode": "gfm",
+ "context": $this.data('context'),
+ "text": $form.find('.tab.segment[data-tab="' + $tabMenu.data('write') + '"] textarea').val()
+ },
function (data) {
var $previewPanel = $form.find('.tab.segment[data-tab="' + $tabMenu.data('preview') + '"]');
$previewPanel.html(data);
@@ -39,11 +39,11 @@ function initEditPreviewTab($form) {
$previewTab.click(function () {
var $this = $(this);
$.post($this.data('url'), {
- "_csrf": csrf,
- "mode": "gfm",
- "context": $this.data('context'),
- "text": $form.find('.tab.segment[data-tab="' + $tabMenu.data('write') + '"] textarea').val()
- },
+ "_csrf": csrf,
+ "mode": "gfm",
+ "context": $this.data('context'),
+ "text": $form.find('.tab.segment[data-tab="' + $tabMenu.data('write') + '"] textarea').val()
+ },
function (data) {
var $previewPanel = $form.find('.tab.segment[data-tab="' + $tabMenu.data('preview') + '"]');
$previewPanel.html(data);
@@ -63,9 +63,9 @@ function initEditDiffTab($form) {
$tabMenu.find('.item[data-tab="' + $tabMenu.data('diff') + '"]').click(function () {
var $this = $(this);
$.post($this.data('url'), {
- "_csrf": csrf,
- "content": $form.find('.tab.segment[data-tab="' + $tabMenu.data('write') + '"] textarea').val()
- },
+ "_csrf": csrf,
+ "content": $form.find('.tab.segment[data-tab="' + $tabMenu.data('write') + '"] textarea').val()
+ },
function (data) {
var $diffPreviewPanel = $form.find('.tab.segment[data-tab="' + $tabMenu.data('diff') + '"]');
$diffPreviewPanel.html(data);
@@ -221,7 +221,7 @@ function initRepository() {
window.location.href = $choice.data('url');
console.log($choice.data('url'))
},
- message: {noResults: $dropdown.data('no-results')}
+ message: { noResults: $dropdown.data('no-results') }
});
}
@@ -346,9 +346,9 @@ function initRepository() {
}
$.post($(this).data('update-url'), {
- "_csrf": csrf,
- "title": $editInput.val()
- },
+ "_csrf": csrf,
+ "title": $editInput.val()
+ },
function (data) {
$editInput.val(data.title);
$issueTitle.text(data.title);
@@ -390,10 +390,10 @@ function initRepository() {
$editContentZone.hide();
$.post($editContentZone.data('update-url'), {
- "_csrf": csrf,
- "content": $textarea.val(),
- "context": $editContentZone.data('context')
- },
+ "_csrf": csrf,
+ "content": $textarea.val(),
+ "context": $editContentZone.data('context')
+ },
function (data) {
if (data.length == 0) {
$renderContent.html($('#no-content').html());
@@ -500,14 +500,14 @@ function initRepository() {
initFilterSearchDropdown('.choose.branch .dropdown');
}
if ($('.repository.view.pull').length > 0) {
- $('.comment.merge.box input[name=merge_style]').change(function () {
- if ($(this).val() === 'create_merge_commit') {
- $('.commit.description.field').show();
- } else {
- $('.commit.description.field').hide();
- }
- })
- }
+ $('.comment.merge.box input[name=merge_style]').change(function () {
+ if ($(this).val() === 'create_merge_commit') {
+ $('.commit.description.field').show();
+ } else {
+ $('.commit.description.field').hide();
+ }
+ })
+ }
}
function initWikiForm() {
@@ -521,11 +521,11 @@ function initWikiForm() {
setTimeout(function () {
// FIXME: still send render request when return back to edit mode
$.post($editArea.data('url'), {
- "_csrf": csrf,
- "mode": "gfm",
- "context": $editArea.data('context'),
- "text": plainText
- },
+ "_csrf": csrf,
+ "mode": "gfm",
+ "context": $editArea.data('context'),
+ "text": plainText
+ },
function (data) {
preview.innerHTML = '<div class="markdown">' + data + '</div>';
emojify.run($('.editor-preview')[0]);
@@ -603,11 +603,11 @@ function setSimpleMDE($editArea) {
setTimeout(function () {
// FIXME: still send render request when return back to edit mode
$.post($editArea.data('url'), {
- "_csrf": csrf,
- "mode": "gfm",
- "context": $editArea.data('context'),
- "text": plainText
- },
+ "_csrf": csrf,
+ "mode": "gfm",
+ "context": $editArea.data('context'),
+ "text": plainText
+ },
function (data) {
preview.innerHTML = '<div class="markdown">' + data + '</div>';
emojify.run($('.editor-preview')[0]);
@@ -652,10 +652,10 @@ function initEditor() {
$('.js-quick-pull-choice-option').change(function () {
if ($(this).val() == 'commit-to-new-branch') {
$('.quick-pull-branch-name').show();
- $('.quick-pull-branch-name input').prop('required',true);
+ $('.quick-pull-branch-name input').prop('required', true);
} else {
$('.quick-pull-branch-name').hide();
- $('.quick-pull-branch-name input').prop('required',false);
+ $('.quick-pull-branch-name input').prop('required', false);
}
});
@@ -705,7 +705,7 @@ function initEditor() {
var tree_path = parts.join('/');
$('#tree_path').val(tree_path);
- $('#preview-tab').data('context', $('#preview-tab').data('root-context') + tree_path.substring(0, tree_path.lastIndexOf("/")+1));
+ $('#preview-tab').data('context', $('#preview-tab').data('root-context') + tree_path.substring(0, tree_path.lastIndexOf("/") + 1));
}).trigger('keyup');
var $editArea = $('.repository.editor textarea#edit_area');
@@ -775,7 +775,7 @@ function initEditor() {
value = value.split('/');
value = value[value.length - 1];
- $.getJSON($editFilename.data('ec-url-prefix')+value, function(editorconfig) {
+ $.getJSON($editFilename.data('ec-url-prefix') + value, function (editorconfig) {
if (editorconfig.indent_style === 'tab') {
codeMirrorEditor.setOption("indentWithTabs", true);
codeMirrorEditor.setOption('extraKeys', {});
@@ -785,7 +785,7 @@ function initEditor() {
// - https://github.com/codemirror/CodeMirror/issues/988
// - https://codemirror.net/doc/manual.html#keymaps
codeMirrorEditor.setOption('extraKeys', {
- Tab: function(cm) {
+ Tab: function (cm) {
var spaces = Array(parseInt(cm.getOption("indentUnit")) + 1).join(" ");
cm.replaceSelection(spaces);
}
@@ -1131,7 +1131,7 @@ function initWebhookSettings() {
$($(this).data('target') + ' .nohighlight').each(function () {
var $this = $(this);
$this.removeClass('nohighlight');
- setTimeout(function(){ hljs.highlightBlock($this[0]) }, 500);
+ setTimeout(function () { hljs.highlightBlock($this[0]) }, 500);
})
})
@@ -1213,7 +1213,7 @@ $(document).ready(function () {
var filenameDict = {};
$dropzone.dropzone({
url: $dropzone.data('upload-url'),
- headers: {"X-Csrf-Token": csrf},
+ headers: { "X-Csrf-Token": csrf },
maxFiles: $dropzone.data('max-file'),
maxFilesize: $dropzone.data('max-size'),
acceptedFiles: ($dropzone.data('accepts') === '*/*') ? null : $dropzone.data('accepts'),
@@ -1336,6 +1336,10 @@ $(document).ready(function () {
window.location.href = $this.data('done-url');
});
});
+ // To make arbitrary form element to behave like a submit button
+ $('.submit-button').click(function () {
+ $($(this).data('form')).submit();
+ });
// Check or select on option to enable/disable target region
$('.enable-system').change(function () {
@@ -1459,7 +1463,7 @@ $(function () {
$('form').areYouSure();
});
- // getByteLen counts bytes in a string's UTF-8 representation.
+// getByteLen counts bytes in a string's UTF-8 representation.
function getByteLen(normalVal) {
// Force string type
normalVal = String(normalVal);
@@ -1467,19 +1471,19 @@ function getByteLen(normalVal) {
var byteLen = 0;
for (var i = 0; i < normalVal.length; i++) {
var c = normalVal.charCodeAt(i);
- byteLen += c < (1 << 7) ? 1 :
- c < (1 << 11) ? 2 :
- c < (1 << 16) ? 3 :
- c < (1 << 21) ? 4 :
- c < (1 << 26) ? 5 :
- c < (1 << 31) ? 6 : Number.NaN;
+ byteLen += c < (1 << 7) ? 1 :
+ c < (1 << 11) ? 2 :
+ c < (1 << 16) ? 3 :
+ c < (1 << 21) ? 4 :
+ c < (1 << 26) ? 5 :
+ c < (1 << 31) ? 6 : Number.NaN;
}
return byteLen;
}
function showMessageMaxLength(maxLen, textElemId, counterId) {
- var $msg = $('#'+textElemId);
- $('#'+counterId).html(maxLen - getByteLen($msg.val()));
+ var $msg = $('#' + textElemId);
+ $('#' + counterId).html(maxLen - getByteLen($msg.val()));
var onMessageKey = function (e) {
var $msg = $(this);
@@ -1492,7 +1496,7 @@ function showMessageMaxLength(maxLen, textElemId, counterId) {
remainder = 0;
}
- $('#'+counterId).html(remainder);
+ $('#' + counterId).html(remainder);
};
$msg.keyup(onMessageKey).keydown(onMessageKey);
diff --git a/public/less/_base.less b/public/less/_base.less
index 4f4c1ed5..a8a8f91a 100644
--- a/public/less/_base.less
+++ b/public/less/_base.less
@@ -384,6 +384,11 @@ footer {
.hide {
display: none;
}
+.display {
+ &.inline {
+ display: inline;
+ }
+}
.center {
text-align: center;
}
diff --git a/public/less/_user.less b/public/less/_user.less
index 8e52a980..3815434b 100644
--- a/public/less/_user.less
+++ b/public/less/_user.less
@@ -60,6 +60,9 @@
&.profile {
.ui.card {
+ .profile-avatar {
+ height: 287px;
+ }
.header {
word-break: break-all;
}
diff --git a/routes/user/profile.go b/routes/user/profile.go
index dfdc80d5..3d96f4cb 100644
--- a/routes/user/profile.go
+++ b/routes/user/profile.go
@@ -6,13 +6,11 @@ package user
import (
"fmt"
- "path"
"strings"
"github.com/Unknwon/paginater"
"github.com/gogs/gogs/models"
- "github.com/gogs/gogs/models/errors"
"github.com/gogs/gogs/pkg/context"
"github.com/gogs/gogs/pkg/setting"
"github.com/gogs/gogs/pkg/tool"
@@ -24,59 +22,30 @@ const (
STARS = "user/meta/stars"
)
-func GetUserByName(c *context.Context, name string) *models.User {
- user, err := models.GetUserByName(name)
- if err != nil {
- c.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err)
- return nil
- }
- return user
-}
-
-// GetUserByParams returns user whose name is presented in URL paramenter.
-func GetUserByParams(c *context.Context) *models.User {
- return GetUserByName(c, c.Params(":username"))
-}
-
-func Profile(c *context.Context) {
- uname := c.Params(":username")
- // Special handle for FireFox requests favicon.ico.
- if uname == "favicon.ico" {
- c.ServeFile(path.Join(setting.StaticRootPath, "public/img/favicon.png"))
- return
- } else if strings.HasSuffix(uname, ".png") {
- c.Error(404)
- return
- }
-
+func Profile(c *context.Context, puser *context.ParamsUser) {
isShowKeys := false
- if strings.HasSuffix(uname, ".keys") {
+ if strings.HasSuffix(c.Params(":username"), ".keys") {
isShowKeys = true
}
- ctxUser := GetUserByName(c, strings.TrimSuffix(uname, ".keys"))
- if c.Written() {
- return
- }
-
// Show SSH keys.
if isShowKeys {
- ShowSSHKeys(c, ctxUser.ID)
+ ShowSSHKeys(c, puser.ID)
return
}
- if ctxUser.IsOrganization() {
+ if puser.IsOrganization() {
showOrgProfile(c)
return
}
- c.Data["Title"] = ctxUser.DisplayName()
- c.Data["PageIsUserProfile"] = true
- c.Data["Owner"] = ctxUser
+ c.Title(puser.DisplayName())
+ c.PageIs("UserProfile")
+ c.Data["Owner"] = puser
- orgs, err := models.GetOrgsByUserID(ctxUser.ID, c.IsLogged && (c.User.IsAdmin || c.User.ID == ctxUser.ID))
+ orgs, err := models.GetOrgsByUserID(puser.ID, c.IsLogged && (c.User.IsAdmin || c.User.ID == puser.ID))
if err != nil {
- c.Handle(500, "GetOrgsByUserIDDesc", err)
+ c.ServerError("GetOrgsByUserIDDesc", err)
return
}
@@ -86,7 +55,7 @@ func Profile(c *context.Context) {
c.Data["TabName"] = tab
switch tab {
case "activity":
- retrieveFeeds(c, ctxUser, -1, true)
+ retrieveFeeds(c, puser.User, -1, true)
if c.Written() {
return
}
@@ -96,65 +65,52 @@ func Profile(c *context.Context) {
page = 1
}
- showPrivate := c.IsLogged && (ctxUser.ID == c.User.ID || c.User.IsAdmin)
+ showPrivate := c.IsLogged && (puser.ID == c.User.ID || c.User.IsAdmin)
c.Data["Repos"], err = models.GetUserRepositories(&models.UserRepoOptions{
- UserID: ctxUser.ID,
+ UserID: puser.ID,
Private: showPrivate,
Page: page,
PageSize: setting.UI.User.RepoPagingNum,
})
if err != nil {
- c.Handle(500, "GetRepositories", err)
+ c.ServerError("GetRepositories", err)
return
}
- count := models.CountUserRepositories(ctxUser.ID, showPrivate)
+ count := models.CountUserRepositories(puser.ID, showPrivate)
c.Data["Page"] = paginater.New(int(count), setting.UI.User.RepoPagingNum, page, 5)
}
- c.HTML(200, PROFILE)
+ c.Success(PROFILE)
}
-func Followers(c *context.Context) {
- u := GetUserByParams(c)
- if c.Written() {
- return
- }
- c.Data["Title"] = u.DisplayName()
+func Followers(c *context.Context, puser *context.ParamsUser) {
+ c.Title(puser.DisplayName())
+ c.PageIs("Followers")
c.Data["CardsTitle"] = c.Tr("user.followers")
- c.Data["PageIsFollowers"] = true
- c.Data["Owner"] = u
- repo.RenderUserCards(c, u.NumFollowers, u.GetFollowers, FOLLOWERS)
+ c.Data["Owner"] = puser
+ repo.RenderUserCards(c, puser.NumFollowers, puser.GetFollowers, FOLLOWERS)
}
-func Following(c *context.Context) {
- u := GetUserByParams(c)
- if c.Written() {
- return
- }
- c.Data["Title"] = u.DisplayName()
+func Following(c *context.Context, puser *context.ParamsUser) {
+ c.Title(puser.DisplayName())
+ c.PageIs("Following")
c.Data["CardsTitle"] = c.Tr("user.following")
- c.Data["PageIsFollowing"] = true
- c.Data["Owner"] = u
- repo.RenderUserCards(c, u.NumFollowing, u.GetFollowing, FOLLOWERS)
+ c.Data["Owner"] = puser
+ repo.RenderUserCards(c, puser.NumFollowing, puser.GetFollowing, FOLLOWERS)
}
func Stars(c *context.Context) {
}
-func Action(c *context.Context) {
- u := GetUserByParams(c)
- if c.Written() {
- return
- }
-
+func Action(c *context.Context, puser *context.ParamsUser) {
var err error
switch c.Params(":action") {
case "follow":
- err = models.FollowUser(c.User.ID, u.ID)
+ err = models.FollowUser(c.UserID(), puser.ID)
case "unfollow":
- err = models.UnfollowUser(c.User.ID, u.ID)
+ err = models.UnfollowUser(c.UserID(), puser.ID)
}
if err != nil {
@@ -164,7 +120,7 @@ func Action(c *context.Context) {
redirectTo := c.Query("redirect_to")
if !tool.IsSameSiteURLPath(redirectTo) {
- redirectTo = u.HomeLink()
+ redirectTo = puser.HomeLink()
}
c.Redirect(redirectTo)
}
diff --git a/templates/.VERSION b/templates/.VERSION
index 31eb16e2..a87c12f6 100644
--- a/templates/.VERSION
+++ b/templates/.VERSION
@@ -1 +1 @@
-0.11.76.1204
+0.11.77.1206
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index 96f77722..27b2f4c7 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -160,9 +160,13 @@
{{end}}
<div class="divider"></div>
- <a class="item" href="{{AppSubURL}}/user/logout">
- <i class="octicon octicon-sign-out"></i> {{.i18n.Tr "sign_out"}}
- </a>
+
+ <form id="logout-form" class="item" action="{{AppSubURL}}/user/logout" method="POST">
+ {{.CSRFTokenHTML}}
+ <div class="submit-button" data-form="#logout-form">
+ <i class="octicon octicon-sign-out"></i> {{.i18n.Tr "sign_out"}}
+ </div>
+ </form>
</div><!-- end content avatar menu -->
</div><!-- end dropdown avatar menu -->
</div><!-- end signed user right menu -->
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index d15b5dde..cacda1ed 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -20,22 +20,28 @@
{{if not $.IsGuest}}
<div class="ui right">
- <div class="ui labeled button" tabindex="0">
- <a class="ui basic button" href="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}">
- <i class="eye{{if not $.IsWatchingRepo}} slash outline{{end}} icon"></i>{{if $.IsWatchingRepo}}{{$.i18n.Tr "repo.unwatch"}}{{else}}{{$.i18n.Tr "repo.watch"}}{{end}}
- </a>
- <a class="ui basic label" href="{{.Link}}/watchers">
- {{.NumWatches}}
- </a>
- </div>
- <div class="ui labeled button" tabindex="0">
- <a class="ui basic button" href="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}un{{end}}star?redirect_to={{$.Link}}">
- <i class="star{{if not $.IsStaringRepo}} outline{{end}} icon"></i>{{if $.IsStaringRepo}}{{$.i18n.Tr "repo.unstar"}}{{else}}{{$.i18n.Tr "repo.star"}}{{end}}
- </a>
- <a class="ui basic label" href="{{.Link}}/stars">
- {{.NumStars}}
- </a>
- </div>
+ <form class="display inline" action="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}" method="POST">
+ {{$.CSRFTokenHTML}}
+ <div class="ui labeled button" tabindex="0">
+ <button class="ui basic button">
+ <i class="eye{{if not $.IsWatchingRepo}} slash outline{{end}} icon"></i>{{if $.IsWatchingRepo}}{{$.i18n.Tr "repo.unwatch"}}{{else}}{{$.i18n.Tr "repo.watch"}}{{end}}
+ </button>
+ <a class="ui basic label" href="{{.Link}}/watchers">
+ {{.NumWatches}}
+ </a>
+ </div>
+ </form>
+ <form class="display inline" action="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}un{{end}}star?redirect_to={{$.Link}}" method="POST">
+ {{$.CSRFTokenHTML}}
+ <div class="ui labeled button" tabindex="0">
+ <button class="ui basic button">
+ <i class="star{{if not $.IsStaringRepo}} outline{{end}} icon"></i>{{if $.IsStaringRepo}}{{$.i18n.Tr "repo.unstar"}}{{else}}{{$.i18n.Tr "repo.star"}}{{end}}
+ </button>
+ <a class="ui basic label" href="{{.Link}}/stars">
+ {{.NumStars}}
+ </a>
+ </div>
+ </form>
{{if .CanBeForked}}
<div class="ui labeled button" tabindex="0">
<a class="ui basic button {{if eq .OwnerID $.LoggedUserID}}poping up{{end}}" href="{{AppSubURL}}/repo/fork/{{.ID}}">
diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl
index d60fce56..285aa12c 100644
--- a/templates/user/profile.tmpl
+++ b/templates/user/profile.tmpl
@@ -5,12 +5,12 @@
<div class="ui five wide column">
<div class="ui card">
{{if eq .LoggedUserName .Owner.Name}}
- <a class="image poping up" href="{{AppSubURL}}/user/settings/avatar" id="profile-avatar" data-content="{{.i18n.Tr "user.change_avatar"}}" data-variation="inverted tiny" data-position="bottom center">
- <img src="{{AppendAvatarSize .Owner.RelAvatarLink 290}}" title="{{.Owner.Name}}"/>
+ <a class="profile-avatar image poping up" href="{{AppSubURL}}/user/settings/avatar" id="profile-avatar" data-content="{{.i18n.Tr "user.change_avatar"}}" data-variation="inverted tiny" data-position="bottom center">
+ <img src="{{AppendAvatarSize .Owner.RelAvatarLink 287}}" title="{{.Owner.Name}}"/>
</a>
{{else}}
- <span class="image">
- <img src="{{AppendAvatarSize .Owner.RelAvatarLink 290}}" title="{{.Owner.Name}}"/>
+ <span class="profile-avatar image">
+ <img src="{{AppendAvatarSize .Owner.RelAvatarLink 287}}" title="{{.Owner.Name}}"/>
</span>
{{end}}
<div class="content">
@@ -61,13 +61,23 @@
</li>
{{end}}
{{if and .IsLogged (ne .LoggedUserName .Owner.Name)}}
- <li class="follow">
- {{if .LoggedUser.IsFollowing .Owner.ID}}
- <a class="ui basic red button" href="{{.Link}}/action/unfollow?redirect_to={{$.Link}}"><i class="octicon octicon-person"></i> {{.i18n.Tr "user.unfollow"}}</a>
- {{else}}
- <a class="ui basic green button" href="{{.Link}}/action/follow?redirect_to={{$.Link}}"><i class="octicon octicon-person"></i> {{.i18n.Tr "user.follow"}}</a>
- {{end}}
- </li>
+ <li class="follow">
+ {{if .LoggedUser.IsFollowing .Owner.ID}}
+ <form action="{{.Link}}/action/unfollow?redirect_to={{$.Link}}" method="POST">
+ {{.CSRFTokenHTML}}
+ <button class="ui basic red button">
+ <i class="octicon octicon-person"></i> {{.i18n.Tr "user.unfollow"}}
+ </button>
+ </form>
+ {{else}}
+ <form action="{{.Link}}/action/follow?redirect_to={{$.Link}}" method="POST">
+ {{.CSRFTokenHTML}}
+ <button class="ui basic green button">
+ <i class="octicon octicon-person"></i> {{.i18n.Tr "user.follow"}}
+ </button>
+ </form>
+ {{end}}
+ </li>
{{end}}
</ul>
</div>