merged
This commit is contained in:
commit
17da2fd2e3
58 changed files with 1831 additions and 344 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -6,3 +6,5 @@ gogs
|
|||
*.log
|
||||
custom/
|
||||
.vendor/
|
||||
.idea/
|
||||
*.iml
|
|
@ -9,13 +9,12 @@ github.com/Unknwon/com=
|
|||
github.com/Unknwon/cae=
|
||||
github.com/Unknwon/goconfig=
|
||||
github.com/dchest/scrypt=
|
||||
github.com/go-sql-driver/mysql=
|
||||
github.com/lib/pq=
|
||||
github.com/lunny/xorm=
|
||||
github.com/gogits/logs=
|
||||
github.com/gogits/binding=
|
||||
github.com/gogits/git=
|
||||
github.com/gogits/gfm=
|
||||
github.com/gogits/cache=
|
||||
|
||||
[res]
|
||||
include=templates|public|conf
|
||||
|
|
49
CONTRIBUTING.md
Normal file
49
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Contributing to Gogs
|
||||
|
||||
Want to hack on Gogs? Awesome! Here are instructions to get you
|
||||
started. They are probably not perfect, please let us know if anything
|
||||
feels wrong or incomplete.
|
||||
|
||||
## Contribution guidelines
|
||||
|
||||
### Pull requests are always welcome
|
||||
|
||||
We are always thrilled to receive pull requests, and do our best to
|
||||
process them as fast as possible. Not sure if that typo is worth a pull
|
||||
request? Do it! We will appreciate it.
|
||||
|
||||
If your pull request is not accepted on the first try, don't be
|
||||
discouraged! If there's a problem with the implementation, hopefully you
|
||||
received feedback on what to improve.
|
||||
|
||||
We're trying very hard to keep Gogs lean and focused. We don't want it
|
||||
to do everything for everybody. This means that we might decide against
|
||||
incorporating a new feature.
|
||||
|
||||
### Discuss your design on the mailing list
|
||||
|
||||
We recommend discussing your plans [on the mailing
|
||||
list](https://groups.google.com/forum/#!forum/gogits)
|
||||
before starting to code - especially for more ambitious contributions.
|
||||
This gives other contributors a chance to point you in the right
|
||||
direction, give feedback on your design, and maybe point out if someone
|
||||
else is working on the same thing.
|
||||
|
||||
We may close your pull request if not first discussed on the mailing
|
||||
list. We aren't doing this to be jerks. We are doing this to prevent
|
||||
people from spending large amounts of time on changes that may need
|
||||
to be designed or architected in a specific way, or may not align with
|
||||
the vision of the project.
|
||||
|
||||
### Create issues...
|
||||
|
||||
Any significant improvement should be documented as [a GitHub
|
||||
issue](https://github.com/gogits/gogs/issues) before anybody
|
||||
starts working on it.
|
||||
|
||||
### ...but check for existing issues first!
|
||||
|
||||
Please take a moment to check that an issue doesn't already exist
|
||||
documenting your bug report or improvement proposal. If it does, it
|
||||
never hurts to add a quick "+1" or "I have this problem too". This will
|
||||
help prioritize the most common problems and requests.
|
|
@ -5,7 +5,7 @@ Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language.
|
|||
|
||||
Since we choose to use pure Go implementation of Git manipulation, Gogs certainly supports **ALL platforms** that Go supports, including Linux, Max OS X, and Windows with **ZERO** dependency.
|
||||
|
||||
##### Current version: 0.1.1 Alpha
|
||||
##### Current version: 0.1.5 Alpha
|
||||
|
||||
## Purpose
|
||||
|
||||
|
@ -15,7 +15,7 @@ There are some very good products in this category such as [gitlab](http://gitla
|
|||
|
||||
- Please see [Wiki](https://github.com/gogits/gogs/wiki) for project design, develop specification, change log and road map.
|
||||
- See [Trello Broad](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
|
||||
- Try it before anything? Go down to **Installation -> Install from binary** section!.
|
||||
- Try it before anything? Do it [online](http://try.gogits.org/Unknown/gogs) or go down to **Installation -> Install from binary** section!
|
||||
- Having troubles? Get help from [Troubleshooting](https://github.com/gogits/gogs/wiki/Troubleshooting).
|
||||
|
||||
## Features
|
||||
|
@ -28,7 +28,8 @@ There are some very good products in this category such as [gitlab](http://gitla
|
|||
- Repository viewer.
|
||||
- Gravatar support.
|
||||
- Mail service(register).
|
||||
- Supports MySQL and PostgreSQL.
|
||||
- Administration panel.
|
||||
- Supports MySQL, PostgreSQL and SQLite3(binary release only).
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
24
conf/app.ini
24
conf/app.ini
|
@ -8,8 +8,8 @@ RUN_MODE = dev
|
|||
|
||||
[repository]
|
||||
ROOT = /Users/%(RUN_USER)s/git/gogs-repositories
|
||||
LANG_IGNS = Google Go|C|Python|Ruby|C Sharp
|
||||
LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|BSD (3-Clause) License
|
||||
LANG_IGNS = Google Go|C|C++|Python|Ruby|C Sharp
|
||||
LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|Artistic License 2.0|BSD (3-Clause) License
|
||||
|
||||
[server]
|
||||
DOMAIN = localhost
|
||||
|
@ -18,7 +18,7 @@ HTTP_ADDR =
|
|||
HTTP_PORT = 3000
|
||||
|
||||
[database]
|
||||
; Either "mysql" or "postgres", it's your choice
|
||||
; Either "mysql", "postgres" or "sqlite3"(binary release only), it's your choice
|
||||
DB_TYPE = mysql
|
||||
HOST =
|
||||
NAME = gogs
|
||||
|
@ -26,6 +26,10 @@ USER = root
|
|||
PASSWD =
|
||||
; For "postgres" only, either "disable", "require" or "verify-full"
|
||||
SSL_MODE = disable
|
||||
; For "sqlite3" only
|
||||
PATH = data/gogs.db
|
||||
|
||||
[admin]
|
||||
|
||||
[security]
|
||||
; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!!
|
||||
|
@ -36,6 +40,10 @@ ACTIVE_CODE_LIVE_MINUTES = 180
|
|||
RESET_PASSWD_CODE_LIVE_MINUTES = 180
|
||||
; User need to confirm e-mail for registration
|
||||
REGISTER_EMAIL_CONFIRM = false
|
||||
; Does not allow register and admin create account only
|
||||
DISENABLE_REGISTERATION = false
|
||||
; User must sign in to view anything.
|
||||
REQUIRE_SIGNIN_VIEW = false
|
||||
|
||||
[mailer]
|
||||
ENABLED = false
|
||||
|
@ -52,6 +60,16 @@ FROM =
|
|||
USER =
|
||||
PASSWD =
|
||||
|
||||
[cache]
|
||||
; Either "memory", "redis", or "memcache", default is "memory"
|
||||
ADAPTER = memory
|
||||
; For "memory" only, GC interval in seconds, default is 60
|
||||
INTERVAL = 60
|
||||
; For "redis" and "memcache", connection host address
|
||||
; redis: ":6039"
|
||||
; memcache: "127.0.0.1:11211"
|
||||
HOST =
|
||||
|
||||
[log]
|
||||
; Either "console", "file", "conn" or "smtp", default is "console"
|
||||
MODE = console
|
||||
|
|
13
conf/gitignore/C++
Normal file
13
conf/gitignore/C++
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
201
conf/license/Artistic License 2.0
Normal file
201
conf/license/Artistic License 2.0
Normal file
|
@ -0,0 +1,201 @@
|
|||
The Artistic License 2.0
|
||||
|
||||
Copyright (c) 2014
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
This license establishes the terms under which a given free software
|
||||
Package may be copied, modified, distributed, and/or redistributed.
|
||||
The intent is that the Copyright Holder maintains some artistic
|
||||
control over the development of that Package while still keeping the
|
||||
Package available as open source and free software.
|
||||
|
||||
You are always permitted to make arrangements wholly outside of this
|
||||
license directly with the Copyright Holder of a given Package. If the
|
||||
terms of this license do not permit the full use that you propose to
|
||||
make of the Package, you should contact the Copyright Holder and seek
|
||||
a different licensing arrangement.
|
||||
|
||||
Definitions
|
||||
|
||||
"Copyright Holder" means the individual(s) or organization(s)
|
||||
named in the copyright notice for the entire Package.
|
||||
|
||||
"Contributor" means any party that has contributed code or other
|
||||
material to the Package, in accordance with the Copyright Holder's
|
||||
procedures.
|
||||
|
||||
"You" and "your" means any person who would like to copy,
|
||||
distribute, or modify the Package.
|
||||
|
||||
"Package" means the collection of files distributed by the
|
||||
Copyright Holder, and derivatives of that collection and/or of
|
||||
those files. A given Package may consist of either the Standard
|
||||
Version, or a Modified Version.
|
||||
|
||||
"Distribute" means providing a copy of the Package or making it
|
||||
accessible to anyone else, or in the case of a company or
|
||||
organization, to others outside of your company or organization.
|
||||
|
||||
"Distributor Fee" means any fee that you charge for Distributing
|
||||
this Package or providing support for this Package to another
|
||||
party. It does not mean licensing fees.
|
||||
|
||||
"Standard Version" refers to the Package if it has not been
|
||||
modified, or has been modified only in ways explicitly requested
|
||||
by the Copyright Holder.
|
||||
|
||||
"Modified Version" means the Package, if it has been changed, and
|
||||
such changes were not explicitly requested by the Copyright
|
||||
Holder.
|
||||
|
||||
"Original License" means this Artistic License as Distributed with
|
||||
the Standard Version of the Package, in its current version or as
|
||||
it may be modified by The Perl Foundation in the future.
|
||||
|
||||
"Source" form means the source code, documentation source, and
|
||||
configuration files for the Package.
|
||||
|
||||
"Compiled" form means the compiled bytecode, object code, binary,
|
||||
or any other form resulting from mechanical transformation or
|
||||
translation of the Source form.
|
||||
|
||||
|
||||
Permission for Use and Modification Without Distribution
|
||||
|
||||
(1) You are permitted to use the Standard Version and create and use
|
||||
Modified Versions for any purpose without restriction, provided that
|
||||
you do not Distribute the Modified Version.
|
||||
|
||||
|
||||
Permissions for Redistribution of the Standard Version
|
||||
|
||||
(2) You may Distribute verbatim copies of the Source form of the
|
||||
Standard Version of this Package in any medium without restriction,
|
||||
either gratis or for a Distributor Fee, provided that you duplicate
|
||||
all of the original copyright notices and associated disclaimers. At
|
||||
your discretion, such verbatim copies may or may not include a
|
||||
Compiled form of the Package.
|
||||
|
||||
(3) You may apply any bug fixes, portability changes, and other
|
||||
modifications made available from the Copyright Holder. The resulting
|
||||
Package will still be considered the Standard Version, and as such
|
||||
will be subject to the Original License.
|
||||
|
||||
|
||||
Distribution of Modified Versions of the Package as Source
|
||||
|
||||
(4) You may Distribute your Modified Version as Source (either gratis
|
||||
or for a Distributor Fee, and with or without a Compiled form of the
|
||||
Modified Version) provided that you clearly document how it differs
|
||||
from the Standard Version, including, but not limited to, documenting
|
||||
any non-standard features, executables, or modules, and provided that
|
||||
you do at least ONE of the following:
|
||||
|
||||
(a) make the Modified Version available to the Copyright Holder
|
||||
of the Standard Version, under the Original License, so that the
|
||||
Copyright Holder may include your modifications in the Standard
|
||||
Version.
|
||||
|
||||
(b) ensure that installation of your Modified Version does not
|
||||
prevent the user installing or running the Standard Version. In
|
||||
addition, the Modified Version must bear a name that is different
|
||||
from the name of the Standard Version.
|
||||
|
||||
(c) allow anyone who receives a copy of the Modified Version to
|
||||
make the Source form of the Modified Version available to others
|
||||
under
|
||||
|
||||
(i) the Original License or
|
||||
|
||||
(ii) a license that permits the licensee to freely copy,
|
||||
modify and redistribute the Modified Version using the same
|
||||
licensing terms that apply to the copy that the licensee
|
||||
received, and requires that the Source form of the Modified
|
||||
Version, and of any works derived from it, be made freely
|
||||
available in that license fees are prohibited but Distributor
|
||||
Fees are allowed.
|
||||
|
||||
|
||||
Distribution of Compiled Forms of the Standard Version
|
||||
or Modified Versions without the Source
|
||||
|
||||
(5) You may Distribute Compiled forms of the Standard Version without
|
||||
the Source, provided that you include complete instructions on how to
|
||||
get the Source of the Standard Version. Such instructions must be
|
||||
valid at the time of your distribution. If these instructions, at any
|
||||
time while you are carrying out such distribution, become invalid, you
|
||||
must provide new instructions on demand or cease further distribution.
|
||||
If you provide valid instructions or cease distribution within thirty
|
||||
days after you become aware that the instructions are invalid, then
|
||||
you do not forfeit any of your rights under this license.
|
||||
|
||||
(6) You may Distribute a Modified Version in Compiled form without
|
||||
the Source, provided that you comply with Section 4 with respect to
|
||||
the Source of the Modified Version.
|
||||
|
||||
|
||||
Aggregating or Linking the Package
|
||||
|
||||
(7) You may aggregate the Package (either the Standard Version or
|
||||
Modified Version) with other packages and Distribute the resulting
|
||||
aggregation provided that you do not charge a licensing fee for the
|
||||
Package. Distributor Fees are permitted, and licensing fees for other
|
||||
components in the aggregation are permitted. The terms of this license
|
||||
apply to the use and Distribution of the Standard or Modified Versions
|
||||
as included in the aggregation.
|
||||
|
||||
(8) You are permitted to link Modified and Standard Versions with
|
||||
other works, to embed the Package in a larger work of your own, or to
|
||||
build stand-alone binary or bytecode versions of applications that
|
||||
include the Package, and Distribute the result without restriction,
|
||||
provided the result does not expose a direct interface to the Package.
|
||||
|
||||
|
||||
Items That are Not Considered Part of a Modified Version
|
||||
|
||||
(9) Works (including, but not limited to, modules and scripts) that
|
||||
merely extend or make use of the Package, do not, by themselves, cause
|
||||
the Package to be a Modified Version. In addition, such works are not
|
||||
considered parts of the Package itself, and are not subject to the
|
||||
terms of this license.
|
||||
|
||||
|
||||
General Provisions
|
||||
|
||||
(10) Any use, modification, and distribution of the Standard or
|
||||
Modified Versions is governed by this Artistic License. By using,
|
||||
modifying or distributing the Package, you accept this license. Do not
|
||||
use, modify, or distribute the Package, if you do not accept this
|
||||
license.
|
||||
|
||||
(11) If your Modified Version has been derived from a Modified
|
||||
Version made by someone other than you, you are nevertheless required
|
||||
to ensure that your Modified Version complies with the requirements of
|
||||
this license.
|
||||
|
||||
(12) This license does not grant you the right to use any trademark,
|
||||
service mark, tradename, or logo of the Copyright Holder.
|
||||
|
||||
(13) This license includes the non-exclusive, worldwide,
|
||||
free-of-charge patent license to make, have made, use, offer to sell,
|
||||
sell, import and otherwise transfer the Package with respect to any
|
||||
patent claims licensable by the Copyright Holder that are necessarily
|
||||
infringed by the Package. If you institute patent litigation
|
||||
(including a cross-claim or counterclaim) against any party alleging
|
||||
that the Package constitutes direct or contributory patent
|
||||
infringement, then this Artistic License to you shall terminate on the
|
||||
date that such litigation is filed.
|
||||
|
||||
(14) Disclaimer of Warranty:
|
||||
THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
|
||||
IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
|
||||
NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
|
||||
LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
8
gogs.go
8
gogs.go
|
@ -15,12 +15,12 @@ import (
|
|||
"github.com/gogits/gogs/modules/base"
|
||||
)
|
||||
|
||||
// +build go1.1
|
||||
// +build go1.2
|
||||
|
||||
// Test that go1.1 tag above is included in builds. main.go refers to this definition.
|
||||
const go11tag = true
|
||||
// Test that go1.2 tag above is included in builds. main.go refers to this definition.
|
||||
const go12tag = true
|
||||
|
||||
const APP_VER = "0.1.1.0320.1"
|
||||
const APP_VER = "0.1.5.0321"
|
||||
|
||||
func init() {
|
||||
base.AppVer = APP_VER
|
||||
|
|
|
@ -64,6 +64,10 @@ func CommitRepoAction(userId int64, userName string,
|
|||
watches = append(watches, Watch{UserId: userId})
|
||||
|
||||
for i := range watches {
|
||||
if userId == watches[i].UserId && i > 0 {
|
||||
continue // Do not add twice in case author watches his/her repository.
|
||||
}
|
||||
|
||||
_, err = orm.InsertOne(&Action{
|
||||
UserId: watches[i].UserId,
|
||||
ActUserId: userId,
|
||||
|
|
19
models/issue.go
Normal file
19
models/issue.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// 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 models
|
||||
|
||||
type Issue struct {
|
||||
Id int64
|
||||
RepoId int64 `xorm:"index"`
|
||||
PosterId int64
|
||||
}
|
||||
|
||||
type PullRequest struct {
|
||||
Id int64
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
Id int64
|
||||
}
|
|
@ -7,6 +7,7 @@ package models
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/lib/pq"
|
||||
|
@ -17,47 +18,36 @@ import (
|
|||
|
||||
var (
|
||||
orm *xorm.Engine
|
||||
RepoRootPath string
|
||||
|
||||
DbCfg struct {
|
||||
Type, Host, Name, User, Pwd, Path, SslMode string
|
||||
}
|
||||
)
|
||||
|
||||
type Members struct {
|
||||
Id int64
|
||||
OrgId int64 `xorm:"unique(s) index"`
|
||||
UserId int64 `xorm:"unique(s)"`
|
||||
}
|
||||
|
||||
type Issue struct {
|
||||
Id int64
|
||||
RepoId int64 `xorm:"index"`
|
||||
PosterId int64
|
||||
}
|
||||
|
||||
type PullRequest struct {
|
||||
Id int64
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
Id int64
|
||||
func LoadModelsConfig() {
|
||||
DbCfg.Type = base.Cfg.MustValue("database", "DB_TYPE")
|
||||
DbCfg.Host = base.Cfg.MustValue("database", "HOST")
|
||||
DbCfg.Name = base.Cfg.MustValue("database", "NAME")
|
||||
DbCfg.User = base.Cfg.MustValue("database", "USER")
|
||||
DbCfg.Pwd = base.Cfg.MustValue("database", "PASSWD")
|
||||
DbCfg.SslMode = base.Cfg.MustValue("database", "SSL_MODE")
|
||||
DbCfg.Path = base.Cfg.MustValue("database", "PATH", "data/gogs.db")
|
||||
}
|
||||
|
||||
func setEngine() {
|
||||
dbType := base.Cfg.MustValue("database", "DB_TYPE")
|
||||
dbHost := base.Cfg.MustValue("database", "HOST")
|
||||
dbName := base.Cfg.MustValue("database", "NAME")
|
||||
dbUser := base.Cfg.MustValue("database", "USER")
|
||||
dbPwd := base.Cfg.MustValue("database", "PASSWD")
|
||||
sslMode := base.Cfg.MustValue("database", "SSL_MODE")
|
||||
|
||||
var err error
|
||||
switch dbType {
|
||||
switch DbCfg.Type {
|
||||
case "mysql":
|
||||
orm, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@%s/%s?charset=utf8",
|
||||
dbUser, dbPwd, dbHost, dbName))
|
||||
DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name))
|
||||
case "postgres":
|
||||
orm, err = xorm.NewEngine("postgres", fmt.Sprintf("user=%s password=%s dbname=%s sslmode=%s",
|
||||
dbUser, dbPwd, dbName, sslMode))
|
||||
DbCfg.User, DbCfg.Pwd, DbCfg.Name, DbCfg.SslMode))
|
||||
case "sqlite3":
|
||||
os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm)
|
||||
orm, err = xorm.NewEngine("sqlite3", DbCfg.Path)
|
||||
default:
|
||||
fmt.Printf("Unknown database type: %s\n", dbType)
|
||||
fmt.Printf("Unknown database type: %s\n", DbCfg.Type)
|
||||
os.Exit(2)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -65,8 +55,8 @@ func setEngine() {
|
|||
os.Exit(2)
|
||||
}
|
||||
|
||||
// TODO: for serv command, MUST remove the output to os.stdout, so
|
||||
// use log file to instead print to stdout
|
||||
// WARNNING: for serv command, MUST remove the output to os.stdout,
|
||||
// so use log file to instead print to stdout.
|
||||
|
||||
//x.ShowDebug = true
|
||||
//orm.ShowErr = true
|
||||
|
@ -77,20 +67,29 @@ func setEngine() {
|
|||
}
|
||||
orm.Logger = f
|
||||
orm.ShowSQL = true
|
||||
|
||||
// Determine and create root git reposiroty path.
|
||||
RepoRootPath = base.Cfg.MustValue("repository", "ROOT")
|
||||
if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil {
|
||||
fmt.Printf("models.init(fail to create RepoRootPath(%s)): %v\n", RepoRootPath, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
func NewEngine() {
|
||||
setEngine()
|
||||
if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Access),
|
||||
new(Action), new(Watch)); err != nil {
|
||||
if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch),
|
||||
new(Action), new(Access)); err != nil {
|
||||
fmt.Printf("sync database struct error: %v\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
type Statistic struct {
|
||||
Counter struct {
|
||||
User, PublicKey, Repo, Watch, Action, Access int64
|
||||
}
|
||||
}
|
||||
|
||||
func GetStatistic() (stats Statistic) {
|
||||
stats.Counter.User, _ = orm.Count(new(User))
|
||||
stats.Counter.PublicKey, _ = orm.Count(new(PublicKey))
|
||||
stats.Counter.Repo, _ = orm.Count(new(Repository))
|
||||
stats.Counter.Watch, _ = orm.Count(new(Watch))
|
||||
stats.Counter.Action, _ = orm.Count(new(Action))
|
||||
stats.Counter.Access, _ = orm.Count(new(Access))
|
||||
return stats
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ import (
|
|||
)
|
||||
|
||||
func init() {
|
||||
LoadModelsConfig()
|
||||
NewEngine()
|
||||
|
||||
var err error
|
||||
orm, err = xorm.NewEngine("sqlite3", "./test.db")
|
||||
if err != nil {
|
||||
|
|
|
@ -27,8 +27,12 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
sshOpLocker = sync.Mutex{}
|
||||
ErrKeyAlreadyExist = errors.New("Public key already exist")
|
||||
)
|
||||
|
||||
var sshOpLocker = sync.Mutex{}
|
||||
|
||||
var (
|
||||
sshPath string
|
||||
appPath string
|
||||
)
|
||||
|
@ -79,10 +83,6 @@ type PublicKey struct {
|
|||
Updated time.Time `xorm:"updated"`
|
||||
}
|
||||
|
||||
var (
|
||||
ErrKeyAlreadyExist = errors.New("Public key already exist")
|
||||
)
|
||||
|
||||
// GenAuthorizedKey returns formatted public key string.
|
||||
func GenAuthorizedKey(keyId int64, key string) string {
|
||||
return fmt.Sprintf(TPL_PUBLICK_KEY+"\n", appPath, keyId, key)
|
||||
|
|
276
models/repo.go
276
models/repo.go
|
@ -12,6 +12,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -26,68 +27,26 @@ import (
|
|||
"github.com/gogits/gogs/modules/log"
|
||||
)
|
||||
|
||||
// Repository represents a git repository.
|
||||
type Repository struct {
|
||||
Id int64
|
||||
OwnerId int64 `xorm:"unique(s)"`
|
||||
ForkId int64
|
||||
LowerName string `xorm:"unique(s) index not null"`
|
||||
Name string `xorm:"index not null"`
|
||||
Description string
|
||||
Website string
|
||||
Private bool
|
||||
NumWatchs int
|
||||
NumStars int
|
||||
NumForks int
|
||||
Created time.Time `xorm:"created"`
|
||||
Updated time.Time `xorm:"updated"`
|
||||
}
|
||||
|
||||
// Watch is connection request for receiving repository notifycation.
|
||||
type Watch struct {
|
||||
Id int64
|
||||
RepoId int64 `xorm:"UNIQUE(watch)"`
|
||||
UserId int64 `xorm:"UNIQUE(watch)"`
|
||||
}
|
||||
|
||||
// Watch or unwatch repository.
|
||||
func WatchRepo(userId, repoId int64, watch bool) (err error) {
|
||||
if watch {
|
||||
_, err = orm.Insert(&Watch{RepoId: repoId, UserId: userId})
|
||||
} else {
|
||||
_, err = orm.Delete(&Watch{0, repoId, userId})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GetWatches returns all watches of given repository.
|
||||
func GetWatches(repoId int64) ([]Watch, error) {
|
||||
watches := make([]Watch, 0, 10)
|
||||
err := orm.Find(&watches, &Watch{RepoId: repoId})
|
||||
return watches, err
|
||||
}
|
||||
|
||||
// IsWatching checks if user has watched given repository.
|
||||
func IsWatching(userId, repoId int64) bool {
|
||||
has, _ := orm.Get(&Watch{0, repoId, userId})
|
||||
return has
|
||||
}
|
||||
|
||||
var (
|
||||
gitInitLocker = sync.Mutex{}
|
||||
LanguageIgns, Licenses []string
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRepoAlreadyExist = errors.New("Repository already exist")
|
||||
ErrRepoNotExist = errors.New("Repository does not exist")
|
||||
ErrRepoFileNotExist = errors.New("Target Repo file does not exist")
|
||||
ErrRepoNameIllegal = errors.New("Repository name contains illegal characters")
|
||||
ErrRepoFileNotLoaded = fmt.Errorf("repo file not loaded")
|
||||
)
|
||||
|
||||
func init() {
|
||||
var gitInitLocker = sync.Mutex{}
|
||||
|
||||
var (
|
||||
LanguageIgns, Licenses []string
|
||||
)
|
||||
|
||||
func LoadRepoConfig() {
|
||||
LanguageIgns = strings.Split(base.Cfg.MustValue("repository", "LANG_IGNS"), "|")
|
||||
Licenses = strings.Split(base.Cfg.MustValue("repository", "LICENSES"), "|")
|
||||
}
|
||||
|
||||
func NewRepoContext() {
|
||||
zip.Verbose = false
|
||||
|
||||
// Check if server has basic git setting.
|
||||
|
@ -104,6 +63,32 @@ func init() {
|
|||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize illegal patterns.
|
||||
for i := range illegalPatterns[1:] {
|
||||
pattern := ""
|
||||
for j := range illegalPatterns[i+1] {
|
||||
pattern += "[" + string(illegalPatterns[i+1][j]-32) + string(illegalPatterns[i+1][j]) + "]"
|
||||
}
|
||||
illegalPatterns[i+1] = pattern
|
||||
}
|
||||
}
|
||||
|
||||
// Repository represents a git repository.
|
||||
type Repository struct {
|
||||
Id int64
|
||||
OwnerId int64 `xorm:"unique(s)"`
|
||||
ForkId int64
|
||||
LowerName string `xorm:"unique(s) index not null"`
|
||||
Name string `xorm:"index not null"`
|
||||
Description string
|
||||
Website string
|
||||
Private bool
|
||||
NumWatches int
|
||||
NumStars int
|
||||
NumForks int
|
||||
Created time.Time `xorm:"created"`
|
||||
Updated time.Time `xorm:"updated"`
|
||||
}
|
||||
|
||||
// IsRepositoryExist returns true if the repository with given name under user has already existed.
|
||||
|
@ -120,8 +105,28 @@ func IsRepositoryExist(user *User, repoName string) (bool, error) {
|
|||
return s.IsDir(), nil
|
||||
}
|
||||
|
||||
var (
|
||||
// Define as all lower case!!
|
||||
illegalPatterns = []string{"[.][Gg][Ii][Tt]", "user", "help", "stars", "issues", "pulls", "commits", "admin", "repo", "template", "admin"}
|
||||
)
|
||||
|
||||
// IsLegalName returns false if name contains illegal characters.
|
||||
func IsLegalName(repoName string) bool {
|
||||
for _, pattern := range illegalPatterns {
|
||||
has, _ := regexp.MatchString(pattern, repoName)
|
||||
if has {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateRepository creates a repository for given user or orgnaziation.
|
||||
func CreateRepository(user *User, repoName, desc, repoLang, license string, private bool, initReadme bool) (*Repository, error) {
|
||||
if !IsLegalName(repoName) {
|
||||
return nil, ErrRepoNameIllegal
|
||||
}
|
||||
|
||||
isExist, err := IsRepositoryExist(user, repoName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -331,6 +336,82 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep
|
|||
return nil
|
||||
}
|
||||
|
||||
// UserRepo reporesents a repository with user name.
|
||||
type UserRepo struct {
|
||||
*Repository
|
||||
UserName string
|
||||
}
|
||||
|
||||
// GetRepos returns given number of repository objects with offset.
|
||||
func GetRepos(num, offset int) ([]UserRepo, error) {
|
||||
repos := make([]Repository, 0, num)
|
||||
if err := orm.Limit(num, offset).Asc("id").Find(&repos); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
urepos := make([]UserRepo, len(repos))
|
||||
for i := range repos {
|
||||
urepos[i].Repository = &repos[i]
|
||||
u := new(User)
|
||||
has, err := orm.Id(urepos[i].Repository.OwnerId).Get(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrUserNotExist
|
||||
}
|
||||
urepos[i].UserName = u.Name
|
||||
}
|
||||
|
||||
return urepos, nil
|
||||
}
|
||||
|
||||
func RepoPath(userName, repoName string) string {
|
||||
return filepath.Join(UserPath(userName), repoName+".git")
|
||||
}
|
||||
|
||||
// DeleteRepository deletes a repository for a user or orgnaztion.
|
||||
func DeleteRepository(userId, repoId int64, userName string) (err error) {
|
||||
repo := &Repository{Id: repoId, OwnerId: userId}
|
||||
has, err := orm.Get(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrRepoNotExist
|
||||
}
|
||||
|
||||
session := orm.NewSession()
|
||||
if err = session.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = session.Delete(&Repository{Id: repoId}); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
if _, err := session.Delete(&Access{UserName: userName, RepoName: repo.Name}); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
rawSql := "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?"
|
||||
if _, err = session.Exec(rawSql, userId); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
if _, err = session.Delete(&Watch{RepoId: repoId}); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
if err = session.Commit(); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
|
||||
// TODO: log and delete manully
|
||||
log.Error("delete repo %s/%s failed: %v", userName, repo.Name, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRepositoryByName returns the repository by given name under user if exists.
|
||||
func GetRepositoryByName(user *User, repoName string) (*Repository, error) {
|
||||
repo := &Repository{
|
||||
|
@ -368,6 +449,45 @@ func GetRepositoryCount(user *User) (int64, error) {
|
|||
return orm.Count(&Repository{OwnerId: user.Id})
|
||||
}
|
||||
|
||||
// Watch is connection request for receiving repository notifycation.
|
||||
type Watch struct {
|
||||
Id int64
|
||||
RepoId int64 `xorm:"UNIQUE(watch)"`
|
||||
UserId int64 `xorm:"UNIQUE(watch)"`
|
||||
}
|
||||
|
||||
// Watch or unwatch repository.
|
||||
func WatchRepo(userId, repoId int64, watch bool) (err error) {
|
||||
if watch {
|
||||
if _, err = orm.Insert(&Watch{RepoId: repoId, UserId: userId}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawSql := "UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?"
|
||||
_, err = orm.Exec(rawSql, repoId)
|
||||
} else {
|
||||
if _, err = orm.Delete(&Watch{0, repoId, userId}); err != nil {
|
||||
return err
|
||||
}
|
||||
rawSql := "UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?"
|
||||
_, err = orm.Exec(rawSql, repoId)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GetWatches returns all watches of given repository.
|
||||
func GetWatches(repoId int64) ([]Watch, error) {
|
||||
watches := make([]Watch, 0, 10)
|
||||
err := orm.Find(&watches, &Watch{RepoId: repoId})
|
||||
return watches, err
|
||||
}
|
||||
|
||||
// IsWatching checks if user has watched given repository.
|
||||
func IsWatching(userId, repoId int64) bool {
|
||||
has, _ := orm.Get(&Watch{0, repoId, userId})
|
||||
return has
|
||||
}
|
||||
|
||||
func StarReposiory(user *User, repoName string) error {
|
||||
return nil
|
||||
}
|
||||
|
@ -388,56 +508,6 @@ func ForkRepository(reposName string, userId int64) {
|
|||
|
||||
}
|
||||
|
||||
func RepoPath(userName, repoName string) string {
|
||||
return filepath.Join(UserPath(userName), repoName+".git")
|
||||
}
|
||||
|
||||
// DeleteRepository deletes a repository for a user or orgnaztion.
|
||||
func DeleteRepository(userId, repoId int64, userName string) (err error) {
|
||||
repo := &Repository{Id: repoId, OwnerId: userId}
|
||||
has, err := orm.Get(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrRepoNotExist
|
||||
}
|
||||
|
||||
session := orm.NewSession()
|
||||
if err = session.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = session.Delete(&Repository{Id: repoId}); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
if _, err := session.Delete(&Access{UserName: userName, RepoName: repo.Name}); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
rawSql := "UPDATE user SET num_repos = num_repos - 1 WHERE id = ?"
|
||||
if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" {
|
||||
rawSql = "UPDATE \"user\" SET num_repos = num_repos - 1 WHERE id = ?"
|
||||
}
|
||||
if _, err = session.Exec(rawSql, userId); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
if err = session.Commit(); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
|
||||
// TODO: log and delete manully
|
||||
log.Error("delete repo %s/%s failed: %v", userName, repo.Name, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrRepoFileNotLoaded = fmt.Errorf("repo file not loaded")
|
||||
)
|
||||
|
||||
// RepoFile represents a file object in git repository.
|
||||
type RepoFile struct {
|
||||
*git.TreeEntry
|
||||
|
|
105
models/user.go
105
models/user.go
|
@ -33,6 +33,14 @@ const (
|
|||
LT_LDAP
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUserOwnRepos = errors.New("User still have ownership of repositories")
|
||||
ErrUserAlreadyExist = errors.New("User already exist")
|
||||
ErrUserNotExist = errors.New("User does not exist")
|
||||
ErrEmailAlreadyUsed = errors.New("E-mail already used")
|
||||
ErrUserNameIllegal = errors.New("User name contains illegal characters")
|
||||
)
|
||||
|
||||
// User represents the object of individual and member of organization.
|
||||
type User struct {
|
||||
Id int64
|
||||
|
@ -51,6 +59,7 @@ type User struct {
|
|||
Location string
|
||||
Website string
|
||||
IsActive bool
|
||||
IsAdmin bool
|
||||
Rands string `xorm:"VARCHAR(10)"`
|
||||
Created time.Time `xorm:"created"`
|
||||
Updated time.Time `xorm:"updated"`
|
||||
|
@ -66,19 +75,28 @@ func (user *User) AvatarLink() string {
|
|||
return "http://1.gravatar.com/avatar/" + user.Avatar
|
||||
}
|
||||
|
||||
type Follow struct {
|
||||
Id int64
|
||||
UserId int64 `xorm:"unique(s)"`
|
||||
FollowId int64 `xorm:"unique(s)"`
|
||||
Created time.Time `xorm:"created"`
|
||||
// NewGitSig generates and returns the signature of given user.
|
||||
func (user *User) NewGitSig() *git.Signature {
|
||||
return &git.Signature{
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
When: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
ErrUserOwnRepos = errors.New("User still have ownership of repositories")
|
||||
ErrUserAlreadyExist = errors.New("User already exist")
|
||||
ErrUserNotExist = errors.New("User does not exist")
|
||||
ErrEmailAlreadyUsed = errors.New("E-mail already used")
|
||||
)
|
||||
// EncodePasswd encodes password to safe format.
|
||||
func (user *User) EncodePasswd() error {
|
||||
newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(base.SecretKey), 16384, 8, 1, 64)
|
||||
user.Passwd = fmt.Sprintf("%x", newPasswd)
|
||||
return err
|
||||
}
|
||||
|
||||
// Member represents user is member of organization.
|
||||
type Member struct {
|
||||
Id int64
|
||||
OrgId int64 `xorm:"unique(member) index"`
|
||||
UserId int64 `xorm:"unique(member)"`
|
||||
}
|
||||
|
||||
// IsUserExist checks if given user name exist,
|
||||
// the user name should be noncased unique.
|
||||
|
@ -91,15 +109,6 @@ func IsEmailUsed(email string) (bool, error) {
|
|||
return orm.Get(&User{Email: email})
|
||||
}
|
||||
|
||||
// NewGitSig generates and returns the signature of given user.
|
||||
func (user *User) NewGitSig() *git.Signature {
|
||||
return &git.Signature{
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
When: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// return a user salt token
|
||||
func GetUserSalt() string {
|
||||
return base.GetRandomString(10)
|
||||
|
@ -107,6 +116,10 @@ func GetUserSalt() string {
|
|||
|
||||
// RegisterUser creates record of a new user.
|
||||
func RegisterUser(user *User) (*User, error) {
|
||||
if !IsLegalName(user.Name) {
|
||||
return nil, ErrUserNameIllegal
|
||||
}
|
||||
|
||||
isExist, err := IsUserExist(user.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -136,7 +149,20 @@ func RegisterUser(user *User) (*User, error) {
|
|||
}
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
|
||||
if user.Id == 1 {
|
||||
user.IsAdmin = true
|
||||
user.IsActive = true
|
||||
_, err = orm.Id(user.Id).UseBool().Update(user)
|
||||
}
|
||||
return user, err
|
||||
}
|
||||
|
||||
// GetUsers returns given number of user objects with offset.
|
||||
func GetUsers(num, offset int) ([]User, error) {
|
||||
users := make([]User, 0, num)
|
||||
err := orm.Limit(num, offset).Asc("id").Find(&users)
|
||||
return users, err
|
||||
}
|
||||
|
||||
// get user by erify code
|
||||
|
@ -217,22 +243,14 @@ func DeleteUser(user *User) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// EncodePasswd encodes password to safe format.
|
||||
func (user *User) EncodePasswd() error {
|
||||
newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(base.SecretKey), 16384, 8, 1, 64)
|
||||
user.Passwd = fmt.Sprintf("%x", newPasswd)
|
||||
return err
|
||||
}
|
||||
|
||||
// UserPath returns the path absolute path of user repositories.
|
||||
func UserPath(userName string) string {
|
||||
return filepath.Join(RepoRootPath, strings.ToLower(userName))
|
||||
return filepath.Join(base.RepoRootPath, strings.ToLower(userName))
|
||||
}
|
||||
|
||||
func GetUserByKeyId(keyId int64) (*User, error) {
|
||||
user := new(User)
|
||||
rawSql := "SELECT a.* FROM `user` AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?"
|
||||
|
||||
has, err := orm.Sql(rawSql, keyId).Get(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -289,6 +307,13 @@ func LoginUserPlain(name, passwd string) (*User, error) {
|
|||
return &user, err
|
||||
}
|
||||
|
||||
// Follow is connection request for receiving user notifycation.
|
||||
type Follow struct {
|
||||
Id int64
|
||||
UserId int64 `xorm:"unique(follow)"`
|
||||
FollowId int64 `xorm:"unique(follow)"`
|
||||
}
|
||||
|
||||
// FollowUser marks someone be another's follower.
|
||||
func FollowUser(userId int64, followId int64) (err error) {
|
||||
session := orm.NewSession()
|
||||
|
@ -300,19 +325,13 @@ func FollowUser(userId int64, followId int64) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
rawSql := "UPDATE user SET num_followers = num_followers + 1 WHERE id = ?"
|
||||
if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" {
|
||||
rawSql = "UPDATE \"user\" SET num_followers = num_followers + 1 WHERE id = ?"
|
||||
}
|
||||
rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?"
|
||||
if _, err = session.Exec(rawSql, followId); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
rawSql = "UPDATE user SET num_followings = num_followings + 1 WHERE id = ?"
|
||||
if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" {
|
||||
rawSql = "UPDATE \"user\" SET num_followings = num_followings + 1 WHERE id = ?"
|
||||
}
|
||||
rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?"
|
||||
if _, err = session.Exec(rawSql, userId); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
|
@ -331,19 +350,13 @@ func UnFollowUser(userId int64, unFollowId int64) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
rawSql := "UPDATE user SET num_followers = num_followers - 1 WHERE id = ?"
|
||||
if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" {
|
||||
rawSql = "UPDATE \"user\" SET num_followers = num_followers - 1 WHERE id = ?"
|
||||
}
|
||||
rawSql := "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?"
|
||||
if _, err = session.Exec(rawSql, unFollowId); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
rawSql = "UPDATE user SET num_followings = num_followings - 1 WHERE id = ?"
|
||||
if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" {
|
||||
rawSql = "UPDATE \"user\" SET num_followings = num_followings - 1 WHERE id = ?"
|
||||
}
|
||||
rawSql = "UPDATE `user` SET num_followings = num_followings - 1 WHERE id = ?"
|
||||
if _, err = session.Exec(rawSql, userId); err != nil {
|
||||
session.Rollback()
|
||||
return err
|
||||
|
|
55
modules/auth/admin.go
Normal file
55
modules/auth/admin.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
// 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 auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/codegangsta/martini"
|
||||
|
||||
"github.com/gogits/binding"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
)
|
||||
|
||||
type AdminEditUserForm struct {
|
||||
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
|
||||
Website string `form:"website" binding:"MaxSize(50)"`
|
||||
Location string `form:"location" binding:"MaxSize(50)"`
|
||||
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
|
||||
Active string `form:"active"`
|
||||
Admin string `form:"admin"`
|
||||
}
|
||||
|
||||
func (f *AdminEditUserForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"Email": "E-mail address",
|
||||
"Website": "Website",
|
||||
"Location": "Location",
|
||||
"Avatar": "Gravatar Email",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *AdminEditUserForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
if req.Method == "GET" || errors.Count() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
data["HasError"] = true
|
||||
AssignForm(f, data)
|
||||
|
||||
if len(errors.Overall) > 0 {
|
||||
for _, err := range errors.Overall {
|
||||
log.Error("AdminEditUserForm.Validate: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
validate(errors, data, f)
|
||||
}
|
|
@ -79,7 +79,7 @@ type UpdateProfileForm struct {
|
|||
|
||||
func (f *UpdateProfileForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"Email": "Email address",
|
||||
"Email": "E-mail address",
|
||||
"Website": "Website",
|
||||
"Location": "Location",
|
||||
"Avatar": "Gravatar Email",
|
||||
|
|
|
@ -15,6 +15,8 @@ import (
|
|||
"github.com/Unknwon/com"
|
||||
"github.com/Unknwon/goconfig"
|
||||
|
||||
"github.com/gogits/cache"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
)
|
||||
|
||||
|
@ -32,12 +34,24 @@ var (
|
|||
AppUrl string
|
||||
Domain string
|
||||
SecretKey string
|
||||
RunUser string
|
||||
RepoRootPath string
|
||||
|
||||
Cfg *goconfig.ConfigFile
|
||||
MailService *Mailer
|
||||
|
||||
Cache cache.Cache
|
||||
CacheAdapter string
|
||||
CacheConfig string
|
||||
|
||||
LogMode string
|
||||
LogConfig string
|
||||
)
|
||||
|
||||
var Service struct {
|
||||
RegisterEmailConfirm bool
|
||||
DisenableRegisteration bool
|
||||
RequireSignInView bool
|
||||
ActiveCodeLives int
|
||||
ResetPwdCodeLives int
|
||||
}
|
||||
|
@ -66,19 +80,21 @@ var logLevels = map[string]string{
|
|||
func newService() {
|
||||
Service.ActiveCodeLives = Cfg.MustInt("service", "ACTIVE_CODE_LIVE_MINUTES", 180)
|
||||
Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180)
|
||||
Service.DisenableRegisteration = Cfg.MustBool("service", "DISENABLE_REGISTERATION", false)
|
||||
Service.RequireSignInView = Cfg.MustBool("service", "REQUIRE_SIGNIN_VIEW", false)
|
||||
}
|
||||
|
||||
func newLogService() {
|
||||
// Get and check log mode.
|
||||
mode := Cfg.MustValue("log", "MODE", "console")
|
||||
modeSec := "log." + mode
|
||||
LogMode = Cfg.MustValue("log", "MODE", "console")
|
||||
modeSec := "log." + LogMode
|
||||
if _, err := Cfg.GetSection(modeSec); err != nil {
|
||||
fmt.Printf("Unknown log mode: %s\n", mode)
|
||||
fmt.Printf("Unknown log mode: %s\n", LogMode)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// Log level.
|
||||
levelName := Cfg.MustValue("log."+mode, "LEVEL", "Trace")
|
||||
levelName := Cfg.MustValue("log."+LogMode, "LEVEL", "Trace")
|
||||
level, ok := logLevels[levelName]
|
||||
if !ok {
|
||||
fmt.Printf("Unknown log level: %s\n", levelName)
|
||||
|
@ -86,14 +102,13 @@ func newLogService() {
|
|||
}
|
||||
|
||||
// Generate log configuration.
|
||||
var config string
|
||||
switch mode {
|
||||
switch LogMode {
|
||||
case "console":
|
||||
config = fmt.Sprintf(`{"level":%s}`, level)
|
||||
LogConfig = fmt.Sprintf(`{"level":%s}`, level)
|
||||
case "file":
|
||||
logPath := Cfg.MustValue(modeSec, "FILE_NAME", "log/gogs.log")
|
||||
os.MkdirAll(path.Dir(logPath), os.ModePerm)
|
||||
config = fmt.Sprintf(
|
||||
LogConfig = fmt.Sprintf(
|
||||
`{"level":%s,"filename":%s,"rotate":%v,"maxlines":%d,"maxsize",%d,"daily":%v,"maxdays":%d}`, level,
|
||||
logPath,
|
||||
Cfg.MustBool(modeSec, "LOG_ROTATE", true),
|
||||
|
@ -102,13 +117,13 @@ func newLogService() {
|
|||
Cfg.MustBool(modeSec, "DAILY_ROTATE", true),
|
||||
Cfg.MustInt(modeSec, "MAX_DAYS", 7))
|
||||
case "conn":
|
||||
config = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":%s,"addr":%s}`, level,
|
||||
LogConfig = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":%s,"addr":%s}`, level,
|
||||
Cfg.MustBool(modeSec, "RECONNECT_ON_MSG", false),
|
||||
Cfg.MustBool(modeSec, "RECONNECT", false),
|
||||
Cfg.MustValue(modeSec, "PROTOCOL", "tcp"),
|
||||
Cfg.MustValue(modeSec, "ADDR", ":7020"))
|
||||
case "smtp":
|
||||
config = fmt.Sprintf(`{"level":%s,"username":%s,"password":%s,"host":%s,"sendTos":%s,"subject":%s}`, level,
|
||||
LogConfig = fmt.Sprintf(`{"level":%s,"username":%s,"password":%s,"host":%s,"sendTos":%s,"subject":%s}`, level,
|
||||
Cfg.MustValue(modeSec, "USER", "example@example.com"),
|
||||
Cfg.MustValue(modeSec, "PASSWD", "******"),
|
||||
Cfg.MustValue(modeSec, "HOST", "127.0.0.1:25"),
|
||||
|
@ -116,8 +131,32 @@ func newLogService() {
|
|||
Cfg.MustValue(modeSec, "SUBJECT", "Diagnostic message from serve"))
|
||||
}
|
||||
|
||||
log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), mode, config)
|
||||
log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName)
|
||||
log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), LogMode, LogConfig)
|
||||
log.Info("Log Mode: %s(%s)", strings.Title(LogMode), levelName)
|
||||
}
|
||||
|
||||
func newCacheService() {
|
||||
CacheAdapter = Cfg.MustValue("cache", "ADAPTER", "memory")
|
||||
|
||||
switch CacheAdapter {
|
||||
case "memory":
|
||||
CacheConfig = fmt.Sprintf(`{"interval":%d}`, Cfg.MustInt("cache", "INTERVAL", 60))
|
||||
case "redis", "memcache":
|
||||
CacheConfig = fmt.Sprintf(`{"conn":"%s"}`, Cfg.MustValue("cache", "HOST"))
|
||||
default:
|
||||
fmt.Printf("Unknown cache adapter: %s\n", CacheAdapter)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
var err error
|
||||
Cache, err = cache.NewCache(CacheAdapter, CacheConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Init cache system failed, adapter: %s, config: %s, %v\n",
|
||||
CacheAdapter, CacheConfig, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
log.Info("Cache Service Enabled")
|
||||
}
|
||||
|
||||
func newMailService() {
|
||||
|
@ -144,7 +183,7 @@ func newRegisterMailService() {
|
|||
log.Info("Register Mail Service Enabled")
|
||||
}
|
||||
|
||||
func init() {
|
||||
func NewConfigContext() {
|
||||
var err error
|
||||
workDir, err := exeDir()
|
||||
if err != nil {
|
||||
|
@ -173,11 +212,20 @@ func init() {
|
|||
AppUrl = Cfg.MustValue("server", "ROOT_URL")
|
||||
Domain = Cfg.MustValue("server", "DOMAIN")
|
||||
SecretKey = Cfg.MustValue("security", "SECRET_KEY")
|
||||
RunUser = Cfg.MustValue("", "RUN_USER")
|
||||
|
||||
// Determine and create root git reposiroty path.
|
||||
RepoRootPath = Cfg.MustValue("repository", "ROOT")
|
||||
if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil {
|
||||
fmt.Printf("models.init(fail to create RepoRootPath(%s)): %v\n", RepoRootPath, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func NewServices() {
|
||||
newService()
|
||||
newLogService()
|
||||
newCacheService()
|
||||
newMailService()
|
||||
newRegisterMailService()
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func isLink(link []byte) bool {
|
|||
func IsMarkdownFile(name string) bool {
|
||||
name = strings.ToLower(name)
|
||||
switch filepath.Ext(name) {
|
||||
case "md", "markdown":
|
||||
case ".md", ".markdown", ".mdown":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -61,7 +61,7 @@ type CustomRender struct {
|
|||
func (options *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
|
||||
if len(link) > 0 && !isLink(link) {
|
||||
if link[0] == '#' {
|
||||
link = append([]byte(options.urlPrefix), link...)
|
||||
// link = append([]byte(options.urlPrefix), link...)
|
||||
} else {
|
||||
link = []byte(path.Join(options.urlPrefix, string(link)))
|
||||
}
|
||||
|
|
|
@ -33,6 +33,10 @@ func List(l *list.List) chan interface{} {
|
|||
return c
|
||||
}
|
||||
|
||||
var mailDomains = map[string]string{
|
||||
"gmail.com": "gmail.com",
|
||||
}
|
||||
|
||||
var TemplateFuncs template.FuncMap = map[string]interface{}{
|
||||
"AppName": func() string {
|
||||
return AppName
|
||||
|
@ -56,7 +60,12 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
|
|||
"DateFormat": DateFormat,
|
||||
"List": List,
|
||||
"Mail2Domain": func(mail string) string {
|
||||
return "mail." + strings.Split(mail, "@")[1]
|
||||
suffix := strings.SplitN(mail, "@", 2)[1]
|
||||
domain, ok := mailDomains[suffix]
|
||||
if !ok {
|
||||
return "mail." + suffix
|
||||
}
|
||||
return domain
|
||||
},
|
||||
"SubStr": func(str string, start, length int) string {
|
||||
return str[start : start+length]
|
||||
|
|
|
@ -40,7 +40,7 @@ func (m Message) Content() string {
|
|||
|
||||
var mailQueue chan *Message
|
||||
|
||||
func init() {
|
||||
func NewMailerContext() {
|
||||
mailQueue = make(chan *Message, base.Cfg.MustInt("mailer", "SEND_BUFFER_LEN", 10))
|
||||
go processMailQueue()
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@ func SignInRequire(redirect bool) martini.Handler {
|
|||
return func(ctx *Context) {
|
||||
if !ctx.IsSigned {
|
||||
if redirect {
|
||||
ctx.Redirect("/")
|
||||
ctx.Redirect("/user/login")
|
||||
}
|
||||
return
|
||||
} else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm {
|
||||
ctx.Data["Title"] = "Activate Your Account"
|
||||
ctx.Render.HTML(200, "user/active", ctx.Data)
|
||||
ctx.HTML(200, "user/active")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,18 @@ func SignOutRequire() martini.Handler {
|
|||
return func(ctx *Context) {
|
||||
if ctx.IsSigned {
|
||||
ctx.Redirect("/")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AdminRequire requires user signed in as administor.
|
||||
func AdminRequire() martini.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.User.IsAdmin {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
ctx.Data["PageIsAdmin"] = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,11 @@ import (
|
|||
"github.com/codegangsta/martini"
|
||||
"github.com/martini-contrib/sessions"
|
||||
|
||||
"github.com/gogits/cache"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/auth"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
)
|
||||
|
||||
|
@ -25,6 +28,7 @@ type Context struct {
|
|||
Req *http.Request
|
||||
Res http.ResponseWriter
|
||||
Session sessions.Session
|
||||
Cache cache.Cache
|
||||
User *models.User
|
||||
IsSigned bool
|
||||
|
||||
|
@ -61,24 +65,29 @@ func (ctx *Context) HasError() bool {
|
|||
return hasErr.(bool)
|
||||
}
|
||||
|
||||
// HTML calls render.HTML underlying but reduce one argument.
|
||||
func (ctx *Context) HTML(status int, name string, htmlOpt ...HTMLOptions) {
|
||||
ctx.Render.HTML(status, name, ctx.Data, htmlOpt...)
|
||||
}
|
||||
|
||||
// RenderWithErr used for page has form validation but need to prompt error to users.
|
||||
func (ctx *Context) RenderWithErr(msg, tpl string, form auth.Form) {
|
||||
ctx.Data["HasError"] = true
|
||||
ctx.Data["ErrorMsg"] = msg
|
||||
auth.AssignForm(form, ctx.Data)
|
||||
ctx.HTML(200, tpl, ctx.Data)
|
||||
ctx.HTML(200, tpl)
|
||||
}
|
||||
|
||||
// Handle handles and logs error by given status.
|
||||
func (ctx *Context) Handle(status int, title string, err error) {
|
||||
log.Error("%s: %v", title, err)
|
||||
if martini.Dev == martini.Prod {
|
||||
ctx.HTML(500, "status/500", ctx.Data)
|
||||
ctx.HTML(500, "status/500")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["ErrorMsg"] = err
|
||||
ctx.HTML(status, fmt.Sprintf("status/%d", status), ctx.Data)
|
||||
ctx.HTML(status, fmt.Sprintf("status/%d", status))
|
||||
}
|
||||
|
||||
// InitContext initializes a classic context for a request.
|
||||
|
@ -92,6 +101,7 @@ func InitContext() martini.Handler {
|
|||
Req: r,
|
||||
Res: res,
|
||||
Session: session,
|
||||
Cache: base.Cache,
|
||||
Render: rd,
|
||||
}
|
||||
|
||||
|
@ -106,6 +116,7 @@ func InitContext() martini.Handler {
|
|||
ctx.Data["SignedUser"] = user
|
||||
ctx.Data["SignedUserId"] = user.Id
|
||||
ctx.Data["SignedUserName"] = user.LowerName
|
||||
ctx.Data["IsAdmin"] = ctx.User.IsAdmin
|
||||
}
|
||||
|
||||
ctx.Data["PageStartTime"] = time.Now()
|
||||
|
|
|
@ -70,6 +70,7 @@ func RepoAssignment(redirect bool) martini.Handler {
|
|||
}
|
||||
ctx.Repo.Repository = repo
|
||||
ctx.Repo.CloneLink.SSH = fmt.Sprintf("git@%s:%s/%s.git", base.Domain, user.LowerName, repo.LowerName)
|
||||
ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("https://%s/%s/%s.git", base.Domain, user.LowerName, repo.LowerName)
|
||||
|
||||
ctx.Data["IsRepositoryValid"] = true
|
||||
ctx.Data["Repository"] = repo
|
||||
|
@ -78,5 +79,6 @@ func RepoAssignment(redirect bool) martini.Handler {
|
|||
ctx.Data["CloneLink"] = ctx.Repo.CloneLink
|
||||
ctx.Data["RepositoryLink"] = ctx.Data["Title"]
|
||||
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner
|
||||
ctx.Data["IsRepositoryWatching"] = ctx.Repo.IsWatching
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,8 +145,9 @@ html, body {
|
|||
float: right;
|
||||
}
|
||||
|
||||
#gogs-nav-signin {
|
||||
#gogs-nav-signin, #gogs-nav-signup {
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
#gogs-nav-out .fa {
|
||||
|
@ -324,6 +325,27 @@ html, body {
|
|||
color: #444;
|
||||
}
|
||||
|
||||
.gogs-admin-nav {
|
||||
background-color: #FFF;
|
||||
padding-top: 10px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
border: 1px solid #D8D8D8;
|
||||
}
|
||||
|
||||
.gogs-admin-nav li {
|
||||
margin-bottom: 8px;
|
||||
border-left: 4px solid transparent;
|
||||
}
|
||||
|
||||
.gogs-admin-nav li:hover {
|
||||
border-left-color: #EEE;
|
||||
}
|
||||
|
||||
.gogs-admin-nav li.active:hover {
|
||||
border-left: 4px solid #DD4B39;
|
||||
}
|
||||
|
||||
/* gogits user ssh keys */
|
||||
|
||||
#gogs-ssh-keys .list-group-item {
|
||||
|
@ -376,7 +398,7 @@ html, body {
|
|||
}
|
||||
|
||||
#gogs-feed-right .repo-panel .list-group-item a {
|
||||
display: inline-block;
|
||||
display: block;
|
||||
margin-left: 0;
|
||||
background-color: transparent;
|
||||
padding-left: 0;
|
||||
|
@ -406,7 +428,6 @@ html, body {
|
|||
|
||||
#gogs-feed-right .repo-panel span.stars {
|
||||
color: #666;
|
||||
line-height: 44px;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
|
@ -445,7 +466,7 @@ html, body {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
#gogs-repo-watching .dropdown-menu .dropdown-item:hover .dropdown-header {
|
||||
#gogs-repo-watching .dropdown-menu .dropdown-item:hover .dropdown-header, #gogs-repo-watching .dropdown-item .dropdown-header.text-primary {
|
||||
color: rgb(65, 131, 196);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -501,8 +522,7 @@ html, body {
|
|||
}
|
||||
|
||||
.activity-list .info {
|
||||
float: left;
|
||||
padding: 0 0 0 10px;
|
||||
margin: 0 0 0 40px;
|
||||
line-height: 1.7em;
|
||||
}
|
||||
|
||||
|
@ -555,6 +575,15 @@ html, body {
|
|||
min-width: 200px;
|
||||
}
|
||||
|
||||
#gogs-repo-clone .dropdown-menu{
|
||||
width: 400px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#gogs-repo-clone .input-group{
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
/* #gogs-source */
|
||||
#gogs-source {
|
||||
margin-top: -20px;
|
||||
|
@ -645,6 +674,62 @@ html, body {
|
|||
|
||||
.file-content .file-body {
|
||||
padding: 30px 30px 50px;
|
||||
border: none;
|
||||
background-color: #FFF;
|
||||
overflow: auto;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.file-content .file-body.file-code pre {
|
||||
background-color: #FFF;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.file-content .file-body.file-code {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.file-content .file-body.file-code .lines-code > pre {
|
||||
border: none;
|
||||
background: none;
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.file-content .file-body.file-code .lines-code ol.linenums > .active {
|
||||
background: #ffffdd;
|
||||
}
|
||||
|
||||
.file-content .file-body.file-code .lines-num {
|
||||
text-align: right;
|
||||
color: #999;
|
||||
background: #fafafa;
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
.file-content .file-body.file-code .lines-num span {
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
line-height: 1.6;
|
||||
padding: 0 8px 0 10px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.file-content .file-body.file-code .lines-num span:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.file-content .file-body.file-code > table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file-content .file-body.file-code > table > tbody > tr,
|
||||
.file-content .file-body.file-code > table > tbody > tr > td,
|
||||
.file-content .file-body.file-code > table {
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.branch-list th, .commit-list th {
|
||||
|
@ -724,6 +809,28 @@ html, body {
|
|||
background-color: #FFF;
|
||||
}
|
||||
|
||||
.commit-list .date {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.commit-list .author {
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.guide-box pre, .guide-box .input-group {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 30px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.guide-box input[readonly] {
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
.guide-box {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* wrapper and footer */
|
||||
|
||||
#wrapper {
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
line-height: 1.7;
|
||||
padding: 15px 0 0;
|
||||
margin: 0 0 15px;
|
||||
color: #666;
|
||||
color: #444;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.markdown h1,
|
||||
|
@ -86,6 +87,10 @@
|
|||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.markdown li:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.markdown dl dt {
|
||||
font-style: italic;
|
||||
margin-top: 9px;
|
||||
|
@ -106,8 +111,8 @@
|
|||
line-height: 1.6;
|
||||
overflow: auto;
|
||||
background: #f8f8f8;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #ddd;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown > pre.linenums {
|
||||
|
@ -115,6 +120,17 @@
|
|||
}
|
||||
|
||||
.markdown > pre > ol.linenums {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown > pre > ol.linenums > li {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.markdown > pre.nums-style > ol.linenums {
|
||||
list-style-type: decimal;
|
||||
padding: 0 0 0 40px;
|
||||
-webkit-box-shadow: inset 40px 0 0 #f5f5f5, inset 41px 0 0 #ccc;
|
||||
box-shadow: inset 40px 0 0 #f5f5f5, inset 41px 0 0 #ccc;
|
||||
}
|
||||
|
@ -130,14 +146,14 @@
|
|||
}
|
||||
|
||||
.markdown > pre > ol.linenums > li:first-child {
|
||||
padding-top: 6px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.markdown > pre > ol.linenums > li:last-child {
|
||||
padding-bottom: 6px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.markdown > pre > ol.linenums > li {
|
||||
.markdown > pre.nums-style > ol.linenums > li {
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
|
@ -163,6 +179,54 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.markdown .anchor-wrap {
|
||||
/*margin-top: -50px;*/
|
||||
/*padding-top: 50px;*/
|
||||
}
|
||||
|
||||
.markdown h1 a, .markdown h2 a, .markdown h3 a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown h1 a.anchor,
|
||||
.markdown h2 a.anchor,
|
||||
.markdown h3 a.anchor,
|
||||
.markdown h4 a.anchor,
|
||||
.markdown h5 a.anchor,
|
||||
.markdown h6 a.anchor {
|
||||
text-decoration:none;
|
||||
line-height:1;
|
||||
padding-left:0;
|
||||
margin-left:5px;
|
||||
top:15%;
|
||||
}
|
||||
.markdown a span.octicon {
|
||||
font-size: 16px;
|
||||
font-family: "FontAwesome";
|
||||
line-height: 1;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.markdown a span.octicon-link {
|
||||
display: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.markdown a span.octicon-link:before {
|
||||
content: "\f0c1";
|
||||
}
|
||||
|
||||
.markdown h1:hover .octicon-link,
|
||||
.markdown h2:hover .octicon-link,
|
||||
.markdown h3:hover .octicon-link,
|
||||
.markdown h4:hover .octicon-link,
|
||||
.markdown h5:hover .octicon-link,
|
||||
.markdown h6:hover .octicon-link {
|
||||
display:inline-block
|
||||
}
|
||||
|
||||
/* Author: jmblog */
|
||||
/* Project: https://github.com/jmblog/color-themes-for-google-code-prettify */
|
||||
/* GitHub Theme */
|
||||
|
@ -187,6 +251,7 @@
|
|||
/* a comment */
|
||||
.com {
|
||||
color: #999988;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* a type name */
|
||||
|
|
106
public/js/app.js
106
public/js/app.js
|
@ -63,12 +63,56 @@ var Gogits = {
|
|||
var $tabs = $('[data-init=tabs]');
|
||||
$tabs.find("li:eq(0) a").tab("show");
|
||||
};
|
||||
// fix dropdown inside click
|
||||
Gogits.initDropDown = function(){
|
||||
$('.dropdown-menu').on('click','a,button,input,select',function(e){
|
||||
e.stopPropagation();
|
||||
});
|
||||
};
|
||||
|
||||
// render markdown
|
||||
Gogits.renderMarkdown = function () {
|
||||
var $pre = $('.markdown').find('pre > code').parent();
|
||||
$pre.addClass("prettyprint");
|
||||
var $md = $('.markdown');
|
||||
var $pre = $md.find('pre > code').parent();
|
||||
$pre.addClass('prettyprint linenums');
|
||||
prettyPrint();
|
||||
|
||||
var $lineNums = $pre.parent().siblings('.lines-num');
|
||||
if ($lineNums.length > 0) {
|
||||
var nums = $pre.find('ol.linenums > li').length;
|
||||
for (var i = 1; i <= nums; i++) {
|
||||
$lineNums.append('<span id="L' + i + '" rel=".L' + i + '">' + i + '</span>');
|
||||
}
|
||||
|
||||
var last;
|
||||
$(document).on('click', '.lines-num span', function () {
|
||||
var $e = $(this);
|
||||
if (last) {
|
||||
last.removeClass('active');
|
||||
}
|
||||
last = $e.parent().siblings('.lines-code').find('ol.linenums > ' + $e.attr('rel'));
|
||||
last.addClass('active');
|
||||
window.location.href = '#' + $e.attr('id');
|
||||
});
|
||||
}
|
||||
|
||||
// Set anchor.
|
||||
var headers = {};
|
||||
$md.find('h1, h2, h3, h4, h5, h6').each(function () {
|
||||
var node = $(this);
|
||||
var val = encodeURIComponent(node.text().toLowerCase().replace(/[^\w\- ]/g, '').replace(/[ ]/g, '-'));
|
||||
var name = val;
|
||||
if (headers[val] > 0) {
|
||||
name = val + '-' + headers[val];
|
||||
}
|
||||
if (headers[val] == undefined) {
|
||||
headers[val] = 1;
|
||||
} else {
|
||||
headers[val] += 1;
|
||||
}
|
||||
node = node.wrap('<div id="' + name + '" class="anchor-wrap" ></div>');
|
||||
node.append('<a class="anchor" href="#' + name + '"><span class="octicon octicon-link"></span></a>');
|
||||
});
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
|
@ -98,6 +142,7 @@ function initCore() {
|
|||
Gogits.initPopovers();
|
||||
Gogits.initTabs();
|
||||
Gogits.initModals();
|
||||
Gogits.initDropDown();
|
||||
Gogits.renderMarkdown();
|
||||
}
|
||||
|
||||
|
@ -142,6 +187,60 @@ function initUserSetting() {
|
|||
});
|
||||
}
|
||||
|
||||
function initRepository() {
|
||||
// clone group button script
|
||||
(function () {
|
||||
var $clone = $('.clone-group-btn');
|
||||
if ($clone.length) {
|
||||
var $url = $('.clone-group-url');
|
||||
$clone.find('button[data-link]').on("click",function (e) {
|
||||
var $this = $(this);
|
||||
if (!$this.hasClass('btn-primary')) {
|
||||
$clone.find('.btn-primary').removeClass('btn-primary').addClass("btn-default");
|
||||
$(this).addClass('btn-primary').removeClass('btn-default');
|
||||
$url.val($this.data("link"));
|
||||
$clone.find('span.clone-url').text($this.data('link'));
|
||||
}
|
||||
}).eq(0).trigger("click");
|
||||
// todo copy to clipboard
|
||||
}
|
||||
})();
|
||||
|
||||
// watching script
|
||||
(function () {
|
||||
var $watch = $('#gogs-repo-watching'),
|
||||
watchLink = $watch.data("watch"),
|
||||
unwatchLink = $watch.data("unwatch");
|
||||
$watch.on('click', '.to-watch',function () {
|
||||
if ($watch.hasClass("watching")) {
|
||||
return false;
|
||||
}
|
||||
$.get(watchLink, function (json) {
|
||||
if (json.ok) {
|
||||
$watch.find('.text-primary').removeClass('text-primary');
|
||||
$watch.find('.to-watch h4').addClass('text-primary');
|
||||
$watch.find('.fa-eye-slash').removeClass('fa-eye-slash').addClass('fa-eye');
|
||||
$watch.removeClass("no-watching").addClass("watching");
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}).on('click', '.to-unwatch', function () {
|
||||
if ($watch.hasClass("no-watching")) {
|
||||
return false;
|
||||
}
|
||||
$.get(unwatchLink, function (json) {
|
||||
if (json.ok) {
|
||||
$watch.find('.text-primary').removeClass('text-primary');
|
||||
$watch.find('.to-unwatch h4').addClass('text-primary');
|
||||
$watch.find('.fa-eye').removeClass('fa-eye').addClass('fa-eye-slash');
|
||||
$watch.removeClass("watching").addClass("no-watching");
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
(function ($) {
|
||||
$(function () {
|
||||
initCore();
|
||||
|
@ -152,5 +251,8 @@ function initUserSetting() {
|
|||
if (body.data("page") == "user") {
|
||||
initUserSetting();
|
||||
}
|
||||
if ($('.gogs-repo-nav').length) {
|
||||
initRepository();
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
|
|
|
@ -340,7 +340,7 @@ q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?
|
|||
s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,
|
||||
q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d=
|
||||
c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute("value",d);var r=j.createElement("ol");
|
||||
r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d)%10,k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a;
|
||||
r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d+1),k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a;
|
||||
a.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\bMSIE\s(\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display="none";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType!==1&&(G=x.substring(g,
|
||||
t))){s&&(G=G.replace(d,"\r"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement("span");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
|
||||
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],
|
||||
|
@ -400,7 +400,7 @@ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\u000
|
|||
["kwd",/^-[_a-z]+/],["typ",/^[A-Z_]\w*/],["pun",/^[,.;]/]]),["erlang","erl"]);
|
||||
|
||||
// lang-go.js
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]);
|
||||
// PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]);
|
||||
|
||||
// lang-hs.js
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\u000b\u000c\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/,
|
||||
|
|
77
routers/admin/admin.go
Normal file
77
routers/admin/admin.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
// 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 admin
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/martini"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/middleware"
|
||||
)
|
||||
|
||||
func Dashboard(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = "Admin Dashboard"
|
||||
ctx.Data["PageIsDashboard"] = true
|
||||
ctx.Data["Stats"] = models.GetStatistic()
|
||||
ctx.HTML(200, "admin/dashboard")
|
||||
}
|
||||
|
||||
func Users(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = "User Management"
|
||||
ctx.Data["PageIsUsers"] = true
|
||||
|
||||
var err error
|
||||
ctx.Data["Users"], err = models.GetUsers(100, 0)
|
||||
if err != nil {
|
||||
ctx.Handle(200, "admin.Users", err)
|
||||
return
|
||||
}
|
||||
ctx.HTML(200, "admin/users")
|
||||
}
|
||||
|
||||
func Repositories(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = "Repository Management"
|
||||
ctx.Data["PageIsRepos"] = true
|
||||
|
||||
var err error
|
||||
ctx.Data["Repos"], err = models.GetRepos(100, 0)
|
||||
if err != nil {
|
||||
ctx.Handle(200, "admin.Repositories", err)
|
||||
return
|
||||
}
|
||||
ctx.HTML(200, "admin/repos")
|
||||
}
|
||||
|
||||
func Config(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = "Server Configuration"
|
||||
ctx.Data["PageIsConfig"] = true
|
||||
|
||||
ctx.Data["AppUrl"] = base.AppUrl
|
||||
ctx.Data["Domain"] = base.Domain
|
||||
ctx.Data["RunUser"] = base.RunUser
|
||||
ctx.Data["RunMode"] = strings.Title(martini.Env)
|
||||
ctx.Data["RepoRootPath"] = base.RepoRootPath
|
||||
|
||||
ctx.Data["Service"] = base.Service
|
||||
|
||||
ctx.Data["DbCfg"] = models.DbCfg
|
||||
|
||||
ctx.Data["MailerEnabled"] = false
|
||||
if base.MailService != nil {
|
||||
ctx.Data["MailerEnabled"] = true
|
||||
ctx.Data["Mailer"] = base.MailService
|
||||
}
|
||||
|
||||
ctx.Data["CacheAdapter"] = base.CacheAdapter
|
||||
ctx.Data["CacheConfig"] = base.CacheConfig
|
||||
|
||||
ctx.Data["LogMode"] = base.LogMode
|
||||
ctx.Data["LogConfig"] = base.LogConfig
|
||||
|
||||
ctx.HTML(200, "admin/config")
|
||||
}
|
109
routers/admin/user.go
Normal file
109
routers/admin/user.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
// 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 admin
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/martini"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/auth"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/middleware"
|
||||
)
|
||||
|
||||
func NewUser(ctx *middleware.Context, form auth.RegisterForm) {
|
||||
ctx.Data["Title"] = "New Account"
|
||||
ctx.Data["PageIsUsers"] = true
|
||||
|
||||
if ctx.Req.Method == "GET" {
|
||||
ctx.HTML(200, "admin/users/new")
|
||||
return
|
||||
}
|
||||
|
||||
if form.Password != form.RetypePasswd {
|
||||
ctx.Data["HasError"] = true
|
||||
ctx.Data["Err_Password"] = true
|
||||
ctx.Data["Err_RetypePasswd"] = true
|
||||
ctx.Data["ErrorMsg"] = "Password and re-type password are not same"
|
||||
auth.AssignForm(form, ctx.Data)
|
||||
}
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(200, "admin/users/new")
|
||||
return
|
||||
}
|
||||
|
||||
u := &models.User{
|
||||
Name: form.UserName,
|
||||
Email: form.Email,
|
||||
Passwd: form.Password,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
var err error
|
||||
if u, err = models.RegisterUser(u); err != nil {
|
||||
switch err {
|
||||
case models.ErrUserAlreadyExist:
|
||||
ctx.RenderWithErr("Username has been already taken", "admin/users/new", &form)
|
||||
case models.ErrEmailAlreadyUsed:
|
||||
ctx.RenderWithErr("E-mail address has been already used", "admin/users/new", &form)
|
||||
case models.ErrUserNameIllegal:
|
||||
ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "admin/users/new", &form)
|
||||
default:
|
||||
ctx.Handle(200, "admin.user.NewUser", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace("%s User created by admin(%s): %s", ctx.Req.RequestURI,
|
||||
ctx.User.LowerName, strings.ToLower(form.UserName))
|
||||
|
||||
ctx.Redirect("/admin/users")
|
||||
}
|
||||
|
||||
func EditUser(ctx *middleware.Context, params martini.Params, form auth.AdminEditUserForm) {
|
||||
ctx.Data["Title"] = "Edit Account"
|
||||
ctx.Data["PageIsUsers"] = true
|
||||
|
||||
uid, err := base.StrTo(params["userid"]).Int()
|
||||
if err != nil {
|
||||
ctx.Handle(200, "admin.user.EditUser", err)
|
||||
return
|
||||
}
|
||||
|
||||
u, err := models.GetUserById(int64(uid))
|
||||
if err != nil {
|
||||
ctx.Handle(200, "admin.user.EditUser", err)
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Req.Method == "GET" {
|
||||
ctx.Data["User"] = u
|
||||
ctx.HTML(200, "admin/users/edit")
|
||||
return
|
||||
}
|
||||
|
||||
u.Email = form.Email
|
||||
u.Website = form.Website
|
||||
u.Location = form.Location
|
||||
u.Avatar = base.EncodeMd5(form.Avatar)
|
||||
u.AvatarEmail = form.Avatar
|
||||
u.IsActive = form.Active == "on"
|
||||
u.IsAdmin = form.Admin == "on"
|
||||
if err := models.UpdateUser(u); err != nil {
|
||||
ctx.Handle(200, "admin.user.EditUser", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["IsSuccess"] = true
|
||||
ctx.Data["User"] = u
|
||||
ctx.HTML(200, "admin/users/edit")
|
||||
|
||||
log.Trace("%s User profile updated by admin(%s): %s", ctx.Req.RequestURI,
|
||||
ctx.User.LowerName, ctx.User.LowerName)
|
||||
}
|
|
@ -15,10 +15,10 @@ func Home(ctx *middleware.Context) {
|
|||
return
|
||||
}
|
||||
ctx.Data["PageIsHome"] = true
|
||||
ctx.HTML(200, "home", ctx.Data)
|
||||
ctx.HTML(200, "home")
|
||||
}
|
||||
|
||||
func Help(ctx *middleware.Context) {
|
||||
ctx.Data["PageIsHelp"] = true
|
||||
ctx.HTML(200, "help", ctx.Data)
|
||||
ctx.HTML(200, "help")
|
||||
}
|
||||
|
|
|
@ -21,5 +21,5 @@ func TemplatePreview(ctx *middleware.Context, params martini.Params) {
|
|||
ctx.Data["Code"] = "2014031910370000009fff6782aadb2162b4a997acb69d4400888e0b9274657374"
|
||||
ctx.Data["ActiveCodeLives"] = base.Service.ActiveCodeLives / 60
|
||||
ctx.Data["ResetPwdCodeLives"] = base.Service.ResetPwdCodeLives / 60
|
||||
ctx.HTML(200, params["_1"], ctx.Data)
|
||||
ctx.HTML(200, params["_1"])
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func Create(ctx *middleware.Context, form auth.CreateRepoForm) {
|
|||
ctx.Data["Licenses"] = models.Licenses
|
||||
|
||||
if ctx.Req.Method == "GET" {
|
||||
ctx.HTML(200, "repo/create", ctx.Data)
|
||||
ctx.HTML(200, "repo/create")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,9 @@ func Create(ctx *middleware.Context, form auth.CreateRepoForm) {
|
|||
} else if err == models.ErrRepoAlreadyExist {
|
||||
ctx.RenderWithErr("Repository name has already been used", "repo/create", &form)
|
||||
return
|
||||
} else if err == models.ErrRepoNameIllegal {
|
||||
ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "repo/create", &form)
|
||||
return
|
||||
}
|
||||
ctx.Handle(200, "repo.Create", err)
|
||||
}
|
||||
|
@ -45,7 +48,7 @@ func SettingPost(ctx *middleware.Context) {
|
|||
case "delete":
|
||||
if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
|
||||
ctx.Data["ErrorMsg"] = "Please make sure you entered repository name is correct."
|
||||
ctx.HTML(200, "repo/setting", ctx.Data)
|
||||
ctx.HTML(200, "repo/setting")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ func Branches(ctx *middleware.Context, params martini.Params) {
|
|||
ctx.Data["Branches"] = brs
|
||||
ctx.Data["IsRepoToolbarBranches"] = true
|
||||
|
||||
ctx.HTML(200, "repo/branches", ctx.Data)
|
||||
ctx.HTML(200, "repo/branches")
|
||||
}
|
||||
|
||||
func Single(ctx *middleware.Context, params martini.Params) {
|
||||
|
@ -61,6 +61,8 @@ func Single(ctx *middleware.Context, params martini.Params) {
|
|||
return
|
||||
}
|
||||
|
||||
ctx.Data["IsRepoToolbarSource"] = true
|
||||
|
||||
// Branches.
|
||||
brs, err := models.GetBranches(params["username"], params["reponame"])
|
||||
if err != nil {
|
||||
|
@ -69,7 +71,7 @@ func Single(ctx *middleware.Context, params martini.Params) {
|
|||
return
|
||||
} else if len(brs) == 0 {
|
||||
ctx.Data["IsBareRepo"] = true
|
||||
ctx.HTML(200, "repo/single", ctx.Data)
|
||||
ctx.HTML(200, "repo/single")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -86,6 +88,11 @@ func Single(ctx *middleware.Context, params martini.Params) {
|
|||
|
||||
branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"]
|
||||
|
||||
if len(treename) != 0 && repoFile == nil {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
|
||||
if repoFile != nil && repoFile.IsFile() {
|
||||
if repoFile.Size > 1024*1024 || repoFile.Filemode != git.FileModeBlob {
|
||||
ctx.Data["FileIsLarge"] = true
|
||||
|
@ -95,6 +102,11 @@ func Single(ctx *middleware.Context, params martini.Params) {
|
|||
} else {
|
||||
ctx.Data["IsFile"] = true
|
||||
ctx.Data["FileName"] = repoFile.Name
|
||||
ext := path.Ext(repoFile.Name)
|
||||
if len(ext) > 0 {
|
||||
ext = ext[1:]
|
||||
}
|
||||
ctx.Data["FileExt"] = ext
|
||||
|
||||
readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name)
|
||||
ctx.Data["ReadmeExist"] = readmeExist
|
||||
|
@ -139,10 +151,9 @@ func Single(ctx *middleware.Context, params martini.Params) {
|
|||
return
|
||||
} else {
|
||||
// current repo branch link
|
||||
urlPrefix := "http://" + base.Domain + branchLink
|
||||
|
||||
ctx.Data["FileName"] = readmeFile.Name
|
||||
ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), urlPrefix))
|
||||
ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), branchLink))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,9 +189,8 @@ func Single(ctx *middleware.Context, params martini.Params) {
|
|||
|
||||
ctx.Data["Paths"] = Paths
|
||||
ctx.Data["Treenames"] = treenames
|
||||
ctx.Data["IsRepoToolbarSource"] = true
|
||||
ctx.Data["BranchLink"] = branchLink
|
||||
ctx.HTML(200, "repo/single", ctx.Data)
|
||||
ctx.HTML(200, "repo/single")
|
||||
}
|
||||
|
||||
func Http(ctx *middleware.Context, params martini.Params) {
|
||||
|
@ -212,6 +222,8 @@ func Setting(ctx *middleware.Context, params martini.Params) {
|
|||
return
|
||||
}
|
||||
|
||||
ctx.Data["IsRepoToolbarSetting"] = true
|
||||
|
||||
// Branches.
|
||||
brs, err := models.GetBranches(params["username"], params["reponame"])
|
||||
if err != nil {
|
||||
|
@ -220,7 +232,7 @@ func Setting(ctx *middleware.Context, params martini.Params) {
|
|||
return
|
||||
} else if len(brs) == 0 {
|
||||
ctx.Data["IsBareRepo"] = true
|
||||
ctx.HTML(200, "repo/setting", ctx.Data)
|
||||
ctx.HTML(200, "repo/setting")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -229,9 +241,13 @@ func Setting(ctx *middleware.Context, params martini.Params) {
|
|||
title = t
|
||||
}
|
||||
|
||||
if len(params["branchname"]) == 0 {
|
||||
params["branchname"] = "master"
|
||||
}
|
||||
|
||||
ctx.Data["Branchname"] = params["branchname"]
|
||||
ctx.Data["Title"] = title + " - settings"
|
||||
ctx.Data["IsRepoToolbarSetting"] = true
|
||||
ctx.HTML(200, "repo/setting", ctx.Data)
|
||||
ctx.HTML(200, "repo/setting")
|
||||
}
|
||||
|
||||
func Commits(ctx *middleware.Context, params martini.Params) {
|
||||
|
@ -255,17 +271,17 @@ func Commits(ctx *middleware.Context, params martini.Params) {
|
|||
ctx.Data["Reponame"] = params["reponame"]
|
||||
ctx.Data["CommitCount"] = commits.Len()
|
||||
ctx.Data["Commits"] = commits
|
||||
ctx.HTML(200, "repo/commits", ctx.Data)
|
||||
ctx.HTML(200, "repo/commits")
|
||||
}
|
||||
|
||||
func Issues(ctx *middleware.Context) {
|
||||
ctx.Data["IsRepoToolbarIssues"] = true
|
||||
ctx.HTML(200, "repo/issues", ctx.Data)
|
||||
ctx.HTML(200, "repo/issues")
|
||||
}
|
||||
|
||||
func Pulls(ctx *middleware.Context) {
|
||||
ctx.Data["IsRepoToolbarPulls"] = true
|
||||
ctx.HTML(200, "repo/pulls", ctx.Data)
|
||||
ctx.HTML(200, "repo/pulls")
|
||||
}
|
||||
|
||||
func Action(ctx *middleware.Context, params martini.Params) {
|
||||
|
|
|
@ -24,13 +24,13 @@ func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) {
|
|||
ctx.Data["Owner"] = user
|
||||
|
||||
if ctx.Req.Method == "GET" {
|
||||
ctx.HTML(200, "user/setting", ctx.Data)
|
||||
ctx.HTML(200, "user/setting")
|
||||
return
|
||||
}
|
||||
|
||||
// below is for POST requests
|
||||
if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) {
|
||||
ctx.HTML(200, "user/setting", ctx.Data)
|
||||
ctx.HTML(200, "user/setting")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,8 @@ func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) {
|
|||
}
|
||||
|
||||
ctx.Data["IsSuccess"] = true
|
||||
ctx.HTML(200, "user/setting", ctx.Data)
|
||||
ctx.HTML(200, "user/setting")
|
||||
|
||||
log.Trace("%s User setting updated: %s", ctx.Req.RequestURI, ctx.User.LowerName)
|
||||
}
|
||||
|
||||
|
@ -55,7 +56,7 @@ func SettingPassword(ctx *middleware.Context, form auth.UpdatePasswdForm) {
|
|||
ctx.Data["IsUserPageSettingPasswd"] = true
|
||||
|
||||
if ctx.Req.Method == "GET" {
|
||||
ctx.HTML(200, "user/password", ctx.Data)
|
||||
ctx.HTML(200, "user/password")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -82,7 +83,7 @@ func SettingPassword(ctx *middleware.Context, form auth.UpdatePasswdForm) {
|
|||
}
|
||||
|
||||
ctx.Data["Owner"] = user
|
||||
ctx.HTML(200, "user/password", ctx.Data)
|
||||
ctx.HTML(200, "user/password")
|
||||
log.Trace("%s User password updated: %s", ctx.Req.RequestURI, ctx.User.LowerName)
|
||||
}
|
||||
|
||||
|
@ -123,7 +124,7 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) {
|
|||
// Add new SSH key.
|
||||
if ctx.Req.Method == "POST" {
|
||||
if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) {
|
||||
ctx.HTML(200, "user/publickey", ctx.Data)
|
||||
ctx.HTML(200, "user/publickey")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -155,7 +156,7 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) {
|
|||
ctx.Data["PageIsUserSetting"] = true
|
||||
ctx.Data["IsUserPageSettingSSH"] = true
|
||||
ctx.Data["Keys"] = keys
|
||||
ctx.HTML(200, "user/publickey", ctx.Data)
|
||||
ctx.HTML(200, "user/publickey")
|
||||
}
|
||||
|
||||
func SettingNotification(ctx *middleware.Context) {
|
||||
|
@ -163,7 +164,7 @@ func SettingNotification(ctx *middleware.Context) {
|
|||
ctx.Data["Title"] = "Notification"
|
||||
ctx.Data["PageIsUserSetting"] = true
|
||||
ctx.Data["IsUserPageSettingNotify"] = true
|
||||
ctx.HTML(200, "user/notification", ctx.Data)
|
||||
ctx.HTML(200, "user/notification")
|
||||
}
|
||||
|
||||
func SettingSecurity(ctx *middleware.Context) {
|
||||
|
@ -171,5 +172,5 @@ func SettingSecurity(ctx *middleware.Context) {
|
|||
ctx.Data["Title"] = "Security"
|
||||
ctx.Data["PageIsUserSetting"] = true
|
||||
ctx.Data["IsUserPageSettingSecurity"] = true
|
||||
ctx.HTML(200, "user/security", ctx.Data)
|
||||
ctx.HTML(200, "user/security")
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ func Dashboard(ctx *middleware.Context) {
|
|||
return
|
||||
}
|
||||
ctx.Data["Feeds"] = feeds
|
||||
ctx.HTML(200, "user/dashboard", ctx.Data)
|
||||
ctx.HTML(200, "user/dashboard")
|
||||
}
|
||||
|
||||
func Profile(ctx *middleware.Context, params martini.Params) {
|
||||
|
@ -70,19 +70,19 @@ func Profile(ctx *middleware.Context, params martini.Params) {
|
|||
}
|
||||
|
||||
ctx.Data["PageIsUserProfile"] = true
|
||||
ctx.HTML(200, "user/profile", ctx.Data)
|
||||
ctx.HTML(200, "user/profile")
|
||||
}
|
||||
|
||||
func SignIn(ctx *middleware.Context, form auth.LogInForm) {
|
||||
ctx.Data["Title"] = "Log In"
|
||||
|
||||
if ctx.Req.Method == "GET" {
|
||||
ctx.HTML(200, "user/signin", ctx.Data)
|
||||
ctx.HTML(200, "user/signin")
|
||||
return
|
||||
}
|
||||
|
||||
if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) {
|
||||
ctx.HTML(200, "user/signin", ctx.Data)
|
||||
ctx.HTML(200, "user/signin")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -112,8 +112,14 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) {
|
|||
ctx.Data["Title"] = "Sign Up"
|
||||
ctx.Data["PageIsSignUp"] = true
|
||||
|
||||
if base.Service.DisenableRegisteration {
|
||||
ctx.Data["DisenableRegisteration"] = true
|
||||
ctx.HTML(200, "user/signup")
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Req.Method == "GET" {
|
||||
ctx.HTML(200, "user/signup", ctx.Data)
|
||||
ctx.HTML(200, "user/signup")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -126,7 +132,7 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) {
|
|||
}
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(200, "user/signup", ctx.Data)
|
||||
ctx.HTML(200, "user/signup")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -139,11 +145,13 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) {
|
|||
|
||||
var err error
|
||||
if u, err = models.RegisterUser(u); err != nil {
|
||||
switch err.Error() {
|
||||
case models.ErrUserAlreadyExist.Error():
|
||||
switch err {
|
||||
case models.ErrUserAlreadyExist:
|
||||
ctx.RenderWithErr("Username has been already taken", "user/signup", &form)
|
||||
case models.ErrEmailAlreadyUsed.Error():
|
||||
case models.ErrEmailAlreadyUsed:
|
||||
ctx.RenderWithErr("E-mail address has been already used", "user/signup", &form)
|
||||
case models.ErrUserNameIllegal:
|
||||
ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "user/signup", &form)
|
||||
default:
|
||||
ctx.Handle(200, "user.SignUp", err)
|
||||
}
|
||||
|
@ -153,12 +161,16 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) {
|
|||
log.Trace("%s User created: %s", ctx.Req.RequestURI, strings.ToLower(form.UserName))
|
||||
|
||||
// Send confirmation e-mail.
|
||||
if base.Service.RegisterEmailConfirm {
|
||||
if base.Service.RegisterEmailConfirm && u.Id > 1 {
|
||||
mailer.SendRegisterMail(ctx.Render, u)
|
||||
ctx.Data["IsSendRegisterMail"] = true
|
||||
ctx.Data["Email"] = u.Email
|
||||
ctx.Data["Hours"] = base.Service.ActiveCodeLives / 60
|
||||
ctx.Render.HTML(200, "user/active", ctx.Data)
|
||||
ctx.HTML(200, "user/active")
|
||||
|
||||
if err = ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.Redirect("/user/login")
|
||||
|
@ -170,7 +182,7 @@ func Delete(ctx *middleware.Context) {
|
|||
ctx.Data["IsUserPageSettingDelete"] = true
|
||||
|
||||
if ctx.Req.Method == "GET" {
|
||||
ctx.HTML(200, "user/delete", ctx.Data)
|
||||
ctx.HTML(200, "user/delete")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -195,7 +207,7 @@ func Delete(ctx *middleware.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
ctx.HTML(200, "user/delete", ctx.Data)
|
||||
ctx.HTML(200, "user/delete")
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -218,15 +230,15 @@ func Feeds(ctx *middleware.Context, form auth.FeedsForm) {
|
|||
}
|
||||
|
||||
func Issues(ctx *middleware.Context) {
|
||||
ctx.HTML(200, "user/issues", ctx.Data)
|
||||
ctx.HTML(200, "user/issues")
|
||||
}
|
||||
|
||||
func Pulls(ctx *middleware.Context) {
|
||||
ctx.HTML(200, "user/pulls", ctx.Data)
|
||||
ctx.HTML(200, "user/pulls")
|
||||
}
|
||||
|
||||
func Stars(ctx *middleware.Context) {
|
||||
ctx.HTML(200, "user/stars", ctx.Data)
|
||||
ctx.HTML(200, "user/stars")
|
||||
}
|
||||
|
||||
func Activate(ctx *middleware.Context) {
|
||||
|
@ -239,12 +251,16 @@ func Activate(ctx *middleware.Context) {
|
|||
}
|
||||
// Resend confirmation e-mail.
|
||||
if base.Service.RegisterEmailConfirm {
|
||||
if ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName) {
|
||||
ctx.Data["ResendLimited"] = true
|
||||
} else {
|
||||
ctx.Data["Hours"] = base.Service.ActiveCodeLives / 60
|
||||
mailer.SendActiveMail(ctx.Render, ctx.User)
|
||||
}
|
||||
} else {
|
||||
ctx.Data["ServiceNotEnabled"] = true
|
||||
}
|
||||
ctx.Render.HTML(200, "user/active", ctx.Data)
|
||||
ctx.HTML(200, "user/active")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -263,5 +279,5 @@ func Activate(ctx *middleware.Context) {
|
|||
}
|
||||
|
||||
ctx.Data["IsActivateFailed"] = true
|
||||
ctx.Render.HTML(200, "user/active", ctx.Data)
|
||||
ctx.HTML(200, "user/active")
|
||||
}
|
||||
|
|
8
serve.go
8
serve.go
|
@ -12,7 +12,9 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -43,6 +45,10 @@ func In(b string, sl map[string]int) bool {
|
|||
}
|
||||
|
||||
func runServ(*cli.Context) {
|
||||
base.NewConfigContext()
|
||||
models.LoadModelsConfig()
|
||||
models.NewEngine()
|
||||
|
||||
keys := strings.Split(os.Args[2], "-")
|
||||
if len(keys) != 2 {
|
||||
fmt.Println("auth file format error")
|
||||
|
@ -144,7 +150,7 @@ func runServ(*cli.Context) {
|
|||
}
|
||||
|
||||
gitcmd := exec.Command(verb, rRepo)
|
||||
gitcmd.Dir = models.RepoRootPath
|
||||
gitcmd.Dir = base.RepoRootPath
|
||||
gitcmd.Stdout = os.Stdout
|
||||
gitcmd.Stdin = os.Stdin
|
||||
gitcmd.Stderr = os.Stderr
|
||||
|
|
93
templates/admin/config.tmpl
Normal file
93
templates/admin/config.tmpl
Normal file
|
@ -0,0 +1,93 @@
|
|||
{{template "base/head" .}}
|
||||
{{template "base/navbar" .}}
|
||||
<div id="gogs-body" class="container" data-page="admin">
|
||||
{{template "admin/nav" .}}
|
||||
<div id="gogs-admin-container" class="col-md-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Server Configuration
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div><b>Application Name:</b> {{AppName}}</div>
|
||||
<div><b>Application Version:</b> {{AppVer}}</div>
|
||||
<div><b>Application URL:</b> {{.AppUrl}}</div>
|
||||
<div><b>Domain:</b> {{.Domain}}</div>
|
||||
<hr/>
|
||||
<div><b>Run User:</b> {{.RunUser}}</div>
|
||||
<div><b>Run Mode:</b> {{.RunMode}}</div>
|
||||
<hr/>
|
||||
<div><b>Repository Root Path:</b> {{.RepoRootPath}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Database Configuration
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div><b>Type:</b> {{.DbCfg.Type}}</div>
|
||||
<div><b>Host:</b> {{.DbCfg.Host}}</div>
|
||||
<div><b>Name:</b> {{.DbCfg.Name}}</div>
|
||||
<div><b>User:</b> {{.DbCfg.User}}</div>
|
||||
<div><b>SslMode:</b> {{.DbCfg.SslMode}} (for "postgres" only)</div>
|
||||
<div><b>Path:</b> {{.DbCfg.Path}} (for "sqlite3" only)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Service Configuration
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div><b>Register Email Confirmation:</b> <i class="fa fa{{if .Service.RegisterEmailConfirm}}-check{{end}}-square-o"></i></div>
|
||||
<div><b>Disenable Registeration:</b> <i class="fa fa{{if .Service.DisenableRegisteration}}-check{{end}}-square-o"></i></div>
|
||||
<div><b>Require Sign In View:</b> <i class="fa fa{{if .Service.RequireSignInView}}-check{{end}}-square-o"></i></div>
|
||||
<hr/>
|
||||
<div><b>Active Code Lives:</b> {{.Service.ActiveCodeLives}} minutes</div>
|
||||
<div><b>Reset Password Code Lives:</b> {{.Service.ResetPwdCodeLives}} minutes</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Mailer Configuration
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div><b>Enabled:</b> <i class="fa fa{{if .MailerEnabled}}-check{{end}}-square-o"></i></div>
|
||||
<div><b>Name:</b> {{.Mailer.Name}}</div>
|
||||
<div><b>Host:</b> {{.Mailer.Host}}</div>
|
||||
<div><b>User:</b> {{.Mailer.User}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Cache Configuration
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div><b>Cache Adapter:</b> {{.CacheAdapter}}</div>
|
||||
<div><b>Cache Config:</b></div>
|
||||
<div style="padding-top: 5px;"><pre>{{.CacheConfig}}</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Log Configuration
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div><b>Log Mode:</b> {{.LogMode}}</div>
|
||||
<div><b>Log Config:</b></div>
|
||||
<div style="padding-top: 5px;"><pre>{{.LogConfig}}</pre></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
26
templates/admin/dashboard.tmpl
Normal file
26
templates/admin/dashboard.tmpl
Normal file
|
@ -0,0 +1,26 @@
|
|||
{{template "base/head" .}}
|
||||
{{template "base/navbar" .}}
|
||||
<div id="gogs-body" class="container" data-page="admin">
|
||||
{{template "admin/nav" .}}
|
||||
<div id="gogs-admin-container" class="col-md-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Statistic
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
Gogs database has <b>{{.Stats.Counter.User}}</b> users, <b>{{.Stats.Counter.PublicKey}}</b> SSH keys, <b>{{.Stats.Counter.Repo}}</b> repositories, <b>{{.Stats.Counter.Watch}}</b> watches, <b>{{.Stats.Counter.Action}}</b> actions, and <b>{{.Stats.Counter.Access}}</b> accesses.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
System Status
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
8
templates/admin/nav.tmpl
Normal file
8
templates/admin/nav.tmpl
Normal file
|
@ -0,0 +1,8 @@
|
|||
<div id="gogs-user-setting-nav" class="col-md-3 gogs-admin-nav">
|
||||
<ul class="list-group" data-init="tabs">
|
||||
<li class="list-group-item{{if .PageIsDashboard}} active{{end}}"><a href="/admin"><i class="fa fa-tachometer fa-lg"></i> Dashboard</a></li>
|
||||
<li class="list-group-item{{if .PageIsUsers}} active{{end}}"><a href="/admin/users"><i class="fa fa-users fa-lg"></i> Users</a></li>
|
||||
<li class="list-group-item{{if .PageIsRepos}} active{{end}}"><a href="/admin/repos"><i class="fa fa-book fa-lg"></i> Repositories</a></li>
|
||||
<li class="list-group-item{{if .PageIsConfig}} active{{end}}"><a href="/admin/config"><i class="fa fa-cogs fa-lg"></i> Configuration</a></li>
|
||||
</ul>
|
||||
</div>
|
42
templates/admin/repos.tmpl
Normal file
42
templates/admin/repos.tmpl
Normal file
|
@ -0,0 +1,42 @@
|
|||
{{template "base/head" .}}
|
||||
{{template "base/navbar" .}}
|
||||
<div id="gogs-body" class="container" data-page="admin">
|
||||
{{template "admin/nav" .}}
|
||||
<div id="gogs-admin-container" class="col-md-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Repository Management
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Owner</th>
|
||||
<th>Name</th>
|
||||
<th>Private</th>
|
||||
<th>Watches</th>
|
||||
<th>Forks</th>
|
||||
<th>Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Repos}}
|
||||
<tr>
|
||||
<td>{{.Id}}</td>
|
||||
<th>{{.UserName}}</th>
|
||||
<td><a href="/{{.UserName}}/{{.Name}}">{{.Name}}</a></td>
|
||||
<td><i class="fa fa{{if .Private}}-check{{end}}-square-o"></i></td>
|
||||
<td>{{.NumWatches}}</td>
|
||||
<td>{{.NumForks}}</td>
|
||||
<td>{{DateFormat .Created "M d, Y"}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
45
templates/admin/users.tmpl
Normal file
45
templates/admin/users.tmpl
Normal file
|
@ -0,0 +1,45 @@
|
|||
{{template "base/head" .}}
|
||||
{{template "base/navbar" .}}
|
||||
<div id="gogs-body" class="container" data-page="admin">
|
||||
{{template "admin/nav" .}}
|
||||
<div id="gogs-admin-container" class="col-md-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
User Management
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<a href="/admin/users/new" class="btn btn-primary">New Account</a>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Name</th>
|
||||
<th>E-mail</th>
|
||||
<th>Actived</th>
|
||||
<th>Admin</th>
|
||||
<th>Repos</th>
|
||||
<th>Join</th>
|
||||
<th>Edit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Users}}
|
||||
<tr>
|
||||
<td>{{.Id}}</td>
|
||||
<td><a href="/user/{{.Name}}">{{.Name}}</a></td>
|
||||
<td>{{.Email}}</td>
|
||||
<td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td>
|
||||
<td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td>
|
||||
<td>{{.NumRepos}}</td>
|
||||
<td>{{DateFormat .Created "M d, Y"}}</td>
|
||||
<td><a href="/admin/users/{{.Id}}"><i class="fa fa-pencil-square-o"></i></a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
83
templates/admin/users/edit.tmpl
Normal file
83
templates/admin/users/edit.tmpl
Normal file
|
@ -0,0 +1,83 @@
|
|||
{{template "base/head" .}}
|
||||
{{template "base/navbar" .}}
|
||||
<div id="gogs-body" class="container" data-page="admin">
|
||||
{{template "admin/nav" .}}
|
||||
<div id="gogs-admin-container" class="col-md-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Edit Account
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<br/>
|
||||
<form action="/admin/users/{{.User.Id}}" method="post" class="form-horizontal">
|
||||
{{if .IsSuccess}}<p class="alert alert-success">Account profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}}
|
||||
<input type="hidden" value="{{.User.Id}}" name="userId"/>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">Username: </label>
|
||||
<label class="control-label">{{.User.Name}}</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group {{if .Err_Email}}has-error has-feedback{{end}}">
|
||||
<label class="col-md-3 control-label">Email<strong class="text-danger">*</strong></label>
|
||||
<div class="col-md-7">
|
||||
<input name="email" class="form-control" placeholder="Type account's e-mail address" value="{{.User.Email}}" required="required">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">Website</label>
|
||||
<div class="col-md-7">
|
||||
<input name="website" class="form-control" placeholder="Type account's website URL" value="{{.User.Website}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">Location</label>
|
||||
<div class="col-md-7">
|
||||
<input name="location" class="form-control" placeholder="Type account's current location" value="{{.User.Location}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group {{if .Err_Avatar}}has-error has-feedback{{end}}">
|
||||
<label class="col-md-3 control-label">Gravatar Email<strong class="text-danger">*</strong></label>
|
||||
<div class="col-md-7">
|
||||
<input name="avatar" class="form-control" placeholder="Type account's Gravatar e-mail address" required="required" value="{{.User.AvatarEmail}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-7 col-md-offset-3">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="active" {{if .User.IsActive}}checked{{end}}>
|
||||
<strong>This account has activated</strong>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-7 col-md-offset-3">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="admin" {{if .User.IsAdmin}}checked{{end}}>
|
||||
<strong>This account has administor permisson</strong>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-3 col-md-6">
|
||||
<button type="submit" class="btn btn-lg btn-primary btn-block">Update account profile</button>
|
||||
<!-- <a type="button" href="/admin/users/{{.User.Id}}/delete" class="btn btn-lg btn-danger btn-block">Delete this account</a> -->
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
54
templates/admin/users/new.tmpl
Normal file
54
templates/admin/users/new.tmpl
Normal file
|
@ -0,0 +1,54 @@
|
|||
{{template "base/head" .}}
|
||||
{{template "base/navbar" .}}
|
||||
<div id="gogs-body" class="container" data-page="admin">
|
||||
{{template "admin/nav" .}}
|
||||
<div id="gogs-admin-container" class="col-md-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
New Account
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<br/>
|
||||
<form action="/admin/users/new" method="post" class="form-horizontal">
|
||||
<div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>
|
||||
<div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}">
|
||||
<label class="col-md-3 control-label">Username: </label>
|
||||
<div class="col-md-7">
|
||||
<input name="username" class="form-control" placeholder="Type account's username" value="{{.username}}" required="required">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group {{if .Err_Email}}has-error has-feedback{{end}}">
|
||||
<label class="col-md-3 control-label">Email: </label>
|
||||
<div class="col-md-7">
|
||||
<input name="email" class="form-control" placeholder="Type account's e-mail address" value="{{.email}}" required="required" title="Email is not valid">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group {{if .Err_Password}}has-error has-feedback{{end}}">
|
||||
<label class="col-md-3 control-label">Password: </label>
|
||||
<div class="col-md-7">
|
||||
<input name="passwd" type="password" class="form-control" placeholder="Type account's password" required="required" title="Password must contain at least 6 characters">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group {{if .Err_RetypePasswd}}has-error has-feedback{{end}}">
|
||||
<label class="col-md-3 control-label">Re-type: </label>
|
||||
<div class="col-md-7">
|
||||
<input name="retypepasswd" type="password" class="form-control" placeholder="Re-type account's password" required="required" title="Re-type Password must be same to Password">
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-3 col-md-7">
|
||||
<button type="submit" class="btn btn-lg btn-primary">Create new account</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
|
@ -2,13 +2,20 @@
|
|||
</div>
|
||||
<footer id="footer">
|
||||
<div class="container footer-wrap">
|
||||
<p>© 2014 Gogs · ver {{AppVer}} ·
|
||||
All: {{LoadTimes .PageStartTime}} ·
|
||||
Tmpl: {{call .TmplLoadTimes}} ·
|
||||
<i class="fa fa-github"></i><a target="_blank" href="https://github.com/gogits/gogs">GitHub</a> ·
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p>© 2014 GoGits · Version: {{AppVer}} ·
|
||||
Page: <b>{{LoadTimes .PageStartTime}}</b> ·
|
||||
Template: <b>{{call .TmplLoadTimes}}</b>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-1" style="margin: -5px;">
|
||||
<a target="_blank" href="https://github.com/gogits/gogs"><i class="fa fa-github fa-2x"></i></a>
|
||||
</div>
|
||||
<p class="desc"></p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
|
@ -10,7 +10,9 @@
|
|||
</a>
|
||||
<a class="navbar-right gogs-nav-item{{if .PageIsNewRepo}} active{{end}}" href="/repo/create" data-toggle="tooltip" data-placement="bottom" title="New Repository"><i class="fa fa-plus fa-lg"></i></a>
|
||||
<a class="navbar-right gogs-nav-item{{if .PageIsUserSetting}} active{{end}}" href="/user/setting" data-toggle="tooltip" data-placement="bottom" title="Setting"><i class="fa fa-cogs fa-lg"></i></a>
|
||||
{{else}}<a id="gogs-nav-signin" class="gogs-nav-item navbar-right navbar-btn btn btn-danger" href="/user/login/">Sign in</a>{{end}}
|
||||
{{if .IsAdmin}}<a class="navbar-right gogs-nav-item{{if .PageIsAdmin}} active{{end}}" href="/admin" data-toggle="tooltip" data-placement="bottom" title="Admin"><i class="fa fa-gear fa-lg"></i></a>{{end}}
|
||||
{{else}}<a id="gogs-nav-signin" class="gogs-nav-item navbar-right navbar-btn btn btn-danger" href="/user/login/">Sign In</a>
|
||||
<a id="gogs-nav-signup" class="gogs-nav-item navbar-right navbar-btn btn btn-info" href="/user/sign_up/">Sign Up</a>{{end}}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,40 +5,62 @@
|
|||
<h3 class="name"><i class="fa fa-book fa-lg"></i><a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / {{.Repository.Name}}</h3>
|
||||
<p class="desc">{{.Repository.Description}}{{if .Repository.Website}}<a href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}</p>
|
||||
</div>
|
||||
<div class="col-md-6 actions text-right clone-group-btn">
|
||||
{{if not .IsBareRepo}}
|
||||
<div class="col-md-6 actions text-right">
|
||||
<div class="btn-group" id="gogs-repo-clone">
|
||||
<!--<div class="btn-group" id="gogs-repo-clone">
|
||||
<button type="button" class="btn btn-default"><i class="fa fa-download fa-lg fa-m"></i></button>
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-container="body" data-toggle="popover" data-placement="bottom" data-content="<label>SSH:</label><div class='input-group'><input type='text' class='form-control' value='{{.CloneLink.SSH}}'></div>" data-html="1">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group" id="gogs-repo-watching">
|
||||
<button type="button" class="btn btn-default"><i class="fa fa-eye fa-lg fa-m"></i></button>
|
||||
</div>-->
|
||||
<div class="btn-group" id="gogs-repo-clone">
|
||||
<button type="button" class="btn btn-default"><i class="fa fa-download fa-lg fa-m"></i></button>
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<div class="dropdown-item text-left" data-val="not-watching">
|
||||
<h4 role="presentation" class="dropdown-header">Not Watching</h4>
|
||||
<div class="dropdown-menu clone-group-btn dropdown-menu-right">
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" data-link="{{.CloneLink.SSH}}" type="button">SSH</button>
|
||||
<button class="btn btn-default" data-link="{{.CloneLink.HTTPS}}" type="button">HTTPS</button>
|
||||
</span>
|
||||
<input type="text" class="form-control clone-group-url" value="" readonly/>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" type="button"><i class="fa fa-copy" data-toggle="tooltip" title="copy to clipboard" data-placement="top"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
<p class="help-block text-center">Need help cloning? Visit <a href="#">Help</a>!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group {{if .IsRepositoryWatching}}watching{{else}}no-watching{{end}}" id="gogs-repo-watching" data-watch="/{{.SignedUser.Name}}/{{.Repository.Name}}/action/watch" data-unwatch="/{{.SignedUser.Name}}/{{.Repository.Name}}/action/unwatch">
|
||||
{{if .IsRepositoryWatching}}
|
||||
<button type="button" class="btn btn-default"><i class="fa fa-eye fa-lg fa-m"></i></button>
|
||||
{{else}}
|
||||
<button type="button" class="btn btn-default"><i class="fa fa-eye-slash fa-lg fa-m"></i></button>
|
||||
{{end}}
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<div class="dropdown-item text-left to-unwatch">
|
||||
<h4 role="presentation" class="dropdown-header {{if not .IsRepositoryWatching}}text-primary{{end}}">Not Watching</h4>
|
||||
<p class="description">You only receive notifications for conversations in which you participate or are @mentioned.</p>
|
||||
<p class="divider"></p>
|
||||
</div>
|
||||
<div class="dropdown-item text-left" data-val="watching">
|
||||
<h4 role="presentation" class="dropdown-header">Watching</h4>
|
||||
<div class="dropdown-item text-left to-watch">
|
||||
<h4 role="presentation" class="dropdown-header {{if .IsRepositoryWatching}}text-primary{{end}}">Watching</h4>
|
||||
<p class="description">You receive notifications for all conversations in this repository.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default"><i class="fa fa-star"></i>Star {{.Repository.NumStars}}</button>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default"><i class="fa fa-code-fork"></i>Fork {{.Repository.NumForks}}</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-default" data-toggle="tooltip" data-placement="top" title="Star"><i class="fa fa-star"></i> {{.Repository.NumStars}}</button>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="btn-group">
|
||||
<a type="button" {{if not .IsRepositoryOwner}}href="/{{.Username}}/{{.Reponame}}/fork"{{end}} class="btn btn-default" data-toggle="tooltip" data-placement="top" title="Fork"><i class="fa fa-code-fork fa-lg"></i> {{.Repository.NumForks}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -10,20 +10,24 @@
|
|||
<li class="list-group-item"><a href="#">Notifications</a></li>-->
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="gogs-repo-setting-container" class="col-md-9">
|
||||
{{if .ErrorMsg}}<p class="alert alert-danger">{{.ErrorMsg}}</p>{{end}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Repository Options
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-warning">
|
||||
<div class="panel-heading">
|
||||
Danger Zone
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<button type="button" class="btn btn-default pull-right" href="#delete-repository-modal" data-toggle="modal">
|
||||
Delete this repository
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div id="gogs-body" class="container">
|
||||
<div id="gogs-source">
|
||||
{{if .IsBareRepo}}
|
||||
Need to fill in some guide.
|
||||
{{template "repo/single_bare" .}}
|
||||
{{else}}
|
||||
<div class="source-toolbar">
|
||||
{{ $n := len .Treenames}}
|
||||
|
@ -15,7 +15,7 @@
|
|||
<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
{{range .Branches}}
|
||||
<li><a {{if eq . $.Branchname}}class="current" {{end}}href="{{$.BranchLink}}">{{.}}</a></li>
|
||||
<li><a {{if eq . $.Branchname}}class="current" {{end}}href="/{{$.Username}}/{{$.Reponame}}/src/{{.}}">{{.}}</a></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
31
templates/repo/single_bare.tmpl
Normal file
31
templates/repo/single_bare.tmpl
Normal file
|
@ -0,0 +1,31 @@
|
|||
<div class="panel panel-default guide-box clone-group-btn">
|
||||
<div class="panel-heading guide-head">
|
||||
<h4>Quick Guide</h4>
|
||||
</div>
|
||||
<div class="panel-body guide-content text-center">
|
||||
<h3>Clone this repository</h3>
|
||||
<div class="input-group col-md-8 col-md-offset-2 guide-buttons">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" data-link="{{.CloneLink.SSH}}" type="button">SSH</button>
|
||||
<button class="btn btn-default" data-link="{{.CloneLink.HTTPS}}" type="button">HTTPS</button>
|
||||
</span>
|
||||
<input type="text" class="form-control clone-group-url" id="guide-clone-url" value="" readonly/>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" type="button"><i class="fa fa-copy" data-toggle="tooltip" title="copy to clipboard" data-placement="top"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
<p>We recommend every repository include a <strong>README</strong>, <strong>LICENSE</strong>, and <strong>.gitignore</strong>.</p>
|
||||
<hr/>
|
||||
<h3>Create a new repository on the command line</h3>
|
||||
<pre class="text-left"><code>touch README.md
|
||||
git init
|
||||
git add README.md
|
||||
git commit -m "first commit"
|
||||
git remote add origin <span class="clone-url"></span>
|
||||
git push -u origin master</code></pre>
|
||||
<hr/>
|
||||
<h3>Push an existing repository from the command line</h3>
|
||||
<pre class="text-left"><code>git remote add origin <span class="clone-url"></span>
|
||||
git push -u origin master</code></pre>
|
||||
</div>
|
||||
</div>
|
|
@ -1,6 +1,10 @@
|
|||
<div class="panel panel-default file-content">
|
||||
<div class="panel-heading file-head">
|
||||
<i class="icon fa fa-book"></i> {{.FileName}}
|
||||
{{if .ReadmeExist}}
|
||||
<i class="icon fa fa-book"></i>
|
||||
{{else}}
|
||||
<i class="icon fa fa-file-text-o"></i>
|
||||
{{end}}{{.FileName}}
|
||||
</div>
|
||||
{{if .FileIsLarge}}
|
||||
<div class="panel-footer">
|
||||
|
@ -12,8 +16,15 @@
|
|||
{{.FileContent|str2html}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="panel-body file-body markdown">
|
||||
<pre><code>{{.FileContent}}</code></pre>
|
||||
<div class="panel-body file-body file-code">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="lines-num"></td>
|
||||
<td class="lines-code markdown"><pre class="linenums lang-{{.FileExt}}"><code>{{.FileContent}}</code></pre></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -15,9 +15,8 @@
|
|||
<li><a href="/{{.RepositoryLink}}/release">Release</a></li>
|
||||
<li><a href="//{{.RepositoryLink}}/wiki">Wiki</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</li>{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{{if not .IsBareRepo}}
|
||||
<li class="dropdown">
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
{{if .IsActivatePage}}
|
||||
{{if .ServiceNotEnabled}}
|
||||
<p>Sorry, Register Mail Confirmation has been disabled.</p>
|
||||
{{else if .ResendLimited}}
|
||||
<p>Sorry, you are sending activation e-mail too frequently, please wait 3 minutes.</p>
|
||||
{{else}}
|
||||
<p>New confirmation e-mail has been sent to <b>{{.SignedUser.Email}}</b>, please check your inbox within {{.Hours}} hours to complete your registeration.</p>
|
||||
<hr/>
|
||||
|
|
|
@ -33,8 +33,9 @@
|
|||
</div>
|
||||
<div class="panel-body">
|
||||
<ul class="list-group">{{range .MyRepos}}
|
||||
<li class="list-group-item"><i class="fa fa-book"></i><a href="/{{$.SignedUserName}}/{{.Name}}">{{.Name}}</a>
|
||||
<li class="list-group-item"><a href="/{{$.SignedUserName}}/{{.Name}}">
|
||||
<span class="stars pull-right"><i class="fa fa-star"></i>{{.NumStars}}</span>
|
||||
<i class="fa fa-book"></i>{{.Name}}</a>
|
||||
</li>{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
<div id="gogs-user-setting-container" class="col-md-9">
|
||||
<div id="gogs-setting-pwd">
|
||||
<h4>Account Profile</h4>
|
||||
<form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting">{{if .IsSuccess}}
|
||||
<p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}}
|
||||
<form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting">
|
||||
{{if .IsSuccess}}<p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}}
|
||||
<p>Your Email will be public and used for Account related notifications and any web based operations made via the web.</p>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">Email</label>
|
||||
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group {{if .Err_Avatar}}has-error has-feedback{{end}}">
|
||||
<label class="col-md-2 control-label">Gravatar Email<strong class="text-danger">*</strong></label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" name="avatar" class="form-control" placeholder="Type your Gravatar e-mail address" required="required" value="{{.Owner.AvatarEmail}}">
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group text-center" id="gogs-social-login">
|
||||
<a class="btn btn-default btn-lg">Social Login</a>
|
||||
<a class="btn btn-danger btn-lg">Register new account</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
{{template "base/navbar" .}}
|
||||
<div class="container" id="gogs-body" data-page="user-signup">
|
||||
<form action="/user/sign_up" method="post" class="form-horizontal gogs-card" id="gogs-login-card">
|
||||
{{if .DisenableRegisteration}}
|
||||
Sorry, registeration has been disenabled, you can only get account from administrator.
|
||||
{{else}}
|
||||
<h3>Sign Up</h3>
|
||||
<div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>
|
||||
<div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}">
|
||||
|
@ -43,6 +46,7 @@
|
|||
<a href="/user/login">Already have an account? Sign in now!</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</form>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
25
web.go
25
web.go
|
@ -16,11 +16,14 @@ import (
|
|||
|
||||
"github.com/gogits/binding"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/auth"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/mailer"
|
||||
"github.com/gogits/gogs/modules/middleware"
|
||||
"github.com/gogits/gogs/routers"
|
||||
"github.com/gogits/gogs/routers/admin"
|
||||
"github.com/gogits/gogs/routers/dev"
|
||||
"github.com/gogits/gogs/routers/repo"
|
||||
"github.com/gogits/gogs/routers/user"
|
||||
|
@ -35,6 +38,16 @@ gogs web`,
|
|||
Flags: []cli.Flag{},
|
||||
}
|
||||
|
||||
// globalInit is for global configuration reload-able.
|
||||
func globalInit() {
|
||||
base.NewConfigContext()
|
||||
mailer.NewMailerContext()
|
||||
models.LoadModelsConfig()
|
||||
models.LoadRepoConfig()
|
||||
models.NewRepoContext()
|
||||
models.NewEngine()
|
||||
}
|
||||
|
||||
// Check run mode(Default of martini is Dev).
|
||||
func checkRunMode() {
|
||||
switch base.Cfg.MustValue("", "RUN_MODE") {
|
||||
|
@ -58,6 +71,7 @@ func newMartini() *martini.ClassicMartini {
|
|||
}
|
||||
|
||||
func runWeb(*cli.Context) {
|
||||
globalInit()
|
||||
base.NewServices()
|
||||
checkRunMode()
|
||||
log.Info("%s %s", base.AppName, base.AppVer)
|
||||
|
@ -73,7 +87,8 @@ func runWeb(*cli.Context) {
|
|||
|
||||
m.Use(middleware.InitContext())
|
||||
|
||||
reqSignIn, ignSignIn := middleware.SignInRequire(true), middleware.SignInRequire(false)
|
||||
reqSignIn := middleware.SignInRequire(true)
|
||||
ignSignIn := middleware.SignInRequire(base.Service.RequireSignInView)
|
||||
reqSignOut := middleware.SignOutRequire()
|
||||
// Routers.
|
||||
m.Get("/", ignSignIn, routers.Home)
|
||||
|
@ -99,6 +114,14 @@ func runWeb(*cli.Context) {
|
|||
|
||||
m.Get("/help", routers.Help)
|
||||
|
||||
adminReq := middleware.AdminRequire()
|
||||
m.Get("/admin", reqSignIn, adminReq, admin.Dashboard)
|
||||
m.Get("/admin/users", reqSignIn, adminReq, admin.Users)
|
||||
m.Any("/admin/users/new", reqSignIn, adminReq, binding.BindIgnErr(auth.RegisterForm{}), admin.NewUser)
|
||||
m.Any("/admin/users/:userid", reqSignIn, adminReq, binding.BindIgnErr(auth.AdminEditUserForm{}), admin.EditUser)
|
||||
m.Get("/admin/repos", reqSignIn, adminReq, admin.Repositories)
|
||||
m.Get("/admin/config", reqSignIn, adminReq, admin.Config)
|
||||
|
||||
m.Post("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.SettingPost)
|
||||
m.Get("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.Setting)
|
||||
|
||||
|
|
Reference in a new issue