Medium changes (#1642)
* lower top margin for comment usernames & avatar * Update classic.css * temporary workaround to hide quadrupled comments * Update view.jet.html * Add "search from user" input below user rss link, change styling of follow button to fit other links * all into the same div * Remove padding of usersearch input in panel, remove useless css rule * min-width for user buttons, max-width for the input * Don't show "search from this user" if it's your profile * remove max-width when responsive userprofile kicks in * remove useless css rule * Update view.jet.html * le fix * optimize stats.go a bit and add comments * Update stats.go * Update template_functions_test.go * Update template_functions.go * Remove hardcoded theme list, generate dynamically * ditto * Add possibility of forcing a theme for everyone * in the .yml config file * Make it ignore both user settings and cookies * add forced theme in default theme config struct * Update publicSettings.go * fix missing , & travis * Update template_functions_test.go * Update main.css * fix travis * Update template_functions_test.go * travis
Cette révision appartient à :
Parent
67d8492380
révision
88904bade9
13 fichiers modifiés avec 139 ajouts et 41 suppressions
|
@ -41,6 +41,9 @@ func WebAddress() string {
|
|||
|
||||
// DefaultTheme : Return the default theme or default dark theme
|
||||
func DefaultTheme(dark bool) string {
|
||||
if Get().DefaultTheme.Forced != "" {
|
||||
return Get().DefaultTheme.Forced
|
||||
}
|
||||
if !dark {
|
||||
return Get().DefaultTheme.Theme
|
||||
} else {
|
||||
|
|
|
@ -220,3 +220,5 @@ models:
|
|||
default_theme:
|
||||
theme: g
|
||||
dark: tomorrow
|
||||
forced:
|
||||
#Put a theme name in forced to force it for everyone
|
||||
|
|
|
@ -212,6 +212,7 @@ type ModelsConfig struct {
|
|||
type DefaultThemeConfig struct {
|
||||
Theme string `yaml:"theme,omitempty"`
|
||||
Dark string `yaml:"dark,omitempty"`
|
||||
Forced string `yaml:"forced,omitempty"`
|
||||
}
|
||||
|
||||
// SearchConfig : Config struct for search
|
||||
|
|
|
@ -29,35 +29,38 @@ func GetStatsHandler(c *gin.Context) {
|
|||
|
||||
var Trackers []string
|
||||
for _, line := range strings.Split(torrent.Trackers[3:], "&tr=") {
|
||||
//Starts at character 3 because the three first characters are always "tr=" so we need to dismiss them
|
||||
tracker, error := url.QueryUnescape(line)
|
||||
if error == nil && tracker[:6] == "udp://" {
|
||||
Trackers = append(Trackers, tracker)
|
||||
}
|
||||
//Cannot scrape from http trackers so don't put them in the array
|
||||
}
|
||||
|
||||
scraper := goscrape.NewBulk(Trackers)
|
||||
|
||||
stats := scraper.ScrapeBulk([]string{
|
||||
stats := goscrape.Single(Trackers, []string{
|
||||
torrent.Hash,
|
||||
})[0]
|
||||
//Single() returns an array which contain results for each torrent Hash it is fed, since we only feed him one we want to directly access the results
|
||||
|
||||
emptyStats := goscrape.Result{stats.Btih, 0, 0, 0}
|
||||
|
||||
if stats == emptyStats {
|
||||
//If we put seeders on -1, the script instantly knows the fetching did not give any result, avoiding having to check all three stats below and in view.jet.html's javascript
|
||||
if stats.Seeders == 0 && stats.Leechers == 0 && stats.Completed == 0 {
|
||||
stats.Seeders = -1
|
||||
//If we put seeders on -1, the script instantly knows the fetching did not give any result, avoiding having to check all three stats below and in view.jet.html's javascript
|
||||
}
|
||||
|
||||
t, err := template.New("foo").Parse(fmt.Sprintf(`{{define "stats"}}{ "seeders": [%d], "leechers": [%d], "downloads": [%d] }{{end}}`, stats.Seeders, stats.Leechers, stats.Completed))
|
||||
t.ExecuteTemplate(c.Writer, "stats", "")
|
||||
//No idea how to output JSON properly
|
||||
|
||||
//We don't want to do useless DB queries if the stats are empty, and we don't want to overwrite good stats with empty ones
|
||||
if stats.Seeders != -1 {
|
||||
var tmp models.Scrape
|
||||
if models.ORM.Where("torrent_id = ?", id).Find(&tmp).RecordNotFound() {
|
||||
torrent.Scrape = torrent.Scrape.Create(uint(id), uint32(stats.Seeders), uint32(stats.Leechers), uint32(stats.Completed), time.Now())
|
||||
//Create entry in the DB because none exist
|
||||
} else {
|
||||
torrent.Scrape = &models.Scrape{uint(id), uint32(stats.Seeders), uint32(stats.Leechers), uint32(stats.Completed), time.Now()}
|
||||
torrent.Scrape.Update(false)
|
||||
//Entry in the DB already exists, simply update it
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -866,6 +866,9 @@ html, body {
|
|||
width: 100% !important;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.profile-panel .user-search {
|
||||
max-width: none;
|
||||
}
|
||||
.header .h-user {
|
||||
width: 46px;
|
||||
}
|
||||
|
@ -1020,7 +1023,6 @@ html, body {
|
|||
|
||||
.profile-sidebar {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.profile-usertitle {
|
||||
|
@ -1046,6 +1048,9 @@ html, body {
|
|||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.profile-usermenu {
|
||||
min-width: 170px;
|
||||
}
|
||||
.profile-usermenu a {
|
||||
display: block;
|
||||
margin-bottom: 11px;
|
||||
|
@ -1053,10 +1058,6 @@ html, body {
|
|||
.profile-usermenu .icon-rss-squared {
|
||||
vertical-align: top;
|
||||
}
|
||||
.profile-userbuttons {
|
||||
display: inline-flex;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.torrent-hr {
|
||||
font-size: large;
|
||||
|
@ -1131,15 +1132,17 @@ html, body {
|
|||
}
|
||||
|
||||
.comment-box {
|
||||
margin-top: 10px;
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
margin-right: 30px;
|
||||
margin-left: 30px;
|
||||
padding: 0 7px;
|
||||
margin: 10px 30px 0 30px;
|
||||
min-height: 70px;
|
||||
word-break: break-word;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.comment-box p:nth-child(2) {
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
.comment-box img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
@ -2146,13 +2149,16 @@ table.multiple-upload {
|
|||
}
|
||||
|
||||
.user-search {
|
||||
word-spacing: -7px;
|
||||
display:block;
|
||||
padding: 0 1rem;
|
||||
word-spacing: -7px;;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
.profile-panel .user-search {
|
||||
padding: 0;
|
||||
max-width: 170px;
|
||||
}
|
||||
.user-search [type="text"] {
|
||||
vertical-align: top;
|
||||
width: calc(100% - 30px);
|
||||
width: calc(100% - 30px);
|
||||
}
|
||||
|
||||
.torrent-info-row .tr-se span, .torrent-info-row .tr-le span, .torrent-info-row .tr-dl span {
|
||||
|
|
|
@ -31,6 +31,10 @@ body, .header {
|
|||
width: 87%;
|
||||
}
|
||||
|
||||
.comment-box p:nth-child(2) {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.form-refine span.spacing {
|
||||
font-size: 0.9em;
|
||||
width: 90px;
|
||||
|
|
|
@ -17,24 +17,16 @@
|
|||
</div>
|
||||
<!-- END SIDEBAR USER TITLE -->
|
||||
<!-- SIDEBAR BUTTONS -->
|
||||
<div class="profile-userbuttons">
|
||||
<div class="profile-usermenu">
|
||||
{{if User.ID > 0 }}
|
||||
{{if !User.CurrentUserIdentical(UserProfile.ID) }}
|
||||
{{if !User.IsFollower(UserProfile)}}
|
||||
<a class="form-input" href="/user/{{UserProfile.ID}}/{{UserProfile.Username}}/follow" class="form-input btn-green">{{ T("follow")}}</a>
|
||||
<br />
|
||||
{{else}}
|
||||
<a class="form-input" href="/user/{{UserProfile.ID}}/{{UserProfile.Username}}/follow" class="form-input btn-orange">{{ T("unfollow")}}</a>
|
||||
<br/>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
<!-- <button type="button" class="btn btn-danger btn-sm">Message</button> -->
|
||||
</div>
|
||||
<br/>
|
||||
<!-- END SIDEBAR BUTTONS -->
|
||||
<!-- SIDEBAR MENU -->
|
||||
<div class="profile-usermenu">
|
||||
{{ if User.ID > 0 && (User.CurrentUserIdentical(UserProfile.ID) || User.CurrentOrAdmin(UserProfile.ID)) }}
|
||||
<a class="form-input btn-green" href="/user/{{ UserProfile.ID }}/{{ UserProfile.Username }}">
|
||||
{{ else }}
|
||||
|
@ -56,6 +48,13 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
<!-- END MENU -->
|
||||
{{ if User.ID != UserProfile.ID }}
|
||||
<div class="user-search" style="max-width: 160px;">
|
||||
<form role="search" action="/user/{{UserProfile.ID}}/{{UserProfile.Username}}/search" id="header-form" method="get">
|
||||
<input class="form-input" name="q" type="text" placeholder="{{T("search_from_user")}}">
|
||||
<button type="submit" class="form-input icon-search"></button>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
@ -174,15 +174,23 @@
|
|||
</div>
|
||||
|
||||
<p class="torrent-hr" id="comments">{{ T("comments")}}</p>
|
||||
{{idx := 1}}
|
||||
{{previousComment := ""}}
|
||||
{{previousUser := 0}}
|
||||
{{range index, element := Torrent.Comments}}
|
||||
{{if previousComment != element.Content || previousUser != element.UserID}}
|
||||
<div class="torrent-info-box comment-box">
|
||||
<span class="comment-index">
|
||||
<a href="#comment_{{index+1}}">{{index}}</a>
|
||||
<a href="#comment_{{idx}}">{{idx}}</a>
|
||||
<small style="padding-left: 4px;" class="date-short">{{formatDateRFC(element.Date)}}</small>
|
||||
</span>
|
||||
<p><img src="https://www.gravatar.com/avatar/{{ element.UserAvatar }}"/><a {{if element.UserID > 0}}href="/user/{{element.UserID}}/{{element.Username}}"{{end}} class="comment-user">{{element.Username}}</a></p>
|
||||
<p class="comment-content">{{element.Content|raw}}</p>
|
||||
</div>
|
||||
{{idx = idx + 1}}
|
||||
{{end}}
|
||||
{{previousComment = element.Content}}
|
||||
{{previousUser = element.UserID}}
|
||||
{{end}}
|
||||
{{ if len(Torrent.Comments) == 0 }}
|
||||
<p id="no-comment-message">{{ T("torrent_no_comments") }}</p>
|
||||
|
|
|
@ -65,10 +65,9 @@
|
|||
<td>
|
||||
<select id="theme-selector" name="theme" class="form-input up-input" onchange="switchThemes()">
|
||||
<option value="">{{ T("theme_select")}}</option>
|
||||
<option value="g"{{ if Theme == "g" }} selected{{end}}>/g/</option>
|
||||
<option value="tomorrow"{{ if Theme == "tomorrow" }} selected{{end}}>Tomorrow</option>
|
||||
<option value="classic"{{ if Theme == "classic" }} selected{{end}}>nyaa.se (beta)</option>
|
||||
<option value="classic_colors"{{ if Theme == "classic_colors" }} selected{{end}}>Classic Colors</option>
|
||||
{{ range theme := getThemeList()}}
|
||||
<option value="{{theme}}"{{ if Theme == theme}} selected{{end}}>{{formatThemeName(theme)}}</option>
|
||||
{{end}}
|
||||
<option value=""{{ if Theme == "" }} selected{{end}}>{{ T("theme_none")}}</option>
|
||||
</select>
|
||||
</td>
|
||||
|
|
|
@ -18,10 +18,9 @@
|
|||
<span class="form-group">
|
||||
<h3>{{ T("theme")}}</h3>
|
||||
<select id="theme-selector" name="theme" class="form-input" onchange="switchThemes()">
|
||||
<option value="g"{{ if Theme == "g" || Theme == "" }} selected{{end}}>/g/</option>
|
||||
<option value="tomorrow"{{ if Theme == "tomorrow" }} selected{{end}}>Tomorrow</option>
|
||||
<option value="classic"{{ if Theme == "classic" }} selected{{end}}>nyaa.se (Beta)</option>
|
||||
<option value="classic_colors"{{ if Theme == "classic_colors" }} selected{{end}}>Classic Colors</option>
|
||||
{{ range theme := getThemeList()}}
|
||||
<option value="{{theme}}"{{ if Theme == theme}} selected{{end}}>{{formatThemeName(theme)}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</span>
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package templates
|
|||
|
||||
import (
|
||||
"html/template"
|
||||
"path/filepath"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
|
@ -56,6 +57,8 @@ func templateFunctions(vars jet.VarMap) jet.VarMap {
|
|||
vars.Set("kilo_strfind", kilo_strfind)
|
||||
vars.Set("kilo_rand", kilo_rand)
|
||||
vars.Set("getDomainName", getDomainName)
|
||||
vars.Set("getThemeList", getThemeList)
|
||||
vars.Set("formatThemeName", formatThemeName)
|
||||
return vars
|
||||
}
|
||||
func getRawQuery(currentURL *url.URL) string {
|
||||
|
@ -396,3 +399,38 @@ func getDomainName() string {
|
|||
}
|
||||
return domain
|
||||
}
|
||||
|
||||
func getThemeList() ([]string) {
|
||||
searchDir := "public/css/themes/"
|
||||
|
||||
themeList := []string{}
|
||||
|
||||
filepath.Walk(searchDir, func(path string, f os.FileInfo, err error) error {
|
||||
if kilo_strfind(path, ".css", len(searchDir)) {
|
||||
//we only want .css file
|
||||
|
||||
fileName := path[len(searchDir):strings.Index(path, ".css")]
|
||||
//Remove file extension and path, keep only file name
|
||||
|
||||
themeList = append(themeList, fileName)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return themeList
|
||||
}
|
||||
|
||||
func formatThemeName(name string) string {
|
||||
Name := name
|
||||
|
||||
if len(Name) == 1 {
|
||||
Name = fmt.Sprintf("/%c/", Name[0])
|
||||
} else if name == "classic" {
|
||||
Name = "nyaa.se (Beta)"
|
||||
} else {
|
||||
Name = strings.Replace(Name, "_", " ", -1)
|
||||
Name = strings.Title(Name)
|
||||
//Upper case at each start of word
|
||||
}
|
||||
return Name
|
||||
}
|
||||
|
|
|
@ -727,7 +727,37 @@ func TestRand(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTheme(t *testing.T) {
|
||||
var tests = []struct {
|
||||
domainName []string
|
||||
}{
|
||||
{
|
||||
domainName: []string{"test", "test", "test"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
test.domainName = getThemeList()
|
||||
}
|
||||
}
|
||||
|
||||
func testformatThemeName(t *testing.T) {
|
||||
var tests = []struct {
|
||||
domainName string
|
||||
}{
|
||||
{
|
||||
domainName: "test",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
value := formatThemeName("path")
|
||||
if value != test.domainName {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func mockupTemplateT(t *testing.T) publicSettings.TemplateTfunc {
|
||||
conf := config.Get().I18n
|
||||
conf.Directory = path.Join("..", conf.Directory)
|
||||
|
@ -743,7 +773,7 @@ func mockupTemplateT(t *testing.T) publicSettings.TemplateTfunc {
|
|||
t.Error("Couldn't load language files!")
|
||||
}
|
||||
var T publicSettings.TemplateTfunc
|
||||
T = func(id string, args ...interface{}) template.HTML {
|
||||
T = func(id string, args ...interface{}) template.HTML {
|
||||
return template.HTML(fmt.Sprintf(Ts(id), args...))
|
||||
}
|
||||
return T
|
||||
|
|
|
@ -172,6 +172,9 @@ func GetTfuncFromRequest(c *gin.Context) TemplateTfunc {
|
|||
|
||||
// GetThemeFromRequest : Gets the user selected theme from the request
|
||||
func GetThemeFromRequest(c *gin.Context) string {
|
||||
if config.Get().DefaultTheme.Forced != "" {
|
||||
return config.Get().DefaultTheme.Forced
|
||||
}
|
||||
user, _ := getCurrentUser(c)
|
||||
if user.ID > 0 && user.Theme != "" {
|
||||
return user.Theme
|
||||
|
@ -185,6 +188,9 @@ func GetThemeFromRequest(c *gin.Context) string {
|
|||
|
||||
// GetDarkThemeFromRequest : Gets the default dark theme
|
||||
func GetDarkThemeFromRequest(c *gin.Context) string {
|
||||
if config.Get().DefaultTheme.Forced != "" {
|
||||
return config.Get().DefaultTheme.Forced
|
||||
}
|
||||
return config.DefaultTheme(true)
|
||||
}
|
||||
|
||||
|
|
Référencer dans un nouveau ticket