mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-26 15:02:16 +00:00
#1146 finsih UI work for access mode of collaborators
Collaborators have write access as default, and can be changed via repository collaboration settings page to change between read, write and admin.
This commit is contained in:
parent
05d8664f15
commit
045f14fbd0
|
@ -190,6 +190,8 @@ func runWeb(ctx *cli.Context) {
|
||||||
|
|
||||||
bindIgnErr := binding.BindIgnErr
|
bindIgnErr := binding.BindIgnErr
|
||||||
|
|
||||||
|
// FIXME: not all routes need go through same middlewares.
|
||||||
|
// Especially some AJAX requests, we can reduce middleware number to improve performance.
|
||||||
// Routers.
|
// Routers.
|
||||||
m.Get("/", ignSignIn, routers.Home)
|
m.Get("/", ignSignIn, routers.Home)
|
||||||
m.Get("/explore", ignSignIn, routers.Explore)
|
m.Get("/explore", ignSignIn, routers.Explore)
|
||||||
|
@ -400,7 +402,11 @@ func runWeb(ctx *cli.Context) {
|
||||||
m.Group("/settings", func() {
|
m.Group("/settings", func() {
|
||||||
m.Combo("").Get(repo.Settings).
|
m.Combo("").Get(repo.Settings).
|
||||||
Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
|
Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
|
||||||
m.Combo("/collaboration").Get(repo.Collaboration).Post(repo.CollaborationPost)
|
m.Group("/collaboration", func() {
|
||||||
|
m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost)
|
||||||
|
m.Post("/access_mode", repo.ChangeCollaborationAccessMode)
|
||||||
|
m.Post("/delete", repo.DeleteCollaboration)
|
||||||
|
})
|
||||||
|
|
||||||
m.Group("/hooks", func() {
|
m.Group("/hooks", func() {
|
||||||
m.Get("", repo.Webhooks)
|
m.Get("", repo.Webhooks)
|
||||||
|
|
|
@ -221,8 +221,6 @@ still_own_repo = Your account still has ownership over at least one repository,
|
||||||
still_has_org = Your account still has membership in at least one organization, you have to leave or delete your memberships first.
|
still_has_org = Your account still has membership in at least one organization, you have to leave or delete your memberships first.
|
||||||
org_still_own_repo = This organization still has ownership of repositories, you must delete or transfer them first.
|
org_still_own_repo = This organization still has ownership of repositories, you must delete or transfer them first.
|
||||||
|
|
||||||
still_own_user = This authentication is still in use by at least one user, please remove them from the authentication and try again.
|
|
||||||
|
|
||||||
target_branch_not_exist = Target branch does not exist.
|
target_branch_not_exist = Target branch does not exist.
|
||||||
|
|
||||||
[user]
|
[user]
|
||||||
|
@ -615,6 +613,9 @@ settings.transfer_succeed = Repository ownership has been transferred successful
|
||||||
settings.confirm_delete = Confirm Deletion
|
settings.confirm_delete = Confirm Deletion
|
||||||
settings.add_collaborator = Add New Collaborator
|
settings.add_collaborator = Add New Collaborator
|
||||||
settings.add_collaborator_success = New collaborator has been added.
|
settings.add_collaborator_success = New collaborator has been added.
|
||||||
|
settings.delete_collaborator = Delete
|
||||||
|
settings.collaborator_deletion = Collaborator Deletion
|
||||||
|
settings.collaborator_deletion_desc = This user will no longer have collaboration access to this repository after deletion. Do you want to continue?
|
||||||
settings.remove_collaborator_success = Collaborator has been removed.
|
settings.remove_collaborator_success = Collaborator has been removed.
|
||||||
settings.search_user_placeholder = Search user...
|
settings.search_user_placeholder = Search user...
|
||||||
settings.org_not_allowed_to_be_collaborator = Organization is not allowed to be added as a collaborator.
|
settings.org_not_allowed_to_be_collaborator = Organization is not allowed to be added as a collaborator.
|
||||||
|
@ -949,6 +950,7 @@ auths.update = Update Authentication Setting
|
||||||
auths.delete = Delete This Authentication
|
auths.delete = Delete This Authentication
|
||||||
auths.delete_auth_title = Authentication Deletion
|
auths.delete_auth_title = Authentication Deletion
|
||||||
auths.delete_auth_desc = This authentication is going to be deleted, do you want to continue?
|
auths.delete_auth_desc = This authentication is going to be deleted, do you want to continue?
|
||||||
|
auths.still_in_used = This authentication is still used by some users, please delete or convert these users to another login type first.
|
||||||
auths.deletion_success = Authentication has been deleted successfully!
|
auths.deletion_success = Authentication has been deleted successfully!
|
||||||
|
|
||||||
config.server_config = Server Configuration
|
config.server_config = Server Configuration
|
||||||
|
|
|
@ -13,11 +13,11 @@ import (
|
||||||
type AccessMode int
|
type AccessMode int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ACCESS_MODE_NONE AccessMode = iota
|
ACCESS_MODE_NONE AccessMode = iota // 0
|
||||||
ACCESS_MODE_READ
|
ACCESS_MODE_READ // 1
|
||||||
ACCESS_MODE_WRITE
|
ACCESS_MODE_WRITE // 2
|
||||||
ACCESS_MODE_ADMIN
|
ACCESS_MODE_ADMIN // 3
|
||||||
ACCESS_MODE_OWNER
|
ACCESS_MODE_OWNER // 4
|
||||||
)
|
)
|
||||||
|
|
||||||
// Access represents the highest access level of a user to the repository. The only access type
|
// Access represents the highest access level of a user to the repository. The only access type
|
||||||
|
@ -151,15 +151,14 @@ func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: should be able to have read-only access.
|
// refreshCollaboratorAccesses retrieves repository collaborations with their access modes.
|
||||||
// Give all collaborators write access.
|
|
||||||
func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error {
|
func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error {
|
||||||
collaborators, err := repo.getCollaborators(e)
|
collaborations, err := repo.getCollaborations(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("getCollaborators: %v", err)
|
return fmt.Errorf("getCollaborations: %v", err)
|
||||||
}
|
}
|
||||||
for _, c := range collaborators {
|
for _, c := range collaborations {
|
||||||
accessMap[c.Id] = ACCESS_MODE_WRITE
|
accessMap[c.UserID] = c.Mode
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose comment action, could be plain comment, close or reopen issue.
|
// Compose comment action, could be plain comment, close or reopen issue/pull request.
|
||||||
// This object will be used to notify watchers in the end of function.
|
// This object will be used to notify watchers in the end of function.
|
||||||
act := &Action{
|
act := &Action{
|
||||||
ActUserID: opts.Doer.Id,
|
ActUserID: opts.Doer.Id,
|
||||||
|
@ -179,6 +179,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case COMMENT_TYPE_CLOSE:
|
case COMMENT_TYPE_CLOSE:
|
||||||
act.OpType = ACTION_CLOSE_ISSUE
|
act.OpType = ACTION_CLOSE_ISSUE
|
||||||
if opts.Issue.IsPull {
|
if opts.Issue.IsPull {
|
||||||
|
|
100
models/repo.go
100
models/repo.go
|
@ -330,7 +330,6 @@ func (repo *Repository) RepoRelLink() string {
|
||||||
return "/" + repo.MustOwner().Name + "/" + repo.Name
|
return "/" + repo.MustOwner().Name + "/" + repo.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
|
func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
|
||||||
return fmt.Sprintf("%s/%s/compare/%s...%s", repo.MustOwner().Name, repo.Name, oldCommitID, newCommitID)
|
return fmt.Sprintf("%s/%s/compare/%s...%s", repo.MustOwner().Name, repo.Name, oldCommitID, newCommitID)
|
||||||
}
|
}
|
||||||
|
@ -1797,105 +1796,6 @@ func CheckRepoStats() {
|
||||||
// ***** END: Repository.NumForks *****
|
// ***** END: Repository.NumForks *****
|
||||||
}
|
}
|
||||||
|
|
||||||
// _________ .__ .__ ___. __ .__
|
|
||||||
// \_ ___ \ ____ | | | | _____ \_ |__ ________________ _/ |_|__| ____ ____
|
|
||||||
// / \ \/ / _ \| | | | \__ \ | __ \ / _ \_ __ \__ \\ __\ |/ _ \ / \
|
|
||||||
// \ \___( <_> ) |_| |__/ __ \| \_\ ( <_> ) | \// __ \| | | ( <_> ) | \
|
|
||||||
// \______ /\____/|____/____(____ /___ /\____/|__| (____ /__| |__|\____/|___| /
|
|
||||||
// \/ \/ \/ \/ \/
|
|
||||||
|
|
||||||
// A Collaboration is a relation between an individual and a repository
|
|
||||||
type Collaboration struct {
|
|
||||||
ID int64 `xorm:"pk autoincr"`
|
|
||||||
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
|
||||||
UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
|
||||||
Created time.Time `xorm:"CREATED"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add collaborator and accompanying access
|
|
||||||
func (repo *Repository) AddCollaborator(u *User) error {
|
|
||||||
collaboration := &Collaboration{
|
|
||||||
RepoID: repo.ID,
|
|
||||||
UserID: u.Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
has, err := x.Get(collaboration)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if has {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = repo.GetOwner(); err != nil {
|
|
||||||
return fmt.Errorf("GetOwner: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := x.NewSession()
|
|
||||||
defer sessionRelease(sess)
|
|
||||||
if err = sess.Begin(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = sess.InsertOne(collaboration); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if repo.Owner.IsOrganization() {
|
|
||||||
err = repo.recalculateTeamAccesses(sess, 0)
|
|
||||||
} else {
|
|
||||||
err = repo.recalculateAccesses(sess)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) getCollaborators(e Engine) ([]*User, error) {
|
|
||||||
collaborations := make([]*Collaboration, 0)
|
|
||||||
if err := e.Find(&collaborations, &Collaboration{RepoID: repo.ID}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
users := make([]*User, len(collaborations))
|
|
||||||
for i, c := range collaborations {
|
|
||||||
user, err := getUserByID(e, c.UserID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
users[i] = user
|
|
||||||
}
|
|
||||||
return users, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCollaborators returns the collaborators for a repository
|
|
||||||
func (repo *Repository) GetCollaborators() ([]*User, error) {
|
|
||||||
return repo.getCollaborators(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete collaborator and accompanying access
|
|
||||||
func (repo *Repository) DeleteCollaborator(u *User) (err error) {
|
|
||||||
collaboration := &Collaboration{
|
|
||||||
RepoID: repo.ID,
|
|
||||||
UserID: u.Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := x.NewSession()
|
|
||||||
defer sessionRelease(sess)
|
|
||||||
if err = sess.Begin(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if has, err := sess.Delete(collaboration); err != nil || has == 0 {
|
|
||||||
return err
|
|
||||||
} else if err = repo.recalculateAccesses(sess); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// __ __ __ .__
|
// __ __ __ .__
|
||||||
// / \ / \_____ _/ |_ ____ | |__
|
// / \ / \_____ _/ |_ ____ | |__
|
||||||
// \ \/\/ /\__ \\ __\/ ___\| | \
|
// \ \/\/ /\__ \\ __\/ ___\| | \
|
||||||
|
|
161
models/repo_collaboration.go
Normal file
161
models/repo_collaboration.go
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
// Copyright 2016 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 models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Collaboration represent the relation between an individual and a repository.
|
||||||
|
type Collaboration struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||||
|
UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||||
|
Mode AccessMode `xorm:"DEFAULT 2 NOT NULL"`
|
||||||
|
Created time.Time `xorm:"CREATED"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collaboration) ModeName() string {
|
||||||
|
switch c.Mode {
|
||||||
|
case ACCESS_MODE_READ:
|
||||||
|
return "Read"
|
||||||
|
case ACCESS_MODE_WRITE:
|
||||||
|
return "Write"
|
||||||
|
case ACCESS_MODE_ADMIN:
|
||||||
|
return "Admin"
|
||||||
|
}
|
||||||
|
return "Undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCollaborator adds new collaboration relation between an individual and a repository.
|
||||||
|
func (repo *Repository) AddCollaborator(u *User) error {
|
||||||
|
collaboration := &Collaboration{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
UserID: u.Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
has, err := x.Get(collaboration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if has {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
collaboration.Mode = ACCESS_MODE_WRITE
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sessionRelease(sess)
|
||||||
|
if err = sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = sess.InsertOne(collaboration); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.Owner.IsOrganization() {
|
||||||
|
err = repo.recalculateTeamAccesses(sess, 0)
|
||||||
|
} else {
|
||||||
|
err = repo.recalculateAccesses(sess)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, error) {
|
||||||
|
collaborations := make([]*Collaboration, 0)
|
||||||
|
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collaborator represents a user with collaboration details.
|
||||||
|
type Collaborator struct {
|
||||||
|
*User
|
||||||
|
Collaboration *Collaboration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) getCollaborators(e Engine) ([]*Collaborator, error) {
|
||||||
|
collaborations, err := repo.getCollaborations(e)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getCollaborations: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
collaborators := make([]*Collaborator, len(collaborations))
|
||||||
|
for i, c := range collaborations {
|
||||||
|
user, err := getUserByID(e, c.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
collaborators[i] = &Collaborator{
|
||||||
|
User: user,
|
||||||
|
Collaboration: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return collaborators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCollaborators returns the collaborators for a repository
|
||||||
|
func (repo *Repository) GetCollaborators() ([]*Collaborator, error) {
|
||||||
|
return repo.getCollaborators(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
|
||||||
|
func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error {
|
||||||
|
// Discard invalid input
|
||||||
|
if mode <= ACCESS_MODE_NONE || mode > ACCESS_MODE_OWNER {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
collaboration := &Collaboration{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
UserID: uid,
|
||||||
|
}
|
||||||
|
has, err := x.Get(collaboration)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get collaboration: %v", err)
|
||||||
|
} else if !has {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
collaboration.Mode = mode
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sessionRelease(sess)
|
||||||
|
if err = sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = sess.Id(collaboration.ID).AllCols().Update(collaboration); err != nil {
|
||||||
|
return fmt.Errorf("update collaboration: %v", err)
|
||||||
|
} else if _, err = sess.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil {
|
||||||
|
return fmt.Errorf("update access table: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCollaboration removes collaboration relation between the user and repository.
|
||||||
|
func (repo *Repository) DeleteCollaboration(uid int64) (err error) {
|
||||||
|
collaboration := &Collaboration{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
UserID: uid,
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sessionRelease(sess)
|
||||||
|
if err = sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if has, err := sess.Delete(collaboration); err != nil || has == 0 {
|
||||||
|
return err
|
||||||
|
} else if err = repo.recalculateAccesses(sess); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -20,15 +20,6 @@
|
||||||
"outputPathIsOutsideProject": 0,
|
"outputPathIsOutsideProject": 0,
|
||||||
"outputPathIsSetByUser": 0
|
"outputPathIsSetByUser": 0
|
||||||
},
|
},
|
||||||
"\/css\/gogs.min.css": {
|
|
||||||
"fileType": 16,
|
|
||||||
"ignore": 1,
|
|
||||||
"ignoreWasSetByUser": 0,
|
|
||||||
"inputAbbreviatedPath": "\/css\/gogs.min.css",
|
|
||||||
"outputAbbreviatedPath": "No Output Path",
|
|
||||||
"outputPathIsOutsideProject": 0,
|
|
||||||
"outputPathIsSetByUser": 0
|
|
||||||
},
|
|
||||||
"\/css\/semantic-2.1.8.min.css": {
|
"\/css\/semantic-2.1.8.min.css": {
|
||||||
"fileType": 16,
|
"fileType": 16,
|
||||||
"ignore": 0,
|
"ignore": 0,
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif, '微软雅黑';
|
font-family: "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,9 @@ code.wrap {
|
||||||
.ui.container.fluid.padded {
|
.ui.container.fluid.padded {
|
||||||
padding: 0 10px 0 10px;
|
padding: 0 10px 0 10px;
|
||||||
}
|
}
|
||||||
|
.ui.form .ui.button {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
.ui .text.red {
|
.ui .text.red {
|
||||||
color: #d95c5c !important;
|
color: #d95c5c !important;
|
||||||
}
|
}
|
||||||
|
@ -234,6 +237,10 @@ code.wrap {
|
||||||
.ui.status.buttons .octicon {
|
.ui.status.buttons .octicon {
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
.ui.inline.delete-button {
|
||||||
|
padding: 8px 15px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
.overflow.menu .items {
|
.overflow.menu .items {
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
@ -1984,10 +1991,11 @@ footer .container .links > *:first-child {
|
||||||
.repository.settings.collaboration .collaborator.list {
|
.repository.settings.collaboration .collaborator.list {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.repository.settings.collaboration .collaborator.list .item {
|
.repository.settings.collaboration .collaborator.list > .item {
|
||||||
padding: 10px 20px;
|
margin: 0;
|
||||||
|
line-height: 2em;
|
||||||
}
|
}
|
||||||
.repository.settings.collaboration .collaborator.list .item:not(:last-child) {
|
.repository.settings.collaboration .collaborator.list > .item:not(:last-child) {
|
||||||
border-bottom: 1px solid #DDD;
|
border-bottom: 1px solid #DDD;
|
||||||
}
|
}
|
||||||
.repository.settings.collaboration #repo-collab-form #search-user-box .results {
|
.repository.settings.collaboration #repo-collab-form #search-user-box .results {
|
||||||
|
|
1
public/css/gogs.min.css
vendored
1
public/css/gogs.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -458,6 +458,20 @@ function initRepository() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initRepositoryCollaboration(){
|
||||||
|
console.log('initRepositoryCollaboration');
|
||||||
|
|
||||||
|
// Change collaborator access mode
|
||||||
|
$('.access-mode.menu .item').click(function(){
|
||||||
|
var $menu = $(this).parent();
|
||||||
|
$.post($menu.data('url'), {
|
||||||
|
"_csrf": csrf,
|
||||||
|
"uid": $menu.data('uid'),
|
||||||
|
"mode": $(this).data('value')
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function initWiki() {
|
function initWiki() {
|
||||||
if ($('.repository.wiki').length == 0) {
|
if ($('.repository.wiki').length == 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -964,7 +978,8 @@ $(document).ready(function () {
|
||||||
initAdmin();
|
initAdmin();
|
||||||
|
|
||||||
var routes = {
|
var routes = {
|
||||||
'div.user.settings': initUserSettings
|
'div.user.settings': initUserSettings,
|
||||||
|
'div.repository.settings.collaboration': initRepositoryCollaboration
|
||||||
};
|
};
|
||||||
|
|
||||||
var selector;
|
var selector;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@footer-margin: 40px;
|
@footer-margin: 40px;
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Helvetica Neue',Arial,Helvetica,sans-serif,'微软雅黑';
|
font-family: "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,12 @@ pre, code {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.form {
|
||||||
|
.ui.button {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
&.red {
|
&.red {
|
||||||
color: #d95c5c !important;
|
color: #d95c5c !important;
|
||||||
|
@ -260,6 +266,11 @@ pre, code {
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.inline.delete-button {
|
||||||
|
padding: 8px 15px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.overflow.menu {
|
.overflow.menu {
|
||||||
|
|
|
@ -1026,8 +1026,9 @@
|
||||||
.collaborator.list {
|
.collaborator.list {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
.item {
|
>.item {
|
||||||
padding: 10px 20px;
|
margin: 0;
|
||||||
|
line-height: 2em;
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
border-bottom: 1px solid #DDD;
|
border-bottom: 1px solid #DDD;
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-xorm/core"
|
"github.com/go-xorm/core"
|
||||||
|
|
||||||
|
@ -218,11 +220,13 @@ func DeleteAuthSource(ctx *middleware.Context) {
|
||||||
if err = models.DeleteSource(source); err != nil {
|
if err = models.DeleteSource(source); err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case models.ErrAuthenticationUserUsed:
|
case models.ErrAuthenticationUserUsed:
|
||||||
ctx.Flash.Error("form.still_own_user")
|
ctx.Flash.Error(ctx.Tr("admin.auths.still_in_used"))
|
||||||
ctx.Redirect(setting.AppSubUrl + "/admin/auths/" + ctx.Params(":authid"))
|
|
||||||
default:
|
default:
|
||||||
ctx.Handle(500, "DeleteSource", err)
|
ctx.Flash.Error(fmt.Sprintf("DeleteSource: %v", err))
|
||||||
}
|
}
|
||||||
|
ctx.JSON(200, map[string]interface{}{
|
||||||
|
"redirect": setting.AppSubUrl + "/admin/auths/" + ctx.Params(":authid"),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Trace("Authentication deleted by admin(%s): %d", ctx.User.Name, source.ID)
|
log.Trace("Authentication deleted by admin(%s): %d", ctx.User.Name, source.ID)
|
||||||
|
|
|
@ -257,30 +257,13 @@ func Collaboration(ctx *middleware.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
||||||
ctx.Data["PageIsSettingsCollaboration"] = true
|
ctx.Data["PageIsSettingsCollaboration"] = true
|
||||||
|
|
||||||
// Delete collaborator.
|
|
||||||
remove := strings.ToLower(ctx.Query("remove"))
|
|
||||||
if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName {
|
|
||||||
u, err := models.GetUserByName(remove)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Handle(500, "GetUserByName", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := ctx.Repo.Repository.DeleteCollaborator(u); err != nil {
|
|
||||||
ctx.Handle(500, "DeleteCollaborator", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
|
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
users, err := ctx.Repo.Repository.GetCollaborators()
|
users, err := ctx.Repo.Repository.GetCollaborators()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "GetCollaborators", err)
|
ctx.Handle(500, "GetCollaborators", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["Collaborators"] = users
|
ctx.Data["Collaborators"] = users
|
||||||
|
|
||||||
ctx.HTML(200, COLLABORATION)
|
ctx.HTML(200, COLLABORATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,6 +315,26 @@ func CollaborationPost(ctx *middleware.Context) {
|
||||||
ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
|
ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ChangeCollaborationAccessMode(ctx *middleware.Context) {
|
||||||
|
if err := ctx.Repo.Repository.ChangeCollaborationAccessMode(
|
||||||
|
ctx.QueryInt64("uid"),
|
||||||
|
models.AccessMode(ctx.QueryInt("mode"))); err != nil {
|
||||||
|
log.Error(4, "ChangeCollaborationAccessMode: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteCollaboration(ctx *middleware.Context) {
|
||||||
|
if err := ctx.Repo.Repository.DeleteCollaboration(ctx.QueryInt64("id")); err != nil {
|
||||||
|
ctx.Flash.Error("DeleteCollaboration: " + err.Error())
|
||||||
|
} else {
|
||||||
|
ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(200, map[string]interface{}{
|
||||||
|
"redirect": ctx.Repo.RepoLink + "/settings/collaboration",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) {
|
func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) {
|
||||||
owner, err := models.GetUserByName(ctx.Params(":username"))
|
owner, err := models.GetUserByName(ctx.Params(":username"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -11,15 +11,31 @@
|
||||||
</h4>
|
</h4>
|
||||||
<div class="ui attached segment collaborator list">
|
<div class="ui attached segment collaborator list">
|
||||||
{{range .Collaborators}}
|
{{range .Collaborators}}
|
||||||
<div class="item">
|
<div class="item ui grid">
|
||||||
{{if not (eq .Id $.Owner.Id)}}
|
<div class="ui five wide column">
|
||||||
<a href="{{$.RepoLink}}/settings/collaboration?remove={{.Name}}" class="ui right text red"><i class="fa fa-times"></i></a>
|
|
||||||
{{end}}
|
|
||||||
<a href="{{AppSubUrl}}/{{.Name}}">
|
<a href="{{AppSubUrl}}/{{.Name}}">
|
||||||
<img class="ui avatar image" src="{{.AvatarLink}}">
|
<img class="ui avatar image" src="{{.AvatarLink}}">
|
||||||
{{.DisplayName}}
|
{{.DisplayName}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ui eight wide column">
|
||||||
|
<span class="octicon octicon-shield"></span>
|
||||||
|
<div class="ui inline dropdown">
|
||||||
|
<div class="text">{{.Collaboration.ModeName}}</div>
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
<div class="access-mode menu" data-url="{{$.Link}}/access_mode" data-uid="{{.Id}}">
|
||||||
|
<div class="item" data-text="Admin" data-value="3">Admin</div>
|
||||||
|
<div class="item" data-text="Write" data-value="2">Write</div>
|
||||||
|
<div class="item" data-text="Read" data-value="1">Read</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui two wide column">
|
||||||
|
<button class="ui red tiny button inline text-thin delete-button" data-url="{{$.Link}}/delete" data-id="{{.Id}}">
|
||||||
|
{{$.i18n.Tr "repo.settings.delete_collaborator"}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="ui bottom attached segment">
|
<div class="ui bottom attached segment">
|
||||||
|
@ -40,4 +56,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="ui small basic delete modal">
|
||||||
|
<div class="ui icon header">
|
||||||
|
<i class="trash icon"></i>
|
||||||
|
{{.i18n.Tr "repo.settings.collaborator_deletion"}}
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>{{.i18n.Tr "repo.settings.collaborator_deletion_desc"}}</p>
|
||||||
|
</div>
|
||||||
|
{{template "base/delete_modal_actions" .}}
|
||||||
|
</div>
|
||||||
{{template "base/footer" .}}
|
{{template "base/footer" .}}
|
||||||
|
|
Loading…
Reference in a new issue