révision
6fba6241a5
|
@ -11,7 +11,7 @@ that anyone will be able to deploy locally or remotely.
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
Ubuntu 17.04 fails to build, use a different OS or docker
|
Ubuntu 17.04 fails to build, use a different OS or docker
|
||||||
* Install [Golang](https://golang.org/doc/install)
|
* Install [Golang](https://golang.org/doc/install) (version >=1.8)
|
||||||
* `go get github.com/ewhal/nyaa`
|
* `go get github.com/ewhal/nyaa`
|
||||||
* `go build`
|
* `go build`
|
||||||
* Download DB and place it in your root folder named as "nyaa.db"
|
* Download DB and place it in your root folder named as "nyaa.db"
|
||||||
|
|
|
@ -16,4 +16,4 @@ const (
|
||||||
EmailTimeout = 10 * time.Second
|
EmailTimeout = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
var EmailTokenHashKey = []byte("CHANGE_THIS_BEFORE_DEPLOYING_YOU_RETARD")
|
var EmailTokenHashKey = []byte("CHANGE_THIS_BEFORE_DEPLOYING_YOU_GIT")
|
||||||
|
|
4
main.go
4
main.go
|
@ -38,8 +38,8 @@ func RunServer(conf *config.Config) {
|
||||||
|
|
||||||
// Set up server,
|
// Set up server,
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
WriteTimeout: 15 * time.Second,
|
WriteTimeout: 24 * time.Second,
|
||||||
ReadTimeout: 15 * time.Second,
|
ReadTimeout: 8 * time.Second,
|
||||||
}
|
}
|
||||||
l, err := network.CreateHTTPListener(conf)
|
l, err := network.CreateHTTPListener(conf)
|
||||||
log.CheckError(err)
|
log.CheckError(err)
|
||||||
|
|
|
@ -345,6 +345,12 @@ footer {
|
||||||
text-shadow: -1px -1px #999999;
|
text-shadow: -1px -1px #999999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select#bottom_language_selector {
|
||||||
|
width: 20%;
|
||||||
|
margin: auto;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* Force images on description to fit width */
|
/* Force images on description to fit width */
|
||||||
#description img {
|
#description img {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -47,3 +47,36 @@ for(var i in list) {
|
||||||
var date = new Date(e.innerText);
|
var date = new Date(e.innerText);
|
||||||
e.innerText = date.toDateString() + " " + date.toLocaleTimeString();
|
e.innerText = date.toDateString() + " " + date.toLocaleTimeString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadLanguages() {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (xhr.readyState == 4 && xhr.status == 200) {
|
||||||
|
var selector = document.getElementById("bottom_language_selector");
|
||||||
|
selector.hidden = false
|
||||||
|
/* Response format is
|
||||||
|
* { "current": "(user current language)",
|
||||||
|
* "languages": {
|
||||||
|
* "(language_code)": "(language_name"),
|
||||||
|
* }} */
|
||||||
|
var response = JSON.parse(xhr.responseText);
|
||||||
|
for (var language in response.languages) {
|
||||||
|
if (!response.languages.hasOwnProperty(language)) continue;
|
||||||
|
|
||||||
|
var opt = document.createElement("option")
|
||||||
|
opt.value = language
|
||||||
|
opt.innerHTML = response.languages[language]
|
||||||
|
if (language == response.current) {
|
||||||
|
opt.selected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
selector.appendChild(opt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.open("GET", "/language?format=json", true)
|
||||||
|
xhr.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
loadLanguages();
|
||||||
|
|
||||||
|
|
62
router/changeLanguageHandler.go
Fichier normal
62
router/changeLanguageHandler.go
Fichier normal
|
@ -0,0 +1,62 @@
|
||||||
|
package router;
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ewhal/nyaa/util/languages"
|
||||||
|
"github.com/ewhal/nyaa/service/user"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LanguagesJSONResponse struct {
|
||||||
|
Current string `json:"current"`
|
||||||
|
Languages map[string]string `json:"languages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SeeLanguagesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, Tlang := languages.GetTfuncAndLanguageFromRequest(r, "en-us")
|
||||||
|
availableLanguages := languages.GetAvailableLanguages()
|
||||||
|
|
||||||
|
format := r.URL.Query().Get("format")
|
||||||
|
if format == "json" {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
err := json.NewEncoder(w).Encode(LanguagesJSONResponse{Tlang.Tag, availableLanguages})
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clv := ChangeLanguageVariables{NewSearchForm(), Navigation{}, Tlang.Tag, availableLanguages, GetUser(r), r.URL, mux.CurrentRoute(r)}
|
||||||
|
languages.SetTranslationFromRequest(changeLanguageTemplate, r, "en-us")
|
||||||
|
err := changeLanguageTemplate.ExecuteTemplate(w, "index.html", clv)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeLanguageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
lang := r.FormValue("language")
|
||||||
|
|
||||||
|
availableLanguages := languages.GetAvailableLanguages()
|
||||||
|
if _, exists := availableLanguages[lang]; !exists {
|
||||||
|
http.Error(w, "Language not available", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If logged in, update user language; if not, set cookie.
|
||||||
|
user, err := userService.CurrentUser(r)
|
||||||
|
if err == nil {
|
||||||
|
user.Language = lang
|
||||||
|
// I don't know if I should use this...
|
||||||
|
userService.UpdateUserCore(&user)
|
||||||
|
} else {
|
||||||
|
http.SetCookie(w, &http.Cookie{Name: "lang", Value: lang})
|
||||||
|
}
|
||||||
|
|
||||||
|
url, _ := Router.Get("home").URL()
|
||||||
|
http.Redirect(w, r, url.String(), http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
|
@ -33,7 +33,8 @@ func IndexModPanel(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
languages.SetTranslationFromRequest(panelIndex, r, "en-us")
|
languages.SetTranslationFromRequest(panelIndex, r, "en-us")
|
||||||
htv := PanelIndexVbs{torrents, model.TorrentReportsToJSON(torrentReports), users, comments, NewSearchForm(), currentUser, r.URL}
|
htv := PanelIndexVbs{torrents, model.TorrentReportsToJSON(torrentReports), users, comments, NewSearchForm(), currentUser, r.URL}
|
||||||
_ = panelIndex.ExecuteTemplate(w, "admin_index.html", htv)
|
err := panelIndex.ExecuteTemplate(w, "admin_index.html", htv)
|
||||||
|
log.CheckError(err)
|
||||||
} else {
|
} else {
|
||||||
http.Error(w, "admins only", http.StatusForbidden)
|
http.Error(w, "admins only", http.StatusForbidden)
|
||||||
}
|
}
|
||||||
|
@ -217,7 +218,8 @@ func TorrentPostEditModPanel(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
languages.SetTranslationFromRequest(panelTorrentEd, r, "en-us")
|
languages.SetTranslationFromRequest(panelTorrentEd, r, "en-us")
|
||||||
htv := PanelTorrentEdVbs{uploadForm, NewSearchForm(), currentUser, err, infos, r.URL}
|
htv := PanelTorrentEdVbs{uploadForm, NewSearchForm(), currentUser, err, infos, r.URL}
|
||||||
_ = panelTorrentEd.ExecuteTemplate(w, "admin_index.html", htv)
|
err_ := panelTorrentEd.ExecuteTemplate(w, "admin_index.html", htv)
|
||||||
|
log.CheckError(err_)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommentDeleteModPanel(w http.ResponseWriter, r *http.Request) {
|
func CommentDeleteModPanel(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
@ -112,5 +112,8 @@ func init() {
|
||||||
//Router.HandleFunc("/moderation/report/delete", gzipTorrentReportDeleteHandler).Name("torrent_report_delete").Methods("POST")
|
//Router.HandleFunc("/moderation/report/delete", gzipTorrentReportDeleteHandler).Name("torrent_report_delete").Methods("POST")
|
||||||
//Router.HandleFunc("/moderation/torrent/delete", gzipTorrentDeleteHandler).Name("torrent_delete").Methods("POST")
|
//Router.HandleFunc("/moderation/torrent/delete", gzipTorrentDeleteHandler).Name("torrent_delete").Methods("POST")
|
||||||
|
|
||||||
|
Router.HandleFunc("/language", SeeLanguagesHandler).Methods("GET").Name("see_languages")
|
||||||
|
Router.HandleFunc("/language", ChangeLanguageHandler).Methods("POST").Name("change_language")
|
||||||
|
|
||||||
Router.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
|
Router.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"github.com/ewhal/nyaa/util/search"
|
"github.com/ewhal/nyaa/util/search"
|
||||||
"github.com/gorilla/feeds"
|
"github.com/gorilla/feeds"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,7 +30,7 @@ func RSSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
for i, torrent := range torrents {
|
for i, torrent := range torrents {
|
||||||
torrentJSON := torrent.ToJSON()
|
torrentJSON := torrent.ToJSON()
|
||||||
feed.Items[i] = &feeds.Item{
|
feed.Items[i] = &feeds.Item{
|
||||||
Id: "https://" + config.WebAddress + "/view/" + strconv.FormatUint(uint64(torrents[i].ID), 10),
|
Id: "https://" + config.WebAddress + "/view/" + torrentJSON.ID,
|
||||||
Title: torrent.Name,
|
Title: torrent.Name,
|
||||||
Link: &feeds.Link{Href: string(torrentJSON.Magnet)},
|
Link: &feeds.Link{Href: string(torrentJSON.Magnet)},
|
||||||
Description: string(torrentJSON.Description),
|
Description: string(torrentJSON.Description),
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
|
|
||||||
var TemplateDir = "templates"
|
var TemplateDir = "templates"
|
||||||
|
|
||||||
var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate, viewProfileEditTemplate, viewUserDeleteTemplate, notFoundTemplate *template.Template
|
var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate, viewProfileEditTemplate, viewUserDeleteTemplate, notFoundTemplate, changeLanguageTemplate *template.Template
|
||||||
|
|
||||||
var panelIndex, panelTorrentList, panelUserList, panelCommentList, panelTorrentEd, panelTorrentReportList *template.Template
|
var panelIndex, panelTorrentList, panelUserList, panelCommentList, panelTorrentEd, panelTorrentReportList *template.Template
|
||||||
|
|
||||||
|
@ -86,6 +86,11 @@ func ReloadTemplates() {
|
||||||
name: "404",
|
name: "404",
|
||||||
file: "404.html",
|
file: "404.html",
|
||||||
},
|
},
|
||||||
|
templateLoader{
|
||||||
|
templ: &changeLanguageTemplate,
|
||||||
|
name: "change_language",
|
||||||
|
file: "change_language.html",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for idx := range pubTempls {
|
for idx := range pubTempls {
|
||||||
pubTempls[idx].indexFile = filepath.Join(TemplateDir, "index.html")
|
pubTempls[idx].indexFile = filepath.Join(TemplateDir, "index.html")
|
||||||
|
|
|
@ -26,6 +26,16 @@ var FuncMap = template.FuncMap{
|
||||||
}
|
}
|
||||||
return "error"
|
return "error"
|
||||||
},
|
},
|
||||||
|
"genViewTorrentRoute": func(torrent_id uint) string {
|
||||||
|
// Helper for when you have an uint while genRoute("view_torrent", ...) takes a string
|
||||||
|
// FIXME better solution?
|
||||||
|
s := strconv.FormatUint(uint64(torrent_id), 10)
|
||||||
|
url, err := Router.Get("view_torrent").URL("id", s)
|
||||||
|
if err == nil {
|
||||||
|
return url.String()
|
||||||
|
}
|
||||||
|
return "error"
|
||||||
|
},
|
||||||
"genNav": func(nav Navigation, currentUrl *url.URL, pagesSelectable int) template.HTML {
|
"genNav": func(nav Navigation, currentUrl *url.URL, pagesSelectable int) template.HTML {
|
||||||
var ret = ""
|
var ret = ""
|
||||||
if (nav.TotalItem > 0) {
|
if (nav.TotalItem > 0) {
|
||||||
|
|
|
@ -113,6 +113,17 @@ type UploadTemplateVariables struct {
|
||||||
Route *mux.Route
|
Route *mux.Route
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChangeLanguageVariables struct {
|
||||||
|
Search SearchForm
|
||||||
|
Navigation Navigation
|
||||||
|
Language string
|
||||||
|
Languages map[string]string
|
||||||
|
User *model.User
|
||||||
|
URL *url.URL
|
||||||
|
Route *mux.Route
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* MODERATION Variables */
|
/* MODERATION Variables */
|
||||||
|
|
||||||
type PanelIndexVbs struct {
|
type PanelIndexVbs struct {
|
||||||
|
|
|
@ -129,9 +129,10 @@ func getTorrentsOrderBy(parameters *serviceBase.WhereParams, orderBy string, lim
|
||||||
if conditions != "" {
|
if conditions != "" {
|
||||||
dbQuery = dbQuery + " WHERE " + conditions
|
dbQuery = dbQuery + " WHERE " + conditions
|
||||||
}
|
}
|
||||||
if strings.Contains(conditions, "torrent_name") {
|
/* This makes all queries take roughly the same amount of time (lots)...
|
||||||
|
if strings.Contains(conditions, "torrent_name") && offset > 0 {
|
||||||
dbQuery = "WITH t AS (SELECT * FROM torrents WHERE " + conditions + ") SELECT * FROM t"
|
dbQuery = "WITH t AS (SELECT * FROM torrents WHERE " + conditions + ") SELECT * FROM t"
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if orderBy == "" { // default OrderBy
|
if orderBy == "" { // default OrderBy
|
||||||
orderBy = "torrent_id DESC"
|
orderBy = "torrent_id DESC"
|
||||||
|
|
|
@ -7,9 +7,12 @@
|
||||||
<th class="col-xs-1">Uploader</th>
|
<th class="col-xs-1">Uploader</th>
|
||||||
<th class="col-xs-1">Action</th>
|
<th class="col-xs-1">Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
{{ range .Torrents}}
|
{{range .Torrents}}
|
||||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">{{ .Name }}</a></td><td><a href="{{ genRoute "mod_tlist" }}?userID={{.UploaderID}}">{{ .UploaderID }}</a></td>
|
<tr>
|
||||||
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
|
<td><a href="{{ genViewTorrentRoute .ID }}">{{ .Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">Edit</a>)</td>
|
||||||
|
<td><a href="{{ genRoute "mod_tlist" }}?userID={{.UploaderID}}">{{ .UploaderID }}</a></td>
|
||||||
|
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td>
|
||||||
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</table>
|
</table>
|
||||||
<nav class="torrentNav" aria-label="Page navigation">
|
<nav class="torrentNav" aria-label="Page navigation">
|
||||||
|
@ -21,7 +24,6 @@
|
||||||
|
|
||||||
<h3 id="torrents">Last Torrents Report</h3>
|
<h3 id="torrents">Last Torrents Report</h3>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
{{ range .TorrentReports}}
|
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-xs-9">Torrent Name</th>
|
<th class="col-xs-9">Torrent Name</th>
|
||||||
<th class="col-xs-1">User</th>
|
<th class="col-xs-1">User</th>
|
||||||
|
@ -29,8 +31,13 @@
|
||||||
<th class="col-xs-1">Action</th>
|
<th class="col-xs-1">Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">{{ .Torrent.Name }}</a></td><td>{{.User.Username}}</td><td>{{.Description}}</td>
|
{{range .TorrentReports}}
|
||||||
<td><a href="{{ genRoute "mod_trdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
|
<tr>
|
||||||
|
<td><a href="{{ genRoute "view_torrent" "id" .Torrent.ID }}">{{ .Torrent.Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">Edit</a>)</td>
|
||||||
|
<td>{{.User.Username}}</td>
|
||||||
|
<td>{{.Description}}</td>
|
||||||
|
<td><a href="{{ genRoute "mod_trdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td>
|
||||||
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</table>
|
</table>
|
||||||
<nav class="torrentNav" aria-label="Page navigation">
|
<nav class="torrentNav" aria-label="Page navigation">
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
<th class="col-xs-1">Action</th>
|
<th class="col-xs-1">Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{{ range .TorrentReports}}
|
{{range .TorrentReports}}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{ genRoute "mod_tedit"}}?id={{.Torrent.ID}}">{{.Torrent.Name}}</a></td>
|
<td><a href="{{ genRoute "view_torrent" "id" .Torrent.ID }}">{{ .Torrent.Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">Edit</a>)</td>
|
||||||
<td>{{.User.Username}}</td>
|
<td>{{.User.Username}}</td>
|
||||||
<td>{{.Description}}</td>
|
<td>{{.Description}}</td>
|
||||||
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .Torrent.ID }}">Delete</a>
|
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .Torrent.ID }}" onclick="if (!confirm('Are you sure?')) return false;">Delete</a><br />
|
||||||
<a href="{{ genRoute "mod_trdelete" }}?id={{ .ID }}">Delete Report</a></td>
|
<a href="{{ genRoute "mod_trdelete" }}?id={{ .ID }}">Delete Report</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{{ range .Torrents}}
|
{{ range .Torrents}}
|
||||||
|
<tr>
|
||||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">{{ .Name }}</a></td><td><a href="{{ genRoute "mod_tlist" }}?userID={{.UploaderID}}">{{ .UploaderID }}</a></td>
|
<td><a href="{{ genViewTorrentRoute .ID }}">{{ .Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">Edit</a>)</td>
|
||||||
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
|
<td><a href="{{ genRoute "mod_tlist" }}?userID={{.UploaderID}}">{{ .UploaderID }}</a></td>
|
||||||
|
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td>
|
||||||
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</table>
|
</table>
|
||||||
<nav class="torrentNav" aria-label="Page navigation">
|
<nav class="torrentNav" aria-label="Page navigation">
|
||||||
|
|
24
templates/change_language.html
Fichier normal
24
templates/change_language.html
Fichier normal
|
@ -0,0 +1,24 @@
|
||||||
|
{{define "title"}}{{T "change_language"}}{{end}}
|
||||||
|
{{define "content"}}
|
||||||
|
<div class="blockBody">
|
||||||
|
<hr>
|
||||||
|
<form role="form" method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="language">{{T "language"}}</label>
|
||||||
|
<div class="ui-select">
|
||||||
|
<select id="language" name="language" class="form-control">
|
||||||
|
{{ $currentLanguage := .Language }}
|
||||||
|
{{ range $tag, $translatedName := $.Languages }}
|
||||||
|
<option value="{{ $tag }}" {{ if eq $currentLanguage $tag }}selected{{end}}>{{ $translatedName }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-success">{{T "save_changes"}}</button>
|
||||||
|
</form>
|
||||||
|
<div style="padding-bottom: 1em"></div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,13 @@
|
||||||
Powered by NyaaPantsu
|
Powered by NyaaPantsu
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<form method="POST" action="{{ .URL.Parse "/language" }}" id="bottom_language_selector_form">
|
||||||
|
<select id="bottom_language_selector" name="language" onchange="javascript:document.getElementById('bottom_language_selector_form').submit()" hidden class="form-control"></select>
|
||||||
|
</form>
|
||||||
|
<noscript>
|
||||||
|
<center><a href="{{ .URL.Parse "/language" }}">{{ T "change_language" }}</a></center>
|
||||||
|
</noscript>
|
||||||
|
|
||||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
{{ if HasAdmin $.User}}
|
{{ if HasAdmin $.User}}
|
||||||
<a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i></a>
|
<a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i></a>
|
||||||
|
<a href="{{ genRoute "mod_tedit" }}?id={{ .ID }}" class="btn btn-warning btn-lg"><i class="glyphicon glyphicon-pencil"></i></a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
|
|
|
@ -546,5 +546,9 @@
|
||||||
{
|
{
|
||||||
"id": "delete_success",
|
"id": "delete_success",
|
||||||
"translation": "S'ha suprimit el compte!"
|
"translation": "S'ha suprimit el compte!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Català"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -598,5 +598,9 @@
|
||||||
{
|
{
|
||||||
"id": "completed",
|
"id": "completed",
|
||||||
"translation": "Komplett"
|
"translation": "Komplett"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Deutsch"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -225,7 +225,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "notice_keep_seeding",
|
"id": "notice_keep_seeding",
|
||||||
"translation": "NOTICE: KEEP SEEDING AND ENABLE DHT YOU RETARD"
|
"translation": "NOTICE: KEEP SEEDING AND ENABLE DHT YOU GIT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "official_nyaapocalipse_faq",
|
"id": "official_nyaapocalipse_faq",
|
||||||
|
@ -273,7 +273,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "answer_is_sukebei_db_lost",
|
"id": "answer_is_sukebei_db_lost",
|
||||||
"translation": "Sukebei, however might be in worse shape. Currently we only have sukebei databases up to 2016, but a newer database might be available for use."
|
"translation": "Sukebei is safe too, and almost nothing is lost either."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "how_are_we_recovering",
|
"id": "how_are_we_recovering",
|
||||||
|
@ -317,7 +317,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "answer_how_can_i_help",
|
"id": "answer_how_can_i_help",
|
||||||
"translation": "If you have website development expertise, you can join the #nyaapantsu IRC channel on irc.rizon.net. If you have any current databases, especially for sukebei, <b>UPLOAD THEM</b>."
|
"translation": "If you have website development expertise, you can join the #nyaapantsu IRC channel on irc.rizon.net. If you have any current databases, especially for sukebei, please upload them."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "your_design_sucks_found_a_bug",
|
"id": "your_design_sucks_found_a_bug",
|
||||||
|
@ -618,5 +618,13 @@
|
||||||
{
|
{
|
||||||
"id": "completed",
|
"id": "completed",
|
||||||
"translation": "Completed"
|
"translation": "Completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "change_language",
|
||||||
|
"translation": "Change Language"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "English"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -542,5 +542,9 @@
|
||||||
{
|
{
|
||||||
"id": "delete_success",
|
"id": "delete_success",
|
||||||
"translation": "Se ha eliminado exitosamente la cuenta!"
|
"translation": "Se ha eliminado exitosamente la cuenta!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Español"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -617,6 +617,10 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "completed",
|
"id": "completed",
|
||||||
"translation": "Complété"
|
"translation": "Terminé"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Français"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -546,5 +546,9 @@
|
||||||
{
|
{
|
||||||
"id": "delete_success",
|
"id": "delete_success",
|
||||||
"translation": "A fiók törlésre került."
|
"translation": "A fiók törlésre került."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Magyar"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -614,5 +614,9 @@
|
||||||
{
|
{
|
||||||
"id": "completed",
|
"id": "completed",
|
||||||
"translation": "Completato"
|
"translation": "Completato"
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Italiano"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":"signup_box_title",
|
"id":"signup_box_title",
|
||||||
"translation": "登録しましょう。<small>ずっと無料です</small>"
|
"translation": "登録 <small>ずっと無料です</small>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":"username",
|
"id":"username",
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":"sign_in_box_title",
|
"id":"sign_in_box_title",
|
||||||
"translation": "ログインします"
|
"translation": "ログイン"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":"sign_in_title",
|
"id":"sign_in_title",
|
||||||
|
@ -125,7 +125,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":"see_more_torrents_from",
|
"id":"see_more_torrents_from",
|
||||||
"translation": "%s の Torrent をもっと見る"
|
"translation": "%s の Torrent をもっと表示"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id":"category",
|
"id":"category",
|
||||||
|
@ -181,7 +181,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "404_not_found",
|
"id": "404_not_found",
|
||||||
"translation": "404 見つかりません"
|
"translation": "404 ページが見つかりません"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "no_torrents_uploaded",
|
"id": "no_torrents_uploaded",
|
||||||
|
@ -197,7 +197,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "member",
|
"id": "member",
|
||||||
"translation": "メンバー"
|
"translation": "会員"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "sign_in",
|
"id": "sign_in",
|
||||||
|
@ -217,11 +217,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "official_nyaapocalipse_faq",
|
"id": "official_nyaapocalipse_faq",
|
||||||
"translation": "nyaa.se の閉鎖についてよくある質問"
|
"translation": "公式 nyaa <ruby>黙示録<rp>(</rp><rt>アポカリプス</rt><rp>)</rp></ruby> のよくある質問"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "links_replacement_mirror",
|
"id": "links_replacement_mirror",
|
||||||
"translation": "nyaa.se のミラーリンク"
|
"translation": "代替 / ミラーリンク"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "what_happened",
|
"id": "what_happened",
|
||||||
|
@ -233,7 +233,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "its_not_a_ddos",
|
"id": "its_not_a_ddos",
|
||||||
"translation": "以前のような DDoS 攻撃ではなく、ドメインが利用停止になった。"
|
"translation": "いつものような DDoS 攻撃ではなく、利用停止になった。"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "future_not_looking_good",
|
"id": "future_not_looking_good",
|
||||||
|
@ -269,7 +269,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "answer_how_are_we_recovering",
|
"id": "answer_how_are_we_recovering",
|
||||||
"translation": "上述のデータベースは nyaa.pantsu.cat と sukebei.pantsu.cat にホストされています。検索機能はすでにあり、nyaa.se にあったほぼすべての機能が近いうちに利用可能になるでしょう。また、Seeder / Leecher 統計はスクレイピングによって収集可能ですが、今は他に優先すべきことがあるため後回しにされます。"
|
"translation": "上述のデータベースは nyaa.pantsu.cat と sukebei.pantsu.cat にホストされています。検索機能はすでにあり、近いうちに nyaa.se にあったほぼすべての機能が利用可能になるでしょう。また、Seeder / Leecher 統計はスクレイピングによって収集可能ですが、今は他に優先すべきことがあるため後回しにされます。"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "are_the_trackers_working",
|
"id": "are_the_trackers_working",
|
||||||
|
@ -277,15 +277,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "answer_are_the_trackers_working",
|
"id": "answer_are_the_trackers_working",
|
||||||
"translation": "トラッカーがダウンしたとしても、シーダーはまだ DHT ネットワークに接続しているはずです。ファイルが DHT ネットワーク上にリスティングされてさえいれば、いつも通り利用できます。"
|
"translation": "トラッカーがダウンしたとしても、Seeder はまだ DHT ネットワークに接続しているはずです。ファイルが DHT ネットワーク上にリスティングされてさえいれば、いつも通り利用できます。"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "how_do_i_download_the_torrents",
|
"id": "how_do_i_download_the_torrents",
|
||||||
"translation": "どうやって Torrent ファイルをダウンロードすればいいの?"
|
"translation": "どうやって Torrent をダウンロードすればいいの?"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "answer_how_do_i_download_the_torrents",
|
"id": "answer_how_do_i_download_the_torrents",
|
||||||
"translation": "<b>magnet リンク</b> をご利用ください。magnet リンクは BitTorrent クライアントが DHT ネットワーク上のファイルを見つけるのに利用されます。もちろんいつも通りダウンロードすることができます。"
|
"translation": "<b>magnet リンク</b> をご利用ください。magnet リンクは BitTorrent クライアントが DHT ネットワーク上のファイルを検出するのに利用されます。もちろんいつも通りダウンロードすることができます。"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "magnet_link_should_look_like",
|
"id": "magnet_link_should_look_like",
|
||||||
|
@ -329,7 +329,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "uploading_file_prefills_fields",
|
"id": "uploading_file_prefills_fields",
|
||||||
"translation": "Torrent ファイルをアップロードしている間にいくつかの項目を入力できます。そうすることをおすすめします。"
|
"translation": "Torrent ファイルをアップロードする前にいくつかの項目を入力できます。そうすることをお勧めします。"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "magnet_link",
|
"id": "magnet_link",
|
||||||
|
@ -521,7 +521,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "trusted_member",
|
"id": "trusted_member",
|
||||||
"translation": "信頼されたメンバー"
|
"translation": "信頼された会員"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "moderator",
|
"id": "moderator",
|
||||||
|
@ -590,5 +590,20 @@
|
||||||
{
|
{
|
||||||
"id":"date_format",
|
"id":"date_format",
|
||||||
"translation": "2006/01/02 15:04"
|
"translation": "2006/01/02 15:04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "日本語"
|
||||||
|
},
|
||||||
|
"id": "seeders",
|
||||||
|
"translation": "Seeder 数"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "leechers",
|
||||||
|
"translation": "Leecher 数"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "completed",
|
||||||
|
"translation": "完了数"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -494,5 +494,9 @@
|
||||||
{
|
{
|
||||||
"id": "submit",
|
"id": "submit",
|
||||||
"translation": "확인"
|
"translation": "확인"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "한국어"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -578,5 +578,9 @@
|
||||||
{
|
{
|
||||||
"id": "email_changed",
|
"id": "email_changed",
|
||||||
"translation": "Epost-addressen har blitt endret! Neste steg er å bekrefte den (sjekk innboksen)."
|
"translation": "Epost-addressen har blitt endret! Neste steg er å bekrefte den (sjekk innboksen)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Norsk bokmål"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -546,5 +546,9 @@
|
||||||
{
|
{
|
||||||
"id": "delete_success",
|
"id": "delete_success",
|
||||||
"translation": "Het account is met succes verwijderd!"
|
"translation": "Het account is met succes verwijderd!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Nederlands"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -578,5 +578,13 @@
|
||||||
{
|
{
|
||||||
"id": "profile_edit_page",
|
"id": "profile_edit_page",
|
||||||
"translation": "Editar o perfil de %s"
|
"translation": "Editar o perfil de %s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "change_language",
|
||||||
|
"translation": "Mudar Idioma"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Português (Brasil)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -614,5 +614,9 @@
|
||||||
{
|
{
|
||||||
"id": "completed",
|
"id": "completed",
|
||||||
"translation": "Скачавшие"
|
"translation": "Скачавшие"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Русский"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -578,5 +578,9 @@
|
||||||
{
|
{
|
||||||
"id": "email_changed",
|
"id": "email_changed",
|
||||||
"translation": "Din mailadress har bytts! Du måste bekräfta adressen genom att klicka på länken som skickats till %s."
|
"translation": "Din mailadress har bytts! Du måste bekräfta adressen genom att klicka på länken som skickats till %s."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "Svenska"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -614,5 +614,9 @@
|
||||||
{
|
{
|
||||||
"id": "completed",
|
"id": "completed",
|
||||||
"translation": "โหลดเสร็จแล้ว"
|
"translation": "โหลดเสร็จแล้ว"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language_name",
|
||||||
|
"translation": "ไทย"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,25 +4,26 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ewhal/nyaa/service/user"
|
"github.com/ewhal/nyaa/service/user"
|
||||||
"github.com/nicksnyder/go-i18n/i18n"
|
"github.com/nicksnyder/go-i18n/i18n"
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/language"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// When go-i18n finds a language with >0 translations, it uses it as the Tfunc
|
// When go-i18n finds a language with >0 translations, it uses it as the Tfunc
|
||||||
// However, if said language has a missing translation, it won't fallback to the "main" language
|
// However, if said language has a missing translation, it won't fallback to the "main" language
|
||||||
func TfuncWithFallback(language string, languages ...string) (i18n.TranslateFunc, error) {
|
func TfuncAndLanguageWithFallback(language string, languages ...string) (i18n.TranslateFunc, *language.Language, error) {
|
||||||
// Use the last language on the args as the fallback one.
|
// Use the last language on the args as the fallback one.
|
||||||
fallbackLanguage := language
|
fallbackLanguage := language
|
||||||
if languages != nil {
|
if languages != nil {
|
||||||
fallbackLanguage = languages[len(languages)-1]
|
fallbackLanguage = languages[len(languages)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
T, err1 := i18n.Tfunc(language, languages...)
|
T, Tlang, err1 := i18n.TfuncAndLanguage(language, languages...)
|
||||||
fallbackT, err2 := i18n.Tfunc(fallbackLanguage)
|
fallbackT, fallbackTlang, err2 := i18n.TfuncAndLanguage(fallbackLanguage)
|
||||||
|
|
||||||
if err1 != nil && err2 != nil {
|
if err1 != nil && err2 != nil {
|
||||||
// fallbackT is still a valid function even with the error, it returns translationID.
|
// fallbackT is still a valid function even with the error, it returns translationID.
|
||||||
return fallbackT, err2
|
return fallbackT, fallbackTlang, err2
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(translationID string, args ...interface{}) string {
|
return func(translationID string, args ...interface{}) string {
|
||||||
|
@ -31,7 +32,7 @@ func TfuncWithFallback(language string, languages ...string) (i18n.TranslateFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
return fallbackT(translationID, args...)
|
return fallbackT(translationID, args...)
|
||||||
}, nil
|
}, Tlang, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAvailableLanguages() (languages map[string]string) {
|
func GetAvailableLanguages() (languages map[string]string) {
|
||||||
|
@ -50,8 +51,7 @@ func GetAvailableLanguages() (languages map[string]string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetTranslation(tmpl *template.Template, language string, languages ...string) i18n.TranslateFunc {
|
func setTranslation(tmpl *template.Template, T i18n.TranslateFunc) {
|
||||||
T, _ := TfuncWithFallback(language, languages...)
|
|
||||||
tmpl.Funcs(map[string]interface{}{
|
tmpl.Funcs(map[string]interface{}{
|
||||||
"T": func(str string, args ...interface{}) template.HTML {
|
"T": func(str string, args ...interface{}) template.HTML {
|
||||||
return template.HTML(fmt.Sprintf(T(str), args...))
|
return template.HTML(fmt.Sprintf(T(str), args...))
|
||||||
|
@ -60,10 +60,9 @@ func SetTranslation(tmpl *template.Template, language string, languages ...strin
|
||||||
return fmt.Sprintf(T(str), args...)
|
return fmt.Sprintf(T(str), args...)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return T
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetTranslationFromRequest(tmpl *template.Template, r *http.Request, defaultLanguage string) i18n.TranslateFunc {
|
func GetTfuncAndLanguageFromRequest(r *http.Request, defaultLanguage string) (T i18n.TranslateFunc, Tlang *language.Language) {
|
||||||
userLanguage := ""
|
userLanguage := ""
|
||||||
user, _, err := userService.RetrieveCurrentUser(r)
|
user, _, err := userService.RetrieveCurrentUser(r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -78,6 +77,14 @@ func SetTranslationFromRequest(tmpl *template.Template, r *http.Request, default
|
||||||
|
|
||||||
// go-i18n supports the format of the Accept-Language header, thankfully.
|
// go-i18n supports the format of the Accept-Language header, thankfully.
|
||||||
headerLanguage := r.Header.Get("Accept-Language")
|
headerLanguage := r.Header.Get("Accept-Language")
|
||||||
r.Header.Add("Vary", "Accept-Encoding")
|
T, Tlang, _ = TfuncAndLanguageWithFallback(userLanguage, cookieLanguage, headerLanguage, defaultLanguage)
|
||||||
return SetTranslation(tmpl, userLanguage, cookieLanguage, headerLanguage, defaultLanguage)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func SetTranslationFromRequest(tmpl *template.Template, r *http.Request, defaultLanguage string) i18n.TranslateFunc {
|
||||||
|
r.Header.Add("Vary", "Accept-Encoding")
|
||||||
|
T, _ := GetTfuncAndLanguageFromRequest(r, defaultLanguage)
|
||||||
|
setTranslation(tmpl, T)
|
||||||
|
return T
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,31 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var searchOperator string
|
var searchOperator string
|
||||||
|
var useTSQuery bool
|
||||||
|
|
||||||
func Configure(conf *config.SearchConfig) (err error) {
|
func Configure(conf *config.SearchConfig) (err error) {
|
||||||
// SQLite has case-insensitive LIKE, but no ILIKE
|
useTSQuery = false
|
||||||
if db.ORM.Dialect().GetName() == "sqlite3" {
|
// Postgres needs ILIKE for case-insensitivity
|
||||||
searchOperator = "LIKE ?"
|
if db.ORM.Dialect().GetName() == "postgres" {
|
||||||
} else {
|
|
||||||
searchOperator = "ILIKE ?"
|
searchOperator = "ILIKE ?"
|
||||||
|
//useTSQuery = true
|
||||||
|
// !!DISABLED!! because this makes search a lot stricter
|
||||||
|
// (only matches at word borders)
|
||||||
|
} else {
|
||||||
|
searchOperator = "LIKE ?"
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stringIsAscii(input string) bool {
|
||||||
|
for _, char := range input {
|
||||||
|
if char > 127 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func SearchByQuery(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
|
func SearchByQuery(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
|
||||||
search, tor, count, err = searchByQuery(r, pagenum, true)
|
search, tor, count, err = searchByQuery(r, pagenum, true)
|
||||||
return
|
return
|
||||||
|
@ -164,7 +178,7 @@ func searchByQuery(r *http.Request, pagenum int, countAll bool) (
|
||||||
}
|
}
|
||||||
|
|
||||||
searchQuerySplit := strings.Fields(search.Query)
|
searchQuerySplit := strings.Fields(search.Query)
|
||||||
for i, word := range searchQuerySplit {
|
for _, word := range searchQuerySplit {
|
||||||
firstRune, _ := utf8.DecodeRuneInString(word)
|
firstRune, _ := utf8.DecodeRuneInString(word)
|
||||||
if len(word) == 1 && unicode.IsPunct(firstRune) {
|
if len(word) == 1 && unicode.IsPunct(firstRune) {
|
||||||
// some queries have a single punctuation character
|
// some queries have a single punctuation character
|
||||||
|
@ -175,9 +189,14 @@ func searchByQuery(r *http.Request, pagenum int, countAll bool) (
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make this faster ?
|
if useTSQuery && stringIsAscii(word) {
|
||||||
|
conditions = append(conditions, "torrent_name @@ plainto_tsquery(?)")
|
||||||
|
parameters.Params = append(parameters.Params, word)
|
||||||
|
} else {
|
||||||
|
// TODO: possible to make this faster?
|
||||||
conditions = append(conditions, "torrent_name "+searchOperator)
|
conditions = append(conditions, "torrent_name "+searchOperator)
|
||||||
parameters.Params = append(parameters.Params, "%"+searchQuerySplit[i]+"%")
|
parameters.Params = append(parameters.Params, "%"+word+"%")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters.Conditions = strings.Join(conditions[:], " AND ")
|
parameters.Conditions = strings.Join(conditions[:], " AND ")
|
||||||
|
|
Référencer dans un nouveau ticket