From d2b53dd43b3bc9719985033bc92b76abb9515b4d Mon Sep 17 00:00:00 2001
From: Unknown <joe2010xtmf@163.com>
Date: Sun, 13 Apr 2014 21:00:12 -0400
Subject: [PATCH] Add weibo oauth

---
 README.md                       |  3 +-
 README_ZH.md                    |  3 +-
 conf/app.ini                    |  8 +++++
 gogs.go                         |  2 +-
 models/action.go                | 14 ++++++--
 models/oauth2.go                |  6 ++++
 models/repo.go                  |  2 +-
 modules/base/conf.go            |  2 +-
 modules/base/template.go        | 22 +++++++++++-
 modules/middleware/repo.go      |  7 ++++
 modules/social/social.go        | 62 ++++++++++++++++++++++++++++++++-
 routers/admin/admin.go          |  6 ++++
 routers/user/setting.go         | 24 ++++++++++---
 templates/admin/config.tmpl     | 26 ++++++++++++--
 templates/repo/toolbar.tmpl     |  2 +-
 templates/user/publickey.tmpl   |  4 +--
 templates/user/setting_nav.tmpl |  1 +
 templates/user/signin.tmpl      |  5 +--
 templates/user/social.tmpl      | 17 +++++++++
 web.go                          |  3 +-
 20 files changed, 198 insertions(+), 21 deletions(-)
 create mode 100644 templates/user/social.tmpl

diff --git a/README.md b/README.md
index 34f8b66f06..20dc8806dd 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language
 
 ![Demo](http://gowalker.org/public/gogs_demo.gif)
 
-##### Current version: 0.2.8 Alpha
+##### Current version: 0.2.9 Alpha
 
 ### NOTICES
 
@@ -40,6 +40,7 @@ More importantly, Gogs only needs one binary to setup your own project hosting o
 - Mail service(register, issue).
 - Administration panel.
 - Supports MySQL, PostgreSQL and SQLite3.
+- Social account login(GitHub, Google, QQ, Weibo)
 
 ## Installation
 
diff --git a/README_ZH.md b/README_ZH.md
index beb5a1050b..97ab07ff23 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。
 
 ![Demo](http://gowalker.org/public/gogs_demo.gif)
 
-##### 当前版本:0.2.8 Alpha
+##### 当前版本:0.2.9 Alpha
 
 ## 开发目的
 
@@ -31,6 +31,7 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依
 - 邮件服务(注册、Issue)
 - 管理员面板
 - 支持 MySQL、PostgreSQL 以及 SQLite3
+- 社交帐号登录(GitHub、Google、QQ、微博)
 
 ## 安装部署
 
diff --git a/conf/app.ini b/conf/app.ini
index 4eaf0a33c2..c70919961c 100644
--- a/conf/app.ini
+++ b/conf/app.ini
@@ -109,6 +109,14 @@ SCOPES = all
 AUTH_URL = https://api.twitter.com/oauth/authorize
 TOKEN_URL = https://api.twitter.com/oauth/access_token
 
+[oauth.weibo]
+ENABLED = false
+CLIENT_ID = 
+CLIENT_SECRET = 
+SCOPES = all
+AUTH_URL = https://api.weibo.com/oauth2/authorize
+TOKEN_URL = https://api.weibo.com/oauth2/access_token
+
 [cache]
 ; Either "memory", "redis", or "memcache", default is "memory"
 ADAPTER = memory
diff --git a/gogs.go b/gogs.go
index 7a7d3ac873..de2bbc7777 100644
--- a/gogs.go
+++ b/gogs.go
@@ -19,7 +19,7 @@ import (
 // Test that go1.2 tag above is included in builds. main.go refers to this definition.
 const go12tag = true
 
-const APP_VER = "0.2.8.0413 Alpha"
+const APP_VER = "0.2.9.0413 Alpha"
 
 func init() {
 	base.AppVer = APP_VER
diff --git a/models/action.go b/models/action.go
index a642a82c98..3edb884e27 100644
--- a/models/action.go
+++ b/models/action.go
@@ -8,6 +8,8 @@ import (
 	"encoding/json"
 	"time"
 
+	// "github.com/gogits/git"
+
 	"github.com/gogits/gogs/modules/base"
 	"github.com/gogits/gogs/modules/log"
 )
@@ -22,6 +24,7 @@ const (
 	OP_CREATE_ISSUE
 	OP_PULL_REQUEST
 	OP_TRANSFER_REPO
+	OP_PUSH_TAG
 )
 
 // Action represents user operation type and other information to repository.,
@@ -67,7 +70,14 @@ func (a Action) GetContent() string {
 // CommitRepoAction adds new action for committing repository.
 func CommitRepoAction(userId int64, userName, actEmail string,
 	repoId int64, repoName string, refName string, commit *base.PushCommits) error {
-	log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName)
+	// log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName)
+
+	opType := OP_COMMIT_REPO
+	// Check it's tag push or branch.
+	// if git.IsTagExist(RepoPath(userName, repoName), refName) {
+	// 	opType = OP_PUSH_TAG
+	// 	commit = &base.PushCommits{}
+	// }
 
 	bs, err := json.Marshal(commit)
 	if err != nil {
@@ -76,7 +86,7 @@ func CommitRepoAction(userId int64, userName, actEmail string,
 	}
 
 	if err = NotifyWatchers(&Action{ActUserId: userId, ActUserName: userName, ActEmail: actEmail,
-		OpType: OP_COMMIT_REPO, Content: string(bs), RepoId: repoId, RepoName: repoName, RefName: refName}); err != nil {
+		OpType: opType, Content: string(bs), RepoId: repoId, RepoName: repoName, RefName: refName}); err != nil {
 		log.Error("action.CommitRepoAction(notify watchers): %d/%s", userId, repoName)
 		return err
 	}
diff --git a/models/oauth2.go b/models/oauth2.go
index 38d21fda1c..d1ae4611b8 100644
--- a/models/oauth2.go
+++ b/models/oauth2.go
@@ -68,3 +68,9 @@ func GetOauth2ById(id int64) (oa *Oauth2, err error) {
 	}
 	return oa, nil
 }
+
+// GetOauthByUserId returns list of oauthes that are releated to given user.
+func GetOauthByUserId(uid int64) (oas []*Oauth2, err error) {
+	err = orm.Find(&oas, Oauth2{Uid: uid})
+	return oas, err
+}
diff --git a/models/repo.go b/models/repo.go
index 1a5a95f047..bb0c164e24 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -75,9 +75,9 @@ type Repository struct {
 	NumStars        int
 	NumForks        int
 	NumIssues       int
-	NumReleases     int `xorm:"NOT NULL"`
 	NumClosedIssues int
 	NumOpenIssues   int `xorm:"-"`
+	NumTags         int `xorm:"-"`
 	IsPrivate       bool
 	IsMirror        bool
 	IsBare          bool
diff --git a/modules/base/conf.go b/modules/base/conf.go
index 0eca5f4fcb..957ec57b4d 100644
--- a/modules/base/conf.go
+++ b/modules/base/conf.go
@@ -38,7 +38,7 @@ type OauthInfo struct {
 // Oauther represents oauth service.
 type Oauther struct {
 	GitHub, Google, Tencent bool
-	Twitter                 bool
+	Twitter, Weibo          bool
 	OauthInfos              map[string]*OauthInfo
 }
 
diff --git a/modules/base/template.go b/modules/base/template.go
index 6241497969..79aeeb9d77 100644
--- a/modules/base/template.go
+++ b/modules/base/template.go
@@ -92,6 +92,7 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
 	"DiffTypeToStr":     DiffTypeToStr,
 	"DiffLineTypeToStr": DiffLineTypeToStr,
 	"ShortSha":          ShortSha,
+	"Oauth2Icon":        Oauth2Icon,
 }
 
 type Actioner interface {
@@ -109,7 +110,7 @@ func ActionIcon(opType int) string {
 	switch opType {
 	case 1: // Create repository.
 		return "plus-circle"
-	case 5: // Commit repository.
+	case 5, 9: // Commit repository.
 		return "arrow-circle-o-right"
 	case 6: // Create issue.
 		return "exclamation-circle"
@@ -127,6 +128,7 @@ const (
 	TPL_CREATE_ISSUE   = `<a href="/user/%s">%s</a> opened issue <a href="/%s/issues/%s">%s#%s</a>
 <div><img src="%s?s=16" alt="user-avatar"/> %s</div>`
 	TPL_TRANSFER_REPO = `<a href="/user/%s">%s</a> transfered repository <code>%s</code> to <a href="/%s">%s</a>`
+	TPL_PUSH_TAG      = `<a href="/user/%s">%s</a> pushed tag <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>`
 )
 
 type PushCommit struct {
@@ -174,6 +176,8 @@ func ActionDesc(act Actioner) string {
 	case 8: // Transfer repository.
 		newRepoLink := content + "/" + repoName
 		return fmt.Sprintf(TPL_TRANSFER_REPO, actUserName, actUserName, repoLink, newRepoLink, newRepoLink)
+	case 9: // Push tag.
+		return fmt.Sprintf(TPL_PUSH_TAG, actUserName, actUserName, repoLink, branch, branch, repoLink, repoLink)
 	default:
 		return "invalid type"
 	}
@@ -197,3 +201,19 @@ func DiffLineTypeToStr(diffType int) string {
 	}
 	return "same"
 }
+
+func Oauth2Icon(t int) string {
+	switch t {
+	case 1:
+		return "fa-github-square"
+	case 2:
+		return "fa-google-plus-square"
+	case 3:
+		return "fa-twitter-square"
+	case 4:
+		return "fa-linux"
+	case 5:
+		return "fa-weibo"
+	}
+	return ""
+}
diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go
index 1e79ce9870..82c1c2dbf6 100644
--- a/modules/middleware/repo.go
+++ b/modules/middleware/repo.go
@@ -123,6 +123,13 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
 		ctx.Repo.GitRepo = gitRepo
 		ctx.Repo.RepoLink = "/" + user.Name + "/" + repo.Name
 
+		tags, err := ctx.Repo.GitRepo.GetTags()
+		if err != nil {
+			ctx.Handle(500, "RepoAssignment(GetTags))", err)
+			return
+		}
+		ctx.Repo.Repository.NumTags = len(tags)
+
 		ctx.Data["Title"] = user.Name + "/" + repo.Name
 		ctx.Data["Repository"] = repo
 		ctx.Data["Owner"] = user
diff --git a/modules/social/social.go b/modules/social/social.go
index 230f478fe4..c2ee541776 100644
--- a/modules/social/social.go
+++ b/modules/social/social.go
@@ -48,7 +48,7 @@ func NewOauthService() {
 	base.OauthService.OauthInfos = make(map[string]*base.OauthInfo)
 
 	socialConfigs := make(map[string]*oauth.Config)
-	allOauthes := []string{"github", "google", "qq", "twitter"}
+	allOauthes := []string{"github", "google", "qq", "twitter", "weibo"}
 	// Load all OAuth config data.
 	for _, name := range allOauthes {
 		base.OauthService.OauthInfos[name] = &base.OauthInfo{
@@ -98,6 +98,13 @@ func NewOauthService() {
 		enabledOauths = append(enabledOauths, "Twitter")
 	}
 
+	// Weibo.
+	if base.Cfg.MustBool("oauth.weibo", "ENABLED") {
+		base.OauthService.Weibo = true
+		newWeiboOauth(socialConfigs["weibo"])
+		enabledOauths = append(enabledOauths, "Weibo")
+	}
+
 	log.Info("Oauth Service Enabled %s", enabledOauths)
 }
 
@@ -331,3 +338,56 @@ func (s *SocialTwitter) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo
 	// }, nil
 	return nil, nil
 }
+
+//  __      __       ._____.
+// /  \    /  \ ____ |__\_ |__   ____
+// \   \/\/   // __ \|  || __ \ /  _ \
+//  \        /\  ___/|  || \_\ (  <_> )
+//   \__/\  /  \___  >__||___  /\____/
+//        \/       \/        \/
+
+type SocialWeibo struct {
+	Token *oauth.Token
+	*oauth.Transport
+}
+
+func (s *SocialWeibo) Type() int {
+	return models.OT_WEIBO
+}
+
+func newWeiboOauth(config *oauth.Config) {
+	SocialMap["weibo"] = &SocialWeibo{
+		Transport: &oauth.Transport{
+			Config:    config,
+			Transport: http.DefaultTransport,
+		},
+	}
+}
+
+func (s *SocialWeibo) SetRedirectUrl(url string) {
+	s.Transport.Config.RedirectURL = url
+}
+
+func (s *SocialWeibo) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
+	transport := &oauth.Transport{Token: token}
+	var data struct {
+		Id   string `json:"id"`
+		Name string `json:"name"`
+	}
+	var err error
+
+	reqUrl := "https://api.weibo.com/2/users/show.json"
+	r, err := transport.Client().Get(reqUrl)
+	if err != nil {
+		return nil, err
+	}
+	defer r.Body.Close()
+	if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
+		return nil, err
+	}
+	return &BasicUserInfo{
+		Identity: data.Id,
+		Name:     data.Name,
+	}, nil
+	return nil, nil
+}
diff --git a/routers/admin/admin.go b/routers/admin/admin.go
index 18a43ff817..d0f737e645 100644
--- a/routers/admin/admin.go
+++ b/routers/admin/admin.go
@@ -153,6 +153,12 @@ func Config(ctx *middleware.Context) {
 		ctx.Data["Mailer"] = base.MailService
 	}
 
+	ctx.Data["OauthEnabled"] = false
+	if base.OauthService != nil {
+		ctx.Data["OauthEnabled"] = true
+		ctx.Data["Oauther"] = base.OauthService
+	}
+
 	ctx.Data["CacheAdapter"] = base.CacheAdapter
 	ctx.Data["CacheConfig"] = base.CacheConfig
 
diff --git a/routers/user/setting.go b/routers/user/setting.go
index 7e66ad3599..a8fdc116c6 100644
--- a/routers/user/setting.go
+++ b/routers/user/setting.go
@@ -69,6 +69,20 @@ func SettingPost(ctx *middleware.Context, form auth.UpdateProfileForm) {
 	ctx.Redirect("/user/setting")
 }
 
+func SettingSocial(ctx *middleware.Context) {
+	ctx.Data["Title"] = "Social Account"
+	ctx.Data["PageIsUserSetting"] = true
+	ctx.Data["IsUserPageSettingSocial"] = true
+	socials, err := models.GetOauthByUserId(ctx.User.Id)
+	if err != nil {
+		ctx.Handle(500, "user.SettingSocial", err)
+		return
+	}
+
+	ctx.Data["Socials"] = socials
+	ctx.HTML(200, "user/social")
+}
+
 func SettingPassword(ctx *middleware.Context) {
 	ctx.Data["Title"] = "Password"
 	ctx.Data["PageIsUserSetting"] = true
@@ -147,7 +161,7 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) {
 
 	// Add new SSH key.
 	if ctx.Req.Method == "POST" {
-		if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) {
+		if ctx.HasError() {
 			ctx.HTML(200, "user/publickey")
 			return
 		}
@@ -162,11 +176,13 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) {
 				ctx.RenderWithErr("Public key name has been used", "user/publickey", &form)
 				return
 			}
-			ctx.Handle(200, "ssh.AddPublicKey", err)
-			log.Trace("%s User SSH key added: %s", ctx.Req.RequestURI, ctx.User.LowerName)
+			ctx.Handle(500, "ssh.AddPublicKey", err)
 			return
 		} else {
-			ctx.Data["AddSSHKeySuccess"] = true
+			log.Trace("%s User SSH key added: %s", ctx.Req.RequestURI, ctx.User.LowerName)
+			ctx.Flash.Success("New SSH Key has been added!")
+			ctx.Redirect("/user/setting/ssh")
+			return
 		}
 	}
 
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl
index 31cfb77bad..757a800c20 100644
--- a/templates/admin/config.tmpl
+++ b/templates/admin/config.tmpl
@@ -88,12 +88,34 @@
                 <dl class="dl-horizontal admin-dl-horizontal">
                     <dt>Enabled</dt>
                     <dd><i class="fa fa{{if .MailerEnabled}}-check{{end}}-square-o"></i></dd>
-                    <dt>Name</dt>
+                    {{if .MailerEnabled}}<dt>Name</dt>
                     <dd>{{.Mailer.Name}}</dd>
                     <dt>Host</dt>
                     <dd>{{.Mailer.Host}}</dd>
                     <dt>User</dt>
-                    <dd>{{.Mailer.User}}</dd>
+                    <dd>{{.Mailer.User}}</dd>{{end}}
+                </dl>
+            </div>
+        </div>
+
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                OAuth Configuration
+            </div>
+
+            <div class="panel-body">
+                <dl class="dl-horizontal admin-dl-horizontal">
+                    <dt>Enabled</dt>
+                    <dd><i class="fa fa{{if .OauthEnabled}}-check{{end}}-square-o"></i></dd>
+                    {{if .OauthEnabled}}<dt>GitHub</dt>
+                    <dd><i class="fa fa{{if .Oauther.GitHub}}-check{{end}}-square-o"></i></dd>
+                    <dt>Google</dt>
+                    <dd><i class="fa fa{{if .Oauther.Google}}-check{{end}}-square-o"></i></dd>
+                    <dt>Tencent QQ</dt>
+                    <dd><i class="fa fa{{if .Oauther.Tencent}}-check{{end}}-square-o"></i></dd>
+                    <dt>Weibo</dt>
+                    <dd><i class="fa fa{{if .Oauther.Weibo}}-check{{end}}-square-o"></i></dd>
+                    <dd>{{.Mailer.User}}</dd>{{end}}
                 </dl>
             </div>
         </div>
diff --git a/templates/repo/toolbar.tmpl b/templates/repo/toolbar.tmpl
index 9c137e5179..bde5bc29e1 100644
--- a/templates/repo/toolbar.tmpl
+++ b/templates/repo/toolbar.tmpl
@@ -13,7 +13,7 @@
                     <li class="tmp">{{if .IsRepoToolbarIssuesList}}<a href="{{.RepoLink}}/issues/new"><button class="btn btn-primary btn-sm">New Issue</button>
                     </a>{{end}}</li>
                     {{end}}
-                    <li class="{{if .IsRepoToolbarReleases}}active{{end}}"><a href="{{.RepoLink}}/releases">{{if .Repository.NumReleases}}<span class="badge">{{.Repository.NumReleases}}</span> {{end}}Releases</a></li>
+                    <li class="{{if .IsRepoToolbarReleases}}active{{end}}"><a href="{{.RepoLink}}/releases">{{if .Repository.NumTags}}<span class="badge">{{.Repository.NumTags}}</span> {{end}}Releases</a></li>
                     {{if .IsRepoToolbarReleases}}
                     <li class="tmp">{{if not .IsRepoReleaseNew}}<a href="{{.RepoLink}}/releases/new"><button class="btn btn-primary btn-sm">New Release</button></a>{{end}}</li>
                     {{end}}
diff --git a/templates/user/publickey.tmpl b/templates/user/publickey.tmpl
index ecdeb035d2..29cfd8f0eb 100644
--- a/templates/user/publickey.tmpl
+++ b/templates/user/publickey.tmpl
@@ -4,8 +4,8 @@
     {{template "user/setting_nav" .}}
     <div id="user-setting-container" class="col-md-9">
         <div id="ssh-keys">
-            <h4>SSH Keys</h4>{{if .AddSSHKeySuccess}}
-            <p class="alert alert-success">New SSH Key has been added !</p>{{else if .HasError}}<p class="alert alert-danger">{{.ErrorMsg}}</p>{{end}}
+            <h4>SSH Keys</h4>
+            {{template "base/alert" .}}
             <ul id="ssh-keys-list" class="list-group">
                 <li class="list-group-item"><span class="name">SSH Key's name</span></li>
                 {{range .Keys}}
diff --git a/templates/user/setting_nav.tmpl b/templates/user/setting_nav.tmpl
index c0f2ae03dd..9c7ae5208f 100644
--- a/templates/user/setting_nav.tmpl
+++ b/templates/user/setting_nav.tmpl
@@ -2,6 +2,7 @@
     <h4>Account Setting</h4>
     <ul class="list-group">
         <li class="list-group-item{{if .IsUserPageSetting}} list-group-item-success{{end}}"><a href="/user/setting">Account Profile</a></li>
+        <li class="list-group-item{{if .IsUserPageSettingSocial}} list-group-item-success{{end}}"><a href="/user/setting/social">Social Account</a></li>
         <li class="list-group-item{{if .IsUserPageSettingPasswd}} list-group-item-success{{end}}"><a href="/user/setting/password">Password</a></li>
         <!-- <li class="list-group-item{{if .IsUserPageSettingNotify}} list-group-item-success{{end}}"><a href="/user/setting/notification">Notifications</a></li> -->
         <li class="list-group-item{{if .IsUserPageSettingSSH}} list-group-item-success{{end}}"><a href="/user/setting/ssh/">SSH Keys</a></li>
diff --git a/templates/user/signin.tmpl b/templates/user/signin.tmpl
index d402c29238..955c82f430 100644
--- a/templates/user/signin.tmpl
+++ b/templates/user/signin.tmpl
@@ -61,8 +61,9 @@
             </a>-->
             {{if .OauthService.GitHub}}<a href="/user/login/github?next=/user/sign_up" class="btn btn-default"><i class="fa fa-github-square fa-2x"></i><span>GitHub</span></a>{{end}}
             {{if .OauthService.Google}}<a href="/user/login/google?next=/user/sign_up" class="btn btn-default"><i class="fa fa-google-plus-square fa-2x"></i><span>Google</span></a>{{end}}
-            {{if .OauthService.Tencent}}<a href="/user/login/twitter?next=/user/sign_up" class="btn btn-default"><i class="fa fa-twitter-square fa-2x"></i><span>Twitter</span></a>{{end}}
-            {{if .OauthService.Tencent}}<a href="/user/login/qq?next=/user/sign_up" class="btn btn-default"><i class="fa fa-linux fa-2x"></i><span>QQ</span></a>{{end}}
+            {{if .OauthService.Twitter}}<a href="/user/login/twitter?next=/user/sign_up" class="btn btn-default"><i class="fa fa-twitter-square fa-2x"></i><span>Twitter</span></a>{{end}}
+            {{if .OauthService.Tencent}}<a href="/user/login/qq?next=/user/sign_up" class="btn btn-default"><i class="fa fa-linux fa-2x"></i><span>Tencent QQ</span></a>{{end}}
+            {{if .OauthService.Weibo}}<a href="/user/login/weibo?next=/user/sign_up" class="btn btn-default"><i class="fa fa-weibo fa-2x"></i><span>Weibo</span></a>{{end}}
         </div>
         {{end}}{{end}}
     </form>
diff --git a/templates/user/social.tmpl b/templates/user/social.tmpl
new file mode 100644
index 0000000000..f0b1132385
--- /dev/null
+++ b/templates/user/social.tmpl
@@ -0,0 +1,17 @@
+{{template "base/head" .}}
+{{template "base/navbar" .}}
+<div id="body" class="container" data-page="user">
+    {{template "user/setting_nav" .}}
+    <div id="user-setting-container" class="col-md-9">
+        <div id="ssh-keys">
+            <h4>Social Account</h4>
+            {{template "base/alert" .}}
+            <ul id="ssh-keys-list" class="list-group">
+                {{range .Socials}}
+                    <i class="fa {{Oauth2Icon .Type}} fa-3x"></i>
+                {{end}}
+            </ul>
+        </div>
+    </div>
+</div>
+{{template "base/footer" .}}
\ No newline at end of file
diff --git a/web.go b/web.go
index 8ae074ec01..268d9e71da 100644
--- a/web.go
+++ b/web.go
@@ -63,7 +63,7 @@ func runWeb(*cli.Context) {
 		SignInRequire: base.Service.RequireSignInView,
 		DisableCsrf:   true,
 	})
-
+	
 	reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true})
 
 	bindIgnErr := middleware.BindIgnErr
@@ -108,6 +108,7 @@ func runWeb(*cli.Context) {
 		r.Post("/forget_password", user.ForgotPasswdPost)
 	})
 	m.Group("/user/setting", func(r martini.Router) {
+		r.Get("/social", user.SettingSocial)
 		r.Get("/password", user.SettingPassword)
 		r.Post("/password", bindIgnErr(auth.UpdatePasswdForm{}), user.SettingPasswordPost)
 		r.Any("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys)