mirror of
https://github.com/gogs/gogs.git
synced 2025-05-04 14:45:14 +00:00
Feature for editing, creating, uploading and deleting files
This commit is contained in:
parent
3d93532c87
commit
fa1b752be2
cmd
conf
models
modules
public
routers/repo
templates/repo
14
cmd/web.go
14
cmd/web.go
@ -489,6 +489,20 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
|
|
||||||
m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest).
|
m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest).
|
||||||
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
|
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
|
||||||
|
|
||||||
|
m.Group("", func() {
|
||||||
|
m.Combo("/edit/*").Get(repo.EditFile).
|
||||||
|
Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost)
|
||||||
|
m.Combo("/new/*").Get(repo.EditNewFile).
|
||||||
|
Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditNewFilePost)
|
||||||
|
m.Post("/preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost)
|
||||||
|
m.Combo("/upload/*").Get(repo.UploadFile).
|
||||||
|
Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost)
|
||||||
|
m.Post("/delete/*", bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost)
|
||||||
|
m.Post("/branches", bindIgnErr(auth.NewBranchForm{}), repo.NewBranchPost)
|
||||||
|
m.Post("/upload-file", repo.UploadFileToServer)
|
||||||
|
m.Post("/upload-remove", bindIgnErr(auth.UploadRemoveFileForm{}), repo.UploadRemoveFileFromServer)
|
||||||
|
}, context.RepoRef(), context.RepoAssignment(), reqRepoWriter)
|
||||||
}, reqSignIn, context.RepoAssignment(), repo.MustBeNotBare)
|
}, reqSignIn, context.RepoAssignment(), repo.MustBeNotBare)
|
||||||
|
|
||||||
m.Group("/:username/:reponame", func() {
|
m.Group("/:username/:reponame", func() {
|
||||||
|
21
conf/app.ini
21
conf/app.ini
@ -19,6 +19,24 @@ FORCE_PRIVATE = false
|
|||||||
MAX_CREATION_LIMIT = -1
|
MAX_CREATION_LIMIT = -1
|
||||||
; Patch test queue length, make it as large as possible
|
; Patch test queue length, make it as large as possible
|
||||||
PULL_REQUEST_QUEUE_LENGTH = 10000
|
PULL_REQUEST_QUEUE_LENGTH = 10000
|
||||||
|
; List of file extensions that should have line wraps in the CodeMirror editor
|
||||||
|
; Separate extensions with a comma. To line wrap files w/o extension, just put a comma
|
||||||
|
LINE_WRAP_EXTENSIONS = .txt,,.md,.markdown,.mdown,.mkd
|
||||||
|
; Valid file modes that have a preview API associated with them, such as api/v1/markdown
|
||||||
|
; Separate values by commas. Preview tab in edit mode won't show if the file extension doesn't match
|
||||||
|
PREVIEW_TAB_APIS = markdown
|
||||||
|
|
||||||
|
[upload]
|
||||||
|
; Whether uploads are enabled. Defaults to `true`
|
||||||
|
ENABLE_UPLOADS = true
|
||||||
|
; Path for uploads. Defaults to `data/tmp/uploads` (tmp gets deleted on gogs restart)
|
||||||
|
UPLOAD_TEMP_PATH = data/tmp/uploads
|
||||||
|
; One or more allowed types, e.g. image/jpeg|image/png. Nothing means any file type
|
||||||
|
UPLOAD_ALLOWED_TYPES =
|
||||||
|
; Max size of each file in MB. Defaults to 32MB
|
||||||
|
UPLOAD_FILE_MAX_SIZE = 32
|
||||||
|
; Max number of files per upload. Defaults to 10
|
||||||
|
UPLOAD_MAX_FILES = 10
|
||||||
|
|
||||||
[ui]
|
[ui]
|
||||||
; Number of repositories that are showed in one explore page
|
; Number of repositories that are showed in one explore page
|
||||||
@ -50,6 +68,9 @@ ENABLE_HARD_LINE_BREAK = false
|
|||||||
; List of custom URL-Schemes that are allowed as links when rendering Markdown
|
; List of custom URL-Schemes that are allowed as links when rendering Markdown
|
||||||
; for example git,magnet
|
; for example git,magnet
|
||||||
CUSTOM_URL_SCHEMES =
|
CUSTOM_URL_SCHEMES =
|
||||||
|
; List of file extensions that should be rendered/edited as Markdown
|
||||||
|
; Separate extensions with a comma. To render files w/o extension as markdown, just put a comma
|
||||||
|
MD_FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
PROTOCOL = http
|
PROTOCOL = http
|
||||||
|
@ -404,6 +404,19 @@ func (err ErrReleaseNotExist) Error() string {
|
|||||||
return fmt.Sprintf("Release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
|
return fmt.Sprintf("Release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrRepoFileAlreadyExist struct {
|
||||||
|
FileName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsErrRepoFileAlreadyExist(err error) bool {
|
||||||
|
_, ok := err.(ErrRepoFileAlreadyExist)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrRepoFileAlreadyExist) Error() string {
|
||||||
|
return fmt.Sprintf("Repo file already exists [file name: %s]", err.FileName)
|
||||||
|
}
|
||||||
|
|
||||||
// __________ .__
|
// __________ .__
|
||||||
// \______ \____________ ____ ____ | |__
|
// \______ \____________ ____ ____ | |__
|
||||||
// | | _/\_ __ \__ \ / \_/ ___\| | \
|
// | | _/\_ __ \__ \ / \_/ ___\| | \
|
||||||
@ -614,3 +627,27 @@ func IsErrTeamAlreadyExist(err error) bool {
|
|||||||
func (err ErrTeamAlreadyExist) Error() string {
|
func (err ErrTeamAlreadyExist) Error() string {
|
||||||
return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
|
return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ____ ___ .__ .___
|
||||||
|
// | | \______ | | _________ __| _/
|
||||||
|
// | | /\____ \| | / _ \__ \ / __ |
|
||||||
|
// | | / | |_> > |_( <_> ) __ \_/ /_/ |
|
||||||
|
// |______/ | __/|____/\____(____ /\____ |
|
||||||
|
// |__| \/ \/
|
||||||
|
//
|
||||||
|
|
||||||
|
type ErrUploadNotExist struct {
|
||||||
|
ID int64
|
||||||
|
UUID string
|
||||||
|
UserID int64
|
||||||
|
RepoID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsErrUploadNotExist(err error) bool {
|
||||||
|
_, ok := err.(ErrAttachmentNotExist)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrUploadNotExist) Error() string {
|
||||||
|
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
|
||||||
|
}
|
||||||
|
@ -287,7 +287,7 @@ func (pr *PullRequest) testPatch() (err error) {
|
|||||||
|
|
||||||
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
|
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
|
||||||
|
|
||||||
if err := pr.BaseRepo.UpdateLocalCopy(); err != nil {
|
if err := pr.BaseRepo.UpdateLocalCopy(pr.BaseRepo.DefaultBranch); err != nil {
|
||||||
return fmt.Errorf("UpdateLocalCopy: %v", err)
|
return fmt.Errorf("UpdateLocalCopy: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
419
models/repo.go
419
models/repo.go
@ -29,12 +29,15 @@ import (
|
|||||||
|
|
||||||
git "github.com/gogits/git-module"
|
git "github.com/gogits/git-module"
|
||||||
api "github.com/gogits/go-gogs-client"
|
api "github.com/gogits/go-gogs-client"
|
||||||
|
gouuid "github.com/satori/go.uuid"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/bindata"
|
"github.com/gogits/gogs/modules/bindata"
|
||||||
"github.com/gogits/gogs/modules/log"
|
"github.com/gogits/gogs/modules/log"
|
||||||
"github.com/gogits/gogs/modules/markdown"
|
"github.com/gogits/gogs/modules/markdown"
|
||||||
"github.com/gogits/gogs/modules/process"
|
"github.com/gogits/gogs/modules/process"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"github.com/gogits/gogs/modules/setting"
|
||||||
|
"mime/multipart"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -406,16 +409,25 @@ func (repo *Repository) LocalCopyPath() string {
|
|||||||
return path.Join(setting.AppDataPath, "tmp/local", com.ToStr(repo.ID))
|
return path.Join(setting.AppDataPath, "tmp/local", com.ToStr(repo.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLocalCopy(repoPath, localPath string) error {
|
func updateLocalCopy(repoPath, localPath, branch string) error {
|
||||||
if !com.IsExist(localPath) {
|
if !com.IsExist(localPath) {
|
||||||
if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{
|
if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{
|
||||||
Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second,
|
Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second,
|
||||||
|
Branch: branch,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("Clone: %v", err)
|
return fmt.Errorf("Clone: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if err := git.Checkout(localPath, git.CheckoutOptions{
|
||||||
|
Branch: branch,
|
||||||
|
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("Checkout: %v", err)
|
||||||
|
}
|
||||||
if err := git.Pull(localPath, git.PullRemoteOptions{
|
if err := git.Pull(localPath, git.PullRemoteOptions{
|
||||||
All: true,
|
All: false,
|
||||||
|
Remote: "origin",
|
||||||
|
Branch: branch,
|
||||||
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
|
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("Pull: %v", err)
|
return fmt.Errorf("Pull: %v", err)
|
||||||
@ -425,8 +437,8 @@ func updateLocalCopy(repoPath, localPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateLocalCopy makes sure the local copy of repository is up-to-date.
|
// UpdateLocalCopy makes sure the local copy of repository is up-to-date.
|
||||||
func (repo *Repository) UpdateLocalCopy() error {
|
func (repo *Repository) UpdateLocalCopy(branch string) error {
|
||||||
return updateLocalCopy(repo.RepoPath(), repo.LocalCopyPath())
|
return updateLocalCopy(repo.RepoPath(), repo.LocalCopyPath(), branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PatchPath returns corresponding patch file path of repository by given issue ID.
|
// PatchPath returns corresponding patch file path of repository by given issue ID.
|
||||||
@ -2099,3 +2111,402 @@ func (repo *Repository) GetForks() ([]*Repository, error) {
|
|||||||
forks := make([]*Repository, 0, repo.NumForks)
|
forks := make([]*Repository, 0, repo.NumForks)
|
||||||
return forks, x.Find(&forks, &Repository{ForkID: repo.ID})
|
return forks, x.Find(&forks, &Repository{ForkID: repo.ID})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ___________ .___.__ __ ___________.__.__
|
||||||
|
// \_ _____/ __| _/|__|/ |_ \_ _____/|__| | ____
|
||||||
|
// | __)_ / __ | | \ __\ | __) | | | _/ __ \
|
||||||
|
// | \/ /_/ | | || | | \ | | |_\ ___/
|
||||||
|
// /_______ /\____ | |__||__| \___ / |__|____/\___ >
|
||||||
|
// \/ \/ \/ \/
|
||||||
|
|
||||||
|
var repoWorkingPool = &workingPool{
|
||||||
|
pool: make(map[string]*sync.Mutex),
|
||||||
|
count: make(map[string]int),
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) LocalRepoPath() string {
|
||||||
|
return path.Join(setting.AppDataPath, "tmp/local-repo", com.ToStr(repo.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLocalRepo makes sure the local copy of repository is up-to-date.
|
||||||
|
func (repo *Repository) UpdateLocalRepo(branchName string) error {
|
||||||
|
return updateLocalCopy(repo.RepoPath(), repo.LocalRepoPath(), branchName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscardLocalRepoChanges makes sure the local copy of repository is the same as the source
|
||||||
|
func (repo *Repository) DiscardLocalRepoChanges(branchName string) error {
|
||||||
|
return discardLocalRepoChanges(repo.LocalRepoPath(), branchName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// discardLocalRepoChanges discards local commits make sure
|
||||||
|
// it is even to remote branch when local copy exists.
|
||||||
|
func discardLocalRepoChanges(localPath string, branch string) error {
|
||||||
|
if !com.IsExist(localPath) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// No need to check if nothing in the repository.
|
||||||
|
if !git.IsBranchExist(localPath, branch) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := git.ResetHEAD(localPath, true, "origin/"+branch); err != nil {
|
||||||
|
return fmt.Errorf("ResetHEAD: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckoutNewBranch checks out a new branch from the given branch name
|
||||||
|
func (repo *Repository) CheckoutNewBranch(oldBranchName, newBranchName string) error {
|
||||||
|
return checkoutNewBranch(repo.RepoPath(), repo.LocalRepoPath(), oldBranchName, newBranchName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error {
|
||||||
|
if !com.IsExist(localPath) {
|
||||||
|
if error := updateLocalCopy(repoPath, localPath, oldBranch); error != nil {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := git.Checkout(localPath, git.CheckoutOptions {
|
||||||
|
Branch: newBranch,
|
||||||
|
OldBranch: oldBranch,
|
||||||
|
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("Checkout New Branch: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateRepoFile adds new file to repository.
|
||||||
|
func (repo *Repository) UpdateRepoFile(doer *User, oldBranchName, branchName, oldTreeName, treeName, content, message string, isNewFile bool) (err error) {
|
||||||
|
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||||
|
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||||
|
|
||||||
|
if err = repo.DiscardLocalRepoChanges(oldBranchName); err != nil {
|
||||||
|
return fmt.Errorf("discardLocalRepoChanges: %s - %v", oldBranchName, err)
|
||||||
|
} else if err = repo.UpdateLocalRepo(oldBranchName); err != nil {
|
||||||
|
return fmt.Errorf("UpdateLocalRepo: %s - %v", oldBranchName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if( oldBranchName != branchName ){
|
||||||
|
if err := repo.CheckoutNewBranch(oldBranchName, branchName); err != nil {
|
||||||
|
return fmt.Errorf("CheckoutNewBranch: %s - %s: %v", oldBranchName, branchName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localPath := repo.LocalRepoPath()
|
||||||
|
filePath := path.Join(localPath, treeName)
|
||||||
|
|
||||||
|
if len(message) == 0 {
|
||||||
|
if isNewFile {
|
||||||
|
message = "Add '" + treeName + "'"
|
||||||
|
} else {
|
||||||
|
message = "Update '" + treeName + "'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! com.IsExist(filepath.Dir(filePath)) {
|
||||||
|
os.MkdirAll(filepath.Dir(filePath), os.ModePerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If new file, make sure it doesn't exist; if old file, move if file name change
|
||||||
|
if isNewFile {
|
||||||
|
if com.IsExist(filePath) {
|
||||||
|
return ErrRepoFileAlreadyExist{filePath}
|
||||||
|
}
|
||||||
|
} else if oldTreeName!="" && treeName!="" && treeName != oldTreeName {
|
||||||
|
if err = git.MoveFile(localPath, oldTreeName, treeName); err != nil {
|
||||||
|
return fmt.Errorf("MoveFile: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil {
|
||||||
|
return fmt.Errorf("WriteFile: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = git.AddChanges(localPath, true); err != nil {
|
||||||
|
return fmt.Errorf("AddChanges: %v", err)
|
||||||
|
} else if err = git.CommitChanges(localPath, message, doer.NewGitSig()); err != nil {
|
||||||
|
return fmt.Errorf("CommitChanges: %v", err)
|
||||||
|
} else if err = git.Push(localPath, "origin", branchName); err != nil {
|
||||||
|
return fmt.Errorf("Push: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) GetPreviewDiff(repoPath, branchName, treeName, text string, maxlines, maxchars, maxfiles int) (diff *Diff, err error) {
|
||||||
|
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||||
|
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||||
|
|
||||||
|
if err = repo.DiscardLocalRepoChanges(branchName); err != nil {
|
||||||
|
return nil, fmt.Errorf("discardLocalRepoChanges: %s - %v", branchName, err)
|
||||||
|
} else if err = repo.UpdateLocalRepo(branchName); err != nil {
|
||||||
|
return nil, fmt.Errorf("UpdateLocalRepo: %s - %v", branchName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
localPath := repo.LocalRepoPath()
|
||||||
|
filePath := path.Join(localPath, treeName)
|
||||||
|
|
||||||
|
if ! com.IsExist(filepath.Dir(filePath)) {
|
||||||
|
os.MkdirAll(filepath.Dir(filePath), os.ModePerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ioutil.WriteFile(filePath, []byte(text), 0666); err != nil {
|
||||||
|
return nil, fmt.Errorf("WriteFile: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
cmd = exec.Command("git", "diff", treeName)
|
||||||
|
cmd.Dir = localPath
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("StdoutPipe: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cmd.Start(); err != nil {
|
||||||
|
return nil, fmt.Errorf("Start: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pid := process.Add(fmt.Sprintf("GetDiffRange (%s)", repoPath), cmd)
|
||||||
|
defer process.Remove(pid)
|
||||||
|
|
||||||
|
diff, err = ParsePatch(maxlines, maxchars, maxfiles, stdout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ParsePatch: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cmd.Wait(); err != nil {
|
||||||
|
return nil, fmt.Errorf("Wait: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ________ .__ __ ___________.__.__
|
||||||
|
// \______ \ ____ | | _____/ |_ ____ \_ _____/|__| | ____
|
||||||
|
// | | \_/ __ \| | _/ __ \ __\/ __ \ | __) | | | _/ __ \
|
||||||
|
// | ` \ ___/| |_\ ___/| | \ ___/ | \ | | |_\ ___/
|
||||||
|
// /_______ /\___ >____/\___ >__| \___ > \___ / |__|____/\___ >
|
||||||
|
// \/ \/ \/ \/ \/ \/
|
||||||
|
//
|
||||||
|
|
||||||
|
func (repo *Repository) DeleteRepoFile(doer *User, branch, treeName, message string) (err error) {
|
||||||
|
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||||
|
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||||
|
|
||||||
|
localPath := repo.LocalRepoPath()
|
||||||
|
if err = discardLocalRepoChanges(localPath, branch); err != nil {
|
||||||
|
return fmt.Errorf("discardLocalRepoChanges: %v", err)
|
||||||
|
} else if err = repo.UpdateLocalRepo(branch); err != nil {
|
||||||
|
return fmt.Errorf("UpdateLocalRepo: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath := path.Join(localPath, treeName)
|
||||||
|
os.Remove(filePath)
|
||||||
|
|
||||||
|
if len(message) == 0 {
|
||||||
|
message = "Delete file '" + treeName + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = git.AddChanges(localPath, true); err != nil {
|
||||||
|
return fmt.Errorf("AddChanges: %v", err)
|
||||||
|
} else if err = git.CommitChanges(localPath, message, doer.NewGitSig()); err != nil {
|
||||||
|
return fmt.Errorf("CommitChanges: %v", err)
|
||||||
|
} else if err = git.Push(localPath, "origin", branch); err != nil {
|
||||||
|
return fmt.Errorf("Push: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ____ ___ .__ .___ ___________.___.__
|
||||||
|
// | | \______ | | _________ __| _/ \_ _____/| | | ____ ______
|
||||||
|
// | | /\____ \| | / _ \__ \ / __ | | __) | | | _/ __ \ / ___/
|
||||||
|
// | | / | |_> > |_( <_> ) __ \_/ /_/ | | \ | | |_\ ___/ \___ \
|
||||||
|
// |______/ | __/|____/\____(____ /\____ | \___ / |___|____/\___ >____ >
|
||||||
|
// |__| \/ \/ \/ \/ \/
|
||||||
|
//
|
||||||
|
|
||||||
|
// uploadRepoFiles uploads new files to repository.
|
||||||
|
func (repo *Repository) UploadRepoFiles(doer *User, oldBranchName, branchName, treeName, message string, uuids []string) (err error) {
|
||||||
|
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||||
|
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||||
|
|
||||||
|
localPath := repo.LocalRepoPath()
|
||||||
|
|
||||||
|
if err = discardLocalRepoChanges(localPath, oldBranchName); err != nil {
|
||||||
|
return fmt.Errorf("discardLocalRepoChanges: %v", err)
|
||||||
|
} else if err = repo.UpdateLocalRepo(oldBranchName); err != nil {
|
||||||
|
return fmt.Errorf("UpdateLocalRepo: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if( oldBranchName != branchName ){
|
||||||
|
repo.CheckoutNewBranch(oldBranchName, branchName)
|
||||||
|
}
|
||||||
|
|
||||||
|
dirPath := path.Join(localPath, treeName)
|
||||||
|
|
||||||
|
if ! com.IsExist(dirPath) {
|
||||||
|
os.MkdirAll(dirPath, os.ModePerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy uploaded files into repository.
|
||||||
|
for _, uuid := range uuids {
|
||||||
|
upload, err := getUpload(uuid, doer.Id, repo.ID)
|
||||||
|
if err != nil {
|
||||||
|
if IsErrUploadNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("getUpload[%s]: %v", uuid, err)
|
||||||
|
}
|
||||||
|
uuidPath := upload.LocalPath()
|
||||||
|
filePath := dirPath+"/"+upload.Name
|
||||||
|
if err := os.Rename(uuidPath, filePath); err != nil {
|
||||||
|
DeleteUpload(upload, true);
|
||||||
|
return fmt.Errorf("Rename[%s -> %s]: %v", uuidPath, filePath, err)
|
||||||
|
}
|
||||||
|
DeleteUpload(upload, false); // false because we have moved the file
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(message) == 0 {
|
||||||
|
message = "Add files to '" + treeName + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = git.AddChanges(localPath, true); err != nil {
|
||||||
|
return fmt.Errorf("AddChanges: %v", err)
|
||||||
|
} else if err = git.CommitChanges(localPath, message, doer.NewGitSig()); err != nil {
|
||||||
|
return fmt.Errorf("CommitChanges: %v", err)
|
||||||
|
} else if err = git.Push(localPath, "origin", branchName); err != nil {
|
||||||
|
return fmt.Errorf("Push: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload represent a uploaded file to a repo to be deleted when moved
|
||||||
|
type Upload struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
UUID string `xorm:"uuid UNIQUE"`
|
||||||
|
UID int64 `xorm:"INDEX"`
|
||||||
|
RepoID int64 `xorm:"INDEX"`
|
||||||
|
Name string
|
||||||
|
Created time.Time `xorm:"-"`
|
||||||
|
CreatedUnix int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Upload) BeforeInsert() {
|
||||||
|
u.CreatedUnix = time.Now().UTC().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Upload) AfterSet(colName string, _ xorm.Cell) {
|
||||||
|
switch colName {
|
||||||
|
case "created_unix":
|
||||||
|
u.Created = time.Unix(u.CreatedUnix, 0).Local()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadLocalPath returns where uploads is stored in local file system based on given UUID.
|
||||||
|
func UploadLocalPath(uuid string) string {
|
||||||
|
return path.Join(setting.UploadTempPath, uuid[0:1], uuid[1:2], uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalPath returns where uploads are temporarily stored in local file system.
|
||||||
|
func (upload *Upload) LocalPath() string {
|
||||||
|
return UploadLocalPath(upload.UUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUpload creates a new upload object.
|
||||||
|
func NewUpload(name string, buf []byte, file multipart.File, userId, repoId int64) (_ *Upload, err error) {
|
||||||
|
up := &Upload{
|
||||||
|
UUID: gouuid.NewV4().String(),
|
||||||
|
Name: name,
|
||||||
|
UID: userId,
|
||||||
|
RepoID: repoId,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.MkdirAll(path.Dir(up.LocalPath()), os.ModePerm); err != nil {
|
||||||
|
return nil, fmt.Errorf("MkdirAll: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fw, err := os.Create(up.LocalPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Create: %v", err)
|
||||||
|
}
|
||||||
|
defer fw.Close()
|
||||||
|
|
||||||
|
if _, err = fw.Write(buf); err != nil {
|
||||||
|
return nil, fmt.Errorf("Write: %v", err)
|
||||||
|
} else if _, err = io.Copy(fw, file); err != nil {
|
||||||
|
return nil, fmt.Errorf("Copy: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sessionRelease(sess)
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := sess.Insert(up); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return up, sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveUpload removes the file by UUID
|
||||||
|
func RemoveUpload(uuid string, userId, repoId int64) (err error) {
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sessionRelease(sess)
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
upload, err := getUpload(uuid, userId, repoId)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("getUpload[%s]: %v", uuid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := DeleteUpload(upload, true); err != nil {
|
||||||
|
return fmt.Errorf("DeleteUpload[%s]: %v", uuid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUpload(uuid string, userId, repoId int64) (*Upload, error) {
|
||||||
|
up := &Upload{UUID: uuid, UID: userId, RepoID: repoId}
|
||||||
|
has, err := x.Get(up)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, ErrUploadNotExist{0, uuid, userId, repoId}
|
||||||
|
}
|
||||||
|
return up, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUpload returns Upload by given UUID.
|
||||||
|
func GetUpload(uuid string, userId, repoId int64) (*Upload, error) {
|
||||||
|
return getUpload(uuid, userId, repoId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUpload deletes the given upload
|
||||||
|
func DeleteUpload(u *Upload, remove bool) error {
|
||||||
|
_, err := DeleteUploads([]*Upload{u}, remove)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUploads deletes the given uploads
|
||||||
|
func DeleteUploads(uploads []*Upload, remove bool) (int, error) {
|
||||||
|
for i, u := range uploads {
|
||||||
|
if remove {
|
||||||
|
if err := os.Remove(u.LocalPath()); err != nil {
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := x.Delete(u); err != nil {
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(uploads), nil
|
||||||
|
}
|
||||||
|
@ -115,7 +115,7 @@ func (repo *Repository) LocalWikiPath() string {
|
|||||||
|
|
||||||
// UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date.
|
// UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date.
|
||||||
func (repo *Repository) UpdateLocalWiki() error {
|
func (repo *Repository) UpdateLocalWiki() error {
|
||||||
return updateLocalCopy(repo.WikiPath(), repo.LocalWikiPath())
|
return updateLocalCopy(repo.WikiPath(), repo.LocalWikiPath(), repo.DefaultBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// discardLocalWikiChanges discards local commits make sure
|
// discardLocalWikiChanges discards local commits make sure
|
||||||
|
@ -268,3 +268,92 @@ type NewWikiForm struct {
|
|||||||
func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ___________ .___.__ __
|
||||||
|
// \_ _____/ __| _/|__|/ |_
|
||||||
|
// | __)_ / __ | | \ __\
|
||||||
|
// | \/ /_/ | | || |
|
||||||
|
// /_______ /\____ | |__||__|
|
||||||
|
// \/ \/
|
||||||
|
|
||||||
|
type EditRepoFileForm struct {
|
||||||
|
TreeName string `binding:"Required;MaxSize(500)"`
|
||||||
|
Content string `binding:"Required"`
|
||||||
|
CommitSummary string `binding:"MaxSize(100)`
|
||||||
|
CommitMessage string
|
||||||
|
CommitChoice string `binding:"Required;MaxSize(50)"`
|
||||||
|
NewBranchName string `binding:"AlphaDashDot;MaxSize(100)"`
|
||||||
|
LastCommit string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *EditRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditPreviewDiffForm struct {
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *EditPreviewDiffForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ____ ___ .__ .___
|
||||||
|
// | | \______ | | _________ __| _/
|
||||||
|
// | | /\____ \| | / _ \__ \ / __ |
|
||||||
|
// | | / | |_> > |_( <_> ) __ \_/ /_/ |
|
||||||
|
// |______/ | __/|____/\____(____ /\____ |
|
||||||
|
// |__| \/ \/
|
||||||
|
//
|
||||||
|
|
||||||
|
type UploadRepoFileForm struct {
|
||||||
|
TreeName string `binding:MaxSize(500)"`
|
||||||
|
CommitSummary string `binding:"MaxSize(100)`
|
||||||
|
CommitMessage string
|
||||||
|
CommitChoice string `binding:"Required;MaxSize(50)"`
|
||||||
|
NewBranchName string `binding:"AlphaDashDot;MaxSize(100)"`
|
||||||
|
Files []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *UploadRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadRemoveFileForm struct {
|
||||||
|
File string `binding:"Required;MaxSize(50)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *UploadRemoveFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ________ .__ __
|
||||||
|
// \______ \ ____ | | _____/ |_ ____
|
||||||
|
// | | \_/ __ \| | _/ __ \ __\/ __ \
|
||||||
|
// | ` \ ___/| |_\ ___/| | \ ___/
|
||||||
|
// /_______ /\___ >____/\___ >__| \___ >
|
||||||
|
// \/ \/ \/ \/
|
||||||
|
|
||||||
|
type DeleteRepoFileForm struct {
|
||||||
|
CommitSummary string `binding:"MaxSize(100)`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *DeleteRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// __________ .__
|
||||||
|
// \______ \____________ ____ ____ | |__
|
||||||
|
// | | _/\_ __ \__ \ / \_/ ___\| | \
|
||||||
|
// | | \ | | \// __ \| | \ \___| Y \
|
||||||
|
// |______ / |__| (____ /___| /\___ >___| /
|
||||||
|
// \/ \/ \/ \/ \/
|
||||||
|
type NewBranchForm struct {
|
||||||
|
OldBranchName string `binding:"Required;MaxSize(100)"`
|
||||||
|
BranchName string `binding:"Required;MaxSize(100)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *NewBranchForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -110,6 +110,8 @@ var (
|
|||||||
ForcePrivate bool
|
ForcePrivate bool
|
||||||
MaxCreationLimit int
|
MaxCreationLimit int
|
||||||
PullRequestQueueLength int
|
PullRequestQueueLength int
|
||||||
|
LineWrapExtensions []string
|
||||||
|
PreviewTabApis []string
|
||||||
}
|
}
|
||||||
RepoRootPath string
|
RepoRootPath string
|
||||||
ScriptType string
|
ScriptType string
|
||||||
@ -129,6 +131,7 @@ var (
|
|||||||
Markdown struct {
|
Markdown struct {
|
||||||
EnableHardLineBreak bool
|
EnableHardLineBreak bool
|
||||||
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
|
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
|
||||||
|
MdFileExtensions []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Picture settings
|
// Picture settings
|
||||||
@ -148,6 +151,13 @@ var (
|
|||||||
AttachmentMaxFiles int
|
AttachmentMaxFiles int
|
||||||
AttachmentEnabled bool
|
AttachmentEnabled bool
|
||||||
|
|
||||||
|
// Repo Upload settings
|
||||||
|
UploadTempPath string
|
||||||
|
UploadAllowedTypes string
|
||||||
|
UploadMaxSize int64
|
||||||
|
UploadMaxFiles int
|
||||||
|
UploadEnabled bool
|
||||||
|
|
||||||
// Time settings
|
// Time settings
|
||||||
TimeFormat string
|
TimeFormat string
|
||||||
|
|
||||||
@ -437,6 +447,16 @@ func NewContext() {
|
|||||||
log.Fatal(4, "Fail to map Repository settings: %v", err)
|
log.Fatal(4, "Fail to map Repository settings: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sec = Cfg.Section("upload")
|
||||||
|
UploadTempPath = sec.Key("UPLOAD_TEMP_PATH").MustString(path.Join(AppDataPath, "tmp/uploads"))
|
||||||
|
if !filepath.IsAbs(UploadTempPath) {
|
||||||
|
UploadTempPath = path.Join(workDir, UploadTempPath)
|
||||||
|
}
|
||||||
|
UploadAllowedTypes = strings.Replace(sec.Key("UPLOAD_ALLOWED_TYPES").MustString(""), "|", ",", -1)
|
||||||
|
UploadMaxSize = sec.Key("UPLOAD_FILE_MAX_SIZE").MustInt64(32)
|
||||||
|
UploadMaxFiles = sec.Key("UPLOAD_MAX_FILES").MustInt(10)
|
||||||
|
UploadEnabled = sec.Key("ENABLE_UPLOADS").MustBool(true)
|
||||||
|
|
||||||
// UI settings.
|
// UI settings.
|
||||||
sec = Cfg.Section("ui")
|
sec = Cfg.Section("ui")
|
||||||
ExplorePagingNum = sec.Key("EXPLORE_PAGING_NUM").MustInt(20)
|
ExplorePagingNum = sec.Key("EXPLORE_PAGING_NUM").MustInt(20)
|
||||||
|
@ -1265,55 +1265,55 @@ footer .ui.language .menu {
|
|||||||
.repository.file.list #file-content .view-raw img {
|
.repository.file.list #file-content .view-raw img {
|
||||||
padding: 5px 5px 0 5px;
|
padding: 5px 5px 0 5px;
|
||||||
}
|
}
|
||||||
.repository.file.list #file-content .code-view * {
|
#file-content .code-view * {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
.repository.file.list #file-content .code-view table {
|
#file-content .code-view table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.repository.file.list #file-content .code-view .lines-num {
|
#file-content .code-view .lines-num {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
color: #999;
|
color: #999;
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
width: 1%;
|
width: 1%;
|
||||||
}
|
}
|
||||||
.repository.file.list #file-content .code-view .lines-num span {
|
#file-content .code-view .lines-num span {
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.repository.file.list #file-content .code-view .lines-num,
|
#file-content .code-view .lines-num,
|
||||||
.repository.file.list #file-content .code-view .lines-code {
|
#file-content .code-view .lines-code {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.repository.file.list #file-content .code-view .lines-num pre,
|
#file-content .code-view .lines-num pre,
|
||||||
.repository.file.list #file-content .code-view .lines-code pre,
|
#file-content .code-view .lines-code pre,
|
||||||
.repository.file.list #file-content .code-view .lines-num ol,
|
#file-content .code-view .lines-num ol,
|
||||||
.repository.file.list #file-content .code-view .lines-code ol,
|
#file-content .code-view .lines-code ol,
|
||||||
.repository.file.list #file-content .code-view .lines-num .hljs,
|
#file-content .code-view .lines-num .hljs,
|
||||||
.repository.file.list #file-content .code-view .lines-code .hljs {
|
#file-content .code-view .lines-code .hljs {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
.repository.file.list #file-content .code-view .lines-num pre li,
|
#file-content .code-view .lines-num pre li,
|
||||||
.repository.file.list #file-content .code-view .lines-code pre li,
|
#file-content .code-view .lines-code pre li,
|
||||||
.repository.file.list #file-content .code-view .lines-num ol li,
|
#file-content .code-view .lines-num ol li,
|
||||||
.repository.file.list #file-content .code-view .lines-code ol li,
|
#file-content .code-view .lines-code ol li,
|
||||||
.repository.file.list #file-content .code-view .lines-num .hljs li,
|
#file-content .code-view .lines-num .hljs li,
|
||||||
.repository.file.list #file-content .code-view .lines-code .hljs li {
|
#file-content .code-view .lines-code .hljs li {
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
.repository.file.list #file-content .code-view .lines-num pre li.active,
|
#file-content .code-view .lines-num pre li.active,
|
||||||
.repository.file.list #file-content .code-view .lines-code pre li.active,
|
#file-content .code-view .lines-code pre li.active,
|
||||||
.repository.file.list #file-content .code-view .lines-num ol li.active,
|
#file-content .code-view .lines-num ol li.active,
|
||||||
.repository.file.list #file-content .code-view .lines-code ol li.active,
|
#file-content .code-view .lines-code ol li.active,
|
||||||
.repository.file.list #file-content .code-view .lines-num .hljs li.active,
|
#file-content .code-view .lines-num .hljs li.active,
|
||||||
.repository.file.list #file-content .code-view .lines-code .hljs li.active {
|
#file-content .code-view .lines-code .hljs li.active {
|
||||||
background: #ffffdd;
|
background: #ffffdd;
|
||||||
}
|
}
|
||||||
.repository.file.list .sidebar {
|
.repository.file.list .sidebar {
|
||||||
@ -1878,7 +1878,7 @@ footer .ui.language .menu {
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
padding: 5px 5px 0 5px;
|
padding: 5px 5px 0 5px;
|
||||||
}
|
}
|
||||||
.repository .code-view {
|
#file-content .code-view {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
@ -2129,13 +2129,13 @@ footer .ui.language .menu {
|
|||||||
.page.buttons {
|
.page.buttons {
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
}
|
}
|
||||||
.ui.comments .dropzone {
|
.ui.comments .dropzone, .ui.upload .dropzone {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
border: 2px dashed #0087F7;
|
border: 2px dashed #0087F7;
|
||||||
box-shadow: none!important;
|
box-shadow: none!important;
|
||||||
}
|
}
|
||||||
.ui.comments .dropzone .dz-error-message {
|
.ui.comments .dropzone .dz-error-message, .ui.upload .dropzone .dz-error-message {
|
||||||
top: 140px;
|
top: 140px;
|
||||||
}
|
}
|
||||||
.settings .content {
|
.settings .content {
|
||||||
@ -2704,3 +2704,159 @@ footer .ui.language .menu {
|
|||||||
.ui.user.list .item .description a:hover {
|
.ui.user.list .item .description a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
.btn-octicon {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 5px;
|
||||||
|
margin-left: 5px;
|
||||||
|
line-height: 1;
|
||||||
|
color: #767676;
|
||||||
|
vertical-align: middle;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.btn-octicon:hover {
|
||||||
|
color: #4078c0;
|
||||||
|
}
|
||||||
|
.btn-octicon-danger:hover {
|
||||||
|
color: #bd2c00;
|
||||||
|
}
|
||||||
|
.inline-form {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.ui.form .breadcrumb input {
|
||||||
|
min-height: 34px;
|
||||||
|
padding: 7px 8px;
|
||||||
|
color: #333;
|
||||||
|
vertical-align: middle;
|
||||||
|
background-color: #fff;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 8px center;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 3px;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0,0,0,0.075);
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
|
#file-actions {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
.CodeMirror.cm-s-default {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 3px;
|
||||||
|
height: 600px;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.commit-form-wrapper {
|
||||||
|
padding-left: 64px;
|
||||||
|
}
|
||||||
|
.commit-form {
|
||||||
|
position: relative;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.commit-form-wrapper .commit-form-avatar {
|
||||||
|
float: left;
|
||||||
|
margin-left: -64px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.commit-form::before {
|
||||||
|
border-width: 8px;
|
||||||
|
border-color: transparent;
|
||||||
|
border-right-color: #ddd;
|
||||||
|
position: absolute;
|
||||||
|
top: 11px;
|
||||||
|
right: 100%;
|
||||||
|
left: -16px;
|
||||||
|
display: block;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
content: " ";
|
||||||
|
border-style: solid solid outset;
|
||||||
|
}
|
||||||
|
.form-checkbox input[type=checkbox], .form-checkbox input[type=radio] {
|
||||||
|
float: left;
|
||||||
|
margin: 2px 0 0 -20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.branch-name {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px 6px;
|
||||||
|
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
|
color: rgba(0,0,0,0.5);
|
||||||
|
background-color: rgba(209,227,237,0.5);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.form-control, .form-select {
|
||||||
|
min-height: 34px;
|
||||||
|
padding: 7px 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #333;
|
||||||
|
vertical-align: middle;
|
||||||
|
background-color: #fff;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 8px center;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 3px;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0,0,0,0.075);
|
||||||
|
}
|
||||||
|
.form-control.input-contrast {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
.form-control.mr-2 {
|
||||||
|
margin-right: 6px !important;
|
||||||
|
}
|
||||||
|
.quick-pull-choice .new-branch-name-input input {
|
||||||
|
width: 240px !important;
|
||||||
|
padding-left: 26px !important;
|
||||||
|
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
|
}
|
||||||
|
.quick-pull-choice .new-branch-name-input .quick-pull-new-branch-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 9px;
|
||||||
|
left: 10px;
|
||||||
|
color: #b0c4ce;
|
||||||
|
}
|
||||||
|
.text-muted, .text-gray {
|
||||||
|
color: #767676 !important;
|
||||||
|
}
|
||||||
|
.quick-pull-choice .new-branch-name-input {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.quick-pull-choice .quick-pull-branch-name {
|
||||||
|
display: none;
|
||||||
|
padding-left: 48px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.quick-pull-choice.will-create-branch .quick-pull-branch-name {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.nowrap {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
#file-buttons {
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
.repository .ui.container .ui.breadcrumb {
|
||||||
|
font-size: 1.5em;
|
||||||
|
color: #767676;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
.repository .ui.container .item:first-child .ui.breadcrumb {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
.repository .ui.container .ui.breadcrumb.field {
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
.repo-edit-file-cancel {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
@ -28,6 +28,61 @@ function initCommentPreviewTab($form) {
|
|||||||
buttonsClickOnEnter();
|
buttonsClickOnEnter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var previewTab;
|
||||||
|
var previewTabApis;
|
||||||
|
|
||||||
|
function initEditPreviewTab($form) {
|
||||||
|
var $tab_menu = $form.find('.tabular.menu');
|
||||||
|
$tab_menu.find('.item').tab();
|
||||||
|
previewTab = $tab_menu.find('.item[data-tab="' + $tab_menu.data('preview') + '"]');
|
||||||
|
|
||||||
|
if(previewTab.length) {
|
||||||
|
previewTabApis = previewTab.data('preview-apis').split(',');
|
||||||
|
previewTab.click(function () {
|
||||||
|
var $this = $(this);
|
||||||
|
$.post($this.data('url'), {
|
||||||
|
"_csrf": csrf,
|
||||||
|
"mode": "gfm",
|
||||||
|
"context": $this.data('context'),
|
||||||
|
"text": $form.find('.tab.segment[data-tab="' + $tab_menu.data('write') + '"] textarea').val()
|
||||||
|
},
|
||||||
|
function (data) {
|
||||||
|
var $preview_tab = $form.find('.tab.segment[data-tab="' + $tab_menu.data('preview') + '"]');
|
||||||
|
$preview_tab.html(data);
|
||||||
|
emojify.run($preview_tab[0]);
|
||||||
|
$('pre code', $preview_tab[0]).each(function (i, block) {
|
||||||
|
hljs.highlightBlock(block);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonsClickOnEnter();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initEditDiffTab($form) {
|
||||||
|
var $tab_menu = $form.find('.tabular.menu');
|
||||||
|
$tab_menu.find('.item').tab();
|
||||||
|
$tab_menu.find('.item[data-tab="' + $tab_menu.data('diff') + '"]').click(function () {
|
||||||
|
var $this = $(this);
|
||||||
|
$.post($this.data('url'), {
|
||||||
|
"_csrf": csrf,
|
||||||
|
"context": $this.data('context'),
|
||||||
|
"content": $form.find('.tab.segment[data-tab="' + $tab_menu.data('write') + '"] textarea').val()
|
||||||
|
},
|
||||||
|
function (data) {
|
||||||
|
var $diff_tab = $form.find('.tab.segment[data-tab="' + $tab_menu.data('diff') + '"]');
|
||||||
|
$diff_tab.html(data);
|
||||||
|
emojify.run($diff_tab[0]);
|
||||||
|
initCodeView()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonsClickOnEnter();
|
||||||
|
}
|
||||||
|
|
||||||
function initCommentForm() {
|
function initCommentForm() {
|
||||||
if ($('.comment.form').length == 0) {
|
if ($('.comment.form').length == 0) {
|
||||||
return
|
return
|
||||||
@ -145,6 +200,11 @@ function initCommentForm() {
|
|||||||
selectItem('.select-assignee', '#assignee_id');
|
selectItem('.select-assignee', '#assignee_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initEditForm(){
|
||||||
|
initEditPreviewTab($('.edit.form'));
|
||||||
|
initEditDiffTab($('.edit.form'));
|
||||||
|
}
|
||||||
|
|
||||||
function initInstall() {
|
function initInstall() {
|
||||||
if ($('.install').length == 0) {
|
if ($('.install').length == 0) {
|
||||||
return;
|
return;
|
||||||
@ -406,7 +466,7 @@ function initRepository() {
|
|||||||
|
|
||||||
// Change status
|
// Change status
|
||||||
var $status_btn = $('#status-button');
|
var $status_btn = $('#status-button');
|
||||||
$('#content').keyup(function () {
|
$('#edit_area').keyup(function () {
|
||||||
if ($(this).val().length == 0) {
|
if ($(this).val().length == 0) {
|
||||||
$status_btn.text($status_btn.data('status'))
|
$status_btn.text($status_btn.data('status'))
|
||||||
} else {
|
} else {
|
||||||
@ -472,15 +532,10 @@ function initRepositoryCollaboration(){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initWiki() {
|
function initWikiForm() {
|
||||||
if ($('.repository.wiki').length == 0) {
|
var $edit_area = $('.repository.wiki textarea#edit_area');
|
||||||
return;
|
if ($edit_area.length > 0) {
|
||||||
}
|
new SimpleMDE({
|
||||||
|
|
||||||
|
|
||||||
if ($('.repository.wiki.new').length > 0) {
|
|
||||||
var $edit_area = $('#edit-area');
|
|
||||||
var simplemde = new SimpleMDE({
|
|
||||||
autoDownloadFontAwesome: false,
|
autoDownloadFontAwesome: false,
|
||||||
element: $edit_area[0],
|
element: $edit_area[0],
|
||||||
forceSync: true,
|
forceSync: true,
|
||||||
@ -508,15 +563,279 @@ function initWiki() {
|
|||||||
spellChecker: false,
|
spellChecker: false,
|
||||||
tabSize: 4,
|
tabSize: 4,
|
||||||
toolbar: ["bold", "italic", "strikethrough", "|",
|
toolbar: ["bold", "italic", "strikethrough", "|",
|
||||||
"heading", "heading-1", "heading-2", "heading-3", "|",
|
"heading-1", "heading-2", "heading-3", "heading-bigger", "heading-smaller", "|",
|
||||||
"code", "quote", "|",
|
"code", "quote", "|",
|
||||||
"unordered-list", "ordered-list", "|",
|
"unordered-list", "ordered-list", "|",
|
||||||
"link", "image", "horizontal-rule", "|",
|
"link", "image", "table", "horizontal-rule", "|",
|
||||||
"preview", "fullscreen"]
|
"clean-block", "preview", "fullscreen", "side-by-side"]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initIssueForm() {
|
||||||
|
var $edit_area = $('.repository.issue textarea.edit_area');
|
||||||
|
console.log($edit_area);
|
||||||
|
console.log($edit_area.length);
|
||||||
|
if ($edit_area.length > 0) {
|
||||||
|
$edit_area.each(function(i, edit_area) {
|
||||||
|
new SimpleMDE({
|
||||||
|
autoDownloadFontAwesome: false,
|
||||||
|
element: edit_area[0],
|
||||||
|
forceSync: true,
|
||||||
|
previewRender: function (plainText, preview) { // Async method
|
||||||
|
setTimeout(function () {
|
||||||
|
// FIXME: still send render request when return back to edit mode
|
||||||
|
$.post($edit_area.data('url'), {
|
||||||
|
"_csrf": csrf,
|
||||||
|
"mode": "gfm",
|
||||||
|
"context": $edit_area.data('context'),
|
||||||
|
"text": plainText
|
||||||
|
},
|
||||||
|
function (data) {
|
||||||
|
preview.innerHTML = '<div class="markdown">' + data + '</div>';
|
||||||
|
emojify.run($('.editor-preview')[0]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return "Loading...";
|
||||||
|
},
|
||||||
|
renderingConfig: {
|
||||||
|
singleLineBreaks: false
|
||||||
|
},
|
||||||
|
spellChecker: false,
|
||||||
|
tabSize: 4,
|
||||||
|
toolbar: ["bold", "italic", "strikethrough", "|",
|
||||||
|
"code", "quote", "|",
|
||||||
|
"unordered-list", "ordered-list", "|",
|
||||||
|
"link", "image", "table"]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var editArea;
|
||||||
|
var editFilename;
|
||||||
|
var smdEditor;
|
||||||
|
var cmEditor;
|
||||||
|
var mdFileExtensions;
|
||||||
|
var lineWrapExtensions;
|
||||||
|
|
||||||
|
// For IE
|
||||||
|
String.prototype.endsWith = function(pattern) {
|
||||||
|
var d = this.length - pattern.length;
|
||||||
|
return d >= 0 && this.lastIndexOf(pattern) === d;
|
||||||
|
};
|
||||||
|
|
||||||
|
function initEditor() {
|
||||||
|
editFilename = $("#file-name");
|
||||||
|
editArea = $('.repository.edit textarea#edit_area');
|
||||||
|
|
||||||
|
if (!editArea.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
(function ($, undefined) {
|
||||||
|
$.fn.getCursorPosition = function () {
|
||||||
|
var el = $(this).get(0);
|
||||||
|
var pos = 0;
|
||||||
|
if ('selectionStart' in el) {
|
||||||
|
pos = el.selectionStart;
|
||||||
|
} else if ('selection' in document) {
|
||||||
|
el.focus();
|
||||||
|
var Sel = document.selection.createRange();
|
||||||
|
var SelLength = document.selection.createRange().text.length;
|
||||||
|
Sel.moveStart('character', -el.value.length);
|
||||||
|
pos = Sel.text.length - SelLength;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
})(jQuery);
|
||||||
|
|
||||||
|
CodeMirror.modeURL = editArea.data("mode-url");
|
||||||
|
mdFileExtensions = editArea.data("md-file-extensions").split(",");
|
||||||
|
lineWrapExtensions = editArea.data("line-wrap-extensions").split(",");
|
||||||
|
|
||||||
|
editFilename.on("keyup", function (e) {
|
||||||
|
var val = editFilename.val(), m, mode, spec, extension, extWithDot, previewLink, dataUrl, apiCall;
|
||||||
|
extension = extWithDot = "";
|
||||||
|
if(m = /.+\.([^.]+)$/.exec(val)) {
|
||||||
|
extension = m[1];
|
||||||
|
extWithDot = "."+extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = CodeMirror.findModeByExtension(extension);
|
||||||
|
previewLink = $('a[data-tab=preview]');
|
||||||
|
if (info) {
|
||||||
|
mode = info.mode;
|
||||||
|
spec = info.mime;
|
||||||
|
apiCall = mode;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
apiCall = extension
|
||||||
|
}
|
||||||
|
|
||||||
|
if(previewLink.length && apiCall && previewTabApis && previewTabApis.length && previewTabApis.indexOf(apiCall) >= 0) {
|
||||||
|
dataUrl = previewLink.attr('data-url');
|
||||||
|
previewLink.attr('data-url', dataUrl.replace(/(.*)\/.*/i, '$1/' + mode));
|
||||||
|
previewLink.show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
previewLink.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this file is a Markdown extensions, we will load that editor and return
|
||||||
|
if (mdFileExtensions.indexOf(extWithDot) >= 0) {
|
||||||
|
if (setSimpleMDE()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else we are going to use CodeMirror
|
||||||
|
if(! cmEditor){
|
||||||
|
if( ! setCodeMirror())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode) {
|
||||||
|
cmEditor.setOption("mode", spec);
|
||||||
|
CodeMirror.autoLoadMode(cmEditor, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineWrapExtensions.indexOf(extWithDot) >= 0) {
|
||||||
|
cmEditor.setOption("lineWrapping", true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cmEditor.setOption("lineWrapping", false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.keyup(function (e) {
|
||||||
|
var sections = $('.breadcrumb span.section');
|
||||||
|
var dividers = $('.breadcrumb div.divider');
|
||||||
|
if (e.keyCode == 8) {
|
||||||
|
if ($(this).getCursorPosition() == 0) {
|
||||||
|
if (sections.length > 0) {
|
||||||
|
var value = sections.last().find('a').text();
|
||||||
|
$(this).val(value + $(this).val());
|
||||||
|
$(this)[0].setSelectionRange(value.length, value.length);
|
||||||
|
sections.last().remove();
|
||||||
|
dividers.last().remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.keyCode == 191) {
|
||||||
|
var parts = $(this).val().split('/');
|
||||||
|
for (var i = 0; i < parts.length; ++i) {
|
||||||
|
var value = parts[i];
|
||||||
|
if (i < parts.length - 1) {
|
||||||
|
if (value.length) {
|
||||||
|
$('<span class="section"><a href="#">' + value + '</a></span>').insertBefore($(this));
|
||||||
|
$('<div class="divider"> / </div>').insertBefore($(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$(this).val(value);
|
||||||
|
}
|
||||||
|
$(this)[0].setSelectionRange(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var parts = [];
|
||||||
|
$('.breadcrumb span.section').each(function (i, element) {
|
||||||
|
element = $(element);
|
||||||
|
if (element.find('a').length) {
|
||||||
|
parts.push(element.find('a').text());
|
||||||
|
} else {
|
||||||
|
parts.push(element.text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if ($(this).val())
|
||||||
|
parts.push($(this).val());
|
||||||
|
$('#tree-name').val(parts.join('/'));
|
||||||
|
}).trigger('keyup');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSimpleMDE() {
|
||||||
|
if(cmEditor) {
|
||||||
|
cmEditor.toTextArea();
|
||||||
|
cmEditor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(smdEditor) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
smdEditor = new SimpleMDE({
|
||||||
|
autoDownloadFontAwesome: false,
|
||||||
|
element: editArea[0],
|
||||||
|
forceSync: true,
|
||||||
|
renderingConfig: {
|
||||||
|
singleLineBreaks: false
|
||||||
|
},
|
||||||
|
tabSize: 4,
|
||||||
|
spellChecker: false,
|
||||||
|
previewRender: function (plainText, preview) { // Async method
|
||||||
|
setTimeout(function () {
|
||||||
|
// FIXME: still send render request when return back to edit mode
|
||||||
|
$.post(editArea.data('url'), {
|
||||||
|
"_csrf": csrf,
|
||||||
|
"mode": "gfm",
|
||||||
|
"context": editArea.data('context'),
|
||||||
|
"text": plainText
|
||||||
|
},
|
||||||
|
function (data) {
|
||||||
|
preview.innerHTML = '<div class="markdown">' + data + '</div>';
|
||||||
|
emojify.run($('.editor-preview')[0]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return "Loading...";
|
||||||
|
},
|
||||||
|
toolbar: ["bold", "italic", "strikethrough", "|",
|
||||||
|
"heading-1", "heading-2", "heading-3", "heading-bigger", "heading-smaller", "|",
|
||||||
|
"code", "quote", "|",
|
||||||
|
"unordered-list", "ordered-list", "|",
|
||||||
|
"link", "image", "table", "horizontal-rule", "|",
|
||||||
|
"clean-block", "preview", "fullscreen", "side-by-side"]
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCodeMirror() {
|
||||||
|
if(smdEditor) {
|
||||||
|
smdEditor.toTextArea();
|
||||||
|
smdEditor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cmEditor){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmEditor = CodeMirror.fromTextArea(editArea[0], {
|
||||||
|
lineNumbers: true
|
||||||
|
});
|
||||||
|
cmEditor.on("change", function (cm, change) {
|
||||||
|
editArea.val(cm.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initQuickPull(){
|
||||||
|
$('.js-quick-pull-choice-option').change(function() {
|
||||||
|
quickPullChoiceChange();
|
||||||
|
});
|
||||||
|
quickPullChoiceChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
function quickPullChoiceChange(){
|
||||||
|
var radio = $('.js-quick-pull-choice-option:checked');
|
||||||
|
if (radio.val() == 'commit-to-new-branch')
|
||||||
|
$('.quick-pull-branch-name').show();
|
||||||
|
else
|
||||||
|
$('.quick-pull-branch-name').hide();
|
||||||
|
}
|
||||||
|
|
||||||
function initOrganization() {
|
function initOrganization() {
|
||||||
if ($('.organization').length == 0) {
|
if ($('.organization').length == 0) {
|
||||||
return;
|
return;
|
||||||
@ -823,6 +1142,53 @@ function searchRepositories() {
|
|||||||
hideWhenLostFocus('#search-repo-box .results', '#search-repo-box');
|
hideWhenLostFocus('#search-repo-box .results', '#search-repo-box');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initCodeView() {
|
||||||
|
if ($('.code-view .linenums').length > 0) {
|
||||||
|
var $block = $('.code-view .linenums');
|
||||||
|
var lines = $block.html().split("\n");
|
||||||
|
$block.html('');
|
||||||
|
|
||||||
|
var $num_list = $('.code-view .lines-num');
|
||||||
|
|
||||||
|
// Building blocks.
|
||||||
|
var $toappendblock = [];
|
||||||
|
var $toappendnum_list = [];
|
||||||
|
for (var i = 0; i < lines.length; i++) {
|
||||||
|
$toappendblock.push('<li class="L' + (i + 1) + '" rel="L' + (i + 1) + '">' + lines[i] + '</li>');
|
||||||
|
$toappendnum_list.push('<span id="L' + (i + 1) + '">' + (i + 1) + '</span>');
|
||||||
|
}
|
||||||
|
$block.append($toappendblock.join(''));
|
||||||
|
$num_list.append($toappendnum_list.join(''));
|
||||||
|
|
||||||
|
$(document).on('click', '.lines-num span', function (e) {
|
||||||
|
var $select = $(this);
|
||||||
|
var $list = $select.parent().siblings('.lines-code').find('ol.linenums > li');
|
||||||
|
selectRange($list, $list.filter('[rel=' + $select.attr('id') + ']'), (e.shiftKey ? $list.filter('.active').eq(0) : null));
|
||||||
|
deSelect();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(window).on('hashchange', function (e) {
|
||||||
|
var m = window.location.hash.match(/^#(L\d+)\-(L\d+)$/);
|
||||||
|
var $list = $('.code-view ol.linenums > li');
|
||||||
|
var $first;
|
||||||
|
if (m) {
|
||||||
|
$first = $list.filter('.' + m[1]);
|
||||||
|
selectRange($list, $first, $list.filter('.' + m[2]));
|
||||||
|
$("html, body").scrollTop($first.offset().top - 200);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m = window.location.hash.match(/^#(L\d+)$/);
|
||||||
|
if (m) {
|
||||||
|
$first = $list.filter('.' + m[1]);
|
||||||
|
selectRange($list, $first);
|
||||||
|
$("html, body").scrollTop($first.offset().top - 200);
|
||||||
|
}
|
||||||
|
}).trigger('hashchange');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var $dropz;
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
csrf = $('meta[name=_csrf]').attr("content");
|
csrf = $('meta[name=_csrf]').attr("content");
|
||||||
suburl = $('meta[name=_suburl]').attr("content");
|
suburl = $('meta[name=_suburl]').attr("content");
|
||||||
@ -872,12 +1238,12 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dropzone
|
// Dropzone
|
||||||
if ($('#dropzone').length > 0) {
|
var $dropz = $('#dropzone');
|
||||||
|
if ($dropz.length > 0) {
|
||||||
// Disable auto discover for all elements:
|
// Disable auto discover for all elements:
|
||||||
Dropzone.autoDiscover = false;
|
Dropzone.autoDiscover = false;
|
||||||
|
|
||||||
var filenameDict = {};
|
var filenameDict = {};
|
||||||
var $dropz = $('#dropzone');
|
|
||||||
$dropz.dropzone({
|
$dropz.dropzone({
|
||||||
url: $dropz.data('upload-url'),
|
url: $dropz.data('upload-url'),
|
||||||
headers: {"X-Csrf-Token": csrf},
|
headers: {"X-Csrf-Token": csrf},
|
||||||
@ -892,12 +1258,16 @@ $(document).ready(function () {
|
|||||||
init: function () {
|
init: function () {
|
||||||
this.on("success", function (file, data) {
|
this.on("success", function (file, data) {
|
||||||
filenameDict[file.name] = data.uuid;
|
filenameDict[file.name] = data.uuid;
|
||||||
$('.attachments').append('<input id="' + data.uuid + '" name="attachments" type="hidden" value="' + data.uuid + '">');
|
var input = $('<input id="' + data.uuid + '" name="files" type="hidden">').val(data.uuid);
|
||||||
|
$('.files').append(input);
|
||||||
});
|
});
|
||||||
this.on("removedfile", function (file) {
|
this.on("removedfile", function (file) {
|
||||||
if (file.name in filenameDict) {
|
if (file.name in filenameDict) {
|
||||||
$('#' + filenameDict[file.name]).remove();
|
$('#' + filenameDict[file.name]).remove();
|
||||||
}
|
}
|
||||||
|
if($dropz.data('remove-url') && $dropz.data('csrf')) {
|
||||||
|
$.post($dropz.data('remove-url'), {file: filenameDict[file.name], _csrf: $dropz.data('csrf')});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -931,6 +1301,15 @@ $(document).ready(function () {
|
|||||||
e.trigger.setAttribute('data-content', e.trigger.getAttribute('data-original'))
|
e.trigger.setAttribute('data-content', e.trigger.getAttribute('data-original'))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Clipboard for copying filename on edit page
|
||||||
|
if($('.clipboard-tree-name').length) {
|
||||||
|
new Clipboard(document.querySelector('.clipboard-tree-name'), {
|
||||||
|
text: function () {
|
||||||
|
return $('#tree-name').val();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Helpers.
|
// Helpers.
|
||||||
$('.delete-button').click(function () {
|
$('.delete-button').click(function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
@ -994,10 +1373,14 @@ $(document).ready(function () {
|
|||||||
initCommentForm();
|
initCommentForm();
|
||||||
initInstall();
|
initInstall();
|
||||||
initRepository();
|
initRepository();
|
||||||
initWiki();
|
initWikiForm();
|
||||||
|
initIssueForm();
|
||||||
|
initEditForm();
|
||||||
|
initEditor();
|
||||||
initOrganization();
|
initOrganization();
|
||||||
initWebhook();
|
initWebhook();
|
||||||
initAdmin();
|
initAdmin();
|
||||||
|
initQuickPull();
|
||||||
|
|
||||||
var routes = {
|
var routes = {
|
||||||
'div.user.settings': initUserSettings,
|
'div.user.settings': initUserSettings,
|
||||||
@ -1013,92 +1396,51 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function changeHash(hash) {
|
||||||
|
if (history.pushState) {
|
||||||
|
history.pushState(null, null, hash);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
location.hash = hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deSelect() {
|
||||||
|
if (window.getSelection) {
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
} else {
|
||||||
|
document.selection.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectRange($list, $select, $from) {
|
||||||
|
$list.removeClass('active');
|
||||||
|
if ($from) {
|
||||||
|
var a = parseInt($select.attr('rel').substr(1));
|
||||||
|
var b = parseInt($from.attr('rel').substr(1));
|
||||||
|
var c;
|
||||||
|
if (a != b) {
|
||||||
|
if (a > b) {
|
||||||
|
c = a;
|
||||||
|
a = b;
|
||||||
|
b = c;
|
||||||
|
}
|
||||||
|
var classes = [];
|
||||||
|
for (i = a; i <= b; i++) {
|
||||||
|
classes.push('.L' + i);
|
||||||
|
}
|
||||||
|
$list.filter(classes.join(',')).addClass('active');
|
||||||
|
changeHash('#L' + a + '-' + 'L' + b);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$select.addClass('active');
|
||||||
|
changeHash('#' + $select.attr('rel'));
|
||||||
|
}
|
||||||
|
|
||||||
$(window).load(function () {
|
$(window).load(function () {
|
||||||
function changeHash(hash) {
|
|
||||||
if (history.pushState) {
|
|
||||||
history.pushState(null, null, hash);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
location.hash = hash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deSelect() {
|
|
||||||
if (window.getSelection) {
|
|
||||||
window.getSelection().removeAllRanges();
|
|
||||||
} else {
|
|
||||||
document.selection.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectRange($list, $select, $from) {
|
|
||||||
$list.removeClass('active');
|
|
||||||
if ($from) {
|
|
||||||
var a = parseInt($select.attr('rel').substr(1));
|
|
||||||
var b = parseInt($from.attr('rel').substr(1));
|
|
||||||
var c;
|
|
||||||
if (a != b) {
|
|
||||||
if (a > b) {
|
|
||||||
c = a;
|
|
||||||
a = b;
|
|
||||||
b = c;
|
|
||||||
}
|
|
||||||
var classes = [];
|
|
||||||
for (i = a; i <= b; i++) {
|
|
||||||
classes.push('.L' + i);
|
|
||||||
}
|
|
||||||
$list.filter(classes.join(',')).addClass('active');
|
|
||||||
changeHash('#L' + a + '-' + 'L' + b);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$select.addClass('active');
|
|
||||||
changeHash('#' + $select.attr('rel'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Code view.
|
// Code view.
|
||||||
if ($('.code-view .linenums').length > 0) {
|
initCodeView();
|
||||||
var $block = $('.code-view .linenums');
|
|
||||||
var lines = $block.html().split("\n");
|
|
||||||
$block.html('');
|
|
||||||
|
|
||||||
var $num_list = $('.code-view .lines-num');
|
|
||||||
|
|
||||||
// Building blocks.
|
|
||||||
var $toappendblock = [];
|
|
||||||
var $toappendnum_list = [];
|
|
||||||
for (var i = 0; i < lines.length; i++) {
|
|
||||||
$toappendblock.push('<li class="L' + (i + 1) + '" rel="L' + (i + 1) + '">' + lines[i] + '</li>');
|
|
||||||
$toappendnum_list.push('<span id="L' + (i + 1) + '">' + (i + 1) + '</span>');
|
|
||||||
}
|
|
||||||
$block.append($toappendblock.join(''));
|
|
||||||
$num_list.append($toappendnum_list.join(''));
|
|
||||||
|
|
||||||
$(document).on('click', '.lines-num span', function (e) {
|
|
||||||
var $select = $(this);
|
|
||||||
var $list = $select.parent().siblings('.lines-code').find('ol.linenums > li');
|
|
||||||
selectRange($list, $list.filter('[rel=' + $select.attr('id') + ']'), (e.shiftKey ? $list.filter('.active').eq(0) : null));
|
|
||||||
deSelect();
|
|
||||||
});
|
|
||||||
|
|
||||||
$(window).on('hashchange', function (e) {
|
|
||||||
var m = window.location.hash.match(/^#(L\d+)\-(L\d+)$/);
|
|
||||||
var $list = $('.code-view ol.linenums > li');
|
|
||||||
var $first;
|
|
||||||
if (m) {
|
|
||||||
$first = $list.filter('.' + m[1]);
|
|
||||||
selectRange($list, $first, $list.filter('.' + m[2]));
|
|
||||||
$("html, body").scrollTop($first.offset().top - 200);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m = window.location.hash.match(/^#(L\d+)$/);
|
|
||||||
if (m) {
|
|
||||||
$first = $list.filter('.' + m[1]);
|
|
||||||
selectRange($list, $first);
|
|
||||||
$("html, body").scrollTop($first.offset().top - 200);
|
|
||||||
}
|
|
||||||
}).trigger('hashchange');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repo clone url.
|
// Repo clone url.
|
||||||
if ($('#repo-clone-url').length > 0) {
|
if ($('#repo-clone-url').length > 0) {
|
||||||
@ -1117,5 +1459,5 @@ $(window).load(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
$('form').areYouSure();
|
$('form').areYouSure();
|
||||||
});
|
});
|
||||||
|
54
routers/repo/delete.go
Normal file
54
routers/repo/delete.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// 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 repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogits/gogs/modules/context"
|
||||||
|
"github.com/gogits/gogs/modules/auth"
|
||||||
|
"github.com/gogits/gogs/modules/log"
|
||||||
|
"github.com/gogits/gogs/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
|
||||||
|
branchName := ctx.Repo.BranchName
|
||||||
|
treeName := ctx.Repo.TreeName
|
||||||
|
|
||||||
|
if ctx.HasError() {
|
||||||
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName + "/" + treeName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.Repo.Repository.DeleteRepoFile(ctx.User, branchName, treeName, form.CommitSummary); err != nil {
|
||||||
|
ctx.Handle(500, "DeleteRepoFile", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Was successful, so now need to call models.CommitRepoAction() with the new commitID for webhooks and watchers
|
||||||
|
if branch, err := ctx.Repo.Repository.GetBranch(branchName); err != nil {
|
||||||
|
log.Error(4, "repo.Repository.GetBranch(%s): %v", branchName, err)
|
||||||
|
} else if commit, err := branch.GetCommit(); err != nil {
|
||||||
|
log.Error(4, "branch.GetCommit(): %v", err)
|
||||||
|
} else {
|
||||||
|
pc := &models.PushCommits{
|
||||||
|
Len: 1,
|
||||||
|
Commits: []*models.PushCommit{&models.PushCommit{
|
||||||
|
commit.ID.String(),
|
||||||
|
commit.Message(),
|
||||||
|
commit.Author.Email,
|
||||||
|
commit.Author.Name,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
oldCommitID := ctx.Repo.CommitID
|
||||||
|
newCommitID := commit.ID.String()
|
||||||
|
if err := models.CommitRepoAction(ctx.User.Id, ctx.Repo.Owner.Id, ctx.User.LowerName, ctx.Repo.Owner.Email,
|
||||||
|
ctx.Repo.Repository.ID, ctx.Repo.Owner.LowerName, ctx.Repo.Repository.Name, "refs/heads/"+branchName, pc,
|
||||||
|
oldCommitID, newCommitID); err != nil {
|
||||||
|
log.Error(4, "models.CommitRepoAction(branch = %s): %v", branchName, err)
|
||||||
|
}
|
||||||
|
models.HookQueue.Add(ctx.Repo.Repository.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Redirect(ctx.Repo.RepoLink+"/src/"+branchName)
|
||||||
|
}
|
359
routers/repo/edit.go
Normal file
359
routers/repo/edit.go
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
// 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 repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/gogits/git-module"
|
||||||
|
"github.com/gogits/gogs/modules/base"
|
||||||
|
"github.com/gogits/gogs/modules/context"
|
||||||
|
"github.com/gogits/gogs/modules/log"
|
||||||
|
"github.com/gogits/gogs/modules/template"
|
||||||
|
"github.com/gogits/gogs/modules/auth"
|
||||||
|
"github.com/gogits/gogs/models"
|
||||||
|
"github.com/gogits/gogs/modules/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EDIT base.TplName = "repo/edit"
|
||||||
|
DIFF_PREVIEW base.TplName = "repo/diff_preview"
|
||||||
|
DIFF_PREVIEW_NEW base.TplName = "repo/diff_preview_new"
|
||||||
|
)
|
||||||
|
|
||||||
|
func EditFile(ctx *context.Context) {
|
||||||
|
editFile(ctx, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditNewFile(ctx *context.Context) {
|
||||||
|
editFile(ctx, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func editFile(ctx *context.Context, isNewFile bool) {
|
||||||
|
ctx.Data["PageIsEdit"] = true
|
||||||
|
ctx.Data["IsNewFile"] = isNewFile
|
||||||
|
ctx.Data["RequireHighlightJS"] = true
|
||||||
|
|
||||||
|
userName := ctx.Repo.Owner.Name
|
||||||
|
repoName := ctx.Repo.Repository.Name
|
||||||
|
branchName := ctx.Repo.BranchName
|
||||||
|
branchLink := ctx.Repo.RepoLink + "/src/" + branchName
|
||||||
|
treeName := ctx.Repo.TreeName
|
||||||
|
|
||||||
|
var treeNames []string
|
||||||
|
if len(treeName) > 0 {
|
||||||
|
treeNames = strings.Split(treeName, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! isNewFile {
|
||||||
|
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treeName)
|
||||||
|
|
||||||
|
if err != nil && git.IsErrNotExist(err) {
|
||||||
|
ctx.Handle(404, "GetTreeEntryByPath", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.Repo.IsViewCommit) || entry == nil || entry.IsDir() {
|
||||||
|
ctx.Handle(404, "repo.Home", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
blob := entry.Blob()
|
||||||
|
|
||||||
|
dataRc, err := blob.Data()
|
||||||
|
if err != nil {
|
||||||
|
ctx.Handle(404, "blob.Data", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["FileSize"] = blob.Size()
|
||||||
|
ctx.Data["FileName"] = blob.Name()
|
||||||
|
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, _ := dataRc.Read(buf)
|
||||||
|
if n > 0 {
|
||||||
|
buf = buf[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
_, isTextFile := base.IsTextFile(buf)
|
||||||
|
|
||||||
|
if ! isTextFile {
|
||||||
|
ctx.Handle(404, "repo.Home", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d, _ := ioutil.ReadAll(dataRc)
|
||||||
|
buf = append(buf, d...)
|
||||||
|
|
||||||
|
if err, content := template.ToUtf8WithErr(buf); err != nil {
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "Convert content encoding: %s", err)
|
||||||
|
}
|
||||||
|
ctx.Data["FileContent"] = string(buf)
|
||||||
|
} else {
|
||||||
|
ctx.Data["FileContent"] = content
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
treeNames = append(treeNames, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["RequireSimpleMDE"] = true
|
||||||
|
|
||||||
|
ctx.Data["UserName"] = userName
|
||||||
|
ctx.Data["RepoName"] = repoName
|
||||||
|
ctx.Data["BranchName"] = branchName
|
||||||
|
ctx.Data["TreeName"] = treeName
|
||||||
|
ctx.Data["TreeNames"] = treeNames
|
||||||
|
ctx.Data["BranchLink"] = branchLink
|
||||||
|
ctx.Data["CommitSummary"] = ""
|
||||||
|
ctx.Data["CommitMessage"] = ""
|
||||||
|
ctx.Data["CommitChoice"] = "direct"
|
||||||
|
ctx.Data["NewBranchName"] = ""
|
||||||
|
ctx.Data["CommitDirectlyToThisBranch"] = ctx.Tr("repo.commit_directly_to_this_branch", "<strong class=\"branch-name\">"+branchName+"</strong>")
|
||||||
|
ctx.Data["CreateNewBranch"] = ctx.Tr("repo.create_new_branch", "<strong>"+ctx.Tr("repo.new_branch")+"</strong>")
|
||||||
|
ctx.Data["LastCommit"] = ctx.Repo.Commit.ID
|
||||||
|
ctx.Data["MdFileExtensions"] = strings.Join(setting.Markdown.MdFileExtensions, ",")
|
||||||
|
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.LineWrapExtensions, ",")
|
||||||
|
ctx.Data["PreviewTabApis"] = strings.Join(setting.Repository.PreviewTabApis, ",")
|
||||||
|
ctx.Data["PreviewDiffUrl"] = ctx.Repo.RepoLink + "/preview/" + branchName + "/" + treeName
|
||||||
|
|
||||||
|
ctx.HTML(200, EDIT)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditFilePost(ctx *context.Context, form auth.EditRepoFileForm) {
|
||||||
|
editFilePost(ctx, form, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditNewFilePost(ctx *context.Context, form auth.EditRepoFileForm) {
|
||||||
|
editFilePost(ctx, form, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bool) {
|
||||||
|
ctx.Data["PageIsEdit"] = true
|
||||||
|
ctx.Data["IsNewFile"] = isNewFile
|
||||||
|
ctx.Data["RequireHighlightJS"] = true
|
||||||
|
|
||||||
|
userName := ctx.Repo.Owner.Name
|
||||||
|
repoName := ctx.Repo.Repository.Name
|
||||||
|
oldBranchName := ctx.Repo.BranchName
|
||||||
|
branchName := oldBranchName
|
||||||
|
branchLink := ctx.Repo.RepoLink + "/src/" + branchName
|
||||||
|
oldTreeName := ctx.Repo.TreeName
|
||||||
|
content := form.Content
|
||||||
|
commitChoice := form.CommitChoice
|
||||||
|
lastCommit := form.LastCommit
|
||||||
|
|
||||||
|
if commitChoice == "commit-to-new-branch" {
|
||||||
|
branchName = form.NewBranchName
|
||||||
|
}
|
||||||
|
|
||||||
|
treeName := form.TreeName
|
||||||
|
treeName = strings.Trim(treeName, " ")
|
||||||
|
treeName = strings.Trim(treeName, "/")
|
||||||
|
|
||||||
|
var treeNames []string
|
||||||
|
if len(treeName) > 0 {
|
||||||
|
treeNames = strings.Split(treeName, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["RequireSimpleMDE"] = true
|
||||||
|
|
||||||
|
ctx.Data["UserName"] = userName
|
||||||
|
ctx.Data["RepoName"] = repoName
|
||||||
|
ctx.Data["BranchName"] = branchName
|
||||||
|
ctx.Data["TreeName"] = treeName
|
||||||
|
ctx.Data["TreeNames"] = treeNames
|
||||||
|
ctx.Data["BranchLink"] = branchLink
|
||||||
|
ctx.Data["FileContent"] = content
|
||||||
|
ctx.Data["CommitSummary"] = form.CommitSummary
|
||||||
|
ctx.Data["CommitMessage"] = form.CommitMessage
|
||||||
|
ctx.Data["CommitChoice"] = commitChoice
|
||||||
|
ctx.Data["NewBranchName"] = branchName
|
||||||
|
ctx.Data["CommitDirectlyToThisBranch"] = ctx.Tr("repo.commit_directly_to_this_branch", "<strong class=\"branch-name\">"+oldBranchName+"</strong>")
|
||||||
|
ctx.Data["CreateNewBranch"] = ctx.Tr("repo.create_new_branch", "<strong>"+ctx.Tr("repo.new_branch")+"</strong>")
|
||||||
|
ctx.Data["LastCommit"] = ctx.Repo.Commit.ID
|
||||||
|
ctx.Data["MdFileExtensions"] = strings.Join(setting.Markdown.MdFileExtensions, ",")
|
||||||
|
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.LineWrapExtensions, ",")
|
||||||
|
ctx.Data["PreviewTabApis"] = strings.Join(setting.Repository.PreviewTabApis, ",")
|
||||||
|
ctx.Data["PreviewDiffUrl"] = ctx.Repo.RepoLink + "/preview/" + branchName + "/" + treeName
|
||||||
|
|
||||||
|
if ctx.HasError() {
|
||||||
|
ctx.HTML(200, EDIT)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(treeName) == 0 {
|
||||||
|
ctx.Data["Err_Filename"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.filename_cannot_be_empty"), EDIT, &form)
|
||||||
|
log.Error(4, "%s: %s", "EditFile", "Filename can't be empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if( oldBranchName != branchName ){
|
||||||
|
if _, err := ctx.Repo.Repository.GetBranch(branchName); err == nil {
|
||||||
|
ctx.Data["Err_Branchname"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.branch_already_exists"), EDIT, &form)
|
||||||
|
log.Error(4, "%s: %s - %s", "BranchName", branchName, "Branch already exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
treepath := ""
|
||||||
|
for index,part := range treeNames {
|
||||||
|
treepath = path.Join(treepath, part)
|
||||||
|
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treepath)
|
||||||
|
if err != nil {
|
||||||
|
// Means there is no item with that name, so we're good
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if index != len(treeNames)-1 {
|
||||||
|
if ! entry.IsDir() {
|
||||||
|
ctx.Data["Err_Filename"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.directory_is_a_file"), EDIT, &form)
|
||||||
|
log.Error(4, "%s: %s - %s", "EditFile", treeName, "Directory given is a file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if entry.IsDir() {
|
||||||
|
ctx.Data["Err_Filename"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.filename_is_a_directory"), EDIT, &form)
|
||||||
|
log.Error(4, "%s: %s - %s", "EditFile", treeName, "Filename given is a dirctory")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! isNewFile {
|
||||||
|
_, err := ctx.Repo.Commit.GetTreeEntryByPath(oldTreeName)
|
||||||
|
if err != nil && git.IsErrNotExist(err) {
|
||||||
|
ctx.Data["Err_Filename"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.file_editing_no_longer_exists"), EDIT, &form)
|
||||||
|
log.Error(4, "%s: %s / %s - %s", "EditFile", branchName, oldTreeName, "File doesn't exist for editing")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if lastCommit != ctx.Repo.CommitID {
|
||||||
|
if files, err := ctx.Repo.Commit.GetFilesChangedSinceCommit(lastCommit); err == nil {
|
||||||
|
for _, file := range files {
|
||||||
|
if file == treeName {
|
||||||
|
name := ctx.Repo.Commit.Author.Name
|
||||||
|
if u, err := models.GetUserByEmail(ctx.Repo.Commit.Author.Email); err == nil {
|
||||||
|
name = `<a href="` + setting.AppSubUrl + "/" + u.Name + `" target="_blank">` + u.Name + `</a>`
|
||||||
|
}
|
||||||
|
message := ctx.Tr("repo.user_has_committed_since_you_started_editing", name) +
|
||||||
|
` <a href="` + ctx.Repo.RepoLink + "/commit/" + ctx.Repo.CommitID + `" target="_blank">` + ctx.Tr("repo.see_what_changed") + `</a>` +
|
||||||
|
" " + ctx.Tr("repo.pressing_commit_again_will_overwrite_those_changes", "<em>" + ctx.Tr("repo.commit_changes") + "</em>")
|
||||||
|
log.Error(4, "%s: %s / %s - %s", "EditFile", branchName, oldTreeName, "File updated by another user")
|
||||||
|
ctx.RenderWithErr(message, EDIT, &form)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if oldTreeName != treeName {
|
||||||
|
// We have a new filename (rename or completely new file) so we need to make sure it doesn't already exist, can't clobber
|
||||||
|
_, err := ctx.Repo.Commit.GetTreeEntryByPath(treeName)
|
||||||
|
if err == nil {
|
||||||
|
ctx.Data["Err_Filename"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.file_already_exists"), EDIT, &form)
|
||||||
|
log.Error(4, "%s: %s - %s", "NewFile", treeName, "File already exists, can't create new")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message := ""
|
||||||
|
if form.CommitSummary!="" {
|
||||||
|
message = strings.Trim(form.CommitSummary, " ")
|
||||||
|
} else {
|
||||||
|
if isNewFile {
|
||||||
|
message = ctx.Tr("repo.add") + " '" + treeName + "'"
|
||||||
|
} else {
|
||||||
|
message = ctx.Tr("repo.update") + " '" + treeName + "'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.Trim(form.CommitMessage, " ")!="" {
|
||||||
|
message += "\n\n" + strings.Trim(form.CommitMessage, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.Repo.Repository.UpdateRepoFile(ctx.User, oldBranchName, branchName, oldTreeName, treeName, content, message, isNewFile); err != nil {
|
||||||
|
ctx.Data["Err_Filename"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.unable_to_update_file"), EDIT, &form)
|
||||||
|
log.Error(4, "%s: %v", "EditFile", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if branch, err := ctx.Repo.Repository.GetBranch(branchName); err != nil {
|
||||||
|
log.Error(4, "repo.Repository.GetBranch(%s): %v", branchName, err)
|
||||||
|
} else if commit, err := branch.GetCommit(); err != nil {
|
||||||
|
log.Error(4, "branch.GetCommit(): %v", err)
|
||||||
|
} else {
|
||||||
|
pc := &models.PushCommits{
|
||||||
|
Len: 1,
|
||||||
|
Commits: []*models.PushCommit{&models.PushCommit{
|
||||||
|
commit.ID.String(),
|
||||||
|
commit.Message(),
|
||||||
|
commit.Author.Email,
|
||||||
|
commit.Author.Name,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
oldCommitID := ctx.Repo.CommitID
|
||||||
|
newCommitID := commit.ID.String()
|
||||||
|
if branchName != oldBranchName {
|
||||||
|
oldCommitID = "0000000000000000000000000000000000000000" // New Branch so we use all 0s
|
||||||
|
}
|
||||||
|
if err := models.CommitRepoAction(ctx.User.Id, ctx.Repo.Owner.Id, ctx.User.LowerName, ctx.Repo.Owner.Email,
|
||||||
|
ctx.Repo.Repository.ID, ctx.Repo.Owner.LowerName, ctx.Repo.Repository.Name, "refs/heads/"+branchName, pc,
|
||||||
|
oldCommitID, newCommitID); err != nil {
|
||||||
|
log.Error(4, "models.CommitRepoAction(branch = %s): %v", branchName, err)
|
||||||
|
}
|
||||||
|
models.HookQueue.Add(ctx.Repo.Repository.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leaving this off until forked repos that get a branch can compare with forks master and not upstream
|
||||||
|
//if oldBranchName != branchName {
|
||||||
|
// ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/compare/" + oldBranchName + "..." + branchName))
|
||||||
|
//} else {
|
||||||
|
ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/src/" + branchName + "/" + treeName))
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DiffPreviewPost(ctx *context.Context, form auth.EditPreviewDiffForm) {
|
||||||
|
userName := ctx.Repo.Owner.Name
|
||||||
|
repoName := ctx.Repo.Repository.Name
|
||||||
|
branchName := ctx.Repo.BranchName
|
||||||
|
treeName := ctx.Repo.TreeName
|
||||||
|
content := form.Content
|
||||||
|
|
||||||
|
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treeName)
|
||||||
|
if (err != nil && git.IsErrNotExist(err)) || entry.IsDir() {
|
||||||
|
ctx.Data["FileContent"] = content
|
||||||
|
ctx.HTML(200, DIFF_PREVIEW_NEW)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
diff, err := ctx.Repo.Repository.GetPreviewDiff(models.RepoPath(userName, repoName), branchName, treeName, content, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(404, err.Error())
|
||||||
|
log.Error(4, "%s: %v", "GetPreviewDiff", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(diff.NumFiles() == 0){
|
||||||
|
ctx.Error(200, ctx.Tr("repo.no_changes_to_show"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["IsSplitStyle"] = ctx.Query("style") == "split"
|
||||||
|
ctx.Data["File"] = diff.Files[0]
|
||||||
|
|
||||||
|
ctx.HTML(200, DIFF_PREVIEW)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EscapeUrl(str string) string {
|
||||||
|
return strings.NewReplacer("?","%3F","%","%25","#","%23"," ","%20","^","%5E","\\","%5C","{","%7B","}","%7D","|","%7C").Replace(str)
|
||||||
|
}
|
257
routers/repo/upload.go
Normal file
257
routers/repo/upload.go
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
// 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 repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/base"
|
||||||
|
"github.com/gogits/gogs/modules/context"
|
||||||
|
"github.com/gogits/gogs/modules/log"
|
||||||
|
"github.com/gogits/gogs/modules/auth"
|
||||||
|
"path"
|
||||||
|
"github.com/gogits/gogs/modules/setting"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"github.com/gogits/gogs/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UPLOAD base.TplName = "repo/upload"
|
||||||
|
)
|
||||||
|
|
||||||
|
func renderUploadSettings(ctx *context.Context) {
|
||||||
|
ctx.Data["RequireDropzone"] = true
|
||||||
|
ctx.Data["IsUploadEnabled"] = setting.UploadEnabled
|
||||||
|
ctx.Data["UploadAllowedTypes"] = setting.UploadAllowedTypes
|
||||||
|
ctx.Data["UploadMaxSize"] = setting.UploadMaxSize
|
||||||
|
ctx.Data["UploadMaxFiles"] = setting.UploadMaxFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
func UploadFile(ctx *context.Context) {
|
||||||
|
ctx.Data["PageIsUpload"] = true
|
||||||
|
|
||||||
|
userName := ctx.Repo.Owner.Name
|
||||||
|
repoName := ctx.Repo.Repository.Name
|
||||||
|
branchName := ctx.Repo.BranchName
|
||||||
|
branchLink := ctx.Repo.RepoLink + "/src/" + branchName
|
||||||
|
treeName := ctx.Repo.TreeName
|
||||||
|
|
||||||
|
treeNames := []string{""}
|
||||||
|
if len(treeName) > 0 {
|
||||||
|
treeNames = strings.Split(treeName, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["UserName"] = userName
|
||||||
|
ctx.Data["RepoName"] = repoName
|
||||||
|
ctx.Data["BranchName"] = branchName
|
||||||
|
ctx.Data["TreeName"] = treeName
|
||||||
|
ctx.Data["TreeNames"] = treeNames
|
||||||
|
ctx.Data["BranchLink"] = branchLink
|
||||||
|
ctx.Data["CommitSummary"] = ""
|
||||||
|
ctx.Data["CommitMessage"] = ""
|
||||||
|
ctx.Data["CommitChoice"] = "direct"
|
||||||
|
ctx.Data["NewBranchName"] = ""
|
||||||
|
ctx.Data["CommitDirectlyToThisBranch"] = ctx.Tr("repo.commit_directly_to_this_branch", "<strong class=\"branch-name\">"+branchName+"</strong>")
|
||||||
|
ctx.Data["CreateNewBranch"] = ctx.Tr("repo.create_new_branch", "<strong>"+ctx.Tr("repo.new_branch")+"</strong>")
|
||||||
|
renderUploadSettings(ctx)
|
||||||
|
|
||||||
|
ctx.HTML(200, UPLOAD)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
|
||||||
|
ctx.Data["PageIsUpload"] = true
|
||||||
|
renderUploadSettings(ctx)
|
||||||
|
|
||||||
|
userName := ctx.Repo.Owner.Name
|
||||||
|
repoName := ctx.Repo.Repository.Name
|
||||||
|
oldBranchName := ctx.Repo.BranchName
|
||||||
|
branchName := oldBranchName
|
||||||
|
branchLink := ctx.Repo.RepoLink + "/src/" + branchName
|
||||||
|
commitChoice := form.CommitChoice
|
||||||
|
files := form.Files
|
||||||
|
|
||||||
|
if commitChoice == "commit-to-new-branch" {
|
||||||
|
branchName = form.NewBranchName
|
||||||
|
}
|
||||||
|
|
||||||
|
treeName := form.TreeName
|
||||||
|
treeName = strings.Trim(treeName, " ")
|
||||||
|
treeName = strings.Trim(treeName, "/")
|
||||||
|
|
||||||
|
treeNames := []string{""}
|
||||||
|
if len(treeName) > 0 {
|
||||||
|
treeNames = strings.Split(treeName, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["UserName"] = userName
|
||||||
|
ctx.Data["RepoName"] = repoName
|
||||||
|
ctx.Data["BranchName"] = branchName
|
||||||
|
ctx.Data["TreeName"] = treeName
|
||||||
|
ctx.Data["TreeNames"] = treeNames
|
||||||
|
ctx.Data["BranchLink"] = branchLink
|
||||||
|
ctx.Data["CommitSummary"] = form.CommitSummary
|
||||||
|
ctx.Data["CommitMessage"] = form.CommitMessage
|
||||||
|
ctx.Data["CommitChoice"] = commitChoice
|
||||||
|
ctx.Data["NewBranchName"] = branchName
|
||||||
|
ctx.Data["CommitDirectlyToThisBranch"] = ctx.Tr("repo.commit_directly_to_this_branch", "<strong class=\"branch-name\">"+oldBranchName+"</strong>")
|
||||||
|
ctx.Data["CreateNewBranch"] = ctx.Tr("repo.create_new_branch", "<strong>"+ctx.Tr("repo.new_branch")+"</strong>")
|
||||||
|
|
||||||
|
if ctx.HasError() {
|
||||||
|
ctx.HTML(200, UPLOAD)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if( oldBranchName != branchName ){
|
||||||
|
if _, err := ctx.Repo.Repository.GetBranch(branchName); err == nil {
|
||||||
|
ctx.Data["Err_Branchname"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.branch_already_exists"), UPLOAD, &form)
|
||||||
|
log.Error(4, "%s: %s - %s", "BranchName", branchName, "Branch already exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
treepath := ""
|
||||||
|
for _, part := range treeNames {
|
||||||
|
treepath = path.Join(treepath, part)
|
||||||
|
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treepath)
|
||||||
|
if err != nil {
|
||||||
|
// Means there is no item with that name, so we're good
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ! entry.IsDir() {
|
||||||
|
ctx.Data["Err_Filename"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.directory_is_a_file"), UPLOAD, &form)
|
||||||
|
log.Error(4, "%s: %s - %s", "UploadFile", treeName, "Directory given is a file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message := ""
|
||||||
|
if form.CommitSummary!="" {
|
||||||
|
message = strings.Trim(form.CommitSummary, " ")
|
||||||
|
} else {
|
||||||
|
message = ctx.Tr("repo.add_files_to_dir", "'" + treeName + "'")
|
||||||
|
}
|
||||||
|
if strings.Trim(form.CommitMessage, " ")!="" {
|
||||||
|
message += "\n\n" + strings.Trim(form.CommitMessage, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.Repo.Repository.UploadRepoFiles(ctx.User, oldBranchName, branchName, treeName, message, files); err != nil {
|
||||||
|
ctx.Data["Err_Directory"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.unable_to_upload_files"), UPLOAD, &form)
|
||||||
|
log.Error(4, "%s: %v", "UploadFile", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Was successful, so now need to call models.CommitRepoAction() with the new commitID for webhooks and watchers
|
||||||
|
if branch, err := ctx.Repo.Repository.GetBranch(branchName); err != nil {
|
||||||
|
log.Error(4, "repo.Repository.GetBranch(%s): %v", branchName, err)
|
||||||
|
} else if commit, err := branch.GetCommit(); err != nil {
|
||||||
|
log.Error(4, "branch.GetCommit(): %v", err)
|
||||||
|
} else {
|
||||||
|
pc := &models.PushCommits{
|
||||||
|
Len: 1,
|
||||||
|
Commits: []*models.PushCommit{&models.PushCommit{
|
||||||
|
commit.ID.String(),
|
||||||
|
commit.Message(),
|
||||||
|
commit.Author.Email,
|
||||||
|
commit.Author.Name,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
oldCommitID := ctx.Repo.CommitID
|
||||||
|
newCommitID := commit.ID.String()
|
||||||
|
if branchName != oldBranchName {
|
||||||
|
oldCommitID = "0000000000000000000000000000000000000000" // New Branch so we use all 0s
|
||||||
|
}
|
||||||
|
if err := models.CommitRepoAction(ctx.User.Id, ctx.Repo.Owner.Id, ctx.User.LowerName, ctx.Repo.Owner.Email,
|
||||||
|
ctx.Repo.Repository.ID, ctx.Repo.Owner.LowerName, ctx.Repo.Repository.Name, "refs/heads/"+branchName, pc,
|
||||||
|
oldCommitID, newCommitID); err != nil {
|
||||||
|
log.Error(4, "models.CommitRepoAction(branch = %s): %v", branchName, err)
|
||||||
|
}
|
||||||
|
models.HookQueue.Add(ctx.Repo.Repository.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leaving this off until forked repos that get a branch can compare with forks master and not upstream
|
||||||
|
//if oldBranchName != branchName {
|
||||||
|
// ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/compare/" + oldBranchName + "..." + branchName))
|
||||||
|
//} else {
|
||||||
|
ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/src/" + branchName + "/" + treeName))
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UploadFileToServer(ctx *context.Context) {
|
||||||
|
if !setting.UploadEnabled {
|
||||||
|
ctx.Error(404, "upload is not enabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, header, err := ctx.Req.FormFile("file")
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(500, fmt.Sprintf("FormFile: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, _ := file.Read(buf)
|
||||||
|
if n > 0 {
|
||||||
|
buf = buf[:n]
|
||||||
|
}
|
||||||
|
fileType := http.DetectContentType(buf)
|
||||||
|
|
||||||
|
if len(setting.UploadAllowedTypes) > 0 {
|
||||||
|
allowedTypes := strings.Split(setting.UploadAllowedTypes, ",")
|
||||||
|
allowed := false
|
||||||
|
for _, t := range allowedTypes {
|
||||||
|
t := strings.Trim(t, " ")
|
||||||
|
if t == "*/*" || t == fileType {
|
||||||
|
allowed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !allowed {
|
||||||
|
ctx.Error(400, ErrFileTypeForbidden.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
up, err := models.NewUpload(header.Filename, buf, file, ctx.User.Id, ctx.Repo.Repository.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(500, fmt.Sprintf("NewUpload: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("New file uploaded: %s", up.UUID)
|
||||||
|
ctx.JSON(200, map[string]string{
|
||||||
|
"uuid": up.UUID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func UploadRemoveFileFromServer(ctx *context.Context, form auth.UploadRemoveFileForm) {
|
||||||
|
if !setting.UploadEnabled {
|
||||||
|
ctx.Error(404, "upload is not enabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(form.File) == 0 {
|
||||||
|
ctx.Error(404, "invalid params")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid := form.File
|
||||||
|
|
||||||
|
if err := models.RemoveUpload(uuid, ctx.User.Id, ctx.Repo.Repository.ID); err != nil {
|
||||||
|
ctx.Error(500, fmt.Sprintf("RemoveUpload: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("Upload file removed: %s", uuid)
|
||||||
|
ctx.JSON(200, map[string]string{
|
||||||
|
"uuid": uuid,
|
||||||
|
})
|
||||||
|
}
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/gogits/gogs/modules/setting"
|
"github.com/gogits/gogs/modules/setting"
|
||||||
"github.com/gogits/gogs/modules/template"
|
"github.com/gogits/gogs/modules/template"
|
||||||
"github.com/gogits/gogs/modules/template/highlight"
|
"github.com/gogits/gogs/modules/template/highlight"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -47,6 +48,10 @@ func Home(ctx *context.Context) {
|
|||||||
branchLink := ctx.Repo.RepoLink + "/src/" + branchName
|
branchLink := ctx.Repo.RepoLink + "/src/" + branchName
|
||||||
treeLink := branchLink
|
treeLink := branchLink
|
||||||
rawLink := ctx.Repo.RepoLink + "/raw/" + branchName
|
rawLink := ctx.Repo.RepoLink + "/raw/" + branchName
|
||||||
|
editLink := ctx.Repo.RepoLink + "/edit/" + branchName
|
||||||
|
forkLink := setting.AppSubUrl + "/repo/fork/" + strconv.FormatInt(ctx.Repo.Repository.ID, 10)
|
||||||
|
newFileLink := ctx.Repo.RepoLink + "/new/" + branchName
|
||||||
|
uploadFileLink := ctx.Repo.RepoLink + "/upload/" + branchName
|
||||||
|
|
||||||
// Get tree path
|
// Get tree path
|
||||||
treename := ctx.Repo.TreeName
|
treename := ctx.Repo.TreeName
|
||||||
@ -102,8 +107,10 @@ func Home(ctx *context.Context) {
|
|||||||
switch {
|
switch {
|
||||||
case isPDFFile:
|
case isPDFFile:
|
||||||
ctx.Data["IsPDFFile"] = true
|
ctx.Data["IsPDFFile"] = true
|
||||||
|
ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.cannot_edit_binary_files")
|
||||||
case isImageFile:
|
case isImageFile:
|
||||||
ctx.Data["IsImageFile"] = true
|
ctx.Data["IsImageFile"] = true
|
||||||
|
ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.cannot_edit_binary_files")
|
||||||
case isTextFile:
|
case isTextFile:
|
||||||
if blob.Size() >= setting.MaxDisplayFileSize {
|
if blob.Size() >= setting.MaxDisplayFileSize {
|
||||||
ctx.Data["IsFileTooLarge"] = true
|
ctx.Data["IsFileTooLarge"] = true
|
||||||
@ -112,8 +119,10 @@ func Home(ctx *context.Context) {
|
|||||||
d, _ := ioutil.ReadAll(dataRc)
|
d, _ := ioutil.ReadAll(dataRc)
|
||||||
buf = append(buf, d...)
|
buf = append(buf, d...)
|
||||||
readmeExist := markdown.IsMarkdownFile(blob.Name()) || markdown.IsReadmeFile(blob.Name())
|
readmeExist := markdown.IsMarkdownFile(blob.Name()) || markdown.IsReadmeFile(blob.Name())
|
||||||
|
isMarkdown := readmeExist || markdown.IsMarkdownFile(blob.Name())
|
||||||
ctx.Data["ReadmeExist"] = readmeExist
|
ctx.Data["ReadmeExist"] = readmeExist
|
||||||
if readmeExist {
|
ctx.Data["IsMarkdown"] = isMarkdown
|
||||||
|
if isMarkdown {
|
||||||
ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
|
ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
|
||||||
} else {
|
} else {
|
||||||
if err, content := template.ToUtf8WithErr(buf); err != nil {
|
if err, content := template.ToUtf8WithErr(buf); err != nil {
|
||||||
@ -126,7 +135,21 @@ func Home(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch {
|
||||||
|
ctx.Data["FileEditLink"] = editLink + "/" + treename
|
||||||
|
ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.edit_this_file")
|
||||||
|
} else {
|
||||||
|
if ! ctx.Repo.IsViewBranch {
|
||||||
|
ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.must_be_on_branch")
|
||||||
|
} else if ! ctx.Repo.IsWriter() {
|
||||||
|
ctx.Data["FileEditLink"] = forkLink
|
||||||
|
ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.fork_before_edit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.cannot_edit_binary_files")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Directory and file list.
|
// Directory and file list.
|
||||||
@ -203,6 +226,12 @@ func Home(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
ctx.Data["LastCommit"] = lastCommit
|
ctx.Data["LastCommit"] = lastCommit
|
||||||
ctx.Data["LastCommitUser"] = models.ValidateCommitWithEmail(lastCommit)
|
ctx.Data["LastCommitUser"] = models.ValidateCommitWithEmail(lastCommit)
|
||||||
|
if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch {
|
||||||
|
ctx.Data["NewFileLink"] = newFileLink + "/" + treename
|
||||||
|
if setting.UploadEnabled {
|
||||||
|
ctx.Data["UploadFileLink"] = uploadFileLink + "/" + treename
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["Username"] = userName
|
ctx.Data["Username"] = userName
|
||||||
|
32
templates/repo/diff_preview.tmpl
Normal file
32
templates/repo/diff_preview.tmpl
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{{$highlightClass := .File.GetHighlightClass}}
|
||||||
|
<div class="diff-file-box diff-box file-content" id="diff-{{.Index}}">
|
||||||
|
<div class="ui attached table segment">
|
||||||
|
<div class="file-body file-code code-view code-diff">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{{range $j, $section := .File.Sections}}
|
||||||
|
{{range $k, $line := $section.Lines}}
|
||||||
|
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}">
|
||||||
|
{{if eq .GetType 4}}
|
||||||
|
<td colspan="2" class="lines-num">
|
||||||
|
{{/* {{if gt $j 0}}<span class="fold octicon octicon-fold"></span>{{end}} */}}
|
||||||
|
</td>
|
||||||
|
{{else}}
|
||||||
|
<td class="lines-num lines-num-old">
|
||||||
|
<span rel="{{if $line.LeftIdx}}diff-{{Sha1 $.File.Name}}L{{$line.LeftIdx}}{{end}}">{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}</span>
|
||||||
|
</td>
|
||||||
|
<td class="lines-num lines-num-new">
|
||||||
|
<span rel="{{if $line.RightIdx}}diff-{{Sha1 $.File.Name}}R{{$line.RightIdx}}{{end}}">{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}</span>
|
||||||
|
</td>
|
||||||
|
{{end}}
|
||||||
|
<td class="lines-code">
|
||||||
|
<pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.GetComputedInlineDiffFor $line}}</code></pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
12
templates/repo/diff_preview_new.tmpl
Normal file
12
templates/repo/diff_preview_new.tmpl
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<div id="file-content" class="ui attached table segment">
|
||||||
|
<div class="file-view code-view has-emoji">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="lines-num"></td>
|
||||||
|
<td class="lines-code"><pre><code><ol class="linenums">{{.FileContent}}</ol></code></pre></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
98
templates/repo/edit.tmpl
Normal file
98
templates/repo/edit.tmpl
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{{template "base/head" .}}
|
||||||
|
<div class="repository file edit">
|
||||||
|
{{template "repo/header" .}}
|
||||||
|
<div class="ui container">
|
||||||
|
{{.branchName}}
|
||||||
|
{{template "base/alert" .}}
|
||||||
|
<form class="ui edit form" action="{{EscapePound $.Link}}" method="post">
|
||||||
|
{{.CsrfTokenHtml}}
|
||||||
|
<input type="hidden" name="last_commit" value="{{.LastCommit}}">
|
||||||
|
<div class="ui secondary menu">
|
||||||
|
<div class="item fitted" style="width:100%;">
|
||||||
|
<div class="ui breadcrumb field{{if .Err_Filename}} error{{end}}">
|
||||||
|
<a class="section" href="{{EscapePound $.BranchLink}}">{{.Repository.Name}}</a>
|
||||||
|
{{ $n := len .TreeNames}}
|
||||||
|
{{ $l := Subtract $n 1}}
|
||||||
|
{{range $i, $v := .TreeNames}}
|
||||||
|
<div class="divider"> / </div>
|
||||||
|
{{if eq $i $l}}
|
||||||
|
<input type="text" id="file-name" value="{{$v}}" placeholder="{{$.i18n.Tr "repo.name_your_file"}}" required> <span class="octicon octicon-info poping up" data-content="{{$.i18n.Tr "repo.filename_help"}}" data-position="bottom center" data-variation="tiny"></span>
|
||||||
|
{{else}}
|
||||||
|
<span class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $v}}">{{$v}}</a></span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
<button class="clipboard-tree-name icon octicon octicon-clippy poping up" type="button" data-content="{{.i18n.Tr "repo.copy_file_path_to_clipboard"}}" data-position="bottom center" data-variation="tiny inverted"></button>
|
||||||
|
<span class="repo-edit-file-cancel">{{.i18n.Tr "repo.or"}} <a href="{{EscapePound $.BranchLink}}/{{EscapePound $.TreeName}}">{{.i18n.Tr "repo.cancel_lower"}}</a></span>
|
||||||
|
<input type="hidden" id="tree-name" name="tree_name" value="{{.TreeName}}" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff">
|
||||||
|
<a class="active item" data-tab="write"><i class="icon octicon octicon-code"></i> {{.i18n.Tr "repo.edit_file"}}</a>
|
||||||
|
<a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}" data-preview-apis="{{.PreviewTabApis}}"><i class="icon octicon octicon-eye"></i> {{.i18n.Tr "repo.release.preview"}}</a>
|
||||||
|
<a class="item" data-tab="diff" data-url="{{.PreviewDiffUrl}}" data-context="{{.BranchLink}}"><i class="icon octicon octicon-diff"></i> {{.i18n.Tr "repo.preview_changes"}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="ui bottom attached active tab segment" data-tab="write">
|
||||||
|
<textarea id="edit_area" name="content" data-id="repo-{{.Repository.Name}}-{{.TreeName}}"
|
||||||
|
data-url="{{AppSubUrl}}/api/v1/markdown"
|
||||||
|
data-context="{{.RepoLink}}"
|
||||||
|
data-mode-url="{{AppSubUrl}}/plugins/simplemde/mode/%N/%N.js"
|
||||||
|
data-md-file-extensions="{{.MdFileExtensions}}"
|
||||||
|
data-line-wrap-extensions="{{.LineWrapExtensions}}">
|
||||||
|
{{.FileContent}}</textarea required>
|
||||||
|
</div>
|
||||||
|
<div class="ui bottom attached tab segment markdown" data-tab="preview">
|
||||||
|
{{.i18n.Tr "repo.release.loading"}}
|
||||||
|
</div>
|
||||||
|
<div class="ui bottom attached tab segment diff" data-tab="diff">
|
||||||
|
{{.i18n.Tr "repo.release.loading"}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="commit-form-wrapper">
|
||||||
|
<img width="48" height="48" class="ui rounded image commit-form-avatar" src="{{.SignedUser.AvatarLink}}">
|
||||||
|
<div class="commit-form">
|
||||||
|
<h3>{{.i18n.Tr "repo.commit_changes"}}</h3>
|
||||||
|
<div class="field">
|
||||||
|
<input name="commit_summary" placeholder="{{if .IsNewFile}}{{.i18n.Tr "repo.add"}} '{{.TreeName}}/<filename>'{{else}}{{.i18n.Tr "repo.update"}} '{{.TreeName}}'{{end}}" value="{{.CommitSummary}}">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<textarea name="commit_message" placeholder="{{.i18n.Tr "repo.default_commit_message"}}">{{.CommitMessage}}</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="quick-pull-choice js-quick-pull-choice ">
|
||||||
|
<dl class="form-group">
|
||||||
|
<dd>
|
||||||
|
<div class="form-checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct"{{if eq .CommitChoice "direct"}} checked="checked"{{end}}>
|
||||||
|
<i class="octicon octicon-git-commit" height="16" width="14"></i>
|
||||||
|
{{.CommitDirectlyToThisBranch | Safe}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch"{{if eq .CommitChoice "commit-to-new-branch"}} checked="checked"{{end}}>
|
||||||
|
<i class="octicon octicon-git-pull-request" height="16" width="12"></i>
|
||||||
|
{{.CreateNewBranch | Safe}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
<div class="quick-pull-branch-name">
|
||||||
|
<div class="new-branch-name-input{{if .Err_Branchname}} error{{end}}">
|
||||||
|
<i class="octicon octicon-git-branch quick-pull-new-branch-icon" height="16" width="10"></i>
|
||||||
|
<input type="text" name="new_branch_name" value="{{.NewBranchName}}" class="form-control input-contrast mr-2 js-quick-pull-new-branch-name" placeholder="New branch name…">
|
||||||
|
<span class="text-muted js-quick-pull-normalization-info"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="ui green button">
|
||||||
|
{{.i18n.Tr "repo.commit_changes"}}
|
||||||
|
</button>
|
||||||
|
<a class="ui button red" href="{{EscapePound $.BranchLink}}/{{EscapePound $.TreeName}}">{{.i18n.Tr "repo.cancel"}}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{template "base/footer" .}}
|
79
templates/repo/upload.tmpl
Normal file
79
templates/repo/upload.tmpl
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
{{template "base/head" .}}
|
||||||
|
<div class="repository file upload">
|
||||||
|
{{template "repo/header" .}}
|
||||||
|
<div class="ui container">
|
||||||
|
{{.branchName}}
|
||||||
|
{{template "base/alert" .}}
|
||||||
|
<form class="ui comment form" action="{{EscapePound $.Link}}" method="post">
|
||||||
|
{{.CsrfTokenHtml}}
|
||||||
|
<div class="ui secondary menu">
|
||||||
|
<div class="item fitted" style="width:100%;">
|
||||||
|
<div class="ui breadcrumb field{{if .Err_Directory}} error{{end}}">
|
||||||
|
<a class="section" href="{{EscapePound $.BranchLink}}">{{.Repository.Name}}</a>
|
||||||
|
{{ $n := len .TreeNames}}
|
||||||
|
{{ $l := Subtract $n 1}}
|
||||||
|
{{range $i, $v := .TreeNames}}
|
||||||
|
<div class="divider"> / </div>
|
||||||
|
{{if eq $i $l}}
|
||||||
|
<input type="text" id="file-name" value="{{$v}}" placeholder="{{$.i18n.Tr "repo.add_subdir"}}">
|
||||||
|
{{else}}
|
||||||
|
<span class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $v}}">{{$v}}</a></span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
<button class="clipboard-tree-name icon octicon octicon-clippy poping up" type="button" data-content="{{.i18n.Tr "repo.copy_file_path_to_clipboard"}}" data-position="bottom center" data-variation="tiny inverted"></button>
|
||||||
|
<span class="repo-edit-file-cancel">{{.i18n.Tr "repo.or"}} <a href="{{EscapePound $.BranchLink}}/{{EscapePound $.TreeName}}">{{.i18n.Tr "repo.cancel_lower"}}</a></span>
|
||||||
|
<input type="hidden" id="tree-name" name="tree_name" value="{{.TreeName}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field ui upload">
|
||||||
|
<div class="files"></div>
|
||||||
|
<div class="ui basic button dropzone" id="dropzone" data-upload-url="{{.RepoLink}}/upload-file" data-remove-url="{{.RepoLink}}/upload-remove" data-csrf="{{.CsrfToken}}" data-accepts="{{.UploadAllowedTypes}}" data-max-file="{{.UploadMaxFiles}}" data-max-size="{{.UploadMaxSize}}" data-default-message="{{.i18n.Tr "dropzone.default_message"}}" data-invalid-input-type="{{.i18n.Tr "dropzone.invalid_input_type"}}" data-file-too-big="{{.i18n.Tr "dropzone.file_too_big"}}" data-remove-file="{{.i18n.Tr "dropzone.remove_file"}}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="commit-form-wrapper">
|
||||||
|
<img width="48" height="48" class="ui rounded image commit-form-avatar" src="{{.SignedUser.AvatarLink}}">
|
||||||
|
<div class="commit-form">
|
||||||
|
<h3>{{.i18n.Tr "repo.commit_changes"}}</h3>
|
||||||
|
<div class="field">
|
||||||
|
<input name="commit_summary" placeholder="{{.i18n.Tr "repo.add_files_to_dir" .TreeName}}" value="{{.CommitSummary}}">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<textarea name="commit_message" placeholder="{{.i18n.Tr "repo.default_commit_message"}}">{{.CommitMessage}}</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="quick-pull-choice js-quick-pull-choice ">
|
||||||
|
<dl class="form-group">
|
||||||
|
<dd>
|
||||||
|
<div class="form-checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct"{{if eq .CommitChoice "direct"}} checked="checked"{{end}}>
|
||||||
|
<i class="octicon octicon-git-commit" height="16" width="14"></i>
|
||||||
|
{{.CommitDirectlyToThisBranch | Safe}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch"{{if eq .CommitChoice "commit-to-new-branch"}} checked="checked"{{end}}>
|
||||||
|
<i class="octicon octicon-git-pull-request" height="16" width="12"></i>
|
||||||
|
{{.CreateNewBranch | Safe}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
<div class="quick-pull-branch-name">
|
||||||
|
<div class="new-branch-name-input{{if .Err_Branchname}} error{{end}}">
|
||||||
|
<i class="octicon octicon-git-branch quick-pull-new-branch-icon" height="16" width="10"></i>
|
||||||
|
<input type="text" name="new_branch_name" value="{{.NewBranchName}}" class="form-control input-contrast mr-2 js-quick-pull-new-branch-name" placeholder="New branch name…">
|
||||||
|
<span class="text-muted js-quick-pull-normalization-info"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="ui green button">
|
||||||
|
{{.i18n.Tr "repo.commit_changes"}}
|
||||||
|
</button>
|
||||||
|
<a class="ui button red" href="{{EscapePound $.BranchLink}}/{{EscapePound $.TreeName}}">{{.i18n.Tr "repo.cancel"}}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{template "base/footer" .}}
|
Loading…
x
Reference in New Issue
Block a user