Quellcode durchsuchen

new user dahsboard issues

Unknwon vor 9 Jahren
Ursprung
Commit
f808df5a7b

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

@@ -124,6 +124,8 @@ collaborative_repos = Collaborative Repositories
 my_orgs = My Organizations
 my_mirrors = My Mirrors
 
+issues.in_your_repos = In your repositories
+
 [explore]
 repos = Repositories
 

+ 20 - 0
config.codekit

@@ -275,6 +275,26 @@
 		"strictMath": 0,
 		"strictUnits": 0
 		},
+	"\/public\/less\/_dashboard.less": {
+		"allowInsecureImports": 0,
+		"createSourceMap": 0,
+		"disableJavascript": 0,
+		"fileType": 1,
+		"ieCompatibility": 1,
+		"ignore": 1,
+		"ignoreWasSetByUser": 0,
+		"inputAbbreviatedPath": "\/public\/less\/_dashboard.less",
+		"outputAbbreviatedPath": "\/public\/css\/_dashboard.css",
+		"outputPathIsOutsideProject": 0,
+		"outputPathIsSetByUser": 0,
+		"outputStyle": 0,
+		"relativeURLS": 0,
+		"shouldRunAutoprefixer": 0,
+		"shouldRunBless": 0,
+		"strictImports": 0,
+		"strictMath": 0,
+		"strictUnits": 0
+		},
 	"\/public\/less\/_form.less": {
 		"allowInsecureImports": 0,
 		"createSourceMap": 0,

+ 51 - 15
models/issue.go

@@ -641,9 +641,8 @@ func parseCountResult(results []map[string][]byte) int64 {
 }
 
 // GetIssueStats returns issue statistic information by given conditions.
-func GetIssueStats(repoID, uid, labelID, milestoneID, assigneeID int64, isShowClosed bool, filterMode int) *IssueStats {
+func GetIssueStats(repoID, uid, labelID, milestoneID, assigneeID int64, filterMode int) *IssueStats {
 	stats := &IssueStats{}
-	// issue := new(Issue)
 
 	queryStr := "SELECT COUNT(*) FROM `issue` "
 	if labelID > 0 {
@@ -659,38 +658,75 @@ func GetIssueStats(repoID, uid, labelID, milestoneID, assigneeID int64, isShowCl
 	}
 	switch filterMode {
 	case FM_ALL, FM_ASSIGN:
-		resutls, _ := x.Query(queryStr+baseCond, repoID, false)
-		stats.OpenCount = parseCountResult(resutls)
-		resutls, _ = x.Query(queryStr+baseCond, repoID, true)
-		stats.ClosedCount = parseCountResult(resutls)
+		results, _ := x.Query(queryStr+baseCond, repoID, false)
+		stats.OpenCount = parseCountResult(results)
+		results, _ = x.Query(queryStr+baseCond, repoID, true)
+		stats.ClosedCount = parseCountResult(results)
 
 	case FM_CREATE:
 		baseCond += " AND poster_id=?"
-		resutls, _ := x.Query(queryStr+baseCond, repoID, false, uid)
-		stats.OpenCount = parseCountResult(resutls)
-		resutls, _ = x.Query(queryStr+baseCond, repoID, true, uid)
-		stats.ClosedCount = parseCountResult(resutls)
+		results, _ := x.Query(queryStr+baseCond, repoID, false, uid)
+		stats.OpenCount = parseCountResult(results)
+		results, _ = x.Query(queryStr+baseCond, repoID, true, uid)
+		stats.ClosedCount = parseCountResult(results)
 
 	case FM_MENTION:
 		queryStr += " INNER JOIN `issue_user` ON `issue`.id=`issue_user`.issue_id"
 		baseCond += " AND `issue_user`.uid=? AND `issue_user`.is_mentioned=?"
-		resutls, _ := x.Query(queryStr+baseCond, repoID, false, uid, true)
-		stats.OpenCount = parseCountResult(resutls)
-		resutls, _ = x.Query(queryStr+baseCond, repoID, true, uid, true)
-		stats.ClosedCount = parseCountResult(resutls)
+		results, _ := x.Query(queryStr+baseCond, repoID, false, uid, true)
+		stats.OpenCount = parseCountResult(results)
+		results, _ = x.Query(queryStr+baseCond, repoID, true, uid, true)
+		stats.ClosedCount = parseCountResult(results)
 	}
 	return stats
 }
 
 // GetUserIssueStats returns issue statistic information for dashboard by given conditions.
-func GetUserIssueStats(uid int64, filterMode int) *IssueStats {
+func GetUserIssueStats(repoID, uid int64, filterMode int) *IssueStats {
 	stats := &IssueStats{}
 	issue := new(Issue)
 	stats.AssignCount, _ = x.Where("assignee_id=?", uid).And("is_closed=?", false).Count(issue)
 	stats.CreateCount, _ = x.Where("poster_id=?", uid).And("is_closed=?", false).Count(issue)
+
+	queryStr := "SELECT COUNT(*) FROM `issue` "
+	baseCond := " WHERE issue.is_closed=?"
+	if repoID > 0 {
+		baseCond += " AND issue.repo_id=" + com.ToStr(repoID)
+	}
+	switch filterMode {
+	case FM_ASSIGN:
+		baseCond += " AND assignee_id=" + com.ToStr(uid)
+
+	case FM_CREATE:
+		baseCond += " AND poster_id=" + com.ToStr(uid)
+	}
+
+	results, _ := x.Query(queryStr+baseCond, false)
+	stats.OpenCount = parseCountResult(results)
+	results, _ = x.Query(queryStr+baseCond, true)
+	stats.ClosedCount = parseCountResult(results)
 	return stats
 }
 
+// GetRepoIssueStats returns number of open and closed repository issues by given filter mode.
+func GetRepoIssueStats(repoID, uid int64, filterMode int) (numOpen int64, numClosed int64) {
+	queryStr := "SELECT COUNT(*) FROM `issue` "
+	baseCond := " WHERE issue.repo_id=? AND issue.is_closed=?"
+	switch filterMode {
+	case FM_ASSIGN:
+		baseCond += " AND assignee_id=" + com.ToStr(uid)
+
+	case FM_CREATE:
+		baseCond += " AND poster_id=" + com.ToStr(uid)
+	}
+
+	results, _ := x.Query(queryStr+baseCond, repoID, false)
+	numOpen = parseCountResult(results)
+	results, _ = x.Query(queryStr+baseCond, repoID, true)
+	numClosed = parseCountResult(results)
+	return numOpen, numClosed
+}
+
 func updateIssue(e Engine, issue *Issue) error {
 	_, err := e.Id(issue.ID).AllCols().Update(issue)
 	return err

+ 5 - 0
models/repo.go

@@ -221,6 +221,11 @@ func (repo *Repository) GetMilestoneByID(milestoneID int64) (*Milestone, error)
 	return GetRepoMilestoneByID(repo.ID, milestoneID)
 }
 
+// IssueStats returns number of open and closed repository issues by given filter mode.
+func (repo *Repository) IssueStats(uid int64, filterMode int) (int64, int64) {
+	return GetRepoIssueStats(repo.ID, uid, filterMode)
+}
+
 func (repo *Repository) GetMirror() (err error) {
 	repo.Mirror, err = GetMirror(repo.ID)
 	return err

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
modules/bindata/bindata.go


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
public/css/gogs.min.css


+ 20 - 0
public/less/_dashboard.less

@@ -0,0 +1,20 @@
+.dashboard {
+	padding-top: 15px;
+	padding-bottom: @footer-margin * 2;
+
+	&.issues {
+		.context.user.menu {
+			min-width: 200px;
+			.ui.header {
+		    font-size: 1rem;
+		    text-transform: none;
+			}
+		}
+		.filter.menu {
+			.item.active {
+		    background-color: #4183c4;
+		    color: #FFF;
+			}
+		}
+	}
+}

+ 42 - 41
public/less/_repository.less

@@ -79,47 +79,6 @@
 	 	}
 	}
 
-	.page.buttons {
-		padding-top: 15px;
-	}
-
-	.issue.list {
-		list-style: none;
-		padding-top: 15px;
-		>.item {
-			padding-top: 15px;
-			padding-bottom: 10px;
-			border-bottom: 1px dashed #AAA;
-			.title {
-				color: #444;
-				font-size: 15px;
-				font-weight: bold;
-				margin: 0 6px;
-				&:hover {
-					color: #000;
-				}
-			}
-			.comment {
-				padding-right: 10px;
-				color: #666;
-			}
-			.desc {
-				padding-top: 5px;
-				color: #999;
-				a.milestone {
-					padding-left: 5px;
-					color: #999!important;
-					&:hover {
-						color: #000!important;
-					}
-				}
-				.assignee {
-					margin-top: -5px;
-					margin-right: 5px;
-				}
-			}
-		}
-	}
 	@comment-avatar-width: 3em;
 	&.new.issue {
 		.comment.form {
@@ -607,6 +566,48 @@
 	}
 }
 
+.issue.list {
+	list-style: none;
+	padding-top: 15px;
+	>.item {
+		padding-top: 15px;
+		padding-bottom: 10px;
+		border-bottom: 1px dashed #AAA;
+		.title {
+			color: #444;
+			font-size: 15px;
+			font-weight: bold;
+			margin: 0 6px;
+			&:hover {
+				color: #000;
+			}
+		}
+		.comment {
+			padding-right: 10px;
+			color: #666;
+		}
+		.desc {
+			padding-top: 5px;
+			color: #999;
+			a.milestone {
+				padding-left: 5px;
+				color: #999!important;
+				&:hover {
+					color: #000!important;
+				}
+			}
+			.assignee {
+				margin-top: -5px;
+				margin-right: 5px;
+			}
+		}
+	}
+}
+
+.page.buttons {
+	padding-top: 15px;
+}
+
 .ui.comments {
 	.dropzone {
 		width: 100%; 

+ 1 - 0
public/less/gogs.less

@@ -6,4 +6,5 @@
 @import "_form";
 @import "_repository";
 @import "_user";
+@import "_dashboard";
 @import "_admin";

+ 3 - 3
routers/repo/issue.go

@@ -79,11 +79,11 @@ func Issues(ctx *middleware.Context) {
 	filterMode := models.FM_ALL
 	switch viewType {
 	case "assigned":
-		assigneeID = ctx.User.Id
 		filterMode = models.FM_ASSIGN
+		assigneeID = ctx.User.Id
 	case "created_by":
-		posterID = ctx.User.Id
 		filterMode = models.FM_CREATE
+		posterID = ctx.User.Id
 	case "mentioned":
 		filterMode = models.FM_MENTION
 	}
@@ -97,7 +97,7 @@ func Issues(ctx *middleware.Context) {
 	selectLabels := ctx.Query("labels")
 	milestoneID := ctx.QueryInt64("milestone")
 	isShowClosed := ctx.Query("state") == "closed"
-	issueStats := models.GetIssueStats(repo.ID, uid, com.StrTo(selectLabels).MustInt64(), milestoneID, assigneeID, isShowClosed, filterMode)
+	issueStats := models.GetIssueStats(repo.ID, uid, com.StrTo(selectLabels).MustInt64(), milestoneID, assigneeID, filterMode)
 
 	page := ctx.QueryInt("page")
 	if page <= 1 {

+ 161 - 151
routers/user/home.go

@@ -10,10 +10,10 @@ import (
 	"strings"
 
 	"github.com/Unknwon/com"
+	"github.com/Unknwon/paginater"
 
 	"github.com/gogits/gogs/models"
 	"github.com/gogits/gogs/modules/base"
-	"github.com/gogits/gogs/modules/log"
 	"github.com/gogits/gogs/modules/middleware"
 	"github.com/gogits/gogs/modules/setting"
 )
@@ -21,18 +21,13 @@ import (
 const (
 	DASHBOARD base.TplName = "user/dashboard/dashboard"
 	PULLS     base.TplName = "user/dashboard/pulls"
-	ISSUES    base.TplName = "user/issues"
+	ISSUES    base.TplName = "user/dashboard/issues"
 	STARS     base.TplName = "user/stars"
 	PROFILE   base.TplName = "user/profile"
 )
 
-func Dashboard(ctx *middleware.Context) {
-	ctx.Data["Title"] = ctx.Tr("dashboard")
-	ctx.Data["PageIsDashboard"] = true
-	ctx.Data["PageIsNews"] = true
-
-	var ctxUser *models.User
-	// Check context type.
+func getDashboardContextUser(ctx *middleware.Context) *models.User {
+	ctxUser := ctx.User
 	orgName := ctx.Params(":org")
 	if len(orgName) > 0 {
 		// Organization.
@@ -43,10 +38,33 @@ func Dashboard(ctx *middleware.Context) {
 			} else {
 				ctx.Handle(500, "GetUserByName", err)
 			}
-			return
+			return nil
 		}
 		ctxUser = org
-	} else {
+	}
+	ctx.Data["ContextUser"] = ctxUser
+
+	if err := ctx.User.GetOrganizations(); err != nil {
+		ctx.Handle(500, "GetOrganizations", err)
+		return nil
+	}
+	ctx.Data["Orgs"] = ctx.User.Orgs
+
+	return ctxUser
+}
+
+func Dashboard(ctx *middleware.Context) {
+	ctx.Data["Title"] = ctx.Tr("dashboard")
+	ctx.Data["PageIsDashboard"] = true
+	ctx.Data["PageIsNews"] = true
+
+	ctxUser := getDashboardContextUser(ctx)
+	if ctx.Written() {
+		return
+	}
+
+	// Check context type.
+	if !ctxUser.IsOrganization() {
 		// Normal user.
 		ctxUser = ctx.User
 		collaborates, err := ctx.User.GetAccessibleRepositories()
@@ -63,13 +81,6 @@ func Dashboard(ctx *middleware.Context) {
 		ctx.Data["CollaborateCount"] = len(repositories)
 		ctx.Data["CollaborativeRepos"] = repositories
 	}
-	ctx.Data["ContextUser"] = ctxUser
-
-	if err := ctx.User.GetOrganizations(); err != nil {
-		ctx.Handle(500, "GetOrganizations", err)
-		return
-	}
-	ctx.Data["Orgs"] = ctx.User.Orgs
 
 	repos, err := models.GetRepositories(ctxUser.Id, true)
 	if err != nil {
@@ -142,6 +153,138 @@ func Pulls(ctx *middleware.Context) {
 	ctx.HTML(200, PULLS)
 }
 
+func Issues(ctx *middleware.Context) {
+	ctx.Data["Title"] = ctx.Tr("issues")
+	ctx.Data["PageIsIssues"] = true
+
+	ctxUser := getDashboardContextUser(ctx)
+	if ctx.Written() {
+		return
+	}
+
+	// Organization does not have view type and filter mode.
+	var (
+		viewType   string
+		filterMode = models.FM_ALL
+		assigneeID int64
+		posterID   int64
+	)
+	if ctxUser.IsOrganization() {
+		viewType = "all"
+	} else {
+		viewType = ctx.Query("type")
+		types := []string{"assigned", "created_by"}
+		if !com.IsSliceContainsStr(types, viewType) {
+			viewType = "all"
+		}
+
+		switch viewType {
+		case "assigned":
+			filterMode = models.FM_ASSIGN
+			assigneeID = ctxUser.Id
+		case "created_by":
+			filterMode = models.FM_CREATE
+			posterID = ctxUser.Id
+		}
+	}
+
+	repoID := ctx.QueryInt64("repo")
+	isShowClosed := ctx.Query("state") == "closed"
+	issueStats := models.GetUserIssueStats(repoID, ctxUser.Id, filterMode)
+
+	page := ctx.QueryInt("page")
+	if page <= 1 {
+		page = 1
+	}
+
+	var total int
+	if !isShowClosed {
+		total = int(issueStats.OpenCount)
+	} else {
+		total = int(issueStats.ClosedCount)
+	}
+	ctx.Data["Page"] = paginater.New(total, setting.IssuePagingNum, page, 5)
+
+	// Get repositories.
+	repos, err := models.GetRepositories(ctxUser.Id, true)
+	if err != nil {
+		ctx.Handle(500, "GetRepositories", err)
+		return
+	}
+
+	repoIDs := make([]int64, 0, len(repos))
+	showRepos := make([]*models.Repository, 0, len(repos))
+	for _, repo := range repos {
+		if repo.NumIssues == 0 {
+			continue
+		}
+
+		repoIDs = append(repoIDs, repo.ID)
+		repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
+		issueStats.AllCount += int64(repo.NumOpenIssues)
+
+		if repo.ID == repoID {
+			repo.NumOpenIssues = int(issueStats.OpenCount)
+			repo.NumClosedIssues = int(issueStats.ClosedCount)
+		} else if filterMode != models.FM_ALL && repo.NumIssues > 0 {
+			// Calculate repository issue count with filter mode.
+			numOpen, numClosed := repo.IssueStats(ctxUser.Id, filterMode)
+			repo.NumOpenIssues, repo.NumClosedIssues = int(numOpen), int(numClosed)
+		}
+
+		if repo.ID == repoID ||
+			(isShowClosed && repo.NumClosedIssues > 0) ||
+			(!isShowClosed && repo.NumOpenIssues > 0) {
+			showRepos = append(showRepos, repo)
+		}
+	}
+	ctx.Data["Repos"] = showRepos
+
+	if repoID > 0 {
+		repoIDs = []int64{repoID}
+	}
+
+	// Get issues.
+	issues, err := models.Issues(ctxUser.Id, assigneeID, repoID, posterID, 0,
+		page, isShowClosed, false, "", "")
+	if err != nil {
+		ctx.Handle(500, "Issues: %v", err)
+		return
+	}
+
+	// Get posters and repository.
+	for i := range issues {
+		issues[i].Repo, err = models.GetRepositoryByID(issues[i].RepoID)
+		if err != nil {
+			ctx.Handle(500, "GetRepositoryByID", fmt.Errorf("[#%d]%v", issues[i].ID, err))
+			return
+		}
+
+		if err = issues[i].Repo.GetOwner(); err != nil {
+			ctx.Handle(500, "GetOwner", fmt.Errorf("[#%d]%v", issues[i].ID, err))
+			return
+		}
+
+		if err = issues[i].GetPoster(); err != nil {
+			ctx.Handle(500, "GetPoster", fmt.Errorf("[#%d]%v", issues[i].ID, err))
+			return
+		}
+	}
+	ctx.Data["Issues"] = issues
+
+	ctx.Data["IssueStats"] = issueStats
+	ctx.Data["ViewType"] = viewType
+	ctx.Data["RepoID"] = repoID
+	ctx.Data["IsShowClosed"] = isShowClosed
+	if isShowClosed {
+		ctx.Data["State"] = "closed"
+	} else {
+		ctx.Data["State"] = "open"
+	}
+
+	ctx.HTML(200, ISSUES)
+}
+
 func ShowSSHKeys(ctx *middleware.Context, uid int64) {
 	keys, err := models.ListPublicKeys(uid)
 	if err != nil {
@@ -256,136 +399,3 @@ func Email2User(ctx *middleware.Context) {
 	}
 	ctx.Redirect(setting.AppSubUrl + "/user/" + u.Name)
 }
-
-func Issues(ctx *middleware.Context) {
-	ctx.Data["Title"] = ctx.Tr("issues")
-	ctx.Data["PageIsDashboard"] = true
-	ctx.Data["PageIsIssues"] = true
-
-	viewType := ctx.Query("type")
-	types := []string{"assigned", "created_by"}
-	if !com.IsSliceContainsStr(types, viewType) {
-		viewType = "all"
-	}
-
-	isShowClosed := ctx.Query("state") == "closed"
-
-	var filterMode int
-	switch viewType {
-	case "assigned":
-		filterMode = models.FM_ASSIGN
-	case "created_by":
-		filterMode = models.FM_CREATE
-	}
-
-	repoId, _ := com.StrTo(ctx.Query("repoid")).Int64()
-	issueStats := models.GetUserIssueStats(ctx.User.Id, filterMode)
-
-	// Get all repositories.
-	repos, err := models.GetRepositories(ctx.User.Id, true)
-	if err != nil {
-		ctx.Handle(500, "user.Issues(GetRepositories)", err)
-		return
-	}
-
-	repoIds := make([]int64, 0, len(repos))
-	showRepos := make([]*models.Repository, 0, len(repos))
-	for _, repo := range repos {
-		if repo.NumIssues == 0 {
-			continue
-		}
-
-		repoIds = append(repoIds, repo.ID)
-		repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
-		issueStats.AllCount += int64(repo.NumOpenIssues)
-
-		if isShowClosed {
-			if repo.NumClosedIssues > 0 {
-				if filterMode == models.FM_CREATE {
-					repo.NumClosedIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.ID, isShowClosed))
-				}
-				showRepos = append(showRepos, repo)
-			}
-		} else {
-			if repo.NumOpenIssues > 0 {
-				if filterMode == models.FM_CREATE {
-					repo.NumOpenIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.ID, isShowClosed))
-				}
-				showRepos = append(showRepos, repo)
-			}
-		}
-	}
-
-	if repoId > 0 {
-		repoIds = []int64{repoId}
-	}
-
-	page, _ := com.StrTo(ctx.Query("page")).Int()
-
-	// Get all issues.
-	var ius []*models.IssueUser
-	switch viewType {
-	case "assigned":
-		fallthrough
-	case "created_by":
-		ius, err = models.GetIssueUserPairsByMode(ctx.User.Id, repoId, isShowClosed, page, filterMode)
-	default:
-		ius, err = models.GetIssueUserPairsByRepoIds(repoIds, isShowClosed, page)
-	}
-	if err != nil {
-		ctx.Handle(500, "user.Issues(GetAllIssueUserPairs)", err)
-		return
-	}
-
-	issues := make([]*models.Issue, len(ius))
-	for i := range ius {
-		issues[i], err = models.GetIssueByID(ius[i].IssueID)
-		if err != nil {
-			if models.IsErrIssueNotExist(err) {
-				log.Warn("user.Issues(GetIssueById #%d): issue not exist", ius[i].IssueID)
-				continue
-			} else {
-				ctx.Handle(500, fmt.Sprintf("user.Issues(GetIssueById #%d)", ius[i].IssueID), err)
-				return
-			}
-		}
-
-		issues[i].Repo, err = models.GetRepositoryByID(issues[i].RepoID)
-		if err != nil {
-			if models.IsErrRepoNotExist(err) {
-				log.Warn("GetRepositoryById[%d]: repository not exist", issues[i].RepoID)
-				continue
-			} else {
-				ctx.Handle(500, fmt.Sprintf("GetRepositoryById[%d]", issues[i].RepoID), err)
-				return
-			}
-		}
-
-		if err = issues[i].Repo.GetOwner(); err != nil {
-			ctx.Handle(500, "user.Issues(GetOwner)", err)
-			return
-		}
-
-		if err = issues[i].GetPoster(); err != nil {
-			ctx.Handle(500, "user.Issues(GetUserById)", err)
-			return
-		}
-	}
-
-	ctx.Data["RepoId"] = repoId
-	ctx.Data["Repos"] = showRepos
-	ctx.Data["Issues"] = issues
-	ctx.Data["ViewType"] = viewType
-	ctx.Data["IssueStats"] = issueStats
-	ctx.Data["IsShowClosed"] = isShowClosed
-	if isShowClosed {
-		ctx.Data["State"] = "closed"
-		ctx.Data["ShowCount"] = issueStats.ClosedCount
-	} else {
-		ctx.Data["ShowCount"] = issueStats.OpenCount
-	}
-
-	ctx.Data["ContextUser"] = ctx.User
-
-	ctx.HTML(200, ISSUES)
-}

+ 1 - 1
templates/base/head.tmpl

@@ -62,6 +62,7 @@
 
 							{{if .IsSigned}}
 							<a class="item{{if .PageIsDashboard}} active{{end}}" href="{{AppSubUrl}}/">{{.i18n.Tr "dashboard"}}</a>
+							<a class="item{{if .PageIsIssues}} active{{end}}" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
 							{{else}}
 							<a class="item{{if .PageIsHome}} active{{end}}" href="{{AppSubUrl}}/">{{.i18n.Tr "home"}}</a>
 							{{end}}
@@ -75,7 +76,6 @@
 							</div> -->
 
 							{{if .IsSigned}}
-							<a class="item{{if .PageIsIssues}} active{{end}}" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
 							<div class="right menu">
 								<div class="ui dropdown head link jump item poping up" data-content="{{.i18n.Tr "create_new"}}" data-variation="tiny inverted">
 									<span class="text">

+ 2 - 0
templates/repo/settings/navbar.tmpl

@@ -10,9 +10,11 @@
 	  <a class="{{if .PageIsSettingsHooks}}active{{end}} item" href="{{.RepoLink}}/settings/hooks">
 	    {{.i18n.Tr "repo.settings.hooks"}}
 	  </a>
+	  {{if or .SignedUser.AllowGitHook .SignedUser.IsAdmin}}
 	  <a class="{{if .PageIsSettingsGitHooks}}active{{end}} item" href="{{.RepoLink}}/settings/hooks/git">
 	    {{.i18n.Tr "repo.settings.githooks"}}
 	  </a>
+	  {{end}}
 	  <a class="{{if .PageIsSettingsKeys}}active{{end}} item" href="{{.RepoLink}}/settings/keys">
 	    {{.i18n.Tr "repo.settings.deploy_keys"}}
 	  </a>

+ 86 - 0
templates/user/dashboard/issues.tmpl

@@ -0,0 +1,86 @@
+{{template "base/head" .}}
+<div class="dashboard issues">
+  {{template "user/dashboard/navbar" .}}
+  <div class="ui container">
+  	<div class="ui grid">
+  		<div class="four wide column">
+  			<div class="ui secondary vertical filter menu">
+  				<a class="{{if eq .ViewType "all"}}active{{end}} item" href="{{.Link}}?repo={{.RepoID}}&state={{.State}}">
+  					{{.i18n.Tr "home.issues.in_your_repos"}}
+  					<strong class="ui right">{{.IssueStats.AllCount}}</strong>
+  				</a>
+  				<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="{{.Link}}?type=assigned&repo={{.RepoID}}&state={{.State}}">
+  					{{.i18n.Tr "repo.issues.filter_type.assigned_to_you"}}
+  					<strong class="ui right">{{.IssueStats.AssignCount}}</strong>
+  				</a>
+  				<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="{{.Link}}?type=created_by&repo={{.RepoID}}&state={{.State}}">
+  					{{.i18n.Tr "repo.issues.filter_type.created_by_you"}}
+  					<strong class="ui right">{{.IssueStats.CreateCount}}</strong>
+  				</a>
+  				<div class="ui divider"></div>
+          {{range .Repos}}
+          <a class="{{if eq $.RepoID .ID}}active{{end}} item" href="{{$.Link}}?type={{$.ViewType}}{{if not (eq $.RepoID .ID)}}&repo={{.ID}}{{end}}&state={{$.State}}">{{$.SignedUser.Name}}/{{.Name}} <strong class="ui right">{{if $.IsShowClosed}}{{.NumClosedIssues}}{{else}}{{.NumOpenIssues}}{{end}}</strong></a>
+          {{end}}
+  			</div>
+  		</div>
+			<div class="twelve wide column content">
+				<div class="ui tiny buttons">
+				  <a class="ui green basic button {{if not .IsShowClosed}}active{{end}}" href="{{.Link}}?type={{$.ViewType}}&repo={{.RepoID}}&state=open">
+				  	<i class="octicon octicon-issue-opened"></i>
+				  	{{.i18n.Tr "repo.issues.open_tab" .IssueStats.OpenCount}}
+				  </a>
+				  <a class="ui red basic button {{if .IsShowClosed}}active{{end}}" href="{{.Link}}?type={{$.ViewType}}&repo={{.RepoID}}&state=closed">
+				  	<i class="octicon octicon-issue-closed"></i>
+				  	{{.i18n.Tr "repo.issues.close_tab" .IssueStats.ClosedCount}}
+				  </a>
+				</div>
+
+				<div class="issue list">
+					{{range .Issues}}
+					{{ $timeStr:= TimeSince .Created $.Lang }}
+		      <li class="item">
+		      	<div class="ui label">#{{.ID}}</div>
+		      	<a class="title" href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}/issues/{{.Index}}">{{.Name}}</a>
+
+		      	{{if .NumComments}}
+		      	<span class="comment ui right"><i class="octicon octicon-comment"></i> {{.NumComments}}</span>
+		      	{{end}}
+
+		        <p class="desc">
+		        	{{$.i18n.Tr "repo.issues.opened_by" $timeStr .Poster.Name | Safe}}
+			        {{if .Assignee}}
+							<a class="ui right assignee poping up" href="{{.Assignee.HomeLink}}" data-content="{{.Assignee.Name}}" data-variation="inverted" data-position="left center">
+								<img class="ui avatar image" src="{{.Assignee.AvatarLink}}">
+							</a>
+			        {{end}}
+		        </p>
+		      </li>
+		      {{end}}
+					
+					{{with .Page}}
+					{{if gt .TotalPages 1}}
+					<div class="center page buttons">
+						<div class="ui borderless pagination menu">
+						  <a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&page={{.Previous}}"{{end}}>
+						    <i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
+						  </a>
+							{{range .Pages}}
+							{{if eq .Num -1}}
+							<a class="disabled item">...</a>
+							{{else}}
+							<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&page={{.Num}}"{{end}}>{{.Num}}</a>
+							{{end}}
+							{{end}}
+						  <a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&page={{.Next}}"{{end}}>
+						    {{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i>
+						  </a>
+						</div>
+					</div>
+					{{end}}
+					{{end}}
+				</div>
+		  </div>
+  	</div>
+	</div>
+</div>
+{{template "base/footer" .}}

+ 30 - 0
templates/user/dashboard/navbar.tmpl

@@ -0,0 +1,30 @@
+<div class="ui container">
+  <div class="ui floating dropdown link jump">
+    <span class="text">
+    	<img class="ui avatar image" src="{{.ContextUser.AvatarLink}}">
+  		{{.ContextUser.Name}}
+    	<i class="dropdown icon"></i>
+  	</span>
+    <div class="context user menu" tabindex="-1">
+			<div class="ui header">
+				{{.i18n.Tr "home.switch_dashboard_context"}}
+			</div>
+    	<a class="{{if eq .ContextUser.Id .SignedUser.Id}}active selected{{end}} item" href="{{AppSubUrl}}/issues">
+      	<img class="ui image" src="{{.SignedUser.AvatarLink}}">
+    		{{.SignedUser.Name}}
+    	</a>
+    	{{range .Orgs}}
+    	{{if .IsOwnedBy $.SignedUser.Id}}
+    	<a class="{{if eq $.ContextUser.Id .Id}}active selected{{end}} item" href="{{AppSubUrl}}/org/{{.Name}}/issues">
+      	<img class="ui image" src="{{.AvatarLink}}">
+    		{{.Name}}
+    	</a>
+    	{{end}}
+    	{{end}}
+    	<a class="item" href="{{AppSubUrl}}/org/create">
+    		<i class="octicon octicon-repo-create"></i>&nbsp;&nbsp;&nbsp;{{.i18n.Tr "new_org"}}
+    	</a>
+    </div>
+  </div>
+</div>
+<div class="ui divider"></div>

+ 0 - 44
templates/user/issues.tmpl

@@ -1,44 +0,0 @@
-{{template "ng/base/head" .}}
-{{template "ng/base/header" .}}
-{{template "user/dashboard/nav" .}}
-<div id="dashboard-wrapper">
-	<div id="dashboard" class="container" data-page="user">
-		{{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}}
-		<div id="issue">
-		    <div class="left grid-1-5 filter-list">
-		        <ul class="list-unstyled menu menu-vertical">
-		            <li><a href="{{AppSubUrl}}/issues?state={{.State}}&repoid={{.RepoId}}" class="radius{{if eq .ViewType "all"}} active{{end}}" >In your repositories <strong class="pull-right">{{.IssueStats.AllCount}}</strong></a></li>
-		            <li><a href="{{AppSubUrl}}/issues?type=assigned&repoid={{.RepoId}}&state={{.State}}" class="radius{{if eq .ViewType "assigned"}} active{{end}}">Assigned to you <strong class="pull-right">{{.IssueStats.AssignCount}}</strong></a></li>
-		            <li><a href="{{AppSubUrl}}/issues?type=created_by&repoid={{.RepoId}}&state={{.State}}" class="radius{{if eq .ViewType "created_by"}} active{{end}}">Created by you <strong class="pull-right">{{.IssueStats.CreateCount}}</strong></a></li>
-		            <li><hr/></li>
-		            {{range .Repos}}
-		            <li><a href="{{AppSubUrl}}/issues?type={{$.ViewType}}{{if eq $.RepoId .ID}}{{else}}&repoid={{.ID}}{{end}}&state={{$.State}}" class="radius{{if eq $.RepoId .ID}} active{{end}}">{{$.SignedUser.Name}}/{{.Name}} <strong class="pull-right">{{if $.IsShowClosed}}{{.NumClosedIssues}}{{else}}{{.NumOpenIssues}}{{end}}</strong></a></li>
-		            {{end}}
-		        </ul>
-		    </div>
-		    <div class="right grid-3-4">
-		        <div class="filter-option">
-		            <div class="btn-group">
-		                <a class="btn btn-white btn-small issue-open{{if not .IsShowClosed}} active{{end}}" href="{{AppSubUrl}}/issues?type={{.ViewType}}&repoid={{.RepoId}}">Open</a>
-		                <a class="btn btn-white btn-small issue-close{{if .IsShowClosed}} active{{end}}" href="{{AppSubUrl}}/issues?type={{.ViewType}}&repoid={{.RepoId}}&state=closed">Closed</a>
-		            </div>
-		        </div>
-		        <div class="issues list-group">
-		            {{range .Issues}}{{if .}}
-		            <div class="list-group-item issue-item" id="issue-{{.ID}}" onclick="window.location.href='{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}/issues/{{.Index}}'">
-		                <span class="number pull-right">#{{.Index}}</span>
-		                <h5 class="title"><a href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}/issues/{{.Index}}">{{.Name}}</a></h5>
-		                <p class="info">
-		                    <span class="author"><img class="avatar" src="{{.Poster.AvatarLink}}" alt="" width="20"/>
-		                    <a href="{{AppSubUrl}}/{{.Poster.Name}}">{{.Poster.Name}}</a></span>
-		                    <span class="time">{{TimeSince .Created $.Lang}}</span>
-		                    <span class="comment"><i class="fa fa-comments"></i> {{.NumComments}}</span>
-		                </p>
-		            </div>
-		            {{end}}{{end}}
-		        </div>
-		    </div>
-		</div>
-	</div>
-</div>
-{{template "ng/base/footer" .}}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.