#13 finish user and repository search

Both are possible on explore and admin panel
This commit is contained in:
Unknwon 2016-03-11 15:33:12 -05:00
parent df2bdf7ea3
commit 2bf8494332
31 changed files with 636 additions and 463 deletions

View File

@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true) ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true)
##### Current version: 0.9.5 ##### Current version: 0.9.6
| Web | UI | Preview | | Web | UI | Preview |
|:-------------:|:-------:|:-------:| |:-------------:|:-------:|:-------:|

View File

@ -193,7 +193,10 @@ func runWeb(ctx *cli.Context) {
// Especially some AJAX requests, we can reduce middleware number to improve performance. // Especially some AJAX requests, we can reduce middleware number to improve performance.
// Routers. // Routers.
m.Get("/", ignSignIn, routers.Home) m.Get("/", ignSignIn, routers.Home)
m.Get("/explore", ignSignIn, routers.Explore) m.Group("/explore", func() {
m.Get("/repos", routers.ExploreRepos)
m.Get("/users", routers.ExploreUsers)
}, ignSignIn)
m.Combo("/install", routers.InstallInit).Get(routers.Install). m.Combo("/install", routers.InstallInit).Get(routers.Install).
Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost)
m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues) m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues)

View File

@ -44,13 +44,6 @@ issues = Issues
cancel = Cancel cancel = Cancel
[search]
search = Search...
repository = Repository
user = User
issue = Issue
code = Code
[install] [install]
install = Installation install = Installation
title = Install Steps For First-time Run title = Install Steps For First-time Run
@ -140,6 +133,8 @@ issues.in_your_repos = In your repositories
[explore] [explore]
repos = Repositories repos = Repositories
users = Users
search = Search
[auth] [auth]
create_new_account = Create New Account create_new_account = Create New Account

View File

@ -17,7 +17,7 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const APP_VER = "0.9.5.0311" const APP_VER = "0.9.6.0311"
func init() { func init() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())

View File

@ -169,7 +169,7 @@ func GetOrgByName(name string) (*User, error) {
} }
u := &User{ u := &User{
LowerName: strings.ToLower(name), LowerName: strings.ToLower(name),
Type: ORGANIZATION, Type: USER_TYPE_ORGANIZATION,
} }
has, err := x.Get(u) has, err := x.Get(u)
if err != nil { if err != nil {

View File

@ -1049,11 +1049,16 @@ func CountPublicRepositories() int64 {
return countRepositories(false) return countRepositories(false)
} }
func Repositories(page, pageSize int) (_ []*Repository, err error) {
repos := make([]*Repository, 0, pageSize)
return repos, x.Limit(pageSize, (page-1)*pageSize).Asc("id").Find(&repos)
}
// RepositoriesWithUsers returns number of repos in given page. // RepositoriesWithUsers returns number of repos in given page.
func RepositoriesWithUsers(page, pageSize int) (_ []*Repository, err error) { func RepositoriesWithUsers(page, pageSize int) (_ []*Repository, err error) {
repos := make([]*Repository, 0, pageSize) repos, err := Repositories(page, pageSize)
if err = x.Limit(pageSize, (page-1)*pageSize).Asc("id").Find(&repos); err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("Repositories: %v", err)
} }
for i := range repos { for i := range repos {
@ -1474,9 +1479,9 @@ func GetRepositories(uid int64, private bool) ([]*Repository, error) {
} }
// GetRecentUpdatedRepositories returns the list of repositories that are recently updated. // GetRecentUpdatedRepositories returns the list of repositories that are recently updated.
func GetRecentUpdatedRepositories(page int) (repos []*Repository, err error) { func GetRecentUpdatedRepositories(page, pageSize int) (repos []*Repository, err error) {
return repos, x.Limit(setting.ExplorePagingNum, (page-1)*setting.ExplorePagingNum). return repos, x.Limit(pageSize, (page-1)*pageSize).
Where("is_private=?", false).Limit(setting.ExplorePagingNum).Desc("updated_unix").Find(&repos) Where("is_private=?", false).Limit(pageSize).Desc("updated_unix").Find(&repos)
} }
func getRepositoryCount(e Engine, u *User) (int64, error) { func getRepositoryCount(e Engine, u *User) (int64, error) {
@ -1488,32 +1493,52 @@ func GetRepositoryCount(u *User) (int64, error) {
return getRepositoryCount(x, u) return getRepositoryCount(x, u)
} }
type SearchOption struct { type SearchRepoOptions struct {
Keyword string Keyword string
Uid int64 OwnerID int64
Limit int OrderBy string
Private bool Private bool // Include private repositories in results
Page int
PageSize int // Can be smaller than or equal to setting.ExplorePagingNum
} }
// SearchRepositoryByName returns given number of repositories whose name contains keyword. // SearchRepositoryByName takes keyword and part of repository name to search,
func SearchRepositoryByName(opt SearchOption) (repos []*Repository, err error) { // it returns results in given range and number of total results.
if len(opt.Keyword) == 0 { func SearchRepositoryByName(opts *SearchRepoOptions) (repos []*Repository, _ int64, _ error) {
return repos, nil if len(opts.Keyword) == 0 {
return repos, 0, nil
} }
opt.Keyword = strings.ToLower(opt.Keyword) opts.Keyword = strings.ToLower(opts.Keyword)
repos = make([]*Repository, 0, opt.Limit) if opts.PageSize <= 0 || opts.PageSize > setting.ExplorePagingNum {
opts.PageSize = setting.ExplorePagingNum
// Append conditions.
sess := x.Limit(opt.Limit)
if opt.Uid > 0 {
sess.Where("owner_id=?", opt.Uid)
} }
if !opt.Private { if opts.Page <= 0 {
opts.Page = 1
}
repos = make([]*Repository, 0, opts.PageSize)
// Append conditions
sess := x.Where("lower_name like ?", "%"+opts.Keyword+"%")
if opts.OwnerID > 0 {
sess.And("owner_id = ?", opts.OwnerID)
}
if !opts.Private {
sess.And("is_private=?", false) sess.And("is_private=?", false)
} }
sess.And("lower_name like ?", "%"+opt.Keyword+"%").Find(&repos) if len(opts.OrderBy) > 0 {
return repos, err sess.OrderBy(opts.OrderBy)
}
var countSess xorm.Session
countSess = *sess
count, err := countSess.Count(new(Repository))
if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)
}
return repos, count, sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&repos)
} }
// DeleteRepositoryArchives deletes all repositories' archives. // DeleteRepositoryArchives deletes all repositories' archives.

View File

@ -36,8 +36,8 @@ import (
type UserType int type UserType int
const ( const (
INDIVIDUAL UserType = iota // Historic reason to make it starts at 0. USER_TYPE_INDIVIDUAL UserType = iota // Historic reason to make it starts at 0.
ORGANIZATION USER_TYPE_ORGANIZATION
) )
var ( var (
@ -389,7 +389,7 @@ func (u *User) IsWriterOfRepo(repo *Repository) bool {
// IsOrganization returns true if user is actually a organization. // IsOrganization returns true if user is actually a organization.
func (u *User) IsOrganization() bool { func (u *User) IsOrganization() bool {
return u.Type == ORGANIZATION return u.Type == USER_TYPE_ORGANIZATION
} }
// IsUserOrgOwner returns true if user is in the owner team of given organization. // IsUserOrgOwner returns true if user is in the owner team of given organization.
@ -1114,16 +1114,45 @@ func GetUserByEmail(email string) (*User, error) {
return nil, ErrUserNotExist{0, email} return nil, ErrUserNotExist{0, email}
} }
// SearchUserByName returns given number of users whose name contains keyword. type SearchUserOptions struct {
func SearchUserByName(opt SearchOption) (us []*User, err error) { Keyword string
if len(opt.Keyword) == 0 { Type UserType
return us, nil OrderBy string
Page int
PageSize int // Can be smaller than or equal to setting.ExplorePagingNum
} }
opt.Keyword = strings.ToLower(opt.Keyword)
us = make([]*User, 0, opt.Limit) // SearchUserByName takes keyword and part of user name to search,
err = x.Limit(opt.Limit).Where("type=0").And("lower_name like ?", "%"+opt.Keyword+"%").Find(&us) // it returns results in given range and number of total results.
return us, err func SearchUserByName(opts *SearchUserOptions) (users []*User, _ int64, _ error) {
if len(opts.Keyword) == 0 {
return users, 0, nil
}
opts.Keyword = strings.ToLower(opts.Keyword)
if opts.PageSize <= 0 || opts.PageSize > setting.ExplorePagingNum {
opts.PageSize = setting.ExplorePagingNum
}
if opts.Page <= 0 {
opts.Page = 1
}
users = make([]*User, 0, opts.PageSize)
// Append conditions
fmt.Println(opts.Type)
sess := x.Where("lower_name like ?", "%"+opts.Keyword+"%").And("type = ?", opts.Type)
if len(opts.OrderBy) > 0 {
sess.OrderBy(opts.OrderBy)
}
var countSess xorm.Session
countSess = *sess
count, err := countSess.Count(new(User))
if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)
}
return users, count, sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&users)
} }
// ___________ .__ .__ // ___________ .__ .__

File diff suppressed because one or more lines are too long

View File

@ -441,30 +441,30 @@
"outputStyle": 1, "outputStyle": 1,
"syntaxCheckerStyle": 1 "syntaxCheckerStyle": 1
}, },
"\/plugins\/highlight-9.1.0\/default.css": { "\/plugins\/highlight-9.2.0\/default.css": {
"fileType": 16, "fileType": 16,
"ignore": 0, "ignore": 0,
"ignoreWasSetByUser": 0, "ignoreWasSetByUser": 0,
"inputAbbreviatedPath": "\/plugins\/highlight-9.1.0\/default.css", "inputAbbreviatedPath": "\/plugins\/highlight-9.2.0\/default.css",
"outputAbbreviatedPath": "No Output Path", "outputAbbreviatedPath": "No Output Path",
"outputPathIsOutsideProject": 0, "outputPathIsOutsideProject": 0,
"outputPathIsSetByUser": 0 "outputPathIsSetByUser": 0
}, },
"\/plugins\/highlight-9.1.0\/github.css": { "\/plugins\/highlight-9.2.0\/github.css": {
"fileType": 16, "fileType": 16,
"ignore": 0, "ignore": 0,
"ignoreWasSetByUser": 0, "ignoreWasSetByUser": 0,
"inputAbbreviatedPath": "\/plugins\/highlight-9.1.0\/github.css", "inputAbbreviatedPath": "\/plugins\/highlight-9.2.0\/github.css",
"outputAbbreviatedPath": "No Output Path", "outputAbbreviatedPath": "No Output Path",
"outputPathIsOutsideProject": 0, "outputPathIsOutsideProject": 0,
"outputPathIsSetByUser": 0 "outputPathIsSetByUser": 0
}, },
"\/plugins\/highlight-9.1.0\/highlight.pack.js": { "\/plugins\/highlight-9.2.0\/highlight.pack.js": {
"fileType": 64, "fileType": 64,
"ignore": 0, "ignore": 0,
"ignoreWasSetByUser": 0, "ignoreWasSetByUser": 0,
"inputAbbreviatedPath": "\/plugins\/highlight-9.1.0\/highlight.pack.js", "inputAbbreviatedPath": "\/plugins\/highlight-9.2.0\/highlight.pack.js",
"outputAbbreviatedPath": "\/plugins\/highlight-9.1.0\/min\/highlight.pack-min.js", "outputAbbreviatedPath": "\/plugins\/highlight-9.2.0\/min\/highlight.pack-min.js",
"outputPathIsOutsideProject": 0, "outputPathIsOutsideProject": 0,
"outputPathIsSetByUser": 0, "outputPathIsSetByUser": 0,
"outputStyle": 1, "outputStyle": 1,

View File

@ -2594,6 +2594,10 @@ footer .container .links > *:first-child {
padding-top: 15px; padding-top: 15px;
padding-bottom: 80px; padding-bottom: 80px;
} }
.explore .navbar .octicon {
width: 16px;
text-align: center;
}
.ui.repository.list .item { .ui.repository.list .item {
padding-bottom: 25px; padding-bottom: 25px;
} }
@ -2620,3 +2624,26 @@ footer .container .links > *:first-child {
font-size: 12px; font-size: 12px;
color: #808080; color: #808080;
} }
.ui.user.list .item {
padding-bottom: 25px;
}
.ui.user.list .item:not(:first-child) {
border-top: 1px solid #eee;
padding-top: 25px;
}
.ui.user.list .item .ui.avatar.image {
width: 40px;
height: 40px;
}
.ui.user.list .item .description {
margin-top: 5px;
}
.ui.user.list .item .description .octicon:not(:first-child) {
margin-left: 5px;
}
.ui.user.list .item .description a {
color: #333;
}
.ui.user.list .item .description a:hover {
text-decoration: underline;
}

View File

@ -1,6 +1,13 @@
.explore { .explore {
padding-top: 15px; padding-top: 15px;
padding-bottom: @footer-margin * 2; padding-bottom: @footer-margin * 2;
.navbar {
.octicon {
width: 16px;
text-align: center;
}
}
} }
.ui.repository.list { .ui.repository.list {
@ -35,3 +42,34 @@
} }
} }
} }
.ui.user.list {
.item {
padding-bottom: 25px;
&:not(:first-child) {
border-top: 1px solid #eee;
padding-top: 25px;
}
.ui.avatar.image {
width: 40px;
height: 40px;
}
.description {
margin-top: 5px;
.octicon:not(:first-child) {
margin-left: 5px;
}
a {
color: #333;
&:hover {
text-decoration: underline;
}
}
}
}
}

View File

@ -5,12 +5,11 @@
package admin package admin
import ( import (
"github.com/Unknwon/paginater"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/routers"
) )
const ( const (
@ -22,22 +21,6 @@ func Organizations(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminOrganizations"] = true ctx.Data["PageIsAdminOrganizations"] = true
total := models.CountOrganizations() routers.RenderUserSearch(ctx, models.USER_TYPE_ORGANIZATION, models.CountOrganizations, models.Organizations,
page := ctx.QueryInt("page") setting.AdminOrgPagingNum, "id ASC", ORGS)
if page <= 1 {
page = 1
}
ctx.Data["Page"] = paginater.New(int(total), setting.AdminOrgPagingNum, page, 5)
orgs, err := models.Organizations(page, setting.AdminOrgPagingNum)
if err != nil {
ctx.Handle(500, "Organizations", err)
return
}
ctx.Data["Orgs"] = orgs
ctx.Data["Total"] = total
ctx.HTML(200, ORGS)
} }

View File

@ -5,13 +5,12 @@
package admin package admin
import ( import (
"github.com/Unknwon/paginater"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/routers"
) )
const ( const (
@ -23,22 +22,8 @@ func Repos(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminRepositories"] = true ctx.Data["PageIsAdminRepositories"] = true
total := models.CountRepositories() routers.RenderRepoSearch(ctx, models.CountRepositories, models.Repositories,
page := ctx.QueryInt("page") setting.AdminRepoPagingNum, "id ASC", REPOS)
if page <= 1 {
page = 1
}
ctx.Data["Page"] = paginater.New(int(total), setting.AdminRepoPagingNum, page, 5)
repos, err := models.RepositoriesWithUsers(page, setting.AdminRepoPagingNum)
if err != nil {
ctx.Handle(500, "RepositoriesWithUsers", err)
return
}
ctx.Data["Repos"] = repos
ctx.Data["Total"] = total
ctx.HTML(200, REPOS)
} }
func DeleteRepo(ctx *context.Context) { func DeleteRepo(ctx *context.Context) {

View File

@ -8,7 +8,6 @@ import (
"strings" "strings"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/Unknwon/paginater"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/auth"
@ -17,6 +16,7 @@ import (
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/mailer"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/routers"
) )
const ( const (
@ -30,22 +30,8 @@ func Users(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminUsers"] = true ctx.Data["PageIsAdminUsers"] = true
total := models.CountUsers() routers.RenderUserSearch(ctx, models.USER_TYPE_INDIVIDUAL, models.CountUsers, models.Users,
page := ctx.QueryInt("page") setting.AdminUserPagingNum, "id ASC", USERS)
if page <= 1 {
page = 1
}
ctx.Data["Page"] = paginater.New(int(total), setting.AdminUserPagingNum, page, 5)
users, err := models.Users(page, setting.AdminUserPagingNum)
if err != nil {
ctx.Handle(500, "Users", err)
return
}
ctx.Data["Users"] = users
ctx.Data["Total"] = total
ctx.HTML(200, USERS)
} }
func NewUser(ctx *context.Context) { func NewUser(ctx *context.Context) {

View File

@ -27,7 +27,7 @@ func CreateOrg(ctx *context.Context, form api.CreateOrgOption) {
Website: form.Website, Website: form.Website,
Location: form.Location, Location: form.Location,
IsActive: true, IsActive: true,
Type: models.ORGANIZATION, Type: models.USER_TYPE_ORGANIZATION,
} }
if err := models.CreateOrganization(org, u); err != nil { if err := models.CreateOrganization(org, u); err != nil {
if models.IsErrUserAlreadyExist(err) || if models.IsErrUserAlreadyExist(err) ||

View File

@ -21,21 +21,21 @@ import (
// https://github.com/gogits/go-gogs-client/wiki/Repositories#search-repositories // https://github.com/gogits/go-gogs-client/wiki/Repositories#search-repositories
func Search(ctx *context.Context) { func Search(ctx *context.Context) {
opt := models.SearchOption{ opts := &models.SearchRepoOptions{
Keyword: path.Base(ctx.Query("q")), Keyword: path.Base(ctx.Query("q")),
Uid: com.StrTo(ctx.Query("uid")).MustInt64(), OwnerID: com.StrTo(ctx.Query("uid")).MustInt64(),
Limit: com.StrTo(ctx.Query("limit")).MustInt(), PageSize: com.StrTo(ctx.Query("limit")).MustInt(),
} }
if opt.Limit == 0 { if opts.PageSize == 0 {
opt.Limit = 10 opts.PageSize = 10
} }
// Check visibility. // Check visibility.
if ctx.IsSigned && opt.Uid > 0 { if ctx.IsSigned && opts.OwnerID > 0 {
if ctx.User.Id == opt.Uid { if ctx.User.Id == opts.OwnerID {
opt.Private = true opts.Private = true
} else { } else {
u, err := models.GetUserByID(opt.Uid) u, err := models.GetUserByID(opts.OwnerID)
if err != nil { if err != nil {
ctx.JSON(500, map[string]interface{}{ ctx.JSON(500, map[string]interface{}{
"ok": false, "ok": false,
@ -44,13 +44,13 @@ func Search(ctx *context.Context) {
return return
} }
if u.IsOrganization() && u.IsOwnedBy(ctx.User.Id) { if u.IsOrganization() && u.IsOwnedBy(ctx.User.Id) {
opt.Private = true opts.Private = true
} }
// FIXME: how about collaborators? // FIXME: how about collaborators?
} }
} }
repos, err := models.SearchRepositoryByName(opt) repos, _, err := models.SearchRepositoryByName(opts)
if err != nil { if err != nil {
ctx.JSON(500, map[string]interface{}{ ctx.JSON(500, map[string]interface{}{
"ok": false, "ok": false,

View File

@ -15,15 +15,16 @@ import (
// https://github.com/gogits/go-gogs-client/wiki/Users#search-users // https://github.com/gogits/go-gogs-client/wiki/Users#search-users
func Search(ctx *context.Context) { func Search(ctx *context.Context) {
opt := models.SearchOption{ opts := &models.SearchUserOptions{
Keyword: ctx.Query("q"), Keyword: ctx.Query("q"),
Limit: com.StrTo(ctx.Query("limit")).MustInt(), Type: models.USER_TYPE_INDIVIDUAL,
PageSize: com.StrTo(ctx.Query("limit")).MustInt(),
} }
if opt.Limit == 0 { if opts.PageSize == 0 {
opt.Limit = 10 opts.PageSize = 10
} }
us, err := models.SearchUserByName(opt) users, _, err := models.SearchUserByName(opts)
if err != nil { if err != nil {
ctx.JSON(500, map[string]interface{}{ ctx.JSON(500, map[string]interface{}{
"ok": false, "ok": false,
@ -32,16 +33,16 @@ func Search(ctx *context.Context) {
return return
} }
results := make([]*api.User, len(us)) results := make([]*api.User, len(users))
for i := range us { for i := range users {
results[i] = &api.User{ results[i] = &api.User{
ID: us[i].Id, ID: users[i].Id,
UserName: us[i].Name, UserName: users[i].Name,
AvatarUrl: us[i].AvatarLink(), AvatarUrl: users[i].AvatarLink(),
FullName: us[i].FullName, FullName: users[i].FullName,
} }
if ctx.IsSigned { if ctx.IsSigned {
results[i].Email = us[i].Email results[i].Email = users[i].Email
} }
} }

View File

@ -19,6 +19,7 @@ import (
const ( const (
HOME base.TplName = "home" HOME base.TplName = "home"
EXPLORE_REPOS base.TplName = "explore/repos" EXPLORE_REPOS base.TplName = "explore/repos"
EXPLORE_USERS base.TplName = "explore/users"
) )
func Home(ctx *context.Context) { func Home(ctx *context.Context) {
@ -43,23 +44,44 @@ func Home(ctx *context.Context) {
ctx.HTML(200, HOME) ctx.HTML(200, HOME)
} }
func Explore(ctx *context.Context) { func RenderRepoSearch(ctx *context.Context,
ctx.Data["Title"] = ctx.Tr("explore") counter func() int64, ranger func(int, int) ([]*models.Repository, error),
ctx.Data["PageIsExplore"] = true pagingNum int, orderBy string, tplName base.TplName) {
ctx.Data["PageIsExploreRepositories"] = true
page := ctx.QueryInt("page") page := ctx.QueryInt("page")
if page <= 1 { if page <= 1 {
page = 1 page = 1
} }
ctx.Data["Page"] = paginater.New(int(models.CountPublicRepositories()), setting.ExplorePagingNum, page, 5) var (
repos []*models.Repository
count int64
err error
)
repos, err := models.GetRecentUpdatedRepositories(page) keyword := ctx.Query("q")
if len(keyword) == 0 {
repos, err = ranger(page, pagingNum)
if err != nil { if err != nil {
ctx.Handle(500, "GetRecentUpdatedRepositories", err) ctx.Handle(500, "ranger", err)
return return
} }
count = counter()
} else {
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
Keyword: keyword,
OrderBy: orderBy,
Page: page,
PageSize: pagingNum,
})
if err != nil {
ctx.Handle(500, "SearchRepositoryByName", err)
return
}
}
ctx.Data["Keyword"] = keyword
ctx.Data["Total"] = count
ctx.Data["Page"] = paginater.New(int(count), pagingNum, page, 5)
for _, repo := range repos { for _, repo := range repos {
if err = repo.GetOwner(); err != nil { if err = repo.GetOwner(); err != nil {
ctx.Handle(500, "GetOwner", fmt.Errorf("%d: %v", repo.ID, err)) ctx.Handle(500, "GetOwner", fmt.Errorf("%d: %v", repo.ID, err))
@ -68,7 +90,68 @@ func Explore(ctx *context.Context) {
} }
ctx.Data["Repos"] = repos ctx.Data["Repos"] = repos
ctx.HTML(200, EXPLORE_REPOS) ctx.HTML(200, tplName)
}
func ExploreRepos(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("explore")
ctx.Data["PageIsExplore"] = true
ctx.Data["PageIsExploreRepositories"] = true
RenderRepoSearch(ctx, models.CountPublicRepositories, models.GetRecentUpdatedRepositories,
setting.ExplorePagingNum, "updated_unix DESC", EXPLORE_REPOS)
}
func RenderUserSearch(ctx *context.Context, userType models.UserType,
counter func() int64, ranger func(int, int) ([]*models.User, error),
pagingNum int, orderBy string, tplName base.TplName) {
page := ctx.QueryInt("page")
if page <= 1 {
page = 1
}
var (
users []*models.User
count int64
err error
)
keyword := ctx.Query("q")
if len(keyword) == 0 {
users, err = ranger(page, pagingNum)
if err != nil {
ctx.Handle(500, "ranger", err)
return
}
count = counter()
} else {
users, count, err = models.SearchUserByName(&models.SearchUserOptions{
Keyword: keyword,
Type: userType,
OrderBy: orderBy,
Page: page,
PageSize: pagingNum,
})
if err != nil {
ctx.Handle(500, "SearchUserByName", err)
return
}
}
ctx.Data["Keyword"] = keyword
ctx.Data["Total"] = count
ctx.Data["Page"] = paginater.New(int(count), pagingNum, page, 5)
ctx.Data["Users"] = users
ctx.HTML(200, tplName)
}
func ExploreUsers(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("explore")
ctx.Data["PageIsExplore"] = true
ctx.Data["PageIsExploreUsers"] = true
RenderUserSearch(ctx, models.USER_TYPE_INDIVIDUAL, models.CountUsers, models.Users,
setting.ExplorePagingNum, "updated_unix DESC", EXPLORE_USERS)
} }
func NotFound(ctx *context.Context) { func NotFound(ctx *context.Context) {

View File

@ -33,7 +33,7 @@ func CreatePost(ctx *context.Context, form auth.CreateOrgForm) {
org := &models.User{ org := &models.User{
Name: form.OrgName, Name: form.OrgName,
IsActive: true, IsActive: true,
Type: models.ORGANIZATION, Type: models.USER_TYPE_ORGANIZATION,
} }
if err := models.CreateOrganization(org, ctx.User); err != nil { if err := models.CreateOrganization(org, ctx.User); err != nil {

View File

@ -1 +1 @@
0.9.5.0311 0.9.6.0311

View File

@ -0,0 +1,23 @@
{{with .Page}}
{{if gt .TotalPages 1}}
<div class="center page buttons">
<div class="ui borderless pagination menu">
<a class="{{if .IsFirst}}disabled{{end}} item" href="{{$.Link}}?q={{$.Keyword}}"><i class="angle double left icon"></i> {{$.i18n.Tr "admin.first_page"}}</a>
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}&q={{$.Keyword}}"{{end}}>
<i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
</a>
{{range .Pages}}
{{if eq .Num -1}}
<a class="disabled item">...</a>
{{else}}
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?page={{.Num}}&q={{$.Keyword}}"{{end}}>{{.Num}}</a>
{{end}}
{{end}}
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?page={{.Next}}&q={{$.Keyword}}"{{end}}>
{{$.i18n.Tr "repo.issues.next"}}&nbsp;<i class="icon right arrow"></i>
</a>
<a class="{{if .IsLast}}disabled{{end}} item" href="{{$.Link}}?page={{.TotalPages}}&q={{$.Keyword}}">{{$.i18n.Tr "admin.last_page"}}&nbsp;<i class="angle double right icon"></i></a>
</div>
</div>
{{end}}
{{end}}

View File

@ -0,0 +1,6 @@
<form class="ui form">
<div class="ui fluid action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
<button class="ui blue button">{{.i18n.Tr "explore.search"}}</button>
</div>
</form>

View File

@ -8,6 +8,9 @@
<h4 class="ui top attached header"> <h4 class="ui top attached header">
{{.i18n.Tr "admin.orgs.org_manage_panel"}} ({{.i18n.Tr "admin.total" .Total}}) {{.i18n.Tr "admin.orgs.org_manage_panel"}} ({{.i18n.Tr "admin.total" .Total}})
</h4> </h4>
<div class="ui attached segment">
{{template "admin/base/search" .}}
</div>
<div class="ui attached table segment"> <div class="ui attached table segment">
<table class="ui very basic striped table"> <table class="ui very basic striped table">
<thead> <thead>
@ -22,7 +25,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{range .Orgs}} {{range .Users}}
<tr> <tr>
<td>{{.Id}}</td> <td>{{.Id}}</td>
<td><a href="{{.HomeLink}}">{{.Name}}</a></td> <td><a href="{{.HomeLink}}">{{.Name}}</a></td>
@ -37,29 +40,7 @@
</table> </table>
</div> </div>
{{with .Page}} {{template "admin/base/page" .}}
{{if gt .TotalPages 1}}
<div class="center page buttons">
<div class="ui borderless pagination menu">
<a class="{{if .IsFirst}}disabled{{end}} item" href="{{$.Link}}"><i class="angle double left icon"></i> {{$.i18n.Tr "admin.first_page"}}</a>
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}"{{end}}>
<i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
</a>
{{range .Pages}}
{{if eq .Num -1}}
<a class="disabled item">...</a>
{{else}}
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?page={{.Num}}"{{end}}>{{.Num}}</a>
{{end}}
{{end}}
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?page={{.Next}}"{{end}}>
{{$.i18n.Tr "repo.issues.next"}}&nbsp;<i class="icon right arrow"></i>
</a>
<a class="{{if .IsLast}}disabled{{end}} item" href="{{$.Link}}?page={{.TotalPages}}">{{$.i18n.Tr "admin.last_page"}}&nbsp;<i class="angle double right icon"></i></a>
</div>
</div>
{{end}}
{{end}}
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,6 +8,9 @@
<h4 class="ui top attached header"> <h4 class="ui top attached header">
{{.i18n.Tr "admin.repos.repo_manage_panel"}} ({{.i18n.Tr "admin.total" .Total}}) {{.i18n.Tr "admin.repos.repo_manage_panel"}} ({{.i18n.Tr "admin.total" .Total}})
</h4> </h4>
<div class="ui attached segment">
{{template "admin/base/search" .}}
</div>
<div class="ui attached table segment"> <div class="ui attached table segment">
<table class="ui very basic striped table"> <table class="ui very basic striped table">
<thead> <thead>
@ -41,29 +44,7 @@
</table> </table>
</div> </div>
{{with .Page}} {{template "admin/base/page" .}}
{{if gt .TotalPages 1}}
<div class="center page buttons">
<div class="ui borderless pagination menu">
<a class="{{if .IsFirst}}disabled{{end}} item" href="{{$.Link}}"><i class="angle double left icon"></i> {{$.i18n.Tr "admin.first_page"}}</a>
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}"{{end}}>
<i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
</a>
{{range .Pages}}
{{if eq .Num -1}}
<a class="disabled item">...</a>
{{else}}
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?page={{.Num}}"{{end}}>{{.Num}}</a>
{{end}}
{{end}}
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?page={{.Next}}"{{end}}>
{{$.i18n.Tr "repo.issues.next"}}&nbsp;<i class="icon right arrow"></i>
</a>
<a class="{{if .IsLast}}disabled{{end}} item" href="{{$.Link}}?page={{.TotalPages}}">{{$.i18n.Tr "admin.last_page"}}&nbsp;<i class="angle double right icon"></i></a>
</div>
</div>
{{end}}
{{end}}
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,9 +8,12 @@
<h4 class="ui top attached header"> <h4 class="ui top attached header">
{{.i18n.Tr "admin.users.user_manage_panel"}} ({{.i18n.Tr "admin.total" .Total}}) {{.i18n.Tr "admin.users.user_manage_panel"}} ({{.i18n.Tr "admin.total" .Total}})
<div class="ui right"> <div class="ui right">
<a class="ui blue tiny button" href="{{AppSubUrl}}/admin/users/new">{{.i18n.Tr "admin.users.new_account"}}</a> <a class="ui black tiny button" href="{{AppSubUrl}}/admin/users/new">{{.i18n.Tr "admin.users.new_account"}}</a>
</div> </div>
</h4> </h4>
<div class="ui attached segment">
{{template "admin/base/search" .}}
</div>
<div class="ui attached table segment"> <div class="ui attached table segment">
<table class="ui very basic striped table"> <table class="ui very basic striped table">
<thead> <thead>
@ -42,29 +45,7 @@
</table> </table>
</div> </div>
{{with .Page}} {{template "admin/base/page" .}}
{{if gt .TotalPages 1}}
<div class="center page buttons">
<div class="ui borderless pagination menu">
<a class="{{if .IsFirst}}disabled{{end}} item" href="{{$.Link}}"><i class="angle double left icon"></i> {{$.i18n.Tr "admin.first_page"}}</a>
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}"{{end}}>
<i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
</a>
{{range .Pages}}
{{if eq .Num -1}}
<a class="disabled item">...</a>
{{else}}
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?page={{.Num}}"{{end}}>{{.Num}}</a>
{{end}}
{{end}}
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?page={{.Next}}"{{end}}>
{{$.i18n.Tr "repo.issues.next"}}&nbsp;<i class="icon right arrow"></i>
</a>
<a class="{{if .IsLast}}disabled{{end}} item" href="{{$.Link}}?page={{.TotalPages}}">{{$.i18n.Tr "admin.last_page"}}&nbsp;<i class="angle double right icon"></i></a>
</div>
</div>
{{end}}
{{end}}
</div> </div>
</div> </div>
</div> </div>

View File

@ -59,7 +59,7 @@
<a class="item{{if .PageIsHome}} active{{end}}" href="{{AppSubUrl}}/">{{.i18n.Tr "home"}}</a> <a class="item{{if .PageIsHome}} active{{end}}" href="{{AppSubUrl}}/">{{.i18n.Tr "home"}}</a>
{{end}} {{end}}
<a class="item{{if .PageIsExplore}} active{{end}}" href="{{AppSubUrl}}/explore">{{.i18n.Tr "explore"}}</a> <a class="item{{if .PageIsExplore}} active{{end}}" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "explore"}}</a>
{{/*<div class="item"> {{/*<div class="item">
<div class="ui icon input"> <div class="ui icon input">
<input class="searchbox" type="text" placeholder="{{.i18n.Tr "search_project"}}"> <input class="searchbox" type="text" placeholder="{{.i18n.Tr "search_project"}}">

View File

@ -1,8 +1,11 @@
<div class="four wide column"> <div class="four wide column">
<div class="ui vertical menu"> <div class="ui vertical menu navbar">
<div class="header item">{{.i18n.Tr "explore"}}</div> <div class="header item">{{.i18n.Tr "explore"}}</div>
<a class="{{if .PageIsExploreRepositories}}active{{end}} item" href="{{AppSubUrl}}/explore"> <a class="{{if .PageIsExploreRepositories}}active{{end}} item" href="{{AppSubUrl}}/explore/repos">
{{.i18n.Tr "explore.repos"}} <span class="octicon octicon-repo"></span> {{.i18n.Tr "explore.repos"}}
</a>
<a class="{{if .PageIsExploreUsers}}active{{end}} item" href="{{AppSubUrl}}/explore/users">
<span class="octicon octicon-person"></span> {{.i18n.Tr "explore.users"}}
</a> </a>
</div> </div>
</div> </div>

View File

@ -0,0 +1,21 @@
{{with .Page}}
{{if gt .TotalPages 1}}
<div class="center page buttons">
<div class="ui borderless pagination menu">
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}&q={{$.Keyword}}"{{end}}>
<i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
</a>
{{range .Pages}}
{{if eq .Num -1}}
<a class="disabled item">...</a>
{{else}}
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?page={{.Num}}&q={{$.Keyword}}"{{end}}>{{.Num}}</a>
{{end}}
{{end}}
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?page={{.Next}}&q={{$.Keyword}}"{{end}}>
{{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i>
</a>
</div>
</div>
{{end}}
{{end}}

View File

@ -4,29 +4,9 @@
<div class="ui grid"> <div class="ui grid">
{{template "explore/navbar" .}} {{template "explore/navbar" .}}
<div class="twelve wide column content"> <div class="twelve wide column content">
{{template "explore/search" .}}
{{template "explore/repo_list" .}} {{template "explore/repo_list" .}}
{{template "explore/page" .}}
{{with .Page}}
{{if gt .TotalPages 1}}
<div class="center page buttons">
<div class="ui borderless pagination menu">
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}"{{end}}>
<i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
</a>
{{range .Pages}}
{{if eq .Num -1}}
<a class="disabled item">...</a>
{{else}}
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?page={{.Num}}"{{end}}>{{.Num}}</a>
{{end}}
{{end}}
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?page={{.Next}}"{{end}}>
{{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i>
</a>
</div>
</div>
{{end}}
{{end}}
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,7 @@
<form class="ui form">
<div class="ui fluid action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
<button class="ui blue button">{{.i18n.Tr "explore.search"}}</button>
</div>
</form>
<div class="ui divider"></div>

View File

@ -0,0 +1,35 @@
{{template "base/head" .}}
<div class="explore users">
<div class="ui container">
<div class="ui grid">
{{template "explore/navbar" .}}
<div class="twelve wide column content">
{{template "explore/search" .}}
<div class="ui user list">
{{range .Users}}
<div class="item">
<img class="ui avatar image" src="{{.AvatarLink}}">
<div class="content">
<span class="header"><a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}</span>
<div class="description">
{{if .Location}}
<i class="octicon octicon-location"></i> {{.Location}}
{{end}}
{{if and .Email $.IsSigned}}
<i class="octicon octicon-mail"></i>
<a href="mailto:{{.Email}}" rel="nofollow">{{.Email}}</a>
{{end}}
<i class="octicon octicon-clock"></i> {{$.i18n.Tr "user.join_on"}} {{DateFmtShort .Created}}
</div>
</div>
</div>
{{end}}
</div>
{{template "explore/page" .}}
</div>
</div>
</div>
</div>
{{template "base/footer" .}}