Explorar o código

Git installation check, show image in single file view, show short SHA1 instead of 40-length string, complete log module

Unknwon %!s(int64=10) %!d(string=hai) anos
pai
achega
5e81383413

+ 6 - 0
models/repo.go

@@ -11,6 +11,7 @@ import (
 	"html/template"
 	"io/ioutil"
 	"os"
+	"os/exec"
 	"path"
 	"path/filepath"
 	"regexp"
@@ -84,6 +85,11 @@ func LoadRepoConfig() {
 func NewRepoContext() {
 	zip.Verbose = false
 
+	// Check Git installation.
+	if _, err := exec.LookPath("git"); err != nil {
+		log.Fatal(4, "Fail to test 'git' command: %v (forgotten install?)", err)
+	}
+
 	// Check Git version.
 	ver, err := git.GetVersion()
 	if err != nil {

+ 1 - 1
models/user.go

@@ -81,7 +81,7 @@ type User struct {
 // DashboardLink returns the user dashboard page link.
 func (u *User) DashboardLink() string {
 	if u.IsOrganization() {
-		return "/org/" + u.Name + "/dashboard"
+		return "/org/" + u.Name + "/dashboard/"
 	}
 	return "/"
 }

+ 104 - 0
modules/log/conn.go

@@ -0,0 +1,104 @@
+// Copyright 2014 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 log
+
+import (
+	"encoding/json"
+	"io"
+	"log"
+	"net"
+)
+
+// ConnWriter implements LoggerInterface.
+// it writes messages in keep-live tcp connection.
+type ConnWriter struct {
+	lg             *log.Logger
+	innerWriter    io.WriteCloser
+	ReconnectOnMsg bool   `json:"reconnectOnMsg"`
+	Reconnect      bool   `json:"reconnect"`
+	Net            string `json:"net"`
+	Addr           string `json:"addr"`
+	Level          int    `json:"level"`
+}
+
+// create new ConnWrite returning as LoggerInterface.
+func NewConn() LoggerInterface {
+	conn := new(ConnWriter)
+	conn.Level = TRACE
+	return conn
+}
+
+// init connection writer with json config.
+// json config only need key "level".
+func (cw *ConnWriter) Init(jsonconfig string) error {
+	return json.Unmarshal([]byte(jsonconfig), cw)
+}
+
+// write message in connection.
+// if connection is down, try to re-connect.
+func (cw *ConnWriter) WriteMsg(msg string, skip, level int) error {
+	if cw.Level > level {
+		return nil
+	}
+	if cw.neddedConnectOnMsg() {
+		if err := cw.connect(); err != nil {
+			return err
+		}
+	}
+
+	if cw.ReconnectOnMsg {
+		defer cw.innerWriter.Close()
+	}
+	cw.lg.Println(msg)
+	return nil
+}
+
+func (_ *ConnWriter) Flush() {
+}
+
+// destroy connection writer and close tcp listener.
+func (cw *ConnWriter) Destroy() {
+	if cw.innerWriter == nil {
+		return
+	}
+	cw.innerWriter.Close()
+}
+
+func (cw *ConnWriter) connect() error {
+	if cw.innerWriter != nil {
+		cw.innerWriter.Close()
+		cw.innerWriter = nil
+	}
+
+	conn, err := net.Dial(cw.Net, cw.Addr)
+	if err != nil {
+		return err
+	}
+
+	if tcpConn, ok := conn.(*net.TCPConn); ok {
+		tcpConn.SetKeepAlive(true)
+	}
+
+	cw.innerWriter = conn
+	cw.lg = log.New(conn, "", log.Ldate|log.Ltime)
+	return nil
+}
+
+func (cw *ConnWriter) neddedConnectOnMsg() bool {
+	if cw.Reconnect {
+		cw.Reconnect = false
+		return true
+	}
+
+	if cw.innerWriter == nil {
+		return true
+	}
+
+	return cw.ReconnectOnMsg
+}
+
+func init() {
+	Register("conn", NewConn)
+}

+ 3 - 3
modules/log/console.go

@@ -61,13 +61,13 @@ func (cw *ConsoleWriter) WriteMsg(msg string, skip, level int) error {
 	return nil
 }
 
-func (_ *ConsoleWriter) Destroy() {
-}
-
 func (_ *ConsoleWriter) Flush() {
 
 }
 
+func (_ *ConsoleWriter) Destroy() {
+}
+
 func init() {
 	Register("console", NewConsole)
 }

+ 68 - 0
modules/log/database.go

@@ -0,0 +1,68 @@
+// Copyright 2014 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 log
+
+import (
+	"encoding/json"
+
+	"github.com/go-xorm/xorm"
+)
+
+type Log struct {
+	Id    int64
+	Level int
+	Msg   string `xorm:"TEXT"`
+}
+
+// DatabaseWriter implements LoggerInterface and is used to log into database.
+type DatabaseWriter struct {
+	Driver string `json:"driver"`
+	Conn   string `json:"conn"`
+	Level  int    `json:"level"`
+	x      *xorm.Engine
+}
+
+func NewDatabase() LoggerInterface {
+	return &DatabaseWriter{Level: TRACE}
+}
+
+// init database writer with json config.
+// config like:
+//	{
+//		"driver": "mysql"
+//		"conn":"root:root@tcp(127.0.0.1:3306)/gogs?charset=utf8",
+//		"level": 0
+//	}
+// connection string is based on xorm.
+func (d *DatabaseWriter) Init(jsonconfig string) (err error) {
+	if err = json.Unmarshal([]byte(jsonconfig), d); err != nil {
+		return err
+	}
+	d.x, err = xorm.NewEngine(d.Driver, d.Conn)
+	if err != nil {
+		return err
+	}
+	return d.x.Sync(new(Log))
+}
+
+// write message in database writer.
+func (d *DatabaseWriter) WriteMsg(msg string, skip, level int) error {
+	if level < d.Level {
+		return nil
+	}
+
+	_, err := d.x.Insert(&Log{Level: level, Msg: msg})
+	return err
+}
+
+func (_ *DatabaseWriter) Flush() {
+}
+
+func (_ *DatabaseWriter) Destroy() {
+}
+
+func init() {
+	Register("database", NewDatabase)
+}

+ 8 - 8
modules/log/log.go

@@ -87,12 +87,12 @@ func Fatal(skip int, format string, v ...interface{}) {
 	os.Exit(1)
 }
 
-// .___ _______________________________________________________  _________ ___________
-// |   |\      \__    ___/\_   _____/\______   \_   _____/  _  \ \_   ___ \\_   _____/
-// |   |/   |   \|    |    |    __)_  |       _/|    __)/  /_\  \/    \  \/ |    __)_
-// |   /    |    \    |    |        \ |    |   \|     \/    |    \     \____|        \
-// |___\____|__  /____|   /_______  / |____|_  /\___  /\____|__  /\______  /_______  /
-//             \/                 \/         \/     \/         \/        \/        \/
+// .___        __                 _____
+// |   | _____/  |_  ____________/ ____\____    ____  ____
+// |   |/    \   __\/ __ \_  __ \   __\\__  \ _/ ___\/ __ \
+// |   |   |  \  | \  ___/|  | \/|  |   / __ \\  \__\  ___/
+// |___|___|  /__|  \___  >__|   |__|  (____  /\___  >___  >
+//          \/          \/                  \/     \/    \/
 
 type LogLevel int
 
@@ -168,7 +168,7 @@ func (l *Logger) SetLogger(adapter string, config string) error {
 		l.outputs[adapter] = lg
 		l.adapter = adapter
 	} else {
-		panic("log: unknown adapter \"" + adapter + "\" (forgotten Register?)")
+		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
 	}
 	return nil
 }
@@ -181,7 +181,7 @@ func (l *Logger) DelLogger(adapter string) error {
 		lg.Destroy()
 		delete(l.outputs, adapter)
 	} else {
-		panic("log: unknown adapter \"" + adapter + "\" (forgotten Register?)")
+		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
 	}
 	return nil
 }

+ 87 - 0
modules/log/smtp.go

@@ -0,0 +1,87 @@
+// Copyright 2014 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 log
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/smtp"
+	"strings"
+	"time"
+)
+
+const (
+	subjectPhrase = "Diagnostic message from server"
+)
+
+// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
+type SmtpWriter struct {
+	Username           string   `json:"Username"`
+	Password           string   `json:"password"`
+	Host               string   `json:"Host"`
+	Subject            string   `json:"subject"`
+	RecipientAddresses []string `json:"sendTos"`
+	Level              int      `json:"level"`
+}
+
+// create smtp writer.
+func NewSmtpWriter() LoggerInterface {
+	return &SmtpWriter{Level: TRACE}
+}
+
+// init smtp writer with json config.
+// config like:
+//	{
+//		"Username":"example@gmail.com",
+//		"password:"password",
+//		"host":"smtp.gmail.com:465",
+//		"subject":"email title",
+//		"sendTos":["email1","email2"],
+//		"level":LevelError
+//	}
+func (sw *SmtpWriter) Init(jsonconfig string) error {
+	return json.Unmarshal([]byte(jsonconfig), sw)
+}
+
+// write message in smtp writer.
+// it will send an email with subject and only this message.
+func (s *SmtpWriter) WriteMsg(msg string, skip, level int) error {
+	if level < s.Level {
+		return nil
+	}
+
+	hp := strings.Split(s.Host, ":")
+
+	// Set up authentication information.
+	auth := smtp.PlainAuth(
+		"",
+		s.Username,
+		s.Password,
+		hp[0],
+	)
+	// Connect to the server, authenticate, set the sender and recipient,
+	// and send the email all in one step.
+	content_type := "Content-Type: text/plain" + "; charset=UTF-8"
+	mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.Username + "<" + s.Username +
+		">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg)
+
+	return smtp.SendMail(
+		s.Host,
+		auth,
+		s.Username,
+		s.RecipientAddresses,
+		mailmsg,
+	)
+}
+
+func (_ *SmtpWriter) Flush() {
+}
+
+func (_ *SmtpWriter) Destroy() {
+}
+
+func init() {
+	Register("smtp", NewSmtpWriter)
+}

+ 20 - 1
public/ng/css/gogs.css

@@ -660,32 +660,41 @@ The dashboard page style
 #dashboard-sidebar-menu > li.last > a {
   border-top-right-radius: .3em;
 }
+#dashboard-my-org li,
 #dashboard-my-repo li {
   border-bottom: 1px solid #EAEAEA;
 }
+#dashboard-my-org li.private,
 #dashboard-my-repo li.private {
   background-color: #fcf8e9;
 }
+#dashboard-my-org li:last-child,
 #dashboard-my-repo li:last-child {
   border-bottom: none;
 }
+#dashboard-my-org li a,
 #dashboard-my-repo li a {
   padding: 6px 1.2em;
   display: block;
 }
+#dashboard-my-org li a .octicon,
 #dashboard-my-repo li a .octicon {
   margin-right: 6px;
   color: #888;
 }
+#dashboard-my-org li a:hover .repo-name,
 #dashboard-my-repo li a:hover .repo-name {
   text-decoration: underline;
 }
+#dashboard-my-org .repo-name,
 #dashboard-my-repo .repo-name {
   font-size: 1.1em;
 }
+#dashboard-my-org .repo-star,
 #dashboard-my-repo .repo-star {
   color: #888;
 }
+#dashboard-my-org .repo-contrib-header,
 #dashboard-my-repo .repo-contrib-header {
   border-top: 1px solid #d6d6d6;
 }
@@ -1139,6 +1148,17 @@ The register and sign-in page style
   overflow: auto;
   overflow-x: auto;
   overflow-y: hidden;
+  background: white;
+}
+.code-view .view-raw {
+  min-height: 40px;
+  text-align: center;
+  padding-top: 20px;
+}
+.code-view .view-raw .btn {
+  font-size: 1.05em;
+  line-height: 16px;
+  padding: 6px 8px;
 }
 .code-view table {
   width: 100%;
@@ -1163,7 +1183,6 @@ The register and sign-in page style
 }
 .code-view .lines-code > pre {
   border: none;
-  background: none;
   border-left: 1px solid #ddd;
 }
 .code-view .lines-code > pre > ol.linenums > li {

+ 3 - 2
routers/install.go

@@ -54,11 +54,12 @@ func GlobalInit() {
 	log.Trace("Log path: %s", setting.LogRootPath)
 	mailer.NewMailerContext()
 	models.LoadModelsConfig()
-	models.LoadRepoConfig()
-	models.NewRepoContext()
 	NewServices()
 
 	if setting.InstallLock {
+		models.LoadRepoConfig()
+		models.NewRepoContext()
+
 		if err := models.NewEngine(); err != nil {
 			log.Fatal(4, "Fail to initialize ORM engine: %v", err)
 		}

+ 0 - 4
routers/user/home.go

@@ -32,7 +32,6 @@ func Dashboard(ctx *middleware.Context) {
 		ctx.Handle(500, "home.Dashboard(GetOrganizations)", err)
 		return
 	}
-	ctx.Data["Orgs"] = ctx.User.Orgs
 	ctx.Data["ContextUser"] = ctx.User
 
 	repos, err := models.GetRepositories(ctx.User.Id, true)
@@ -40,9 +39,6 @@ func Dashboard(ctx *middleware.Context) {
 		ctx.Handle(500, "GetRepositories", err)
 		return
 	}
-	for _, repo := range repos {
-		repo.Owner = ctx.User
-	}
 	ctx.Data["Repos"] = repos
 
 	ctx.Data["CollaborativeRepos"], err = models.GetCollaborativeRepos(ctx.User.Name)

+ 1 - 1
templates/org/home.tmpl

@@ -34,7 +34,7 @@
                     </div>
                     <h3 class="org-repo-name"><a href="/{{$.Org.Name}}/{{.Name}}">{{.Name}}</a></h3>
                     <p class="org-repo-description">{{.Description}}</p>
-                    <p class="org-repo-update">Updated {{TimeSince .Updated}}</p>
+                    <p class="org-repo-update">Updated {{TimeSince .Updated $.Lang}}</p>
                 </div>
                 {{end}}
             </div>

+ 3 - 3
templates/repo/home.tmpl

@@ -74,7 +74,7 @@
                     <a href="#">
                         <button class="btn btn-gray btn-small btn-radius">
                             <i class="octicon octicon-git-branch"></i> Branch :
-                            <strong id="repo-branch-current">{{.BranchName}}</strong>
+                            <strong id="repo-branch-current">{{if .IsViewBranch}}{{.BranchName}}{{else}}{{ShortSha .BranchName}}{{end}}</strong>
                         </button>
                     </a>
                     <div class="drop-down panel">
@@ -87,7 +87,7 @@
                             </ul>
                             <ul class="menu menu-vertical switching-list" id="repo-branch-list">
                                 {{range .Branches}}
-                                <li {{if eq . $.BranchName}}class="checked"{{end}}><a href="{{$.RepoLink}}/src/{{.}}"><i class="octicon octicon-check"></i>master</a></li>
+                                <li {{if eq . $.BranchName}}class="checked"{{end}}><a href="{{$.RepoLink}}/src/{{.}}"><i class="octicon octicon-check"></i>{{.}}</a></li>
                                 {{end}}
                             </ul>
                             <ul class="menu menu-vertical switching-list" id="repo-tag-list">
@@ -137,7 +137,7 @@
                     <a class="radius" href="{{.RepoLink}}/pulls"><i class="octicon octicon-git-pull-request"></i>Pull Requests<span class="num right label label-blue label-radius">{{.Repository.NumOpenPulls}}</span></a>
                 </li>
                 <li class="border-bottom"></li>
-                <li class="head">{{.BranchName}}</li>
+                <li class="head">{{if .IsViewBranch}}{{.BranchName}}{{else}}{{ShortSha .BranchName}}{{end}}</li>
                 <li>
                     <a class="radius" href="{{.RepoLink}}/commits/{{.BranchName}}"><i class="octicon octicon-history"></i>Commits <span class="num right label label-gray label-radius">{{.CommitsCount}}</span></a>
                 </li>

+ 8 - 0
templates/repo/view_file.tmpl

@@ -15,6 +15,14 @@
     <div class="{{if .ReadmeExist}}panel-content markdown{{end}} code-view">
     	{{if .ReadmeExist}}
     	{{.FileContent | Str2html}}
+        {{else if not .IsFileText}}
+        <div class="view-raw">
+            {{if .IsImageFile}}
+            <img src="{{.FileLink}}">
+            {{else}}
+            <a href="{{.FileLink}}" rel="nofollow" class="btn btn-gray btn-radius">View Raw</a>
+            {{end}}
+        </div>
     	{{else if .FileSize}}
         <table>
             <tbody>

+ 46 - 2
templates/user/dashboard/dashboard.tmpl

@@ -48,7 +48,9 @@
         <div id="dashboard-sidebar" class="right grid-1-3">
             <ul id="dashboard-sidebar-menu" class="menu menu-line">
                 <li class="js-tab-nav js-tab-nav-show first" data-tab-target="#dashboard-my-repo"><a href="#">{{.i18n.Tr "repository"}}</a></li>
+                {{if not .ContextUser.IsOrganization}}
                 <li class="js-tab-nav" data-tab-target="#dashboard-my-org"><a href="#">{{.i18n.Tr "organization"}}</a></li>
+                {{end}}
                 <li class="js-tab-nav last" data-tab-target="#dashboard-my-mirror"><a href="#">{{.i18n.Tr "mirror"}}</a></li>
                 <li class="drop right">
                     <button class="btn btn-green text-bold" id="dashboard-new-repo">
@@ -70,26 +72,68 @@
                 <div class="panel-body">
                     <ul class="list-no-style">
                         {{range .Repos}}
-                            {{template "user/dashboard/repo_list" .}}
+                        <li {{if .IsPrivate}}class="private"{{end}}>
+                            <a href="{{$.ContextUser.Name}}/{{.Name}}">
+                                <i class="octicon octicon-{{if .IsPrivate}}lock{{else if .IsFork}}repo-forked{{else if .IsMirror}}repo-clone{{else}}repo{{end}}"></i>
+                                <span class="repo-name">
+                                    <strong class="repo">{{.Name}}</strong>
+                                </span>
+                                <span class="right repo-star">
+                                    <i class="octicon octicon-star"></i>{{.NumStars}}
+                                </span>
+                            </a>
+                        </li>
                         {{end}}
                     </ul>
                 </div>
+                {{if not .ContextUser.IsOrganization}}
                 <div class="panel-header repo-contrib-header">
                     <h4 class="text-bold">{{.i18n.Tr "home.collaborative_repos"}}</h4>
                 </div>
                 <div class="panel-body">
                     <ul class="list-no-style">
                         {{range .CollaborativeRepos}}
-                            {{template "user/dashboard/repo_list" .}}
+                        <li {{if .IsPrivate}}class="private"{{end}}>
+                            <a href="{{.Owner.Name}}/{{.Name}}">
+                                <i class="octicon octicon-{{if .IsPrivate}}lock{{else if .IsFork}}repo-forked{{else if .IsMirror}}repo-clone{{else}}repo{{end}}"></i>
+                                <span class="repo-name">
+                                    <span class="repo-name-prefix">{{.Owner.Name}} / </span>
+                                    <strong class="repo">{{.Name}}</strong>
+                                </span>
+                                <span class="right repo-star">
+                                    <i class="octicon octicon-star"></i>{{.NumStars}}
+                                </span>
+                            </a>
+                        </li>
                         {{end}}
                     </ul>
                 </div>
+                {{end}}
             </div>
+            {{if not .ContextUser.IsOrganization}}
             <div class="panel" id="dashboard-my-org">
                 <div class="panel-header">
                     <h4 class="text-bold">{{.i18n.Tr "home.my_orgs"}}</h4>
                 </div>
+                <div class="panel-body">
+                    <ul class="list-no-style">
+                        {{range .ContextUser.Orgs}}
+                        <li>
+                            <a href="/{{.Name}}">
+                                <i class="octicon octicon-organization"></i>
+                                <span class="repo-name">
+                                    <strong class="repo">{{.Name}}</strong>
+                                </span>
+                                <span class="right repo-star">
+                                    <i class="octicon octicon-repo"></i>{{.NumRepos}}
+                                </span>
+                            </a>
+                        </li>
+                        {{end}}
+                    </ul>
+                </div>
             </div>
+            {{end}}
             <div class="panel" id="dashboard-my-mirror">
                 <div class="panel-header">
                     <h4 class="text-bold">{{.i18n.Tr "home.my_mirrors"}}</h4>

+ 1 - 1
templates/user/dashboard/nav.tmpl

@@ -15,7 +15,7 @@
                             {{.SignedUser.Name}}
                         </a>
                     </li>
-                    {{range .Orgs}}
+                    {{range .ContextUser.Orgs}}
                     <li class="org {{if eq $.ContextUser.Id .Id}}checked{{end}}">
                         <a href="{{.DashboardLink}}">
                             <i class="octicon octicon-check"></i>

+ 0 - 12
templates/user/dashboard/repo_list.tmpl

@@ -1,12 +0,0 @@
-<li {{if .IsPrivate}}class="private"{{end}}>
-    <a href="{{.Owner.Name}}/{{.Name}}">
-        <i class="octicon octicon-{{if .IsPrivate}}lock{{else if .IsFork}}repo-forked{{else if .IsMirror}}repo-clone{{else}}repo{{end}}"></i>
-        <span class="repo-name">
-            <!-- <span class="repo-name-prefix">gogits / </span> -->
-            <strong class="repo">{{.Name}}</strong>
-        </span>
-        <span class="right repo-star">
-            <i class="octicon octicon-star"></i>{{.NumStars}}
-        </span>
-    </a>
-</li>