wiki: finish new

This commit is contained in:
Unknwon 2015-11-27 00:24:24 -05:00
parent c50a3503e6
commit 392f3ee210
15 changed files with 474 additions and 2236 deletions

View File

@ -534,6 +534,7 @@ func runWeb(ctx *cli.Context) {
m.Group("/wiki", func() { m.Group("/wiki", func() {
m.Get("/?:page", repo.Wiki) m.Get("/?:page", repo.Wiki)
m.Get("/_list", repo.WikiList)
m.Group("", func() { m.Group("", func() {
m.Combo("/_new").Get(repo.NewWiki). m.Combo("/_new").Get(repo.NewWiki).

View File

@ -542,6 +542,7 @@ wiki.create_first_page = Create the first page
wiki.new_page = Create New Page wiki.new_page = Create New Page
wiki.default_commit_message = Write a note about this update (optional). wiki.default_commit_message = Write a note about this update (optional).
wiki.save_page = Save Page wiki.save_page = Save Page
wiki.last_commit_info = %s edited this page %s
settings = Settings settings = Settings
settings.options = Options settings.options = Options

File diff suppressed because it is too large Load Diff

View File

@ -51,6 +51,10 @@ func IsReleaseExist(repoID int64, tagName string) (bool, error) {
return x.Get(&Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)}) return x.Get(&Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)})
} }
func init() {
git.GetVersion()
}
func createTag(gitRepo *git.Repository, rel *Release) error { func createTag(gitRepo *git.Repository, rel *Release) error {
// Only actual create when publish. // Only actual create when publish.
if !rel.IsDraft { if !rel.IsDraft {

View File

@ -31,7 +31,6 @@ import (
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/bindata" "github.com/gogits/gogs/modules/bindata"
oldgit "github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process" "github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
@ -317,27 +316,24 @@ 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))
} }
// UpdateLocalCopy makes sure the local copy of repository is up-to-date. func updateLocalCopy(repoPath, localPath string) error {
func (repo *Repository) UpdateLocalCopy() error {
repoPath := repo.RepoPath()
localPath := repo.LocalCopyPath()
if !com.IsExist(localPath) { if !com.IsExist(localPath) {
_, stderr, err := process.Exec( if err := git.Clone(repoPath, localPath); err != nil {
fmt.Sprintf("UpdateLocalCopy(git clone): %s", repoPath), "git", "clone", repoPath, localPath) return fmt.Errorf("Clone: %v", err)
if err != nil {
return fmt.Errorf("git clone: %v - %s", err, stderr)
} }
} else { } else {
_, stderr, err := process.ExecDir(-1, localPath, if err := git.Pull(localPath, true); err != nil {
fmt.Sprintf("UpdateLocalCopy(git pull --all): %s", repoPath), "git", "pull", "--all") return fmt.Errorf("Pull: %v", err)
if err != nil {
return fmt.Errorf("git pull: %v - %s", err, stderr)
} }
} }
return nil return nil
} }
// UpdateLocalCopy makes sure the local copy of repository is up-to-date.
func (repo *Repository) UpdateLocalCopy() error {
return updateLocalCopy(repo.RepoPath(), repo.LocalCopyPath())
}
// PatchPath returns corresponding patch file path of repository by given issue ID. // PatchPath returns corresponding patch file path of repository by given issue ID.
func (repo *Repository) PatchPath(index int64) (string, error) { func (repo *Repository) PatchPath(index int64) (string, error) {
if err := repo.GetOwner(); err != nil { if err := repo.GetOwner(); err != nil {
@ -471,6 +467,11 @@ func UpdateMirror(m *Mirror) error {
return updateMirror(x, m) return updateMirror(x, m)
} }
func createUpdateHook(repoPath string) error {
return git.SetUpdateHook(repoPath,
fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf))
}
// MirrorRepository creates a mirror repository from source. // MirrorRepository creates a mirror repository from source.
func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) error { func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) error {
_, stderr, err := process.ExecTimeout(10*time.Minute, _, stderr, err := process.ExecTimeout(10*time.Minute,
@ -568,20 +569,26 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
} }
} }
// Check if repository has master branch, if so set it to default branch. // Try to get HEAD branch and set it as default branch.
gitRepo, err := oldgit.OpenRepository(repoPath) gitRepo, err := git.OpenRepository(repoPath)
if err != nil { if err != nil {
return repo, fmt.Errorf("open git repository: %v", err) log.Error(4, "OpenRepository: %v", err)
return repo, nil
} }
if gitRepo.IsBranchExist("master") { headBranch, err := gitRepo.GetHEADBranch()
repo.DefaultBranch = "master" if err != nil {
log.Error(4, "GetHEADBranch: %v", err)
return repo, nil
}
if headBranch != nil {
repo.DefaultBranch = headBranch.Name
} }
return repo, UpdateRepository(repo, false) return repo, UpdateRepository(repo, false)
} }
// initRepoCommit temporarily changes with work directory. // initRepoCommit temporarily changes with work directory.
func initRepoCommit(tmpPath string, sig *oldgit.Signature) (err error) { func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
var stderr string var stderr string
if _, stderr, err = process.ExecDir(-1, if _, stderr, err = process.ExecDir(-1,
tmpPath, fmt.Sprintf("initRepoCommit (git add): %s", tmpPath), tmpPath, fmt.Sprintf("initRepoCommit (git add): %s", tmpPath),
@ -604,13 +611,6 @@ func initRepoCommit(tmpPath string, sig *oldgit.Signature) (err error) {
return nil return nil
} }
func createUpdateHook(repoPath string) error {
hookPath := path.Join(repoPath, "hooks/update")
os.MkdirAll(path.Dir(hookPath), os.ModePerm)
return ioutil.WriteFile(hookPath,
[]byte(fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf)), 0777)
}
type CreateRepoOptions struct { type CreateRepoOptions struct {
Name string Name string
Description string Description string
@ -699,22 +699,17 @@ func prepareRepoCommit(repo *Repository, tmpDir, repoPath string, opts CreateRep
} }
// InitRepository initializes README and .gitignore if needed. // InitRepository initializes README and .gitignore if needed.
func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts CreateRepoOptions) error { func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts CreateRepoOptions) (err error) {
// Somehow the directory could exist. // Somehow the directory could exist.
if com.IsExist(repoPath) { if com.IsExist(repoPath) {
return fmt.Errorf("initRepository: path already exists: %s", repoPath) return fmt.Errorf("initRepository: path already exists: %s", repoPath)
} }
// Init bare new repository. // Init bare new repository.
os.MkdirAll(repoPath, os.ModePerm) if err = git.InitRepository(repoPath, true); err != nil {
_, stderr, err := process.ExecDir(-1, repoPath, return fmt.Errorf("InitRepository: %v", err)
fmt.Sprintf("initRepository (git init --bare): %s", repoPath), "git", "init", "--bare") } else if err = createUpdateHook(repoPath); err != nil {
if err != nil { return fmt.Errorf("createUpdateHook: %v", err)
return fmt.Errorf("git init --bare: %v - %s", err, stderr)
}
if err := createUpdateHook(repoPath); err != nil {
return err
} }
tmpDir := filepath.Join(os.TempDir(), "gogs-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond())) tmpDir := filepath.Join(os.TempDir(), "gogs-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond()))

View File

@ -25,9 +25,11 @@ import (
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"github.com/nfnt/resize" "github.com/nfnt/resize"
"github.com/gogits/git-shell"
"github.com/gogits/gogs/modules/avatar" "github.com/gogits/gogs/modules/avatar"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/git" oldgit "github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
@ -938,11 +940,11 @@ func MakeEmailPrimary(email *EmailAddress) error {
// UserCommit represents a commit with validation of user. // UserCommit represents a commit with validation of user.
type UserCommit struct { type UserCommit struct {
User *User User *User
*git.Commit *oldgit.Commit
} }
// ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user. // ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user.
func ValidateCommitWithEmail(c *git.Commit) *User { func ValidateCommitWithEmail(c *oldgit.Commit) *User {
u, err := GetUserByEmail(c.Author.Email) u, err := GetUserByEmail(c.Author.Email)
if err != nil { if err != nil {
return nil return nil
@ -959,7 +961,7 @@ func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
e = oldCommits.Front() e = oldCommits.Front()
) )
for e != nil { for e != nil {
c := e.Value.(*git.Commit) c := e.Value.(*oldgit.Commit)
if v, ok := emails[c.Author.Email]; !ok { if v, ok := emails[c.Author.Email]; !ok {
u, _ = GetUserByEmail(c.Author.Email) u, _ = GetUserByEmail(c.Author.Email)

View File

@ -6,19 +6,72 @@ package models
import ( import (
"fmt" "fmt"
"io/ioutil"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
"sync"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/gogits/git-shell" "github.com/gogits/git-shell"
"github.com/gogits/gogs/modules/setting"
) )
// ToWikiPageName formats a string to corresponding wiki URL name. // workingPool represents a pool of working status which makes sure
func ToWikiPageName(name string) string { // that only one instance of same task is performing at a time.
// However, different type of tasks can performing at the same time.
type workingPool struct {
lock sync.Mutex
pool map[string]*sync.Mutex
count map[string]int
}
// CheckIn checks in a task and waits if others are running.
func (p *workingPool) CheckIn(name string) {
p.lock.Lock()
lock, has := p.pool[name]
if !has {
lock = &sync.Mutex{}
p.pool[name] = lock
}
p.count[name]++
p.lock.Unlock()
lock.Lock()
}
// CheckOut checks out a task to let other tasks run.
func (p *workingPool) CheckOut(name string) {
p.lock.Lock()
defer p.lock.Unlock()
p.pool[name].Unlock()
if p.count[name] == 1 {
delete(p.pool, name)
delete(p.count, name)
} else {
p.count[name]--
}
}
var wikiWorkingPool = &workingPool{
pool: make(map[string]*sync.Mutex),
count: make(map[string]int),
}
// ToWikiPageURL formats a string to corresponding wiki URL name.
func ToWikiPageURL(name string) string {
return strings.Replace(name, " ", "-", -1) return strings.Replace(name, " ", "-", -1)
} }
// ToWikiPageName formats a URL back to corresponding wiki page name.
func ToWikiPageName(name string) string {
return strings.Replace(name, "-", " ", -1)
}
// WikiPath returns wiki data path by given user and repository name. // WikiPath returns wiki data path by given user and repository name.
func WikiPath(userName, repoName string) string { func WikiPath(userName, repoName string) string {
return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".wiki.git") return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".wiki.git")
@ -46,11 +99,56 @@ func (repo *Repository) InitWiki() error {
return nil return nil
} }
func (repo *Repository) LocalWikiPath() string {
return path.Join(setting.AppDataPath, "tmp/local-wiki", com.ToStr(repo.ID))
}
// UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date.
func (repo *Repository) UpdateLocalWiki() error {
return updateLocalCopy(repo.WikiPath(), repo.LocalWikiPath())
}
// AddWikiPage adds new page to repository wiki. // AddWikiPage adds new page to repository wiki.
func (repo *Repository) AddWikiPage(title, content, message string) (err error) { func (repo *Repository) AddWikiPage(doer *User, title, content, message string) (err error) {
wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
if err = repo.InitWiki(); err != nil { if err = repo.InitWiki(); err != nil {
return fmt.Errorf("InitWiki: %v", err) return fmt.Errorf("InitWiki: %v", err)
} }
localPath := repo.LocalWikiPath()
// Discard local commits make sure even to remote when local copy exists.
if com.IsExist(localPath) {
// No need to check if nothing in the repository.
if git.IsBranchExist(localPath, "master") {
if err = git.Reset(localPath, true, "origin/master"); err != nil {
return fmt.Errorf("Reset: %v", err)
}
}
}
if err = repo.UpdateLocalWiki(); err != nil {
return fmt.Errorf("UpdateLocalWiki: %v", err)
}
title = strings.Replace(title, "/", " ", -1)
filename := path.Join(localPath, title+".md")
if err = ioutil.WriteFile(filename, []byte(content), 0666); err != nil {
return fmt.Errorf("WriteFile: %v", err)
}
if len(message) == 0 {
message = "Update page '" + title + "'"
}
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", "master"); err != nil {
return fmt.Errorf("Push: %v", err)
}
return nil return nil
} }

View File

@ -247,7 +247,7 @@ func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) bi
type NewWikiForm struct { type NewWikiForm struct {
Title string `binding:"Required"` Title string `binding:"Required"`
Content string Content string `binding:"Required"`
Message string Message string
} }

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{ {
"CodeKitInfo": "This is a CodeKit 2.x project configuration file. It is designed to sync project settings across multiple machines. MODIFYING THE CONTENTS OF THIS FILE IS A POOR LIFE DECISION. If you do so, you will likely cause CodeKit to crash. This file is not useful unless accompanied by the project that created it in CodeKit 2. This file is not backwards-compatible with CodeKit 1.x. For more information, see: http:\/\/incident57.com\/codekit", "CodeKitInfo": "This is a CodeKit 2.x project configuration file. It is designed to sync project settings across multiple machines. MODIFYING THE CONTENTS OF THIS FILE IS A POOR LIFE DECISION. If you do so, you will likely cause CodeKit to crash. This file is not useful unless accompanied by the project that created it in CodeKit 2. This file is not backwards-compatible with CodeKit 1.x. For more information, see: http:\/\/incident57.com\/codekit",
"creatorBuild": "19102", "creatorBuild": "19076",
"files": { "files": {
"\/css\/dropzone-4.2.0.css": { "\/css\/dropzone-4.2.0.css": {
"fileType": 16, "fileType": 16,
@ -616,18 +616,10 @@
"active": 0, "active": 0,
"flagValue": -1 "flagValue": -1
}, },
"no_nested_string_interpolation": {
"active": 1,
"flagValue": -1
},
"no_plusplus": { "no_plusplus": {
"active": 0, "active": 0,
"flagValue": -1 "flagValue": -1
}, },
"no_private_function_fat_arrows": {
"active": 1,
"flagValue": -1
},
"no_stand_alone_at": { "no_stand_alone_at": {
"active": 1, "active": 1,
"flagValue": -1 "flagValue": -1
@ -636,10 +628,6 @@
"active": 1, "active": 1,
"flagValue": -1 "flagValue": -1
}, },
"no_this": {
"active": 0,
"flagValue": -1
},
"no_throwing_strings": { "no_throwing_strings": {
"active": 1, "active": 1,
"flagValue": -1 "flagValue": -1

View File

@ -2531,6 +2531,20 @@ footer .container .links > *:first-child {
.repository.wiki.new .editor-preview { .repository.wiki.new .editor-preview {
background-color: white; background-color: white;
} }
.repository.wiki.view .ui.sub.header {
text-transform: none;
}
.repository.wiki.view .markdown {
padding: 15px 30px;
}
.repository.wiki.view .markdown h1:first-of-type,
.repository.wiki.view .markdown h2:first-of-type,
.repository.wiki.view .markdown h3:first-of-type,
.repository.wiki.view .markdown h4:first-of-type,
.repository.wiki.view .markdown h5:first-of-type,
.repository.wiki.view .markdown h6:first-of-type {
margin-top: 0;
}
.repository.settings.collaboration .collaborator.list { .repository.settings.collaboration .collaborator.list {
padding: 0; padding: 0;
} }

View File

@ -970,6 +970,24 @@
background-color: white; background-color: white;
} }
} }
&.view {
.header:not(.sub) {
// padding-left: 30px;
}
.ui.sub.header {
text-transform: none;
}
.markdown {
padding: 15px 30px;
h1, h2, h3, h4, h5, h6 {
&:first-of-type {
margin-top: 0;
}
}
}
}
} }
&.settings { &.settings {

View File

@ -5,6 +5,10 @@
package repo package repo
import ( import (
"io/ioutil"
"github.com/gogits/git-shell"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
@ -18,17 +22,68 @@ const (
) )
func Wiki(ctx *middleware.Context) { func Wiki(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("repo.wiki")
ctx.Data["PageIsWiki"] = true ctx.Data["PageIsWiki"] = true
if !ctx.Repo.Repository.HasWiki() { if !ctx.Repo.Repository.HasWiki() {
ctx.Data["Title"] = ctx.Tr("repo.wiki")
ctx.HTML(200, WIKI_START) ctx.HTML(200, WIKI_START)
return return
} }
wikiRepo, err := git.OpenRepository(ctx.Repo.Repository.WikiPath())
if err != nil {
ctx.Handle(500, "OpenRepository", err)
return
}
commit, err := wikiRepo.GetCommitOfBranch("master")
if err != nil {
ctx.Handle(500, "GetCommitOfBranch", err)
return
}
page := models.ToWikiPageName(ctx.Params(":page"))
if len(page) == 0 {
page = "Home"
}
ctx.Data["Title"] = page
ctx.Data["RequireHighlightJS"] = true
blob, err := commit.GetBlobByPath(page + ".md")
if err != nil {
if git.IsErrNotExist(err) {
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_list")
} else {
ctx.Handle(500, "GetBlobByPath", err)
}
return
}
r, err := blob.Data()
if err != nil {
ctx.Handle(500, "Data", err)
return
}
data, err := ioutil.ReadAll(r)
if err != nil {
ctx.Handle(500, "ReadAll", err)
return
}
ctx.Data["Content"] = string(base.RenderMarkdown(data, ctx.Repo.RepoLink))
// Get last change information.
lastCommit, err := wikiRepo.GetCommitByPath(page + ".md")
if err != nil {
ctx.Handle(500, "GetCommitByPath", err)
return
}
ctx.Data["Author"] = lastCommit.Author
ctx.HTML(200, WIKI_VIEW) ctx.HTML(200, WIKI_VIEW)
} }
func WikiList(ctx *middleware.Context) {
}
func NewWiki(ctx *middleware.Context) { func NewWiki(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page") ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
ctx.Data["PageIsWiki"] = true ctx.Data["PageIsWiki"] = true
@ -51,12 +106,12 @@ func NewWikiPost(ctx *middleware.Context, form auth.NewWikiForm) {
return return
} }
if err := ctx.Repo.Repository.AddWikiPage(form.Title, form.Content, form.Message); err != nil { if err := ctx.Repo.Repository.AddWikiPage(ctx.User, form.Title, form.Content, form.Message); err != nil {
ctx.Handle(500, "AddWikiPage", err) ctx.Handle(500, "AddWikiPage", err)
return return
} }
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + models.ToWikiPageName(form.Title)) ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + models.ToWikiPageURL(form.Title))
} }
func EditWiki(ctx *middleware.Context) { func EditWiki(ctx *middleware.Context) {

View File

@ -12,7 +12,7 @@
<input name="title" value="{{.title}}" autofocus required> <input name="title" value="{{.title}}" autofocus required>
</div> </div>
<div class="field"> <div class="field">
<textarea id="edit-area" name="content" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "repo.wiki.welcome"}}</textarea> <textarea id="edit-area" name="content" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "repo.wiki.welcome"}}</textarea required>
</div> </div>
<div class="field"> <div class="field">
<input name="message" placeholder="{{.i18n.Tr "repo.wiki.default_commit_message"}}"> <input name="message" placeholder="{{.i18n.Tr "repo.wiki.default_commit_message"}}">

View File

@ -0,0 +1,18 @@
{{template "base/head" .}}
<div class="repository wiki view">
{{template "repo/header" .}}
<div class="ui container">
{{template "repo/sidebar" .}}
<div class="ui dividing header">
{{.Title}}
<div class="ui sub header">
{{$timeSince := TimeSince .Author.When $.Lang}}
{{.i18n.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}}
</div>
</div>
<div class="ui segment markdown">
{{.Content | Str2html}}
</div>
</div>
</div>
{{template "base/footer" .}}