Browse Source

Merge branch 'dev' of github.com:gogits/gogs into dev

lunnyxiao 10 years ago
parent
commit
7c7014262b

+ 2 - 1
.gitignore

@@ -32,7 +32,8 @@ _testmain.go
 
 *.exe
 *.exe~
-gogs
+/gogs
+profile/
 __pycache__
 *.pem
 output*

+ 15 - 7
cmd/web.go

@@ -19,7 +19,9 @@ import (
 	"github.com/macaron-contrib/csrf"
 	"github.com/macaron-contrib/i18n"
 	"github.com/macaron-contrib/session"
+	"github.com/macaron-contrib/toolbox"
 
+	"github.com/gogits/gogs/models"
 	"github.com/gogits/gogs/modules/auth"
 	"github.com/gogits/gogs/modules/auth/apiv1"
 	"github.com/gogits/gogs/modules/avatar"
@@ -62,20 +64,20 @@ func newMacaron() *macaron.Macaron {
 	m := macaron.New()
 	m.Use(macaron.Logger())
 	m.Use(macaron.Recovery())
-	if setting.EnableGzip {
-		m.Use(macaron.Gzip())
-	}
 	m.Use(macaron.Static("public",
 		macaron.StaticOptions{
 			SkipLogging: !setting.DisableRouterLog,
 		},
 	))
+	if setting.EnableGzip {
+		m.Use(macaron.Gzip())
+	}
 	m.Use(macaron.Renderer(macaron.RenderOptions{
 		Directory:  path.Join(setting.StaticRootPath, "templates"),
 		Funcs:      []template.FuncMap{base.TemplateFuncs},
 		IndentJSON: macaron.Env != macaron.PROD,
 	}))
-	m.Use(i18n.I18n(i18n.LocaleOptions{
+	m.Use(i18n.I18n(i18n.Options{
 		Langs:    setting.Langs,
 		Names:    setting.Names,
 		Redirect: true,
@@ -94,6 +96,14 @@ func newMacaron() *macaron.Macaron {
 		Secret:    setting.SecretKey,
 		SetCookie: true,
 	}))
+	m.Use(toolbox.Toolboxer(m, toolbox.Options{
+		HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{
+			&toolbox.HealthCheckFuncDesc{
+				Desc: "Database connection",
+				Func: models.Ping,
+			},
+		},
+	}))
 	m.Use(middleware.Contexter())
 	return m
 }
@@ -208,7 +218,6 @@ func runWeb(*cli.Context) {
 
 	if macaron.Env == macaron.DEV {
 		m.Get("/template/*", dev.TemplatePreview)
-		dev.RegisterDebugRoutes(m)
 	}
 
 	reqTrueOwner := middleware.RequireTrueOwner()
@@ -245,8 +254,7 @@ func runWeb(*cli.Context) {
 		r.Get("/settings", repo.Settings)
 		r.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
 		m.Group("/settings", func(r *macaron.Router) {
-			r.Get("/collaboration", repo.Collaboration)
-			r.Post("/collaboration", repo.CollaborationPost)
+			r.Route("/collaboration", "GET,POST", repo.SettingsCollaboration)
 			r.Get("/hooks", repo.WebHooks)
 			r.Get("/hooks/add", repo.WebHooksAdd)
 			r.Post("/hooks/add", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksAddPost)

+ 4 - 0
conf/locale/locale_en-US.ini

@@ -86,6 +86,7 @@ username_password_incorrect = Username or password is not correct.
 enterred_invalid_repo_name = Please make sure you entered repository name is correct.
 enterred_invalid_owner_name = Please make sure you entered owner name is correct.
 enterred_invalid_password = Please make sure you entered passord is correct.
+user_not_exist = Given user does not exist.
 
 invalid_ssh_key = Sorry, we're not able to verify your SSH key: %s
 auth_failed = Authentication failed: %v
@@ -174,6 +175,9 @@ settings.update_settings_success = Repository options has been successfully upda
 settings.transfer_owner = New Owner
 settings.make_transfer = Make Transfer
 settings.confirm_delete = Confirm Deletion
+settings.add_collaborator = Add New Collaborator
+settings.add_collaborator_success = New collaborator has been added.
+settings.remove_collaborator_success = Collaborator has been removed.
 
 [org]
 org_name_holder = Organization Name

+ 4 - 0
conf/locale/locale_zh-CN.ini

@@ -86,6 +86,7 @@ username_password_incorrect = 用户名或密码不正确。
 enterred_invalid_repo_name = 请检查您输入的仓库名称是正确。
 enterred_invalid_owner_name = 请检查您输入的新所有者用户名是否正确。
 enterred_invalid_password = 请检查您输入的密码是否正确。
+user_not_exist = 被操作的用户不存在!
 
 invalid_ssh_key = 很抱歉,我们无法验证您输入的 SSH 密钥:%s
 auth_failed = 授权验证失败:%v
@@ -174,6 +175,9 @@ settings.update_settings_success = 仓库设置更新成功!
 settings.transfer_owner = 新拥有者
 settings.make_transfer = 确认转移仓库
 settings.confirm_delete = 确认删除仓库
+settings.add_collaborator = 增加新的协作者
+settings.add_collaborator_success = 成功添加新的协作者!
+settings.remove_collaborator_success = 被操作的协作者已经被收回权限!
 
 [org]
 org_name_holder = 组织名称

+ 1 - 1
gogs.go

@@ -17,7 +17,7 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.4.7.0802 Alpha"
+const APP_VER = "0.4.7.0807 Alpha"
 
 func init() {
 	runtime.GOMAXPROCS(runtime.NumCPU())

+ 4 - 0
models/models.go

@@ -165,6 +165,10 @@ func GetStatistic() (stats Statistic) {
 	return
 }
 
+func Ping() error {
+	return x.Ping()
+}
+
 // DumpDatabase dumps all data from database to file system.
 func DumpDatabase(filePath string) error {
 	return x.DumpAllToFile(filePath)

+ 11 - 1
models/publickey.go

@@ -69,7 +69,7 @@ func init() {
 
 	// Determine and create .ssh path.
 	SshPath = filepath.Join(homeDir(), ".ssh")
-	if err = os.MkdirAll(SshPath, os.ModePerm); err != nil {
+	if err = os.MkdirAll(SshPath, 0700); err != nil {
 		log.Fatal(4, "fail to create SshPath(%s): %v\n", SshPath, err)
 	}
 }
@@ -156,6 +156,16 @@ func saveAuthorizedKeyFile(key *PublicKey) error {
 		return err
 	}
 	defer f.Close()
+	finfo, err := f.Stat()
+	if err != nil {
+		return err
+	}
+	if finfo.Mode().Perm() > 0600 {
+		log.Error(4, "authorized_keys file has unusual permission flags: %s - setting to -rw-------", finfo.Mode().Perm().String())
+		if err = f.Chmod(0600); err != nil {
+			return err
+		}
+	}
 
 	_, err = f.WriteString(key.GetAuthorizedString())
 	return err

+ 5 - 7
models/repo.go

@@ -446,7 +446,9 @@ func initRepository(f string, u *User, repo *Repository, initReadme bool, repoLa
 	}
 
 	if len(fileName) == 0 {
-		return nil
+		repo.IsBare = true
+		repo.DefaultBranch = "master"
+		return UpdateRepository(repo)
 	}
 
 	// Apply changes and commit.
@@ -479,10 +481,6 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror
 		LowerName:   strings.ToLower(name),
 		Description: desc,
 		IsPrivate:   private,
-		IsBare:      lang == "" && license == "" && !initReadme,
-	}
-	if !repo.IsBare {
-		repo.DefaultBranch = "master"
 	}
 
 	if _, err = sess.Insert(repo); err != nil {
@@ -550,11 +548,11 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror
 	if u.IsOrganization() {
 		ous, err := GetOrgUsersByOrgId(u.Id)
 		if err != nil {
-			log.Error(4, "repo.CreateRepository(GetOrgUsersByOrgId): %v", err)
+			log.Error(4, "GetOrgUsersByOrgId: %v", err)
 		} else {
 			for _, ou := range ous {
 				if err = WatchRepo(ou.Uid, repo.Id, true); err != nil {
-					log.Error(4, "repo.CreateRepository(WatchRepo): %v", err)
+					log.Error(4, "WatchRepo: %v", err)
 				}
 			}
 		}

+ 0 - 2
modules/middleware/repo.go

@@ -245,8 +245,6 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
 			ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
 		}
 
-		log.Debug("displayBare: %v; IsBare: %v", displayBare, ctx.Repo.Repository.IsBare)
-
 		// repo is bare and display enable
 		if displayBare && ctx.Repo.Repository.IsBare {
 			log.Debug("Bare repository: %s", ctx.Repo.RepoLink)

+ 235 - 33
public/ng/css/gogs.css

@@ -30,6 +30,16 @@ img.avatar-30 {
   height: 30px;
   vertical-align: middle;
 }
+img.avatar-40 {
+  width: 40px;
+  height: 40px;
+  vertical-align: middle;
+}
+img.avatar-48 {
+  width: 48px;
+  height: 48px;
+  vertical-align: middle;
+}
 #wrapper {
   padding: 0;
   margin: 0 0 -55px 0;
@@ -188,15 +198,65 @@ img.avatar-30 {
 .main-wrapper {
   padding: 20px 0 40px;
 }
+.user-list {
+  width: auto;
+  min-width: 180px;
+  max-width: 300px;
+}
+.user-list img {
+  width: 28px;
+  height: 28px;
+  margin-right: 1em;
+  margin-top: 1px;
+  vertical-align: middle;
+}
+.user-list li {
+  cursor: pointer;
+  font-weight: bold;
+}
 .markdown {
   background-color: white;
-  font-size: 14px;
+  font-size: 16px;
   line-height: 24px;
 }
 .markdown .markdown-body {
   padding-left: 24px;
   padding-right: 16px;
 }
+.markdown h5,
+.markdown h6 {
+  font-size: 1em;
+}
+.markdown ul {
+  padding: 10px 0 0 15px;
+}
+.markdown ul li {
+  list-style: inside;
+}
+.markdown ol li {
+  list-style: decimal inside;
+}
+.markdown li {
+  line-height: 1.6;
+  margin-top: 6px;
+}
+.markdown li:first-child {
+  margin-top: 0;
+}
+.markdown > pre {
+  font-size: 14px;
+  line-height: 1.6;
+  overflow: auto;
+  border: 1px solid #ddd;
+  border-radius: .25em;
+  margin: 5px 0;
+  padding: 10px;
+  background-color: #f8f8f8;
+}
+.markdown img {
+  padding: 10px 0;
+  max-width: 100%;
+}
 .markdown a {
   color: #428BCA;
 }
@@ -233,12 +293,6 @@ img.avatar-30 {
 .markdown h4 {
   font-size: 18px;
 }
-.markdown h5 {
-  font-size: 14px;
-}
-.markdown h6 {
-  font-size: 14px;
-}
 .markdown table {
   border-collapse: collapse;
   border-spacing: 0;
@@ -262,19 +316,6 @@ img.avatar-30 {
 .markdown table tr:nth-child(2n) {
   background-color: #f8f8f8;
 }
-.markdown ul li {
-  list-style: inside;
-}
-.markdown ol li {
-  list-style: decimal inside;
-}
-.markdown li {
-  line-height: 1.6;
-  margin-top: 6px;
-}
-.markdown li:first-child {
-  margin-top: 0;
-}
 .markdown dl dt {
   font-style: italic;
   margin-top: 9px;
@@ -288,15 +329,6 @@ img.avatar-30 {
   font-size: 14px;
   background-color: #f5f5f5;
 }
-.markdown > pre {
-  line-height: 1.6;
-  overflow: auto;
-  border: 1px solid #ddd;
-  border-radius: .25em;
-  margin: 5px 0;
-  padding: 10px;
-  background-color: #f8f8f8;
-}
 .markdown > pre.linenums {
   padding: 0;
 }
@@ -345,9 +377,6 @@ img.avatar-30 {
 .markdown p:last-child {
   margin-bottom: 0;
 }
-.markdown img {
-  max-width: 100%;
-}
 .markdown .btn {
   color: #fff;
 }
@@ -740,9 +769,24 @@ The dashboard page style
   margin-right: 6px;
   font-size: 1.1em;
 }
+#dashboard-selection-menu {
+  width: auto;
+  max-width: 300px;
+}
 #dashboard-selection-menu > .drop-down {
   top: 56px;
 }
+#dashboard-selection-menu li {
+  white-space: nowrap;
+}
+#dashboard-selection-menu li.checked .octicon {
+  opacity: 1;
+}
+#dashboard-selection-menu li a {
+  text-overflow: ellipsis;
+  -o-text-overflow: ellipsis;
+  overflow: hidden;
+}
 #dashboard-switch-menu {
   border-bottom-left-radius: .3em;
   border-bottom-right-radius: .3em;
@@ -1145,6 +1189,8 @@ The register and sign-in page style
 #repo-create-owner-list {
   top: 30px;
   left: 0;
+  width: auto;
+  max-width: 300px;
 }
 #repo-create-owner-list .octicon {
   margin-right: 12px;
@@ -1154,9 +1200,17 @@ The register and sign-in page style
   width: 20px;
   height: 20px;
 }
+#repo-create-owner-list li {
+  white-space: nowrap;
+}
 #repo-create-owner-list li.checked .octicon {
   opacity: 1;
 }
+#repo-create-owner-list li a {
+  text-overflow: ellipsis;
+  -o-text-overflow: ellipsis;
+  overflow: hidden;
+}
 .file-name {
   margin-left: 1em;
 }
@@ -1215,6 +1269,34 @@ The register and sign-in page style
 .repo-setting-zone {
   padding: 30px;
 }
+#repo-collab-list {
+  list-style: none;
+  padding: 10px 0 5px 0;
+}
+#repo-collab-list li.collab {
+  clear: both;
+  height: 50px;
+  padding: 0 15px 0 15px;
+}
+#repo-collab-list a.member {
+  color: #444;
+  height: 50px;
+  line-height: 50px;
+}
+#repo-collab-list a.member:hover {
+  color: #4183C4;
+}
+#repo-collab-list .avatar {
+  margin-right: 1em;
+  width: 40px;
+}
+#repo-collab-list .remove-collab {
+  color: #DD4B39;
+}
+.repo-user-list-block {
+  position: relative;
+  top: 5px;
+}
 #setting-wrapper {
   padding-bottom: 100px;
 }
@@ -1368,6 +1450,7 @@ The register and sign-in page style
 .pr-nav {
   border-bottom: 1px solid #DDD;
   margin-top: 16px;
+  margin-bottom: 16px;
 }
 .pr-nav .octicon {
   margin-right: 4px;
@@ -1384,7 +1467,7 @@ The register and sign-in page style
   font-size: 12px;
   margin-left: 4px;
 }
-.pr-nav li.current > a {
+.pr-nav li.js-tab-nav-show > a {
   background-color: #FFF;
   border-color: #E6E6E6;
 }
@@ -1410,3 +1493,122 @@ The register and sign-in page style
   border-top-left-radius: .2em;
   border-bottom-left-radius: .2em;
 }
+#pr-commit,
+#pr-file-diff,
+#issue-add-comment-preview {
+  display: none;
+}
+#pr-conversation-list {
+  padding-right: 30px;
+  box-sizing: border-box;
+}
+.issue-comment,
+.issue-commit,
+.issue-line,
+.issue-merge,
+.issue-add-comment {
+  margin-bottom: 16px;
+}
+.issue-comment .author-avatar img {
+  margin-right: 12px;
+}
+.issue-comment .panel {
+  margin-left: 60px;
+  margin-top: -40px;
+}
+.issue-comment .panel-header {
+  font-size: 13px;
+}
+.issue-comment .author-name {
+  font-weight: bold;
+}
+.issue-comment .date {
+  margin-left: 4px;
+  font-style: italic;
+  color: #888;
+}
+.issue-comment .action > * {
+  margin-left: 4px;
+  font-size: 12px;
+}
+.issue-comment .action i {
+  font-size: 13px;
+}
+.issue-commit {
+  line-height: 32px;
+}
+.issue-commit i,
+.issue-commit .author-avatar img {
+  margin-right: 16px;
+}
+.issue-commit .sha {
+  margin-left: 24px;
+}
+.issue-commit .message {
+  display: block;
+  margin-left: 88px;
+  padding-top: 4px;
+  line-height: 24px;
+}
+.issue-merge .ico {
+  width: 40px;
+  height: 40px;
+  text-align: center;
+  color: #FFF;
+  margin-right: 12px;
+}
+.issue-merge .ico i {
+  margin-top: 8px;
+  font-size: 24px;
+}
+.issue-merge .panel {
+  margin-left: 60px;
+  margin-top: -40px;
+}
+.issue-merge .panel-header {
+  font-size: 13px;
+}
+.issue-merge-ok .ico {
+  background-color: #65AD4E;
+}
+.issue-merge-ok .panel,
+.issue-merge-ok .panel-content,
+.issue-merge-ok .panel-header {
+  border-color: #65AD4E;
+  background-color: #FFF;
+}
+.issue-merge-ok .panel-header {
+  color: #508a3e;
+}
+.issue-line {
+  height: 4px;
+  background-color: #E6E6E6;
+}
+.issue-add-comment .panel {
+  margin-left: 60px;
+  margin-top: -40px;
+}
+.issue-add-comment .panel-header {
+  font-size: 13px;
+  padding-bottom: 0;
+}
+.issue-add-comment .add-nav > li > a {
+  padding: 4px 12px;
+  color: #444;
+  border: 1px solid #CCC;
+  margin-bottom: -1px;
+  font-size: 14px;
+  border-top-left-radius: .3em;
+  border-top-right-radius: .3em;
+}
+.issue-add-comment .add-nav > li > a:hover {
+  background-color: #FFF;
+}
+.issue-add-comment .add-nav > li.js-tab-nav-show > a {
+  background-color: #FFF;
+}
+textarea#issue-add-content {
+  width: 100%;
+  box-sizing: border-box;
+  height: 120px;
+}

+ 13 - 1
public/ng/css/ui.css

@@ -59,7 +59,8 @@ audio:not([controls]) {
   height: 0;
 }
 [hidden],
-template .hidden {
+template,
+.hidden {
   display: none;
 }
 .opacity {
@@ -72,6 +73,7 @@ a,
 .text-link {
   color: #428bca;
   text-decoration: none;
+  cursor: pointer;
 }
 a:hover,
 .text-link:hover {
@@ -604,6 +606,12 @@ ul.menu-down {
   box-shadow: 0 0 2px #666666;
   background-color: #ffffff;
 }
+ul.menu-down-show {
+  position: absolute;
+  z-index: 99;
+  box-shadow: 0 0 2px #666666;
+  background-color: #ffffff;
+}
 ul.menu-radius {
   border-radius: .3em;
 }
@@ -681,6 +689,10 @@ ul.menu-radius > li:last-child > a {
   border-bottom-left-radius: .3em;
   border-bottom-right-radius: .3em;
 }
+.panel.panel-radius .panel-content {
+  border-bottom-left-radius: .3em;
+  border-bottom-right-radius: .3em;
+}
 .panel.panel-warning {
   border-color: #F0C36D;
 }

+ 95 - 0
public/ng/js/gogs.js

@@ -52,6 +52,59 @@ var Gogs = {};
             }
         }
     });
+    $.fn.extend({
+        toggleHide: function () {
+            $(this).addClass("hidden");
+        },
+        toggleShow: function () {
+            $(this).removeClass("hidden");
+        },
+        toggleAjax: function (successCallback, errorCallback) {
+            var url = $(this).data("ajax");
+            var method = $(this).data('ajax-method') || 'get';
+            var ajaxName = $(this).data('ajax-name');
+            var data = {};
+
+            if (ajaxName.endsWith("preview")) {
+                data["mode"] = "gfm";
+                data["context"] = $(this).data('ajax-context');
+            }
+
+            $('[data-ajax-rel=' + ajaxName + ']').each(function () {
+                var field = $(this).data("ajax-field");
+                var t = $(this).data("ajax-val");
+                if (t == "val") {
+                    data[field] = $(this).val();
+                    return true;
+                }
+                if (t == "txt") {
+                    data[field] = $(this).text();
+                    return true;
+                }
+                if (t == "html") {
+                    data[field] = $(this).html();
+                    return true;
+                }
+                if (t == "data") {
+                    data[field] = $(this).data("ajax-data");
+                    return true;
+                }
+                return true;
+            });
+            console.log("toggleAjax:", method, url, data);
+            $.ajax({
+                url: url,
+                method: method.toUpperCase(),
+                data: data,
+                error: errorCallback,
+                success: function (d) {
+                    if (successCallback) {
+                        successCallback(d);
+                    }
+                }
+            })
+        }
+    });
 }(jQuery));
 
 (function ($) {
@@ -145,6 +198,26 @@ var Gogs = {};
             }
         }).trigger('hashchange');
     };
+
+    // Search users by keyword.
+    Gogs.searchUsers = function (val, $target) {
+        $.ajax({
+            url: '/api/v1/users/search?q=' + val,
+            dataType: "json",
+            success: function (json) {
+                if (json.ok && json.data.length) {
+                    var html = '';
+                    $.each(json.data, function (i, item) {
+                        html += '<li><a><img src="' + item.avatar + '">' + item.username + '</a></li>';
+                    });
+                    $target.html(html);
+                    $target.toggleShow();
+                } else {
+                    $target.toggleHide();
+                }
+            }
+        });
+    }
 })(jQuery);
 
 function initCore() {
@@ -175,6 +248,7 @@ function initRepoCreate() {
 }
 
 function initRepoSetting() {
+    // Options.
     // Confirmation of changing repository name.
     $('#repo-setting-form').submit(function (e) {
         var $reponame = $('#repo_name');
@@ -189,6 +263,27 @@ function initRepoSetting() {
     $('#delete-button').click(function () {
         $('#delete-form').show();
     });
+
+    // Collaboration.
+    $('#repo-collab-list hr:last-child').remove();
+    var $ul = $('#repo-collaborator').next().next().find('ul');
+    $('#repo-collaborator').on('keyup', function () {
+        var $this = $(this);
+        if (!$this.val()) {
+            $ul.toggleHide();
+            return;
+        }
+        Gogs.searchUsers($this.val(), $ul);
+    }).on('focus', function () {
+        if (!$(this).val()) {
+            $ul.toggleHide();
+        } else {
+            $ul.toggleShow();
+        }
+    }).next().next().find('ul').on("click", 'li', function () {
+        $('#repo-collaborator').val($(this).text());
+        $ul.toggleHide();
+    });
 }
 
 $(document).ready(function () {

+ 233 - 0
public/ng/less/gogs/base.less

@@ -0,0 +1,233 @@
+@import "../ui/var";
+@headerBgColor: #428BCA;
+@headerLinkFontColor: #FFF;
+@headerLinkHoverColor: #fff65f;
+@headerLinkCurrentColor: #fff65f;
+@headerSignOutColor: #ff908b;
+@footerBorderColor: #D6D6D6;
+@footerFontColor: #888;
+@langNum: 2px;
+// means 2 items
+html,
+body {
+    height: 100%;
+}
+.octicon,
+.fa {
+    width: 16px;
+    text-align: center;
+}
+.fa {
+    font-size: 14px;
+}
+.container {
+    max-width: 1170px;
+    padding: 0 1.5em;
+    margin: auto;
+}
+img.avatar-16 {
+    width: 16px;
+    height: 16px;
+    vertical-align: middle;
+}
+img.avatar-24 {
+    width: 24px;
+    height: 24px;
+    vertical-align: middle;
+}
+img.avatar-30 {
+    width: 30px;
+    height: 30px;
+    vertical-align: middle;
+}
+img.avatar-40 {
+  width: 40px;
+  height: 40px;
+  vertical-align: middle;
+}
+img.avatar-48{
+  width: 48px;
+  height: 48px;
+  vertical-align: middle;
+}
+#wrapper {
+    padding: 0;
+    margin: 0 0 -55px 0;
+    min-height: 100%;
+}
+#footer {
+    background-color: white;
+    border-top: 1px solid@footerBorderColor;
+
+clear: both;
+    width: 100%;
+    .container {
+        padding: 15px;
+    }
+    color:@footerFontColor;
+.official,
+    .version {
+        color: @footerFontColor;
+    }
+}
+#footer-links {
+    > * {
+        border-left: 1px solid@footerBorderColor;
+        padding-left: 8px;
+        margin-left: 5px;
+        &:first-child {
+            border-left: none;
+        }
+    }
+}
+#footer-lang {
+    position: relative;
+    .drop-down {
+        top: -2-31*@langNum;
+        left: -2px;
+        position: absolute;
+        height: -3+31*@langNum;
+        z-index: 100;
+        font-size: 12px;
+        width: 120%;
+        li > a {
+            padding: 3px 9px;
+        }
+    }
+}
+#header {
+    background-color: @headerBgColor;
+    height: 44px;
+    > .menu-line {
+        > li > a {
+            display: inline-block;
+            color:@headerLinkFontColor;
+            &:hover {
+                background-color: transparent;
+                color: @headerLinkHoverColor;
+            }
+        }
+        > li.head {
+            color: @headerLinkFontColor;
+        }
+        > li.hover a:after {
+            bottom: -9px;
+            color: @headerLinkFontColor;
+        }
+        > li.current > a {
+            color: @headerLinkCurrentColor;
+            font-weight: bold;
+        }
+    }
+}
+#header-nav-user {
+    height: 44px;
+    img {
+        margin: -4px 10px 0 0;
+        border-radius: 3px;
+    }
+}
+#header-nav-sign-out > a:hover {
+    color: @headerSignOutColor !important;
+}
+#header-nav-logo {
+    padding: 6px 1.2em;
+}
+#header-nav-explore,
+#header-nav-help {
+    font-size: 14px;
+}
+#header-new-repo-menu {
+    width: 180px;
+    background-color: #FFF;
+    top: 44px;
+    border-top: none;
+    .octicon {
+        margin-right: 6px;
+        font-size: 1.1em;
+    }
+    left:-66px;
+}
+.switching-list {
+    width: 100%;
+    list-style: none;
+    > li {
+        border-bottom: 1px solid #eaeaea;
+        &:last-child {
+            border-bottom: none;
+        }
+        > a {
+            padding: .4em 1.2em;
+            display: block;
+            color: #444;
+            &:hover {
+                background-color: #428bca !important;
+                color: #fff !important;
+            }
+        }
+    }
+}
+.social-buttons {
+    .btn {
+        border: none;
+        font-size: 16px;
+        border-radius: 4px;
+        margin-right: 12px;
+        font-family: 'PT Sans Narrow', sans-serif;
+        padding: 5px 12px;
+        color: #FFF;
+        .fa {
+            margin-right: 6px;
+            font-size: 16px;
+        }
+    }
+    .twitter {
+        background-color: #1c6399;
+        &:hover {
+            background-color: #1c5487;
+        }
+    }
+    .github {
+        background-color: #444;
+        &:hover {
+            background-color: #333;
+        }
+    }
+    .google {
+        background-color: #C03D20;
+        &:hover {
+            background-color: #D56060;
+        }
+    }
+    .weibo {
+        background-color: #bf1324;
+        &:hover {
+            background-color: #b94c4a;
+        }
+    }
+    .qq {
+        background-color: #03a2ef;
+        &:hover {
+            background-color: #3cb3ff;
+        }
+    }
+}
+.main-wrapper {
+    padding: 20px 0 40px;
+}
+.user-list {
+    width: auto;
+    min-width: 180px;
+    max-width: 300px;
+    img {
+        width: 28px;
+        height: 28px;
+        margin-right: 1em;
+        margin-top: 1px;
+        vertical-align: middle;
+    }
+    li {
+        cursor: pointer;
+        font-weight: bold;
+    }
+}

+ 259 - 0
public/ng/less/gogs/dashboard.less

@@ -0,0 +1,259 @@
+@import "../ui/var";
+
+/*
+The dashboard page style
+*/
+
+@dashboardHeaderBorderColor: #D6D6D6;
+@dashboardHeaderLinkColor: #444;
+@dashboardHeaderLinkHoverColor: #D9453D;
+@dashboardSwitchMenuHoverBgColor: @linkColor;
+@dashboardSwitchMenuHoverFontColor: #FFF;
+
+// dashboard header, contains dashboard selection menu and nav of Feed/PR/Issues.
+#dashboard-header {
+  border-bottom: 1px solid @dashboardHeaderBorderColor;
+  height: 69px;
+  > .menu-line {
+    > li {
+      padding: 12px 0;
+    }
+    > li.right {
+      > a {
+        font-size: 1.2em;
+        color: @dashboardHeaderLinkColor;
+        &:hover {
+          background-color: transparent;
+          color: @dashboardHeaderLinkHoverColor;
+        }
+        .octicon {
+          margin-right: 6px;
+        }
+      }
+      .current {
+        border-bottom: 2px solid #D26911;
+      }
+    }
+  }
+}
+
+// dashboard context switch selection
+#dashboard-selection-menu {
+  a img {
+    margin: -4px 10px 0 0;
+  }
+}
+
+#dashboard {
+  padding: 24px 0;
+}
+
+// dashboard sidebar contains contributed repositories panel,
+// and my repositories panel
+#dashboard-sidebar {
+  .panel-header h4 {
+    margin: 0;
+  }
+  > .panel {
+    margin-bottom: 24px;
+    border-bottom-left-radius: .3em;
+    border-bottom-right-radius: .3em;
+  }
+}
+
+#dashboard-sidebar-menu {
+  border-top-left-radius: .3em;
+  border-top-right-radius: .3em;
+  > li {
+    border: 1px solid #d6d6d6;
+    float: left;
+    margin-right: -1px;
+    border-bottom: none;
+    > a {
+      padding-top: .4em;
+      padding-bottom: .4em;
+    }
+  }
+  > li.first {
+    border-top-left-radius: .3em;
+    > a {
+      border-top-left-radius: .3em;
+    }
+  }
+  > li.drop {
+    border: none;
+    float: right;
+  }
+  width: 100%;
+  height: 35px;
+  > li.js-tab-nav-show {
+    background-color: #EEEEEE;
+  }
+  > li.last {
+    border-top-right-radius: .3em;
+    > a {
+      border-top-right-radius: .3em;
+    }
+  }
+}
+
+#dashboard-my-mirror,
+#dashboard-my-org,
+#dashboard-my-repo {
+  li {
+    &.private {
+      background-color: #fcf8e9;
+    }
+    border-bottom: 1px solid #EAEAEA;
+    &:last-child {
+      border-bottom: none;
+    }
+    a {
+      padding: 6px 1.2em;
+      display: block;
+      .octicon {
+        margin-right: 6px;
+        color: #888;
+      }
+      &:hover {
+        .repo-name {
+          text-decoration: underline;
+        }
+      }
+    }
+  }
+  .repo-name {
+    font-size: 1.1em;
+  }
+  .repo-star {
+    color: #888;
+  }
+  .repo-contrib-header {
+    border-top: 1px solid #d6d6d6;
+  }
+}
+
+#dashboard-my-repo {
+  .panel-header {
+    .octicon {
+      margin-right: 6px;
+      font-size: 12px;
+    }
+  }
+  .repo-count {
+    margin-left: 4px;
+  }
+}
+
+#dashboard-my-org,
+#dashboard-my-mirror {
+  display: none;
+}
+
+// the button of new repository in my repositories panel
+#dashboard-new-repo {
+  width: 50px;
+  height: 35px;
+  padding-top: 6px;
+  margin-right: 1px;
+  .octicon {
+    font-size: 2em;
+  }
+  border-top-left-radius: .3em;
+  border-top-right-radius: .3em;
+}
+
+// the drop-down menu of #dashboard-new-repo
+#dashboard-new-repo-menu {
+  top: 35px;
+  width: 180px;
+  background-color: #FFF;
+  left: -132px;
+  .octicon {
+    margin-right: 6px;
+    font-size: 1.1em;
+  }
+}
+
+#dashboard-selection-menu {
+    width: auto;
+    max-width: 300px;
+    > .drop-down {
+        top: 56px;
+    }
+    li {
+        white-space: nowrap;
+        &.checked {
+            .octicon {
+                opacity: 1;
+            }
+        }
+        a {
+            text-overflow: ellipsis; 
+            -o-text-overflow: ellipsis; 
+            overflow: hidden;
+        }
+    }
+}
+
+// the drop-down menu of #dashboard-selection-menu
+#dashboard-switch-menu {
+  > li {
+    > a {
+      img {
+        margin-top: 0;
+      }
+      .octicon {
+        margin-right: 12px;
+      }
+    }
+    &:last-child {
+      > a {
+        border-bottom-left-radius: .3em;
+        border-bottom-right-radius: .3em;
+      }
+    }
+  }
+  > li.org > a {
+    .octicon {
+      opacity: 0;
+    }
+  }
+  > li.checked > a {
+    .octicon {
+      opacity: 1;
+    }
+    font-weight: bold;
+  }
+  border-bottom-left-radius: .3em;
+  border-bottom-right-radius: .3em;
+}
+
+#dashboard-news {
+  .news {
+    margin-right: 2.4em;
+    .mega-octicon {
+      color: #CCC;
+    }
+    .avatar {
+      margin: 0 1.2em;
+    }
+    .news-content,
+    .news-time {
+      color: #888;
+    }
+    padding-bottom: 1em;
+    margin-bottom: 1em;
+    border-bottom: 1px solid #E6E6E6;
+    min-height: 30px;
+  }
+  .push-news {
+    .news-content li {
+      margin-left: 1em;
+      img {
+        margin-bottom: -2px;
+      }
+    }
+  }
+}
+

+ 97 - 0
public/ng/less/gogs/external.less

@@ -0,0 +1,97 @@
+@import "base";
+@import "../ui/var";
+#promo-wrapper {
+  padding-top: 50px;
+  background-color: @headerBgColor;
+}
+
+#promo-logo {
+  img {
+    max-width: 250px;
+  }
+  margin-right: 50px;
+  padding-bottom: 50px;
+}
+
+#promo-content {
+  color: #FFF;
+  margin-left: 300px;
+  h1,
+  h2 {
+    font-family: 'PT Sans Narrow', sans-serif;
+    line-height: 60px;
+    margin-bottom: 0;
+    text-shadow: 0 2px 1px rgba(0, 0, 0, 0.5);
+  }
+  h1 {
+    font-size: 96px;
+    line-height: 96px;
+    margin-bottom: 30px;
+  }
+  h2 {
+    font-size: 52px;
+    line-height: 70px;
+    font-weight: normal;
+  }
+}
+
+#promo-form {
+  padding: 40px 0;
+  .ipt-large {
+    border: none;
+    border-radius: 4px;
+    font-size: 18px;
+    &:focus {
+      box-shadow: 0 0 3px #FFF;
+    }
+    margin-right: 12px;
+  }
+  .btn-large {
+    border-radius: 4px;
+    font-size: 18px;
+    margin-right: 12px;
+  }
+}
+
+#promo-social {
+  padding-bottom: 60px;
+  .qq{
+    box-shadow: 0 0 1px #1c6399;
+  }
+}
+
+#feature-wrapper {
+  font-family: Lato, sans-serif;
+  font-size: 18px;
+  padding: 50px 0 100px 0;
+  .octicon {
+    color: @btnRedColor;
+    font-size: 60px;
+    height: 60px;
+    width: 60px;
+    line-height: 60px;
+    margin-right: 12px;
+    vertical-align: middle;
+    display: inline-block;
+  }
+  b {
+    color: #000;
+    font-size: 24px;
+    display: inline-block;
+    line-height: 60px;
+  }
+  p {
+    margin: 1em 0;
+    line-height: 40px;
+    padding-right: 30px;
+  }
+  a {
+    color: @btnRedColor;
+    &:hover {
+      color: @btnHoverRedColor;
+    }
+  }
+  .grid-1-2 {
+    margin-bottom: 30px;
+  }
+}

+ 262 - 0
public/ng/less/gogs/issue.less

@@ -0,0 +1,262 @@
+@import "../ui/var";
+
+.repo-issue-wrapper {
+  padding: 18px 0;
+}
+
+.pr-main {
+  padding-right: 40px;
+  box-sizing: border-box;
+}
+
+.pr-sidebar {
+  border-left: 1px solid #DDD;
+  box-sizing: border-box;
+}
+
+#pr-sidebar-nav {
+  margin-top: 6px;
+  li {
+    margin-bottom: 4px;
+  }
+  li > a {
+    border: 1px solid transparent;
+    border-left: none;
+    &:hover {
+      background-color: #FFF;
+      border-color: #DDD;
+    }
+  }
+  .label {
+    font-size: 12px;
+    line-height: 1.4em;
+    margin-top: 1px;
+  }
+  li.current {
+    a {
+      background-color: #FFF;
+      border-color: #DDD;
+    }
+  }
+}
+
+.pr-title {
+  .pr-num {
+    font-weight: normal;
+    color: #888;
+  }
+}
+
+.pr-meta {
+  color: #888;
+  .pr-author {
+    margin: 0 8px;
+    color: #444;
+    &:hover {
+      text-decoration: underline;
+    }
+  }
+  .pr-branch {
+    margin: 0 4px;
+    font-size: 12px;
+    padding: 4px 6px;
+  }
+}
+
+.pr-nav {
+  border-bottom: 1px solid #DDD;
+  margin-top: 16px;
+  margin-bottom: 16px;
+  .octicon {
+    margin-right: 4px;
+  }
+  li > a {
+    padding: 3px 9px !important;
+    border: 1px solid transparent;
+    border-bottom: none;
+    .label {
+      padding: 1px 5px;
+      font-size: 12px;
+      margin-left: 4px;
+    }
+    border-top-left-radius: .2em;
+    border-top-right-radius: .2em;
+  }
+  li.js-tab-nav-show {
+    > a {
+      background-color: #FFF;
+      border-color: #E6E6E6;
+    }
+  }
+}
+
+.diff-bar {
+  .diff-add {
+    color: @btnGreenColor;
+  }
+  .diff-delete {
+    color: @btnRedColor;
+  }
+  .diff-status {
+    width: 50px;
+    background-color: @btnRedColor;
+    height: 10px;
+    margin-top: 7px;
+    margin-left: 4px;
+    margin-right: 4px;
+    border-radius: .2em;
+  }
+  .diff-status-inner {
+    width: 45%;
+    background-color: @btnGreenColor;
+    height: 10px;
+    border-top-left-radius: .2em;
+    border-bottom-left-radius: .2em;
+  }
+}
+
+#pr-commit,
+#pr-file-diff,
+#issue-add-comment-preview {
+  display: none;
+}
+
+#pr-conversation-list {
+  padding-right: 30px;
+  box-sizing: border-box;
+}
+
+.issue-comment,
+.issue-commit,
+.issue-line,
+.issue-merge,
+.issue-add-comment {
+  margin-bottom: 16px;
+}
+
+.issue-comment {
+  .author-avatar {
+    img {
+      margin-right: 12px;
+    }
+  }
+  .panel {
+    margin-left: 60px;
+    margin-top: -40px;
+  }
+  .panel-header {
+    font-size: 13px;
+  }
+  .author-name {
+    font-weight: bold;
+  }
+  .date {
+    margin-left: 4px;
+    font-style: italic;
+    color: #888;
+  }
+  .action {
+    > * {
+      margin-left: 4px;
+      font-size: 12px;
+    }
+    i {
+      font-size: 13px;
+    }
+  }
+}
+
+.issue-commit {
+  line-height: 32px;
+  i, .author-avatar img {
+    margin-right: 16px;
+  }
+  .sha {
+    margin-left: 24px;
+  }
+  .message {
+    display: block;
+    margin-left: 88px;
+    padding-top: 4px;
+    line-height: 24px;
+  }
+}
+
+.issue-merge {
+  .ico {
+    width: 40px;
+    height: 40px;
+    text-align: center;
+    color: #FFF;
+    i {
+      margin-top: 8px;
+      font-size: 24px;
+    }
+    margin-right: 12px;
+  }
+  .panel {
+    margin-left: 60px;
+    margin-top: -40px;
+  }
+  .panel-header {
+    font-size: 13px;
+  }
+}
+
+.issue-merge-ok {
+  .ico {
+    background-color: #65AD4E;
+  }
+  .panel,
+  .panel-content,
+  .panel-header {
+    border-color: #65AD4E;
+    background-color: #FFF;
+  }
+  .panel-header {
+    color: darken(#65AD4E, 10%);
+  }
+}
+
+.issue-line {
+  height: 4px;
+  background-color: #E6E6E6;
+}
+
+.issue-add-comment {
+  .panel {
+    margin-left: 60px;
+    margin-top: -40px;
+  }
+  .panel-header {
+    font-size: 13px;
+    padding-bottom: 0;
+  }
+  .add-nav {
+    > li {
+      > a {
+        padding: 4px 12px;
+        color: #444;
+        border: 1px solid #CCC;
+        margin-bottom: -1px;
+        font-size: 14px;
+        border-top-left-radius: .3em;
+        border-top-right-radius: .3em;
+        &:hover {
+          background-color: #FFF;
+        }
+      }
+      &.js-tab-nav-show {
+        > a {
+          background-color: #FFF;
+        }
+      }
+    }
+  }
+}
+
+textarea#issue-add-content {
+  width: 100%;
+  box-sizing: border-box;
+  height: 120px;
+}

+ 322 - 0
public/ng/less/gogs/markdown.less

@@ -0,0 +1,322 @@
+.markdown {
+    background-color: white;
+    font-size: 16px;
+    line-height: 24px;
+    .markdown-body {
+        padding-left: 24px;
+        padding-right: 16px;
+    }
+    h5,
+    h6 {
+        font-size: 1em;
+    }
+    ul {
+        padding: 10px 0 0 15px;
+        li {
+            list-style: inside;
+        }
+    }
+    ol li {
+        list-style: decimal inside;
+    }
+    li {
+        line-height: 1.6;
+        margin-top: 6px;
+        &:first-child {
+            margin-top: 0;
+        }
+    }
+    > pre {
+        font-size: 14px;
+        line-height: 1.6;
+        overflow: auto;
+        border: 1px solid #ddd;
+        border-radius: .25em;
+        margin: 5px 0;
+        padding: 10px;
+        background-color: #f8f8f8;
+    }
+    img {
+        padding: 10px 0;
+        max-width: 100%;
+    }
+}
+.markdown a {
+    color: #428BCA;
+}
+.markdown h1,
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+    line-height: 1.7;
+    padding: 15px 0 0;
+    margin: 0 0 15px;
+    color: #444;
+    font-weight: bold;
+}
+.markdown h1,
+.markdown h2 {
+    border-bottom: 1px solid #E0E0E0;
+}
+.markdown h2 {
+    border-bottom: 1px solid #E0E0E0;
+}
+.markdown h1 {
+    color: #000;
+    font-size: 33px
+}
+.markdown h2 {
+    color: #333;
+    font-size: 28px
+}
+.markdown h3 {
+    font-size: 22px
+}
+.markdown h4 {
+    font-size: 18px
+}
+.markdown table {
+    border-collapse: collapse;
+    border-spacing: 0;
+    display: block;
+    overflow: auto;
+    width: 100%;
+    margin: 0 0 9px;
+}
+.markdown table th {
+    font-weight: 700
+}
+.markdown table th,
+.markdown table td {
+    border: 1px solid #DDD;
+    padding: 6px 13px;
+}
+.markdown table tr {
+    background-color: #FFF;
+    border-top: 1px solid #CCC;
+}
+.markdown table tr:nth-child(2n) {
+    background-color: #F8F8F8
+}
+.markdown dl dt {
+    font-style: italic;
+    margin-top: 9px;
+}
+.markdown dl dd {
+    margin: 0 0 9px;
+    padding: 0 9px;
+}
+.markdown blockquote,
+.markdown blockquote p {
+    font-size: 14px;
+    background-color: #f5f5f5;
+}
+.markdown > pre.linenums {
+    padding: 0;
+}
+.markdown > pre > ol.linenums {
+    list-style: none;
+    padding: 0;
+}
+.markdown > pre > ol.linenums > li {
+    margin-top: 2px;
+}
+.markdown > pre.nums-style > ol.linenums {
+    list-style-type: decimal;
+    padding: 0 0 0 40px;
+    -webkit-box-shadow: inset 40px 0 0 #f5f5f5, inset 41px 0 0 #ccc;
+    box-shadow: inset 40px 0 0 #f5f5f5, inset 41px 0 0 #ccc;
+}
+.markdown > pre > code {
+    white-space: pre;
+    word-wrap: normal;
+}
+.markdown > pre > ol.linenums > li {
+    padding: 0 10px;
+}
+.markdown > pre > ol.linenums > li:first-child {
+    padding-top: 12px;
+}
+.markdown > pre > ol.linenums > li:last-child {
+    padding-bottom: 12px;
+}
+.markdown > pre.nums-style > ol.linenums > li {
+    border-left: 1px solid #ddd;
+}
+.markdown hr {
+    border: none;
+    color: #ccc;
+    height: 4px;
+    padding: 0;
+    margin: 15px 0;
+    border-bottom: 2px solid #EEE;
+}
+.markdown blockquote:last-child,
+.markdown ul:last-child,
+.markdown ol:last-child,
+.markdown > pre:last-child,
+.markdown > pre:last-child,
+.markdown p:last-child {
+    margin-bottom: 0;
+}
+.markdown .btn {
+    color: #fff;
+}
+.markdown h1 a,
+.markdown h2 a,
+.markdown h3 a {
+    text-decoration: none;
+}
+.markdown h1 a.anchor,
+.markdown h2 a.anchor,
+.markdown h3 a.anchor,
+.markdown h4 a.anchor,
+.markdown h5 a.anchor,
+.markdown h6 a.anchor {
+    text-decoration: none;
+    line-height: 1;
+    padding-left: 0;
+    margin-left: -24px;
+    top: 15%;
+}
+.markdown a span.octicon {
+    font-size: 16px;
+    line-height: 1;
+    display: inline-block;
+    text-decoration: none;
+    -webkit-font-smoothing: antialiased;
+    margin-left: 30px;
+}
+.markdown a span.octicon-link {
+    opacity: 0;
+    color: #444;
+}
+.markdown h1:hover .octicon-link,
+.markdown h2:hover .octicon-link,
+.markdown h3:hover .octicon-link,
+.markdown h4:hover .octicon-link,
+.markdown h5:hover .octicon-link,
+.markdown h6:hover .octicon-link {
+    display: inline-block;
+    opacity: 1;
+}
+/* Author: jmblog */
+
+/* Project: https://github.com/jmblog/color-themes-for-google-code-prettify */
+
+/* GitHub Theme */
+
+/* Pretty printing styles. Used with prettify.js. */
+
+/* SPAN elements with the classes below are added by prettyprint. */
+
+/* plain text */
+
+.pln {
+    color: #333333;
+}
+@media screen {
+    /* string content */
+    .str {
+        color: #dd1144;
+    }
+    /* a keyword */
+    .kwd {
+        color: #333333;
+    }
+    /* a comment */
+    .com {
+        color: #999988;
+        font-style: italic;
+    }
+    /* a type name */
+    .typ {
+        color: #445588;
+    }
+    /* a literal value */
+    .lit {
+        color: #445588;
+    }
+    /* punctuation */
+    .pun {
+        color: #333333;
+    }
+    /* lisp open bracket */
+    .opn {
+        color: #333333;
+    }
+    /* lisp close bracket */
+    .clo {
+        color: #333333;
+    }
+    /* a markup tag name */
+    .tag {
+        color: navy;
+    }
+    /* a markup attribute name */
+    .atn {
+        color: teal;
+    }
+    /* a markup attribute value */
+    .atv {
+        color: #dd1144;
+    }
+    /* a declaration */
+    .dec {
+        color: #333333;
+    }
+    /* a variable name */
+    .var {
+        color: teal;
+    }
+    /* a function name */
+    .fun {
+        color: #990000;
+    }
+}
+/* Use higher contrast and text-weight for printable form. */
+
+@media print,
+projection {
+    .str {
+        color: #006600;
+    }
+    .kwd {
+        color: #006;
+        font-weight: bold;
+    }
+    .com {
+        color: #600;
+        font-style: italic;
+    }
+    .typ {
+        color: #404;
+        font-weight: bold;
+    }
+    .lit {
+        color: #004444;
+    }
+    .pun,
+    .opn,
+    .clo {
+        color: #444400;
+    }
+    .tag {
+        color: #006;
+        font-weight: bold;
+    }
+    .atn {
+        color: #440044;
+    }
+    .atv {
+        color: #006600;
+    }
+}
+/* Specify class=linenums on a pre to get line numbering */
+
+ol.linenums {
+    margin-top: 0;
+    margin-bottom: 0;
+}

+ 472 - 0
public/ng/less/gogs/repository.less

@@ -0,0 +1,472 @@
+@import "../ui/var";
+
+@repoHeaderBorderColor: #D6D6D6;
+@repoHeaderBgColor: #FFF;
+@repoHeaderNameColor: #888;
+
+/* repository main */
+#repo-wrapper {
+    padding-bottom: 100px;
+}
+#repo-header {
+  height: 69px;
+  border-bottom: 1px solid @repoHeaderBorderColor;
+  background-color: @repoHeaderBgColor;
+}
+#repo-header-name {
+  line-height: 66px;
+  color: @repoHeaderNameColor;
+  font-size: 1.6em;
+  font-weight: normal;
+  margin-bottom: 0;
+  i {
+    margin-right: 12px;
+    vertical-align: middle;
+  }
+  .divider {
+    margin: 0 4px;
+  }
+}
+
+#repo-header-meta {
+  line-height: 66px;
+  li {
+    > a {
+      padding: 0;
+      &:hover {
+        background-color: transparent;
+      }
+    }
+  }
+  a > .btn {
+    font-size: 1.05em;
+    margin-left: 16px;
+    i {
+      margin-right: 6px;
+    }
+    line-height: 16px;
+    .num {
+      margin-left: 6px;
+    }
+  }
+}
+
+#repo-header-download-btn {
+  > .btn > i {
+    margin-right: 0 !important;
+  }
+  &:hover {
+    &:after,
+    .btn {
+      background-color: @btnHoverBlackColor;
+      color: #FFF;
+    }
+  }
+  &:after {
+    background-color: @btnBlackColor;
+    padding: 9px 16px 8px 0;
+    margin-left: -8px !important;
+    color: #FFF;
+    border-top: 1px solid @btnBlackColor;
+    border-bottom: 1px solid @btnBlackColor;
+    border-top-right-radius: .25em;
+    border-bottom-right-radius: .25em;
+  }
+}
+
+#repo-header-download-drop {
+  line-height: 24px;
+  width: 440px;
+  top: 50px;
+  left: -354px;
+  padding: 20px;
+  box-sizing: border-box;
+  .btn > i {
+    margin-right: 6px;
+  }
+}
+
+#repo-content {
+  padding: 18px 0;
+}
+
+#repo-clone-url {
+  border-right: none;
+  width: 196px;
+  border-left: none;
+}
+
+#repo-clone-help {
+  line-height: 48px;
+}
+
+#repo-clone-zip {
+  line-height: 48px;
+  a {
+    cursor: pointer;
+    color: white;
+    overflow: visible;
+    padding: .6em 1.2em;
+  }
+  .btn {
+    margin: 0 6px;
+  }
+}
+
+#repo-desc {
+  font-size: 1.2em;
+}
+
+#repo-sidebar-nav {
+  .label {
+    font-size: 12px;
+    line-height: 1.4em;
+    margin-top: 2px;
+  }
+  i {
+    margin-right: 6px;
+  }
+}
+
+#repo-file-nav {
+  padding: .6em 0 1em 0;
+  > li > a {
+    padding-left: 0;
+    &:hover {
+      background-color: transparent;
+    }
+  }
+  li.repo-jump > a {
+    padding-right: 0;
+    .btn {
+      margin-left: -1px;
+    }
+  }
+}
+
+#repo-branch-switch {
+  > a {
+    .btn {
+      padding-right: 30px;
+    }
+    &:after {
+      position: absolute;
+      top: 12px;
+      right: 30px;
+      margin-left: 0;
+      color: @baseFontColor;
+    }
+  }
+  > .drop-down {
+    top: 40px;
+    left: 0;
+  }
+}
+
+#repo-branch-filter-ipt {
+  width: 100%;
+  border-left: none;
+  border-right: none;
+  box-sizing: border-box;
+}
+
+#repo-branch-tag {
+  .tab-nav {
+    border-bottom: 1px solid #EAEAEA;
+    a {
+      padding: .3em .8em;
+    }
+    .js-tab-nav-show {
+      background-color: #EEE;
+      font-weight: bold;
+    }
+  }
+}
+
+#repo-branch-list,
+#repo-tag-list {
+  li {
+    i {
+      margin-right: 12px;
+      opacity: 0;
+    }
+  }
+  li.checked {
+    i {
+      opacity: 1;
+    }
+  }
+}
+
+#repo-tag-list {
+  display: none;
+}
+
+#repo-bread {
+  .bread {
+    padding-right: 0;
+    font-size: 16px;
+    font-weight: bold;
+  }
+}
+
+#repo-main {
+  padding-right: 40px;
+  box-sizing: border-box;
+}
+
+#repo-files-table {
+  margin-bottom: 20px;
+  th,
+  td {
+    text-align: left;
+    line-height: 32px;
+  }
+  td.icon {
+    width: 16px;
+    padding-right: .1em;
+    padding-left: 1em;
+  }
+  td.name {
+    max-width: 120px;
+    .text-truncate {
+      max-width: 100%;
+    }
+  }
+  td.age {
+    max-width: 120px;
+    text-align: right;
+  }
+  td.msg {
+    max-width: 440px;
+    .text-truncate {
+      max-width: 100%;
+    }
+  }
+  td.age,
+  td.size,
+  td.msg a {
+    color: #888;
+  }
+  td.msg a:hover {
+    color: #428BCA;
+    text-decoration: underline;
+  }
+  tbody {
+    background-color: #FFF;
+    tr:hover {
+      background-color: #ffffEE;
+    }
+  }
+  thead {
+    background-color: #F0F0F0;
+    .author {
+      a {
+        margin: 0 .4em;
+      }
+    }
+    .last-commit {
+      strong {
+        color: #444;
+      }
+      .text-truncate {
+        margin-left: .4em;
+      }
+    }
+    .last-commit .text-truncate,
+    .age {
+      font-weight: normal;
+      color: #888;
+    }
+  }
+}
+
+#repo-readme {
+  margin-bottom: 80px;
+}
+
+#repo-bare-start {
+  margin-bottom: 100px;
+  .panel-content {
+    background-color: #FFF;
+  }
+  pre {
+    margin: 0 40px;
+    padding: 6px 10px;
+    border: 1px solid #ddd;
+    background: #f8f8f8;
+  }
+}
+
+.repo-bare {
+  #repo-bare-start {
+    h2 {
+      margin-top: 30px;
+      margin-bottom: 24px;
+    }
+  }
+  #repo-header-meta {
+    display: none;
+  }
+  #repo-clone-ssh {
+    margin-left: 200px;
+  }
+  #repo-clone-copy {
+    margin-right: 200px;
+  }
+  #repo-clone-help {
+    clear: both;
+    width: 100%;
+  }
+  #repo-clone-url {
+    width: 520px;
+  }
+}
+
+/* repository create */
+
+#repo-migrate-form,
+#repo-create-form {
+  width: 800px;
+  margin: 60px auto auto auto;
+  background: white;
+  h2 {
+    margin: .5em 1em;
+  }
+  .field {
+    margin: 1.2em 0 2em 0;
+  }
+  .ipt {
+    width: 540px;
+  }
+  textarea {
+    height: 120px;
+  }
+  .avatar {
+    vertical-align: middle;
+    margin-right: .6em;
+    width: 28px;
+    height: 28px;
+  }
+  &:hover {
+    box-shadow: 0px 0px 6px #CCC;
+  }
+}
+
+#repo-create-cancel {
+  margin-left: 4em;
+}
+
+#repo-create-owner-list {
+    top: 30px;
+    left: 0;
+    width: auto;
+    max-width: 300px;
+    .octicon {
+        margin-right: 12px;
+        opacity: 0;
+    }
+    .avatar {
+        width: 20px;
+        height: 20px;
+    }
+    li {
+        white-space: nowrap;
+        &.checked {
+            .octicon {
+                opacity: 1;
+            }
+        }
+        a {
+            text-overflow: ellipsis; 
+            -o-text-overflow: ellipsis; 
+            overflow: hidden;
+        }
+    }
+}
+.file-name {
+  margin-left: 1em;
+}
+.file-size {
+  font-size: 13px;
+  color: #888;
+  margin-left: 1em;
+}
+.code-view {
+  overflow: auto;
+  overflow-x: auto;
+  overflow-y: hidden;
+    background: white;
+  .view-raw {
+    min-height: 40px;
+    text-align: center;
+    padding-top: 20px;
+    .btn {
+      font-size: 1.05em;
+      line-height: 16px;
+      padding: 6px 8px;
+    }
+  }
+  table {
+    width: 100%;
+    td {
+      padding: 0;
+    }
+  }
+  .lines-num {
+    text-align: right;
+    color: #999;
+    background: #f5f5f5;
+    width: 1%;
+    span {
+      font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
+      line-height: 18px;
+       padding: 0 8px 0 10px; 
+       cursor: pointer; 
+      display: block;
+      margin-top: 2px;
+      font-size: 12px;
+    }
+  }
+  .lines-code > pre {
+    border: none;
+    border-left: 1px solid #ddd;
+    > ol.linenums > li {
+      padding: 0 10px;
+      &.active {
+        background: #ffffdd;
+      }
+    }
+  }
+}
+.repo-setting-zone {
+    padding: 30px;
+}
+#repo-collab-list {
+    list-style: none;
+    padding: 10px 0 5px 0;
+    li.collab {
+        clear: both;
+        height: 50px;
+        padding: 0 15px 0 15px;
+    }
+    a.member {
+        color: #444;
+        height: 50px;
+        line-height: 50px;
+        &:hover {
+            color: #4183C4;
+        }
+    }
+    .avatar {
+        margin-right: 1em;
+        width: 40px;
+    }
+    .remove-collab {
+        color: #DD4B39;
+    }
+}
+.repo-user-list-block {
+    position: relative;
+    top: 5px;
+}

+ 103 - 0
public/ng/less/gogs/settings.less

@@ -0,0 +1,103 @@
+@import "../ui/var";
+
+#setting-wrapper {
+    padding-bottom: 100px;
+}
+
+#setting-menu {
+  box-sizing: border-box;
+  li > a {
+    border-left: 2px solid #FFF;
+    background-color: #FFF;
+  }
+  li:hover {
+    a {
+      border-left: 2px solid #EFEFEF;
+      background-color: #EFEFEF !important;
+      color: #000 !important;
+    }
+    border-color: #EAEAEA;
+  }
+  li.current {
+    a {
+      color: #000 !important;
+      font-weight: bold;
+      border-left: 2px solid #d26911;
+    }
+  }
+}
+
+.setting-content {
+  margin-left: 32px;
+}
+
+#repo-setting-form,
+#user-profile-form {
+    background-color: #FFF;
+    padding: 30px 0;
+    textarea {
+        margin-left: 4px;
+        height: 100px;
+    }
+    label,
+    .form-label {
+        width: 240px;
+    }
+    .ipt {
+        width: 360px;
+    }
+    .field {
+        margin-bottom: 24px;
+    }
+}
+
+#user-ssh-panel {
+  margin-bottom: 20px;
+  .switching-list {
+    background-color: #FFF;
+    li {
+      padding: 8px 20px;
+      &.ssh:hover {
+        background-color: #ffffEE;
+      }
+    }
+  }
+  .active-icon {
+    width: 10px;
+    height: 10px;
+    border-radius: 6px;
+    padding: 0;
+    margin-right: 20px;
+    margin-top: 10px;
+  }
+  .ssh-content {
+    margin-left: 24px;
+    .octicon {
+      margin-right: 4px;
+    }
+    .print,
+    .activity {
+      color: #888;
+    }
+  }
+  .ssh-delete-btn {
+    margin-top: 6px;
+  }
+}
+
+#user-ssh-add-form {
+  .panel-body {
+    background-color: #FFF;
+    padding: 30px 0;
+  }
+  .ipt {
+    width: 500px;
+  }
+  textarea {
+    height: 120px;
+    margin-left: 3px;
+  }
+  .field {
+    margin-bottom: 24px;
+  }
+}

+ 63 - 0
public/ng/less/gogs/sign.less

@@ -0,0 +1,63 @@
+@import "../ui/var";
+
+/*
+The register and sign-in page style
+*/
+
+@signPanelBgColor: #FFF;
+
+#sign-wrapper {
+  padding: 60px 0;
+}
+
+.sign-panel {
+  background-color: @signPanelBgColor;
+}
+
+.sign-form.form-align {
+  .field {
+    margin: 1.2em 0 2em 0;
+  }
+  .ipt-large {
+    width: 300px;
+  }
+  label,
+  .form-label {
+    width: 160px;
+  }
+  .alert{
+    margin:0 30px 24px 30px;
+  }
+  &:hover{
+    box-shadow: 0 0 6px #CCC;
+  }
+}
+
+.sign-form.container{
+  padding: 0;
+  width: 600px;
+  margin-bottom: 80px;
+}
+
+// register form element
+#sign-up-form {
+  .panel-content{
+    margin-top: 1.2em;
+  }
+  h2 {
+    margin: .5em 1em;
+  }
+}
+
+#sign-social{
+  position: relative;
+  margin: 40px 0;
+  .or{
+    position: absolute;
+    width: 30px;
+    top: -52px;
+    left: 50%;
+    background-color: #FFF;
+    margin-left: -15px;
+  }
+}

+ 14 - 6
public/ng/less/ui/menu.less

@@ -94,12 +94,20 @@ ul.menu-down {
   }
 }
 
-ul.menu-down {
-  position: absolute;
-  display: none;
-  z-index: 99;
-  box-shadow: 0 0 2px @menuShadowColor;
-  background-color: @menuDownBgColor;
+ul {
+    &.menu-down {
+        position: absolute;
+        display: none;
+        z-index: 99;
+        box-shadow: 0 0 2px @menuShadowColor;
+        background-color: @menuDownBgColor;
+    }
+    &.menu-down-show {
+        position: absolute;
+        z-index: 99;
+        box-shadow: 0 0 2px @menuShadowColor;
+        background-color: @menuDownBgColor;
+    }
 }
 
 ul.menu-radius {

+ 4 - 0
public/ng/less/ui/panel.less

@@ -31,6 +31,10 @@
             border-bottom-left-radius: .3em;
             border-bottom-right-radius: .3em;
         }
+        .panel-content{
+          border-bottom-left-radius: .3em;
+          border-bottom-right-radius: .3em;
+        }
     }
     &.panel-warning {
         border-color: #F0C36D;

+ 5 - 6
public/ng/less/ui/reset.less

@@ -81,17 +81,15 @@ audio:not([controls]) {
 // display hidden elements
 
 [hidden],
-template
+template,
 .hidden {
-  display: none;
+    display: none;
 }
-
 .opacity {
-  opacity: 0;
+    opacity: 0;
 }
-
 .opacity-half {
-  opacity: .5;
+    opacity: .5;
 }
 
 // links element
@@ -100,6 +98,7 @@ a,
 .text-link {
   color: @linkColor;
   text-decoration: none;
+  cursor: pointer;
   &:hover {
     color: @linkHoverColor;
     text-decoration: none;

+ 0 - 18
routers/dev/debug.go

@@ -1,18 +0,0 @@
-// 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 dev
-
-import (
-	"net/http/pprof"
-
-	"github.com/Unknwon/macaron"
-)
-
-func RegisterDebugRoutes(r *macaron.Macaron) {
-	r.Any("/debug/pprof/cmdline", pprof.Cmdline)
-	r.Any("/debug/pprof/profile", pprof.Profile)
-	r.Any("/debug/pprof/symbol", pprof.Symbol)
-	r.Any("/debug/pprof/**", pprof.Index)
-}

+ 5 - 0
routers/home.go

@@ -28,6 +28,11 @@ func Home(ctx *middleware.Context) {
 		return
 	}
 
+	if setting.OauthService != nil {
+		ctx.Data["OauthEnabled"] = true
+		ctx.Data["OauthService"] = setting.OauthService
+	}
+
 	ctx.Data["PageIsHome"] = true
 	ctx.HTML(200, HOME)
 }

+ 54 - 9
routers/repo/setting.go

@@ -22,7 +22,7 @@ import (
 
 const (
 	SETTINGS_OPTIONS base.TplName = "repo/settings/options"
-	COLLABORATION    base.TplName = "repo/collaboration"
+	COLLABORATION    base.TplName = "repo/settings/collaboration"
 
 	HOOKS     base.TplName = "repo/hooks"
 	HOOK_ADD  base.TplName = "repo/hook_add"
@@ -134,26 +134,71 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
 	}
 }
 
-func Collaboration(ctx *middleware.Context) {
+func SettingsCollaboration(ctx *middleware.Context) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings")
+	ctx.Data["PageIsSettingsCollaboration"] = true
+
 	repoLink := strings.TrimPrefix(ctx.Repo.RepoLink, "/")
-	ctx.Data["IsRepoToolbarCollaboration"] = true
-	ctx.Data["Title"] = repoLink + " - collaboration"
+
+	if ctx.Req.Method == "POST" {
+		name := strings.ToLower(ctx.Query("collaborator"))
+		if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
+			ctx.Redirect(ctx.Req.URL.Path)
+			return
+		}
+		has, err := models.HasAccess(name, repoLink, models.WRITABLE)
+		if err != nil {
+			ctx.Handle(500, "HasAccess", err)
+			return
+		} else if has {
+			ctx.Redirect(ctx.Req.URL.Path)
+			return
+		}
+
+		u, err := models.GetUserByName(name)
+		if err != nil {
+			if err == models.ErrUserNotExist {
+				ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
+				ctx.Redirect(ctx.Req.URL.Path)
+			} else {
+				ctx.Handle(500, "GetUserByName", err)
+			}
+			return
+		}
+
+		if err = models.AddAccess(&models.Access{UserName: name, RepoName: repoLink,
+			Mode: models.WRITABLE}); err != nil {
+			ctx.Handle(500, "AddAccess2", err)
+			return
+		}
+
+		if setting.Service.EnableNotifyMail {
+			if err = mailer.SendCollaboratorMail(ctx.Render, u, ctx.User, ctx.Repo.Repository); err != nil {
+				ctx.Handle(500, "SendCollaboratorMail", err)
+				return
+			}
+		}
+
+		ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
+		ctx.Redirect(ctx.Req.URL.Path)
+		return
+	}
 
 	// Delete collaborator.
 	remove := strings.ToLower(ctx.Query("remove"))
 	if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName {
 		if err := models.DeleteAccess(&models.Access{UserName: remove, RepoName: repoLink}); err != nil {
-			ctx.Handle(500, "setting.Collaboration(DeleteAccess)", err)
+			ctx.Handle(500, "DeleteAccess", err)
 			return
 		}
-		ctx.Flash.Success("Collaborator has been removed.")
+		ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
 		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
 		return
 	}
 
 	names, err := models.GetCollaboratorNames(repoLink)
 	if err != nil {
-		ctx.Handle(500, "setting.Collaboration(GetCollaborators)", err)
+		ctx.Handle(500, "GetCollaborators", err)
 		return
 	}
 
@@ -161,7 +206,7 @@ func Collaboration(ctx *middleware.Context) {
 	for i, name := range names {
 		us[i], err = models.GetUserByName(name)
 		if err != nil {
-			ctx.Handle(500, "setting.Collaboration(GetUserByName)", err)
+			ctx.Handle(500, "GetUserByName", err)
 			return
 		}
 	}
@@ -170,7 +215,7 @@ func Collaboration(ctx *middleware.Context) {
 	ctx.HTML(200, COLLABORATION)
 }
 
-func CollaborationPost(ctx *middleware.Context) {
+func SettingsCollaborationPost(ctx *middleware.Context) {
 	repoLink := strings.TrimPrefix(ctx.Repo.RepoLink, "/")
 	name := strings.ToLower(ctx.Query("collaborator"))
 	if len(name) == 0 || ctx.Repo.Owner.LowerName == name {

+ 55 - 55
routers/user/auth.go

@@ -31,16 +31,16 @@ const (
 func SignIn(ctx *middleware.Context) {
 	ctx.Data["Title"] = ctx.Tr("sign_in")
 
-	// if _, ok := ctx.Session.Get("socialId").(int64); ok {
-	// 		ctx.Data["IsSocialLogin"] = true
-	// 		ctx.HTML(200, SIGNIN)
-	// 		return
-	// 	}
+	if _, ok := ctx.Session.Get("socialId").(int64); ok {
+		ctx.Data["IsSocialLogin"] = true
+		ctx.HTML(200, SIGNIN)
+		return
+	}
 
-	// if setting.OauthService != nil {
-	// 	ctx.Data["OauthEnabled"] = true
-	// 	ctx.Data["OauthService"] = setting.OauthService
-	// }
+	if setting.OauthService != nil {
+		ctx.Data["OauthEnabled"] = true
+		ctx.Data["OauthService"] = setting.OauthService
+	}
 
 	// Check auto-login.
 	uname := ctx.GetCookie(setting.CookieUserName)
@@ -89,13 +89,13 @@ func SignIn(ctx *middleware.Context) {
 func SignInPost(ctx *middleware.Context, form auth.SignInForm) {
 	ctx.Data["Title"] = ctx.Tr("sign_in")
 
-	// sid, isOauth := ctx.Session.Get("socialId").(int64)
-	// if isOauth {
-	// 	ctx.Data["IsSocialLogin"] = true
-	// } else if setting.OauthService != nil {
-	// 	ctx.Data["OauthEnabled"] = true
-	// 	ctx.Data["OauthService"] = setting.OauthService
-	// }
+	sid, isOauth := ctx.Session.Get("socialId").(int64)
+	if isOauth {
+		ctx.Data["IsSocialLogin"] = true
+	} else if setting.OauthService != nil {
+		ctx.Data["OauthEnabled"] = true
+		ctx.Data["OauthService"] = setting.OauthService
+	}
 
 	if ctx.HasError() {
 		ctx.HTML(200, SIGNIN)
@@ -121,18 +121,18 @@ func SignInPost(ctx *middleware.Context, form auth.SignInForm) {
 	}
 
 	// Bind with social account.
-	// if isOauth {
-	// 	if err = models.BindUserOauth2(user.Id, sid); err != nil {
-	// 		if err == models.ErrOauth2RecordNotExist {
-	// 			ctx.Handle(404, "user.SignInPost(GetOauth2ById)", err)
-	// 		} else {
-	// 			ctx.Handle(500, "user.SignInPost(GetOauth2ById)", err)
-	// 		}
-	// 		return
-	// 	}
-	// 	ctx.Session.Delete("socialId")
-	// 	log.Trace("%s OAuth binded: %s -> %d", ctx.Req.RequestURI, form.UserName, sid)
-	// }
+	if isOauth {
+		if err = models.BindUserOauth2(u.Id, sid); err != nil {
+			if err == models.ErrOauth2RecordNotExist {
+				ctx.Handle(404, "GetOauth2ById", err)
+			} else {
+				ctx.Handle(500, "GetOauth2ById", err)
+			}
+			return
+		}
+		ctx.Session.Delete("socialId")
+		log.Trace("%s OAuth binded: %s -> %d", ctx.Req.RequestURI, form.UserName, sid)
+	}
 
 	ctx.Session.Set("uid", u.Id)
 	ctx.Session.Set("uname", u.Name)
@@ -148,14 +148,34 @@ func SignInPost(ctx *middleware.Context, form auth.SignInForm) {
 func SignOut(ctx *middleware.Context) {
 	ctx.Session.Delete("uid")
 	ctx.Session.Delete("uname")
-	// ctx.Session.Delete("socialId")
-	// ctx.Session.Delete("socialName")
-	// ctx.Session.Delete("socialEmail")
+	ctx.Session.Delete("socialId")
+	ctx.Session.Delete("socialName")
+	ctx.Session.Delete("socialEmail")
 	ctx.SetCookie(setting.CookieUserName, "", -1)
 	ctx.SetCookie(setting.CookieRememberName, "", -1)
 	ctx.Redirect("/")
 }
 
+func oauthSignUp(ctx *middleware.Context, sid int64) {
+	// ctx.Data["Title"] = "OAuth Sign Up"
+	// ctx.Data["PageIsSignUp"] = true
+
+	// if _, err := models.GetOauth2ById(sid); err != nil {
+	// 	if err == models.ErrOauth2RecordNotExist {
+	// 		ctx.Handle(404, "user.oauthSignUp(GetOauth2ById)", err)
+	// 	} else {
+	// 		ctx.Handle(500, "user.oauthSignUp(GetOauth2ById)", err)
+	// 	}
+	// 	return
+	// }
+
+	// ctx.Data["IsSocialLogin"] = true
+	// ctx.Data["username"] = strings.Replace(ctx.Session.Get("socialName").(string), " ", "", -1)
+	// ctx.Data["email"] = ctx.Session.Get("socialEmail")
+	// log.Trace("user.oauthSignUp(social ID): %v", ctx.Session.Get("socialId"))
+	// ctx.HTML(200, SIGNUP)
+}
+
 func SignUp(ctx *middleware.Context) {
 	ctx.Data["Title"] = ctx.Tr("sign_up")
 
@@ -165,34 +185,14 @@ func SignUp(ctx *middleware.Context) {
 		return
 	}
 
-	// if sid, ok := ctx.Session.Get("socialId").(int64); ok {
-	// 	oauthSignUp(ctx, sid)
-	// 	return
-	// }
+	if sid, ok := ctx.Session.Get("socialId").(int64); ok {
+		oauthSignUp(ctx, sid)
+		return
+	}
 
 	ctx.HTML(200, SIGNUP)
 }
 
-// func oauthSignUp(ctx *middleware.Context, sid int64) {
-// 	ctx.Data["Title"] = "OAuth Sign Up"
-// 	ctx.Data["PageIsSignUp"] = true
-
-// 	if _, err := models.GetOauth2ById(sid); err != nil {
-// 		if err == models.ErrOauth2RecordNotExist {
-// 			ctx.Handle(404, "user.oauthSignUp(GetOauth2ById)", err)
-// 		} else {
-// 			ctx.Handle(500, "user.oauthSignUp(GetOauth2ById)", err)
-// 		}
-// 		return
-// 	}
-
-// 	ctx.Data["IsSocialLogin"] = true
-// 	ctx.Data["username"] = strings.Replace(ctx.Session.Get("socialName").(string), " ", "", -1)
-// 	ctx.Data["email"] = ctx.Session.Get("socialEmail")
-// 	log.Trace("user.oauthSignUp(social ID): %v", ctx.Session.Get("socialId"))
-// 	ctx.HTML(200, SIGNUP)
-// }
-
 func SignUpPost(ctx *middleware.Context, cpt *captcha.Captcha, form auth.RegisterForm) {
 	ctx.Data["Title"] = ctx.Tr("sign_up")
 

+ 12 - 5
routers/user/home.go

@@ -119,12 +119,19 @@ func Profile(ctx *middleware.Context) {
 	ctx.Data["Title"] = "Profile"
 	ctx.Data["PageIsUserProfile"] = true
 
-	u, err := models.GetUserByName(ctx.Params(":username"))
+	uname := ctx.Params(":username")
+	// Special handle for FireFox requests favicon.ico.
+	if uname == "favicon.ico" {
+		ctx.Redirect("/img/favicon.png")
+		return
+	}
+
+	u, err := models.GetUserByName(uname)
 	if err != nil {
 		if err == models.ErrUserNotExist {
-			ctx.Handle(404, "user.Profile(GetUserByName)", err)
+			ctx.Handle(404, "GetUserByName", err)
 		} else {
-			ctx.Handle(500, "user.Profile(GetUserByName)", err)
+			ctx.Handle(500, "GetUserByName", err)
 		}
 		return
 	}
@@ -146,13 +153,13 @@ func Profile(ctx *middleware.Context) {
 	case "activity":
 		ctx.Data["Feeds"], err = models.GetFeeds(u.Id, 0, true)
 		if err != nil {
-			ctx.Handle(500, "user.Profile(GetFeeds)", err)
+			ctx.Handle(500, "GetFeeds", err)
 			return
 		}
 	default:
 		ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id)
 		if err != nil {
-			ctx.Handle(500, "user.Profile(GetRepositories)", err)
+			ctx.Handle(500, "GetRepositories", err)
 			return
 		}
 	}

+ 1 - 1
templates/.VERSION

@@ -1 +1 @@
-0.4.7.0802 Alpha
+0.4.7.0807 Alpha

+ 5 - 0
templates/.brackets.json

@@ -0,0 +1,5 @@
+{
+    "language.fileExtensions": {
+        "tmpl": "html"
+    }
+}

+ 4 - 5
templates/home.tmpl

@@ -1,17 +1,16 @@
-{{template "ng/base/head" .}}
-{{template "ng/base/header" .}}
+{{template "ng/base/head" .}} {{template "ng/base/header" .}}
 <div id="promo-wrapper">
     <div class="container clear">
         <div id="promo-logo" class="left">
-            <img src="/img/gogs-lg.png" alt="logo"/>
+            <img src="/img/gogs-lg.png" alt="logo" />
         </div>
         <div id="promo-content">
             <h1>Gogs</h1>
             <h2>{{.i18n.Tr "app_desc"}}</h2>
             <form id="promo-form" action="/user/login" method="post">
                 {{.CsrfTokenHtml}}
-                <input class="ipt ipt-large" id="username" name="uname" type="text" placeholder="{{.i18n.Tr "home.uname_holder"}}"/>
-                <input class="ipt ipt-large" name="password" type="password" placeholder="{{.i18n.Tr "home.password_holder"}}"/>
+                <input class="ipt ipt-large" id="username" name="uname" type="text" placeholder="{{.i18n.Tr " home.uname_holder "}}"/>
+                <input class="ipt ipt-large" name="password" type="password" placeholder="{{.i18n.Tr " home.password_holder "}}"/>
                 <input name="from" type="hidden" value="home">
                 <button class="btn btn-black btn-large">{{.i18n.Tr "sign_in"}}</button>
                 <button class="btn btn-green btn-large" id="register-button">{{.i18n.Tr "register"}}</button>

+ 4 - 4
templates/ng/base/social.tmpl

@@ -1,4 +1,4 @@
-<button class="btn github"><i class="fa fa-github"></i>GitHub</button>
-<button class="btn google"><i class="fa fa-google"></i>Google +</button>
-<button class="btn weibo"><i class="fa fa-weibo"></i>新浪微博</button>
-<button class="btn qq"><i class="fa fa-qq"></i>腾讯 QQ&nbsp;</button>
+{{if .OauthService.GitHub}}<a class="btn github" href="/user/login/github?next=/user/sign_up"><i class="fa fa-github"></i>GitHub</a>{{end}}
+{{if .OauthService.Google}}<a class="btn google" href="/user/login/google?next=/user/sign_up"><i class="fa fa-google"></i>Google +</a>{{end}}
+{{if .OauthService.Weibo}}<a class="btn weibo" href="/user/login/weibo?next=/user/sign_up"><i class="fa fa-weibo"></i>新浪微博</a>{{end}}
+{{if .OauthService.Tencent}}<a class="btn qq" href="/user/login/qq?next=/user/sign_up"><i class="fa fa-qq"></i>腾讯 QQ&nbsp;</a>{{end}}

+ 0 - 47
templates/repo/collaboration.tmpl

@@ -1,47 +0,0 @@
-{{template "base/head" .}}
-{{template "base/navbar" .}}
-{{template "repo/nav" .}}
-{{template "repo/toolbar" .}}
-<div id="body" class="container">
-    {{template "repo/setting_nav" .}}
-    <div id="repo-setting-container" class="col-md-10">
-        {{template "base/alert" .}}
-        <div class="panel panel-default">
-            <div class="panel-heading">
-                Collaborators
-            </div>
-            
-            <div class="panel-body">
-                <ul id="repo-collab-list" class="list-unstyled">
-                    {{range .Collaborators}}
-                    <li class="collab">
-                        {{if not (eq .LowerName $.Owner.LowerName)}}<a href="{{$.RepoLink}}/settings/collaboration?remove={{.Name}}" class="remove-collab pull-right"><i class="fa fa-times"></i></a>{{end}}
-                        <a class="member" href="/user/{{.Name}}">
-                            <img alt="{{.Name}}" class="pull-left avatar" src="{{.AvatarLink}}">
-                            <strong class="access-member-fullname">{{.FullName}}</strong><br/>
-                            {{.Name}}
-                        </a>
-                    </li>
-                    {{end}}
-                </ul>
-            </div>
-
-            <div class="panel-footer">
-                <form action="{{.RepoLink}}/settings/collaboration" method="post" class="form-horizontal" id="repo-collab-form">
-                    {{.CsrfTokenHtml}}
-                    <div class="form-group" style="margin-bottom: 0">
-                        <div class="col-md-4">
-                            <input type="text" name="collaborator" class="form-control dropdown-toggle" id="repo-collaborator" autocomplete="off" required="required" data-toggle="dropdown"/>
-                            <div class="dropdown-menu">
-                                <ul class="list-unstyled"></ul>
-                            </div>
-                        </div>
-                        <button class="col-md-2 btn btn-primary">Add collaborator</button>
-                    </div>
-                </form>
-            </div>
-        </div>
-
-    </div>
-</div>
-{{template "base/footer" .}}

+ 2 - 1
templates/repo/header.tmpl

@@ -1,10 +1,11 @@
 <div id="repo-header" class="clear">
     <div class="container clear">
         <h1 id="repo-header-name" class="left public">
-            <i class="mega-octicon octicon-{{if .Repository.IsPrivate}}lock{{else}}repo{{end}}"></i>
+            <i class="mega-octicon octicon-{{if .Repository.IsPrivate}}lock{{else if .Repository.IsMirror}}repo-clone{{else}}repo{{end}}"></i>
             <a class="author" href="/{{.Owner.Name}}">{{.Owner.Name}}</a>
             <span class="divider">/</span>
             <a class="repo text-bold" href="{{.RepoLink}}">{{.Repository.Name}}</a>
+            {{if .Repository.IsMirror}}<span class="label label-gray">{{.i18n.Tr "mirror"}}</span>{{end}}
         </h1>
         <ul id="repo-header-meta" class="right menu menu-line">
             <li id="repo-header-download" class="inline-block down drop">

+ 47 - 0
templates/repo/settings/collaboration.tmpl

@@ -0,0 +1,47 @@
+{{template "ng/base/head" .}}
+{{template "ng/base/header" .}}
+<div id="repo-wrapper">
+    {{template "repo/header" .}}
+	<div id="setting-wrapper" class="main-wrapper">
+	    <div id="repo-setting" class="container clear">
+	        {{template "repo/settings/nav" .}}
+	        <div class="grid-4-5 left">
+	            <div class="setting-content">
+	                {{template "ng/base/alert" .}}
+	                <div id="setting-content">
+	                    <div id="user-profile-setting-content" class="panel panel-radius">
+	                        <div class="panel-header">
+	                        	<strong>{{.i18n.Tr "repo.settings.collaboration"}}</strong>
+	                        </div>
+	                        <div class="panel-body">
+	                        	<ul id="repo-collab-list">
+	                        		{{range .Collaborators}}
+	                        		<li class="collab">
+	                        			{{if not (eq .Id $.Owner.Id)}}<a href="{{$.RepoLink}}/settings/collaboration?remove={{.Name}}" class="remove-collab right"><i class="fa fa-times"></i></a>{{end}}
+										<a class="member" href="/{{.Name}}">
+										    <img alt="{{.Name}}" class="pull-left avatar" src="{{.AvatarLink}}">
+										    <strong>{{.FullName}}</strong> ({{.Name}})
+										</a>
+	                        		</li>
+	                        		<hr>
+	                        		{{end}}
+	                        	</ul>
+							</div>
+				            <div class="panel-footer">
+				                <form class="form form-align" action="{{.RepoLink}}/settings/collaboration" method="post" id="repo-collab-form">
+				                    {{.CsrfTokenHtml}}
+	                                <input class="ipt ipt-large ipt-radius" id="repo-collaborator" name="collaborator" autocomplete="off" required />
+	                                <button class="btn btn-blue btn-large btn-radius">{{.i18n.Tr "repo.settings.add_collaborator"}}</button>
+									<div class="repo-user-list-block">
+										<ul class="menu-down-show menu-vertical menu-radius switching-list user-list" id="repo-collaborator-list"></ul>
+									</div>
+				                </form>
+				            </div>
+	                    </div>
+	                </div>
+	            </div>
+	        </div>
+	    </div>
+	</div>
+</div>
+{{template "ng/base/footer" .}} 

+ 2 - 0
templates/user/signin.tmpl

@@ -28,10 +28,12 @@
                 <span class="form-label"></span>
                 <a href="/user/sign_up">{{.i18n.Tr "auth.sign_up_now" | Str2html}}</a>
             </p>
+            {{if .OauthEnabled}}
             <hr/>
             <div id="sign-social" class="text-center social-buttons">
                 {{template "ng/base/social" .}}
             </div>
+            {{end}}
         </div>
     </form>
 </div>