Browse Source

#1146 finish new access rights for collaborators

Unknwon 6 years ago
parent
commit
a5b0400be7

+ 1 - 1
README.md

@@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
 
 ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true)
 
-##### Current version: 0.8.58
+##### Current version: 0.8.59
 
 | Web | UI  | Preview  |
 |:-------------:|:-------:|:-------:|

+ 7 - 7
cmd/web.go

@@ -334,7 +334,7 @@ func runWeb(ctx *cli.Context) {
 	}
 
 	reqRepoAdmin := middleware.RequireRepoAdmin()
-	reqRepoPusher := middleware.RequireRepoPusher()
+	reqRepoWriter := middleware.RequireRepoWriter()
 
 	// ***** START: Organization *****
 	m.Group("/org", func() {
@@ -448,7 +448,7 @@ func runWeb(ctx *cli.Context) {
 				m.Post("/label", repo.UpdateIssueLabel)
 				m.Post("/milestone", repo.UpdateIssueMilestone)
 				m.Post("/assignee", repo.UpdateIssueAssignee)
-			}, reqRepoAdmin)
+			}, reqRepoWriter)
 
 			m.Group("/:index", func() {
 				m.Post("/title", repo.UpdateIssueTitle)
@@ -460,7 +460,7 @@ func runWeb(ctx *cli.Context) {
 			m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
 			m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
 			m.Post("/delete", repo.DeleteLabel)
-		}, reqRepoAdmin, middleware.RepoRef())
+		}, reqRepoWriter, middleware.RepoRef())
 		m.Group("/milestones", func() {
 			m.Combo("/new").Get(repo.NewMilestone).
 				Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
@@ -468,7 +468,7 @@ func runWeb(ctx *cli.Context) {
 			m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost)
 			m.Get("/:id/:action", repo.ChangeMilestonStatus)
 			m.Post("/delete", repo.DeleteMilestone)
-		}, reqRepoAdmin, middleware.RepoRef())
+		}, reqRepoWriter, middleware.RepoRef())
 
 		m.Group("/releases", func() {
 			m.Get("/new", repo.NewRelease)
@@ -476,7 +476,7 @@ func runWeb(ctx *cli.Context) {
 			m.Get("/edit/:tagname", repo.EditRelease)
 			m.Post("/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
 			m.Post("/delete", repo.DeleteRelease)
-		}, reqRepoAdmin, middleware.RepoRef())
+		}, reqRepoWriter, middleware.RepoRef())
 
 		m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest).
 			Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
@@ -503,7 +503,7 @@ func runWeb(ctx *cli.Context) {
 				m.Combo("/:page/_edit").Get(repo.EditWiki).
 					Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost)
 				m.Post("/:page/delete", repo.DeleteWikiPagePost)
-			}, reqSignIn, reqRepoPusher)
+			}, reqSignIn, reqRepoWriter)
 		}, repo.MustEnableWiki, middleware.RepoRef())
 
 		m.Get("/archive/*", repo.Download)
@@ -511,7 +511,7 @@ func runWeb(ctx *cli.Context) {
 		m.Group("/pulls/:index", func() {
 			m.Get("/commits", middleware.RepoRef(), repo.ViewPullCommits)
 			m.Get("/files", middleware.RepoRef(), repo.ViewPullFiles)
-			m.Post("/merge", reqRepoAdmin, repo.MergePullRequest)
+			m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
 		}, repo.MustAllowPulls)
 
 		m.Group("", func() {

+ 1 - 1
conf/locale/locale_en-US.ini

@@ -476,7 +476,7 @@ issues.closed_at = `closed <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.reopened_at = `reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.commit_ref_at = `referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.poster = Poster
-issues.admin = Admin
+issues.collaborator = Collaborator
 issues.owner = Owner
 issues.sign_up_for_free = Sign up for free
 issues.sign_in_require_desc = to join this conversation. Already have an account? <a href="%s">Sign in to comment</a>

+ 1 - 1
gogs.go

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

+ 11 - 9
models/issue.go

@@ -298,16 +298,18 @@ func newIssue(e *xorm.Session, repo *Repository, issue *Issue, labelIDs []int64,
 		return err
 	}
 
-	// During the session, SQLite3 dirver cannot handle retrieve objects after update something.
-	// So we have to get all needed labels first.
-	labels := make([]*Label, 0, len(labelIDs))
-	if err = e.In("id", labelIDs).Find(&labels); err != nil {
-		return fmt.Errorf("find all labels: %v", err)
-	}
+	if len(labelIDs) > 0 {
+		// During the session, SQLite3 dirver cannot handle retrieve objects after update something.
+		// So we have to get all needed labels first.
+		labels := make([]*Label, 0, len(labelIDs))
+		if err = e.In("id", labelIDs).Find(&labels); err != nil {
+			return fmt.Errorf("find all labels: %v", err)
+		}
 
-	for _, label := range labels {
-		if err = issue.addLabel(e, label); err != nil {
-			return fmt.Errorf("addLabel: %v", err)
+		for _, label := range labels {
+			if err = issue.addLabel(e, label); err != nil {
+				return fmt.Errorf("addLabel: %v", err)
+			}
 		}
 	}
 

+ 1 - 1
models/issue_comment.go

@@ -39,7 +39,7 @@ type CommentTag int
 const (
 	COMMENT_TAG_NONE CommentTag = iota
 	COMMENT_TAG_POSTER
-	COMMENT_TAG_ADMIN
+	COMMENT_TAG_WRITER
 	COMMENT_TAG_OWNER
 )
 

+ 6 - 10
models/user.go

@@ -348,19 +348,15 @@ func (u *User) UploadAvatar(data []byte) error {
 
 // IsAdminOfRepo returns true if user has admin or higher access of repository.
 func (u *User) IsAdminOfRepo(repo *Repository) bool {
-	if repo.MustOwner().IsOrganization() {
-		has, err := HasAccess(u, repo, ACCESS_MODE_ADMIN)
-		if err != nil {
-			log.Error(3, "HasAccess: %v", err)
-		}
-		return has
+	has, err := HasAccess(u, repo, ACCESS_MODE_ADMIN)
+	if err != nil {
+		log.Error(3, "HasAccess: %v", err)
 	}
-
-	return repo.IsOwnedBy(u.Id)
+	return has
 }
 
-// CanWriteTo returns true if user has write access to given repository.
-func (u *User) CanWriteTo(repo *Repository) bool {
+// IsWriterOfRepo returns true if user has write access to given repository.
+func (u *User) IsWriterOfRepo(repo *Repository) bool {
 	has, err := HasAccess(u, repo, ACCESS_MODE_WRITE)
 	if err != nil {
 		log.Error(3, "HasAccess: %v", err)

File diff suppressed because it is too large
+ 2 - 2
modules/bindata/bindata.go


+ 3 - 3
modules/middleware/context.go

@@ -84,12 +84,12 @@ func (r *RepoContext) IsAdmin() bool {
 	return r.AccessMode >= models.ACCESS_MODE_ADMIN
 }
 
-// IsPusher returns true if current user has write or higher access of repository.
-func (r *RepoContext) IsPusher() bool {
+// IsWriter returns true if current user has write or higher access of repository.
+func (r *RepoContext) IsWriter() bool {
 	return r.AccessMode >= models.ACCESS_MODE_WRITE
 }
 
-// Return if the current user has read access for this repository
+// HasAccess returns true if the current user has at least read access for this repository
 func (r *RepoContext) HasAccess() bool {
 	return r.AccessMode >= models.ACCESS_MODE_READ
 }

+ 5 - 5
modules/middleware/repo.go

@@ -140,7 +140,7 @@ func RepoAssignment(args ...bool) macaron.Handler {
 		ctx.Data["Owner"] = ctx.Repo.Repository.Owner
 		ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
 		ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
-		ctx.Data["IsRepositoryPusher"] = ctx.Repo.IsPusher()
+		ctx.Data["IsRepositoryWriter"] = ctx.Repo.IsWriter()
 
 		if repo.IsFork {
 			RetrieveBaseRepo(ctx, repo)
@@ -150,7 +150,7 @@ func RepoAssignment(args ...bool) macaron.Handler {
 		}
 
 		// People who have push access and propose a new pull request.
-		if ctx.Repo.IsPusher() {
+		if ctx.Repo.IsWriter() {
 			// Pull request is allowed if this is a fork repository
 			// and base repository accepts pull requests.
 			if repo.BaseRepo != nil {
@@ -336,16 +336,16 @@ func RepoRef() macaron.Handler {
 
 func RequireRepoAdmin() macaron.Handler {
 	return func(ctx *Context) {
-		if !ctx.Repo.IsAdmin() {
+		if !ctx.IsSigned || (!ctx.Repo.IsAdmin() && !ctx.User.IsAdmin) {
 			ctx.Handle(404, ctx.Req.RequestURI, nil)
 			return
 		}
 	}
 }
 
-func RequireRepoPusher() macaron.Handler {
+func RequireRepoWriter() macaron.Handler {
 	return func(ctx *Context) {
-		if !ctx.Repo.IsPusher() {
+		if !ctx.IsSigned || (!ctx.Repo.IsWriter() && !ctx.User.IsAdmin) {
 			ctx.Handle(404, ctx.Req.RequestURI, nil)
 			return
 		}

+ 9 - 9
routers/repo/issue.go

@@ -273,7 +273,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *middleware.Context, repo *models.Re
 }
 
 func RetrieveRepoMetas(ctx *middleware.Context, repo *models.Repository) []*models.Label {
-	if !ctx.Repo.IsAdmin() {
+	if !ctx.Repo.IsWriter() {
 		return nil
 	}
 
@@ -356,7 +356,7 @@ func ValidateRepoMetas(ctx *middleware.Context, form auth.CreateIssueForm) ([]in
 		return nil, 0, 0
 	}
 
-	if !ctx.Repo.IsAdmin() {
+	if !ctx.Repo.IsWriter() {
 		return nil, 0, 0
 	}
 
@@ -624,7 +624,7 @@ func ViewIssue(ctx *middleware.Context) {
 	ctx.Data["Labels"] = labels
 
 	// Check milestone and assignee.
-	if ctx.Repo.IsAdmin() {
+	if ctx.Repo.IsWriter() {
 		RetrieveRepoMilestonesAndAssignees(ctx, repo)
 		if ctx.Written() {
 			return
@@ -664,8 +664,8 @@ func ViewIssue(ctx *middleware.Context) {
 			if repo.IsOwnedBy(comment.PosterID) ||
 				(repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(comment.PosterID)) {
 				comment.ShowTag = models.COMMENT_TAG_OWNER
-			} else if comment.Poster.IsAdminOfRepo(repo) {
-				comment.ShowTag = models.COMMENT_TAG_ADMIN
+			} else if comment.Poster.IsWriterOfRepo(repo) {
+				comment.ShowTag = models.COMMENT_TAG_WRITER
 			} else if comment.PosterID == issue.PosterID {
 				comment.ShowTag = models.COMMENT_TAG_POSTER
 			}
@@ -688,7 +688,7 @@ func ViewIssue(ctx *middleware.Context) {
 	ctx.Data["Participants"] = participants
 	ctx.Data["NumParticipants"] = len(participants)
 	ctx.Data["Issue"] = issue
-	ctx.Data["IsIssueOwner"] = ctx.Repo.IsAdmin() || (ctx.IsSigned && issue.IsPoster(ctx.User.Id))
+	ctx.Data["IsIssueOwner"] = ctx.Repo.IsWriter() || (ctx.IsSigned && (issue.IsPoster(ctx.User.Id) || ctx.User.IsAdmin))
 	ctx.Data["SignInLink"] = setting.AppSubUrl + "/user/login"
 
 	ctx.Data["RequireHighlightJS"] = true
@@ -715,7 +715,7 @@ func UpdateIssueTitle(ctx *middleware.Context) {
 		return
 	}
 
-	if !ctx.IsSigned || (ctx.User.Id != issue.PosterID && !ctx.Repo.IsAdmin()) {
+	if !ctx.IsSigned || (ctx.User.Id != issue.PosterID && !ctx.Repo.IsWriter() && !ctx.User.IsAdmin) {
 		ctx.Error(403)
 		return
 	}
@@ -742,7 +742,7 @@ func UpdateIssueContent(ctx *middleware.Context) {
 		return
 	}
 
-	if !ctx.IsSigned || (ctx.User.Id != issue.PosterID && !ctx.Repo.IsAdmin()) {
+	if !ctx.IsSigned || (ctx.User.Id != issue.PosterID && !ctx.Repo.IsWriter() && !ctx.User.IsAdmin) {
 		ctx.Error(403)
 		return
 	}
@@ -883,7 +883,7 @@ func NewComment(ctx *middleware.Context, form auth.CreateCommentForm) {
 	var comment *models.Comment
 	defer func() {
 		// Check if issue admin/poster changes the status of issue.
-		if (ctx.Repo.IsAdmin() || (ctx.IsSigned && issue.IsPoster(ctx.User.Id))) &&
+		if (ctx.Repo.IsWriter() || (ctx.IsSigned && issue.IsPoster(ctx.User.Id))) &&
 			(form.Status == "reopen" || form.Status == "close") &&
 			!(issue.IsPull && issue.HasMerged) {
 

+ 1 - 1
routers/repo/pull.go

@@ -490,7 +490,7 @@ func ParseCompareInfo(ctx *middleware.Context) (*models.User, *models.Repository
 		}
 	}
 
-	if !ctx.User.CanWriteTo(headRepo) && !ctx.User.IsAdmin {
+	if !ctx.User.IsWriterOfRepo(headRepo) && !ctx.User.IsAdmin {
 		log.Trace("ParseCompareInfo[%d]: does not have write access or site admin", baseRepo.ID)
 		ctx.Handle(404, "ParseCompareInfo", nil)
 		return nil, nil, nil, nil, "", ""

+ 18 - 0
routers/repo/setting.go

@@ -142,6 +142,10 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
 		ctx.Redirect(ctx.Repo.RepoLink + "/settings")
 
 	case "convert":
+		if !ctx.Repo.IsOwner() {
+			ctx.Error(404)
+			return
+		}
 		if repo.Name != form.RepoName {
 			ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil)
 			return
@@ -172,6 +176,10 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
 		ctx.Redirect(setting.AppSubUrl + "/" + ctx.Repo.Owner.Name + "/" + repo.Name)
 
 	case "transfer":
+		if !ctx.Repo.IsOwner() {
+			ctx.Error(404)
+			return
+		}
 		if repo.Name != form.RepoName {
 			ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil)
 			return
@@ -205,7 +213,12 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
 		log.Trace("Repository transfered: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
 		ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed"))
 		ctx.Redirect(setting.AppSubUrl + "/" + newOwner + "/" + repo.Name)
+
 	case "delete":
+		if !ctx.Repo.IsOwner() {
+			ctx.Error(404)
+			return
+		}
 		if repo.Name != form.RepoName {
 			ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil)
 			return
@@ -226,7 +239,12 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
 
 		ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
 		ctx.Redirect(ctx.Repo.Owner.DashboardLink())
+
 	case "delete-wiki":
+		if !ctx.Repo.IsOwner() {
+			ctx.Error(404)
+			return
+		}
 		if repo.Name != form.RepoName {
 			ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), SETTINGS_OPTIONS, nil)
 			return

+ 1 - 1
templates/.VERSION

@@ -1 +1 @@
-0.8.58.0305
+0.8.59.0305

+ 3 - 3
templates/repo/issue/labels.tmpl

@@ -4,7 +4,7 @@
 	<div class="ui container">
 		<div class="navbar">
 			{{template "repo/issue/navbar" .}}
-			{{if .IsRepositoryAdmin}}
+			{{if .IsRepositoryWriter}}
 				<div class="ui right">
 					<div class="ui green new-label button">{{.i18n.Tr "repo.issues.new_label"}}</div>
 				</div>
@@ -40,7 +40,7 @@
 			{{range .Labels}}
 				<li class="item">
 					<div class="ui label" style="color: {{.ForegroundColor}}; background-color: {{.Color}}"><i class="octicon octicon-tag"></i> {{.Name}}</div>
-					{{if $.IsRepositoryAdmin}}
+					{{if $.IsRepositoryWriter}}
 						<a class="ui right delete-button" href="#" data-url="{{$.RepoLink}}/labels/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a>
 						<a class="ui right edit-label-button" href="#" data-id={{.ID}} data-title={{.Name}} data-color={{.Color}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a>
 					{{end}}
@@ -51,7 +51,7 @@
 	</div>
 </div>
 
-{{if .IsRepositoryAdmin}}
+{{if .IsRepositoryWriter}}
 	<div class="ui small basic delete modal">
 		<div class="ui icon header">
 			<i class="trash icon"></i>

+ 1 - 1
templates/repo/issue/milestone_new.tmpl

@@ -4,7 +4,7 @@
 	<div class="ui container">
 		<div class="navbar">
 			{{template "repo/issue/navbar" .}}
-			{{if and .IsRepositoryAdmin .PageIsEditMilestone}}
+			{{if and .IsRepositoryWriter .PageIsEditMilestone}}
 				<div class="ui right floated secondary menu">
 					<a class="ui green button" href="{{$.RepoLink}}/milestones/new">{{.i18n.Tr "repo.milestones.new"}}</a>
 				</div>

+ 3 - 3
templates/repo/issue/milestones.tmpl

@@ -4,7 +4,7 @@
 	<div class="ui container">
 		<div class="navbar">
 			{{template "repo/issue/navbar" .}}
-			{{if .IsRepositoryAdmin}}
+			{{if .IsRepositoryWriter}}
 				<div class="ui right">
 					<a class="ui green button" href="{{$.Link}}/new">{{.i18n.Tr "repo.milestones.new"}}</a>
 				</div>
@@ -49,7 +49,7 @@
 							<i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.close_tab" .NumClosedIssues}}
 						</span>
 					</div>
-					{{if $.IsRepositoryAdmin}}
+					{{if $.IsRepositoryWriter}}
 						<div class="ui right operate">
 							<a href="{{$.Link}}/{{.ID}}/edit" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a>
 							{{if .IsClosed}}
@@ -93,7 +93,7 @@
 	</div>
 </div>
 
-{{if .IsRepositoryAdmin}}
+{{if .IsRepositoryWriter}}
 	<div class="ui small basic delete modal">
 		<div class="ui icon header">
 			<i class="trash icon"></i>

+ 5 - 5
templates/repo/issue/view_content.tmpl

@@ -65,7 +65,7 @@
 											{{if eq .ShowTag 1}}
 												{{$.i18n.Tr "repo.issues.poster"}}
 											{{else if eq .ShowTag 2}}
-												{{$.i18n.Tr "repo.issues.admin"}}
+												{{$.i18n.Tr "repo.issues.collaborator"}}
 											{{else if eq .ShowTag 3}}
 												{{$.i18n.Tr "repo.issues.owner"}}
 											{{end}}
@@ -165,7 +165,7 @@
 									<span class="octicon octicon-check"></span>
 									{{$.i18n.Tr "repo.pulls.can_auto_merge_desc"}}
 								</div>
-								{{if .IsRepositoryAdmin}}
+								{{if .IsRepositoryWriter}}
 									<div class="ui divider"></div>
 									<div>
 										<form class="ui form" action="{{.Link}}/merge" method="post">
@@ -231,7 +231,7 @@
 
 	<div class="four wide column">
 		<div class="ui segment metas">
-			<div class="ui {{if not .IsRepositoryAdmin}}disabled{{end}} floating jump select-label dropdown">
+			<div class="ui {{if not .IsRepositoryWriter}}disabled{{end}} floating jump select-label dropdown">
 				<span class="text">
 					<strong>{{.i18n.Tr "repo.issues.new.labels"}}</strong>
 					<span class="octicon octicon-gear"></span>
@@ -252,7 +252,7 @@
 
 			<div class="ui divider"></div>
 
-			<div class="ui {{if not .IsRepositoryAdmin}}disabled{{end}} floating jump select-milestone dropdown">
+			<div class="ui {{if not .IsRepositoryWriter}}disabled{{end}} floating jump select-milestone dropdown">
 				<span class="text">
 					<strong>{{.i18n.Tr "repo.issues.new.milestone"}}</strong>
 					<span class="octicon octicon-gear"></span>
@@ -293,7 +293,7 @@
 			<div class="ui divider"></div>
 
 			<input id="assignee_id" name="assignee_id" type="hidden" value="{{.assignee_id}}">
-			<div class="ui {{if not .IsRepositoryAdmin}}disabled{{end}} floating jump select-assignee dropdown">
+			<div class="ui {{if not .IsRepositoryWriter}}disabled{{end}} floating jump select-assignee dropdown">
 				<span class="text">
 					<strong>{{.i18n.Tr "repo.issues.new.assignee"}}</strong>
 					<span class="octicon octicon-gear"></span>

+ 2 - 2
templates/repo/release/list.tmpl

@@ -5,7 +5,7 @@
 		{{template "base/alert" .}}
 		<h2 class="ui header">
 			{{.i18n.Tr "repo.release.releases"}}
-			{{if .IsRepositoryAdmin}}
+			{{if .IsRepositoryWriter}}
 				<div class="ui right">
 					<a class="ui small green button" href="{{$.RepoLink}}/releases/new">
 						{{.i18n.Tr "repo.release.new_release"}}
@@ -37,7 +37,7 @@
 						{{if .PublisherID}}
 							<h3>
 								<a href="{{$.RepoLink}}/src/{{.TagName}}">{{.Title}}</a>
-								{{if $.IsRepositoryAdmin}}<small>(<a href="{{$.RepoLink}}/releases/edit/{{.TagName}}" rel="nofollow">{{$.i18n.Tr "repo.release.edit"}}</a>)</small>{{end}}
+								{{if $.IsRepositoryWriter}}<small>(<a href="{{$.RepoLink}}/releases/edit/{{.TagName}}" rel="nofollow">{{$.i18n.Tr "repo.release.edit"}}</a>)</small>{{end}}
 							</h3>
 							<p class="text grey">
 								<span class="author">

+ 119 - 115
templates/repo/settings/options.tmpl

@@ -136,6 +136,7 @@
 					</form>
 				</div>
 
+				{{if .IsRepositoryOwner}}
 				<h4 class="ui top attached warning header">
 					{{.i18n.Tr "repo.settings.danger_zone"}}
 				</h4>
@@ -189,145 +190,148 @@
 						</div>
 					</div>
 				</div>
+				{{end}}
 			</div>
 		</div>
 	</div>
 </div>
 
-{{if .Repository.IsMirror}}
-<div class="ui small modal" id="convert-repo-modal">
-	<div class="header">
-		{{.i18n.Tr "repo.settings.convert"}}
-	</div>
-	<div class="content">
-		<div class="ui warning message text left">
-			{{.i18n.Tr "repo.settings.convert_notices_1" | Safe}}
+{{if .IsRepositoryOwner}}
+	{{if .Repository.IsMirror}}
+	<div class="ui small modal" id="convert-repo-modal">
+		<div class="header">
+			{{.i18n.Tr "repo.settings.convert"}}
 		</div>
-		<form class="ui form" action="{{.Link}}" method="post">
-			{{.CsrfTokenHtml}}
-			<input type="hidden" name="action" value="convert">
-			<div class="field">
-				<label>
-					{{.i18n.Tr "repo.settings.transfer_form_title"}}
-					<span class="text red">{{.Repository.Name}}</span>
-				</label>
-			</div>
-			<div class="required field">
-				<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
-				<input id="repo_name" name="repo_name" required>
+		<div class="content">
+			<div class="ui warning message text left">
+				{{.i18n.Tr "repo.settings.convert_notices_1" | Safe}}
 			</div>
+			<form class="ui form" action="{{.Link}}" method="post">
+				{{.CsrfTokenHtml}}
+				<input type="hidden" name="action" value="convert">
+				<div class="field">
+					<label>
+						{{.i18n.Tr "repo.settings.transfer_form_title"}}
+						<span class="text red">{{.Repository.Name}}</span>
+					</label>
+				</div>
+				<div class="required field">
+					<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
+					<input id="repo_name" name="repo_name" required>
+				</div>
 
-			<div class="text right actions">
-				<div class="ui cancel button">{{.i18n.Tr "settings.cancel"}}</div>
-				<button class="ui red button">{{.i18n.Tr "repo.settings.convert_confirm"}}</button>
-			</div>
-		</form>
+				<div class="text right actions">
+					<div class="ui cancel button">{{.i18n.Tr "settings.cancel"}}</div>
+					<button class="ui red button">{{.i18n.Tr "repo.settings.convert_confirm"}}</button>
+				</div>
+			</form>
+		</div>
 	</div>
-</div>
-{{end}}
+	{{end}}
 
-<div class="ui small modal" id="transfer-repo-modal">
-	<div class="header">
-		{{.i18n.Tr "repo.settings.transfer"}}
-	</div>
-	<div class="content">
-		<div class="ui warning message text left">
-			{{.i18n.Tr "repo.settings.transfer_notices_1" | Safe}} <br>
-			{{.i18n.Tr "repo.settings.transfer_notices_2" | Safe}}
+	<div class="ui small modal" id="transfer-repo-modal">
+		<div class="header">
+			{{.i18n.Tr "repo.settings.transfer"}}
 		</div>
-		<form class="ui form" action="{{.Link}}" method="post">
-			{{.CsrfTokenHtml}}
-			<input type="hidden" name="action" value="transfer">
-			<div class="field">
-				<label>
-					{{.i18n.Tr "repo.settings.transfer_form_title"}}
-					<span class="text red">{{.Repository.Name}}</span>
-				</label>
-			</div>
-			<div class="required field">
-				<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
-				<input id="repo_name" name="repo_name" required>
-			</div>
-			<div class="required field">
-				<label for="new_owner_name">{{.i18n.Tr "repo.settings.transfer_owner"}}</label>
-				<input id="new_owner_name" name="new_owner_name" required>
+		<div class="content">
+			<div class="ui warning message text left">
+				{{.i18n.Tr "repo.settings.transfer_notices_1" | Safe}} <br>
+				{{.i18n.Tr "repo.settings.transfer_notices_2" | Safe}}
 			</div>
+			<form class="ui form" action="{{.Link}}" method="post">
+				{{.CsrfTokenHtml}}
+				<input type="hidden" name="action" value="transfer">
+				<div class="field">
+					<label>
+						{{.i18n.Tr "repo.settings.transfer_form_title"}}
+						<span class="text red">{{.Repository.Name}}</span>
+					</label>
+				</div>
+				<div class="required field">
+					<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
+					<input id="repo_name" name="repo_name" required>
+				</div>
+				<div class="required field">
+					<label for="new_owner_name">{{.i18n.Tr "repo.settings.transfer_owner"}}</label>
+					<input id="new_owner_name" name="new_owner_name" required>
+				</div>
 
-			<div class="text right actions">
-				<div class="ui cancel button">{{.i18n.Tr "settings.cancel"}}</div>
-				<button class="ui red button">{{.i18n.Tr "repo.settings.make_transfer"}}</button>
-			</div>
-		</form>
+				<div class="text right actions">
+					<div class="ui cancel button">{{.i18n.Tr "settings.cancel"}}</div>
+					<button class="ui red button">{{.i18n.Tr "repo.settings.make_transfer"}}</button>
+				</div>
+			</form>
+		</div>
 	</div>
-</div>
 
-<div class="ui small modal" id="delete-repo-modal">
-	<div class="header">
-		{{.i18n.Tr "repo.settings.delete"}}
-	</div>
-	<div class="content">
-		<div class="ui warning message text left">
-			{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}} <br>
-			{{.i18n.Tr "repo.settings.delete_notices_2" | Safe}}
-			{{if .Repository.NumForks}}<br>
-			{{.i18n.Tr "repo.settings.delete_notices_fork_1" | Safe}} <br>
-			{{.i18n.Tr "repo.settings.delete_notices_fork_2" | Safe}} <br>
-			{{.i18n.Tr "repo.settings.delete_notices_fork_3" | Safe}}
-			{{end}}
+	<div class="ui small modal" id="delete-repo-modal">
+		<div class="header">
+			{{.i18n.Tr "repo.settings.delete"}}
 		</div>
-		<form class="ui form" action="{{.Link}}" method="post">
-			{{.CsrfTokenHtml}}
-			<input type="hidden" name="action" value="delete">
-			<div class="field">
-				<label>
-					{{.i18n.Tr "repo.settings.transfer_form_title"}}
-					<span class="text red">{{.Repository.Name}}</span>
-				</label>
-			</div>
-			<div class="required field">
-				<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
-				<input id="repo_name" name="repo_name" required>
+		<div class="content">
+			<div class="ui warning message text left">
+				{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}} <br>
+				{{.i18n.Tr "repo.settings.delete_notices_2" | Safe}}
+				{{if .Repository.NumForks}}<br>
+				{{.i18n.Tr "repo.settings.delete_notices_fork_1" | Safe}} <br>
+				{{.i18n.Tr "repo.settings.delete_notices_fork_2" | Safe}} <br>
+				{{.i18n.Tr "repo.settings.delete_notices_fork_3" | Safe}}
+				{{end}}
 			</div>
+			<form class="ui form" action="{{.Link}}" method="post">
+				{{.CsrfTokenHtml}}
+				<input type="hidden" name="action" value="delete">
+				<div class="field">
+					<label>
+						{{.i18n.Tr "repo.settings.transfer_form_title"}}
+						<span class="text red">{{.Repository.Name}}</span>
+					</label>
+				</div>
+				<div class="required field">
+					<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
+					<input id="repo_name" name="repo_name" required>
+				</div>
 
-			<div class="text right actions">
-				<div class="ui cancel button">{{.i18n.Tr "settings.cancel"}}</div>
-				<button class="ui red button">{{.i18n.Tr "repo.settings.confirm_delete"}}</button>
-			</div>
-		</form>
+				<div class="text right actions">
+					<div class="ui cancel button">{{.i18n.Tr "settings.cancel"}}</div>
+					<button class="ui red button">{{.i18n.Tr "repo.settings.confirm_delete"}}</button>
+				</div>
+			</form>
+		</div>
 	</div>
-</div>
 
-{{if .Repository.EnableWiki}}
-<div class="ui small modal" id="delete-wiki-modal">
-	<div class="header">
-		{{.i18n.Tr "repo.settings.wiki-delete"}}
-	</div>
-	<div class="content">
-		<div class="ui warning message text left">
-			{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}}<br>
-			{{.i18n.Tr "repo.settings.wiki_delete_notices_1" .Repository.Name | Safe}}
+	{{if .Repository.EnableWiki}}
+	<div class="ui small modal" id="delete-wiki-modal">
+		<div class="header">
+			{{.i18n.Tr "repo.settings.wiki-delete"}}
 		</div>
-		<form class="ui form" action="{{.Link}}" method="post">
-			{{.CsrfTokenHtml}}
-			<input type="hidden" name="action" value="delete-wiki">
-			<div class="field">
-				<label>
-					{{.i18n.Tr "repo.settings.transfer_form_title"}}
-					<span class="text red">{{.Repository.Name}}</span>
-				</label>
-			</div>
-			<div class="required field">
-				<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
-				<input id="repo_name" name="repo_name" required>
+		<div class="content">
+			<div class="ui warning message text left">
+				{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}}<br>
+				{{.i18n.Tr "repo.settings.wiki_delete_notices_1" .Repository.Name | Safe}}
 			</div>
+			<form class="ui form" action="{{.Link}}" method="post">
+				{{.CsrfTokenHtml}}
+				<input type="hidden" name="action" value="delete-wiki">
+				<div class="field">
+					<label>
+						{{.i18n.Tr "repo.settings.transfer_form_title"}}
+						<span class="text red">{{.Repository.Name}}</span>
+					</label>
+				</div>
+				<div class="required field">
+					<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
+					<input id="repo_name" name="repo_name" required>
+				</div>
 
-			<div class="text right actions">
-				<div class="ui cancel button">{{.i18n.Tr "settings.cancel"}}</div>
-				<button class="ui red button">{{.i18n.Tr "repo.settings.confirm_delete"}}</button>
-			</div>
-		</form>
+				<div class="text right actions">
+					<div class="ui cancel button">{{.i18n.Tr "settings.cancel"}}</div>
+					<button class="ui red button">{{.i18n.Tr "repo.settings.confirm_delete"}}</button>
+				</div>
+			</form>
+		</div>
 	</div>
-</div>
+	{{end}}
 {{end}}
 
 {{template "base/footer" .}}

+ 1 - 1
templates/repo/wiki/start.tmpl

@@ -6,7 +6,7 @@
 			<span class="mega-octicon octicon-book"></span>
 			<h2>{{.i18n.Tr "repo.wiki.welcome"}}</h2>
 			<p>{{.i18n.Tr "repo.wiki.welcome_desc"}}</p>
-			{{if .IsRepositoryPusher}}
+			{{if .IsRepositoryWriter}}
 				<a class="ui green button" href="{{.RepoLink}}/wiki/_new">{{.i18n.Tr "repo.wiki.create_first_page"}}</a>
 			{{end}}
 		</div>

+ 1 - 1
templates/repo/wiki/view.tmpl

@@ -46,7 +46,7 @@
 		</div>
 		<div class="ui dividing header">
 			{{.title}}
-			{{if .IsRepositoryPusher}}
+			{{if .IsRepositoryWriter}}
 				<div class="ui right">
 					<a class="ui small button" href="{{.RepoLink}}/wiki/{{.PageURL}}/_edit">{{.i18n.Tr "repo.wiki.edit_page_button"}}</a>
 					<a class="ui green small button" href="{{.RepoLink}}/wiki/_new">{{.i18n.Tr "repo.wiki.new_page_button"}}</a>