Didn't have the chance to migrate //all// templates just yet. We'll get there. * Implement yet another template system * Move orphans to the new system and fix a bug in it * Link orphans in the admin panel * Move the backlink handlers to the web package * Move auth routing to web * Move /user-list to the new system * Move change password and translate it * Move stuff * Move admin-related stuff to the web * Move a lot of files into internal dir Outside of it are web and stuff that needs further refactoring * Fix static not loading and de-qtpl tree * Move tree to internal * Keep the globe on the same line #230 * Revert "Keep the globe on the same line #230" This reverts commit ae78e5e459b1e980ba89bf29e61f75c0625ed2c7. * Migrate templates from hypview: delete, edit, start empty and existing WIP The delete media view was removed, I didn't even know it still existed as a GET. A rudiment. * Make views multi-file and break compilation * Megarefactoring of hypha views * Auth-related stuffs * Fix some of those weird imports * Migrate cat views * Fix cat js * Lower standards * Internalize trauma
126
admin/view.go
@@ -1,126 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"embed"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
const adminTranslationRu = `
|
|
||||||
{{define "panel title"}}Панель админстратора{{end}}
|
|
||||||
{{define "panel safe section title"}}Безопасная секция{{end}}
|
|
||||||
{{define "panel link about"}}Об этой вики{{end}}
|
|
||||||
{{define "panel update header"}}Обновить ссылки в верхней панели{{end}}
|
|
||||||
{{define "panel link user list"}}Список пользователей{{end}}
|
|
||||||
{{define "panel users"}}Управление пользователями{{end}}
|
|
||||||
{{define "panel unsafe section title"}}Опасная секция{{end}}
|
|
||||||
{{define "panel shutdown"}}Выключить вики{{end}}
|
|
||||||
{{define "panel reindex hyphae"}}Переиндексировать гифы{{end}}
|
|
||||||
{{define "panel interwiki"}}Интервики{{end}}
|
|
||||||
|
|
||||||
{{define "manage users"}}Управление пользователями{{end}}
|
|
||||||
{{define "create user"}}Создать пользователя{{end}}
|
|
||||||
{{define "reindex users"}}Переиндексировать пользователей{{end}}
|
|
||||||
{{define "name"}}Имя{{end}}
|
|
||||||
{{define "group"}}Группа{{end}}
|
|
||||||
{{define "registered at"}}Зарегистрирован{{end}}
|
|
||||||
{{define "actions"}}Действия{{end}}
|
|
||||||
{{define "edit"}}Изменить{{end}}
|
|
||||||
|
|
||||||
{{define "new user"}}Новый пользователь{{end}}
|
|
||||||
{{define "password"}}Пароль{{end}}
|
|
||||||
{{define "confirm password"}}Подтвердить пароль{{end}}
|
|
||||||
{{define "change password"}}Изменить пароль{{end}}
|
|
||||||
{{define "non local password change"}}Поменять пароль можно только у локальных пользователей.{{end}}
|
|
||||||
{{define "create"}}Создать{{end}}
|
|
||||||
|
|
||||||
{{define "change group"}}Изменить группу{{end}}
|
|
||||||
{{define "user x"}}Пользователь {{.}}{{end}}
|
|
||||||
{{define "update"}}Обновить{{end}}
|
|
||||||
{{define "delete user"}}Удалить пользователя{{end}}
|
|
||||||
{{define "delete user tip"}}Удаляет пользователя из базы данных. Правки пользователя будут сохранены. Имя пользователя освободится для повторной регистрации.{{end}}
|
|
||||||
|
|
||||||
{{define "delete user?"}}Удалить пользователя {{.}}?{{end}}
|
|
||||||
{{define "delete user warning"}}Вы уверены, что хотите удалить этого пользователя из базы данных? Это действие нельзя отменить.{{end}}
|
|
||||||
`
|
|
||||||
|
|
||||||
var (
|
|
||||||
//go:embed *.html
|
|
||||||
fs embed.FS
|
|
||||||
panelChain, listChain, newUserChain, editUserChain, deleteUserChain viewutil.Chain
|
|
||||||
)
|
|
||||||
|
|
||||||
func Init(rtr *mux.Router) {
|
|
||||||
rtr.HandleFunc("/shutdown", handlerAdminShutdown).Methods(http.MethodPost)
|
|
||||||
rtr.HandleFunc("/reindex-users", handlerAdminReindexUsers).Methods(http.MethodPost)
|
|
||||||
|
|
||||||
rtr.HandleFunc("/new-user", handlerAdminUserNew).Methods(http.MethodGet, http.MethodPost)
|
|
||||||
rtr.HandleFunc("/users/{username}/edit", handlerAdminUserEdit).Methods(http.MethodGet, http.MethodPost)
|
|
||||||
rtr.HandleFunc("/users/{username}/change-password", handlerAdminUserChangePassword).Methods(http.MethodPost)
|
|
||||||
rtr.HandleFunc("/users/{username}/delete", handlerAdminUserDelete).Methods(http.MethodGet, http.MethodPost)
|
|
||||||
rtr.HandleFunc("/users", handlerAdminUsers)
|
|
||||||
|
|
||||||
rtr.HandleFunc("/", handlerAdmin)
|
|
||||||
|
|
||||||
panelChain = viewutil.CopyEnRuWith(fs, "view_panel.html", adminTranslationRu)
|
|
||||||
listChain = viewutil.CopyEnRuWith(fs, "view_user_list.html", adminTranslationRu)
|
|
||||||
newUserChain = viewutil.CopyEnRuWith(fs, "view_new_user.html", adminTranslationRu)
|
|
||||||
editUserChain = viewutil.CopyEnRuWith(fs, "view_edit_user.html", adminTranslationRu)
|
|
||||||
deleteUserChain = viewutil.CopyEnRuWith(fs, "view_delete_user.html", adminTranslationRu)
|
|
||||||
}
|
|
||||||
|
|
||||||
func viewPanel(meta viewutil.Meta) {
|
|
||||||
viewutil.ExecutePage(meta, panelChain, &viewutil.BaseData{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type listData struct {
|
|
||||||
*viewutil.BaseData
|
|
||||||
UserHypha string
|
|
||||||
Users []*user.User
|
|
||||||
}
|
|
||||||
|
|
||||||
func viewList(meta viewutil.Meta, users []*user.User) {
|
|
||||||
viewutil.ExecutePage(meta, listChain, listData{
|
|
||||||
BaseData: &viewutil.BaseData{},
|
|
||||||
UserHypha: cfg.UserHypha,
|
|
||||||
Users: users,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type newUserData struct {
|
|
||||||
*viewutil.BaseData
|
|
||||||
Form util.FormData
|
|
||||||
}
|
|
||||||
|
|
||||||
func viewNewUser(meta viewutil.Meta, form util.FormData) {
|
|
||||||
viewutil.ExecutePage(meta, newUserChain, newUserData{
|
|
||||||
BaseData: &viewutil.BaseData{},
|
|
||||||
Form: form,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type editDeleteUserData struct {
|
|
||||||
*viewutil.BaseData
|
|
||||||
Form util.FormData
|
|
||||||
U *user.User
|
|
||||||
}
|
|
||||||
|
|
||||||
func viewEditUser(meta viewutil.Meta, form util.FormData, u *user.User) {
|
|
||||||
viewutil.ExecutePage(meta, editUserChain, editDeleteUserData{
|
|
||||||
BaseData: &viewutil.BaseData{},
|
|
||||||
Form: form,
|
|
||||||
U: u,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func viewDeleteUser(meta viewutil.Meta, form util.FormData, u *user.User) {
|
|
||||||
viewutil.ExecutePage(meta, deleteUserChain, editDeleteUserData{
|
|
||||||
BaseData: &viewutil.BaseData{},
|
|
||||||
Form: form,
|
|
||||||
U: u,
|
|
||||||
})
|
|
||||||
}
|
|
213
auth/auth.qtpl
@@ -1,213 +0,0 @@
|
|||||||
{% import "net/http" %}
|
|
||||||
{% import "sort" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
|
||||||
|
|
||||||
{% func Register(rq *http.Request) %}
|
|
||||||
{% code
|
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
%}
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
{% if cfg.AllowRegistration %}
|
|
||||||
<form class="modal" method="post" action="/register?{%s rq.URL.RawQuery %}" id="register-form" enctype="multipart/form-data" autocomplete="off">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title">{%s lc.Get("auth.register_header", &l18n.Replacements{"name": cfg.WikiName}) %}</legend>
|
|
||||||
|
|
||||||
<label for="register-form__username">{%s lc.Get("auth.username") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">{%s lc.Get("auth.password") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password">
|
|
||||||
<p>{%s lc.Get("auth.password_tip") %}</p>
|
|
||||||
<p>{%s lc.Get("auth.cookie_tip") %}</p>
|
|
||||||
<button class="btn" type="submit" value="Register">{%s lc.Get("auth.register_button") %}</button>
|
|
||||||
<a class="btn btn_weak" href="/{%s rq.URL.RawQuery %}">{%s lc.Get("ui.cancel") %}</a>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{%= telegramWidget(lc) %}
|
|
||||||
{% elseif cfg.UseAuth %}
|
|
||||||
<p>{%s lc.Get("auth.noregister") %}</p>
|
|
||||||
<p><a href="/{%s rq.URL.RawQuery %}">← {%s lc.Get("auth.go_back") %}</a></p>
|
|
||||||
{% else %}
|
|
||||||
<p>{%s lc.Get("auth.noauth") %}</p>
|
|
||||||
<p><a href="/{%s rq.URL.RawQuery %}">← {%s lc.Get("auth.go_back") %}</a></p>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func Login(lc *l18n.Localizer) %}
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
{% if cfg.UseAuth %}
|
|
||||||
<form class="modal" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title">{%s lc.Get("auth.login_header", &l18n.Replacements{"name": cfg.WikiName}) %}</legend>
|
|
||||||
<label for="login-form__username">{%s lc.Get("auth.username") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">{%s lc.Get("auth.password") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password" autocomplete="current-password">
|
|
||||||
<p>{%s lc.Get("auth.cookie_tip") %}</p>
|
|
||||||
<button class="btn" type="submit" value="Log in">{%s lc.Get("auth.login_button") %}</button>
|
|
||||||
<a class="btn btn_weak" href="/">{%s lc.Get("ui.cancel") %}</a>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{%= telegramWidget(lc) %}
|
|
||||||
{% else %}
|
|
||||||
<p>{%s lc.Get("auth.noauth") %}</p>
|
|
||||||
<p><a class="btn btn_weak" href="/">← {%s lc.Get("auth.go_home") %}</a></p>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
Telegram auth widget was requested by Yogurt. As you can see, we don't offer user administrators control over it. Of course we don't.
|
|
||||||
{% func telegramWidget(lc *l18n.Localizer) %}
|
|
||||||
{% if cfg.TelegramEnabled %}
|
|
||||||
<p class="telegram-notice">{%s lc.Get("auth.telegram_tip") %}</p>
|
|
||||||
<script async src="https://telegram.org/js/telegram-widget.js?15" data-telegram-login="{%s cfg.TelegramBotName %}" data-size="medium" data-userpic="false" data-auth-url="{%s cfg.URL %}/telegram-login"></script>
|
|
||||||
{% endif %}
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func LoginError(err string, lc *l18n.Localizer) %}
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
{% switch err %}
|
|
||||||
{% case "unknown username" %}
|
|
||||||
<p class="error">{%s lc.Get("auth.error_username") %}</p>
|
|
||||||
{% case "wrong password" %}
|
|
||||||
<p class="error">{%s lc.Get("auth.error_password") %}</p>
|
|
||||||
{% default %}
|
|
||||||
<p class="error">{%s err %}</p>
|
|
||||||
{% endswitch %}
|
|
||||||
<p><a href="/login">← {%s lc.Get("auth.try_again") %}</a></p>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func Logout(can bool, lc *l18n.Localizer) %}
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
{% if can %}
|
|
||||||
<h1>{%s lc.Get("auth.logout_header") %}</h1>
|
|
||||||
<form method="POST" action="/logout">
|
|
||||||
<input class="btn btn_accent" type="submit" value="{%s lc.Get("auth.logout_button") %}"/>
|
|
||||||
<a class="btn btn_weak" href="/">{%s lc.Get("auth.go_home") %}</a>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<p>{%s lc.Get("auth.logout_anon") %}</p>
|
|
||||||
<p><a href="/login">{%s lc.Get("auth.login_title") %}</a></p>
|
|
||||||
<p><a href="/">← {%s lc.Get("auth.go_home") %}</a></p>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func Lock(lc *l18n.Localizer) %}
|
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>🔒 {%s lc.Get("auth.lock_title") %}</title>
|
|
||||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main class="locked-notice main-width">
|
|
||||||
<section class="locked-notice__message">
|
|
||||||
<p class="locked-notice__lock">🔒</p>
|
|
||||||
<h1 class="locked-notice__title">{%s lc.Get("auth.lock_title") %}</h1>
|
|
||||||
<form class="locked-notice__login-form" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
|
||||||
<label for="login-form__username">{%s lc.Get("auth.username") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">{%s lc.Get("auth.password") %}</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password" autocomplete="current-password">
|
|
||||||
<br>
|
|
||||||
<button class="btn" type="submit" value="Log in">{%s lc.Get("auth.login_button") %}</button>
|
|
||||||
</form>
|
|
||||||
{%= telegramWidget(lc) %}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% code
|
|
||||||
var userListL10n = map[string]L10nEntry{
|
|
||||||
"heading": En("List of users").Ru("Список пользователей"),
|
|
||||||
"administrators": En("Administrators").Ru("Администраторы"),
|
|
||||||
"moderators": En("Moderators").Ru("Модераторы"),
|
|
||||||
"editors": En("Editors").Ru("Редакторы"),
|
|
||||||
"readers": En("Readers").Ru("Читатели"),
|
|
||||||
}
|
|
||||||
%}
|
|
||||||
|
|
||||||
{% func UserList(lc *l18n.Localizer) %}
|
|
||||||
<main class="main-width user-list">
|
|
||||||
{% code
|
|
||||||
var get = func(key string) string {
|
|
||||||
return userListL10n[key].Get(lc.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
admins = make([]string, 0)
|
|
||||||
moderators = make([]string, 0)
|
|
||||||
editors = make([]string, 0)
|
|
||||||
readers = make([]string, 0)
|
|
||||||
)
|
|
||||||
for u := range user.YieldUsers() {
|
|
||||||
switch u.Group {
|
|
||||||
// What if we place the users into sorted slices?
|
|
||||||
case "admin":
|
|
||||||
admins = append(admins, u.Name)
|
|
||||||
case "moderator":
|
|
||||||
moderators = append(moderators, u.Name)
|
|
||||||
case "editor", "trusted":
|
|
||||||
editors = append(editors, u.Name)
|
|
||||||
case "reader":
|
|
||||||
readers = append(readers, u.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(admins)
|
|
||||||
sort.Strings(moderators)
|
|
||||||
sort.Strings(editors)
|
|
||||||
sort.Strings(readers)
|
|
||||||
%}
|
|
||||||
<h1>{%s get("heading") %}</h1>
|
|
||||||
<section>
|
|
||||||
<h2>{%s get("administrators") %}</h2>
|
|
||||||
<ol>{% for _, name := range admins %}
|
|
||||||
<li><a href="/hypha/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
|
||||||
{% endfor %}</ol>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>{%s get("moderators") %}</h2>
|
|
||||||
<ol>{% for _, name := range moderators %}
|
|
||||||
<li><a href="/hypha/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
|
||||||
{% endfor %}</ol>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>{%s get("editors") %}</h2>
|
|
||||||
<ol>{% for _, name := range editors %}
|
|
||||||
<li><a href="/hypha/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
|
||||||
{% endfor %}</ol>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>{%s get("readers") %}</h2>
|
|
||||||
<ol>{% for _, name := range readers %}
|
|
||||||
<li><a href="/hypha/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
|
||||||
{% endfor %}</ol>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endfunc %}
|
|
@@ -1,805 +0,0 @@
|
|||||||
// Code generated by qtc from "auth.qtpl". DO NOT EDIT.
|
|
||||||
// See https://github.com/valyala/quicktemplate for details.
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:1
|
|
||||||
package auth
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:1
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:2
|
|
||||||
import "sort"
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:3
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:4
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/l18n"
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:5
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:7
|
|
||||||
import (
|
|
||||||
qtio422016 "io"
|
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
|
||||||
)
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:7
|
|
||||||
var (
|
|
||||||
_ = qtio422016.Copy
|
|
||||||
_ = qt422016.AcquireByteBuffer
|
|
||||||
)
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:7
|
|
||||||
func StreamRegister(qw422016 *qt422016.Writer, rq *http.Request) {
|
|
||||||
//line auth/auth.qtpl:7
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:9
|
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:10
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:13
|
|
||||||
if cfg.AllowRegistration {
|
|
||||||
//line auth/auth.qtpl:13
|
|
||||||
qw422016.N().S(`
|
|
||||||
<form class="modal" method="post" action="/register?`)
|
|
||||||
//line auth/auth.qtpl:14
|
|
||||||
qw422016.E().S(rq.URL.RawQuery)
|
|
||||||
//line auth/auth.qtpl:14
|
|
||||||
qw422016.N().S(`" id="register-form" enctype="multipart/form-data" autocomplete="off">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title">`)
|
|
||||||
//line auth/auth.qtpl:16
|
|
||||||
qw422016.E().S(lc.Get("auth.register_header", &l18n.Replacements{"name": cfg.WikiName}))
|
|
||||||
//line auth/auth.qtpl:16
|
|
||||||
qw422016.N().S(`</legend>
|
|
||||||
|
|
||||||
<label for="register-form__username">`)
|
|
||||||
//line auth/auth.qtpl:18
|
|
||||||
qw422016.E().S(lc.Get("auth.username"))
|
|
||||||
//line auth/auth.qtpl:18
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">`)
|
|
||||||
//line auth/auth.qtpl:22
|
|
||||||
qw422016.E().S(lc.Get("auth.password"))
|
|
||||||
//line auth/auth.qtpl:22
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password">
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:25
|
|
||||||
qw422016.E().S(lc.Get("auth.password_tip"))
|
|
||||||
//line auth/auth.qtpl:25
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:26
|
|
||||||
qw422016.E().S(lc.Get("auth.cookie_tip"))
|
|
||||||
//line auth/auth.qtpl:26
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<button class="btn" type="submit" value="Register">`)
|
|
||||||
//line auth/auth.qtpl:27
|
|
||||||
qw422016.E().S(lc.Get("auth.register_button"))
|
|
||||||
//line auth/auth.qtpl:27
|
|
||||||
qw422016.N().S(`</button>
|
|
||||||
<a class="btn btn_weak" href="/`)
|
|
||||||
//line auth/auth.qtpl:28
|
|
||||||
qw422016.E().S(rq.URL.RawQuery)
|
|
||||||
//line auth/auth.qtpl:28
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line auth/auth.qtpl:28
|
|
||||||
qw422016.E().S(lc.Get("ui.cancel"))
|
|
||||||
//line auth/auth.qtpl:28
|
|
||||||
qw422016.N().S(`</a>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:31
|
|
||||||
streamtelegramWidget(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:31
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:32
|
|
||||||
} else if cfg.UseAuth {
|
|
||||||
//line auth/auth.qtpl:32
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:33
|
|
||||||
qw422016.E().S(lc.Get("auth.noregister"))
|
|
||||||
//line auth/auth.qtpl:33
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p><a href="/`)
|
|
||||||
//line auth/auth.qtpl:34
|
|
||||||
qw422016.E().S(rq.URL.RawQuery)
|
|
||||||
//line auth/auth.qtpl:34
|
|
||||||
qw422016.N().S(`">← `)
|
|
||||||
//line auth/auth.qtpl:34
|
|
||||||
qw422016.E().S(lc.Get("auth.go_back"))
|
|
||||||
//line auth/auth.qtpl:34
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:35
|
|
||||||
} else {
|
|
||||||
//line auth/auth.qtpl:35
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:36
|
|
||||||
qw422016.E().S(lc.Get("auth.noauth"))
|
|
||||||
//line auth/auth.qtpl:36
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p><a href="/`)
|
|
||||||
//line auth/auth.qtpl:37
|
|
||||||
qw422016.E().S(rq.URL.RawQuery)
|
|
||||||
//line auth/auth.qtpl:37
|
|
||||||
qw422016.N().S(`">← `)
|
|
||||||
//line auth/auth.qtpl:37
|
|
||||||
qw422016.E().S(lc.Get("auth.go_back"))
|
|
||||||
//line auth/auth.qtpl:37
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:38
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:38
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
func WriteRegister(qq422016 qtio422016.Writer, rq *http.Request) {
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
StreamRegister(qw422016, rq)
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
func Register(rq *http.Request) string {
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
WriteRegister(qb422016, rq)
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:41
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:43
|
|
||||||
func StreamLogin(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:43
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:46
|
|
||||||
if cfg.UseAuth {
|
|
||||||
//line auth/auth.qtpl:46
|
|
||||||
qw422016.N().S(`
|
|
||||||
<form class="modal" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title">`)
|
|
||||||
//line auth/auth.qtpl:49
|
|
||||||
qw422016.E().S(lc.Get("auth.login_header", &l18n.Replacements{"name": cfg.WikiName}))
|
|
||||||
//line auth/auth.qtpl:49
|
|
||||||
qw422016.N().S(`</legend>
|
|
||||||
<label for="login-form__username">`)
|
|
||||||
//line auth/auth.qtpl:50
|
|
||||||
qw422016.E().S(lc.Get("auth.username"))
|
|
||||||
//line auth/auth.qtpl:50
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">`)
|
|
||||||
//line auth/auth.qtpl:54
|
|
||||||
qw422016.E().S(lc.Get("auth.password"))
|
|
||||||
//line auth/auth.qtpl:54
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password" autocomplete="current-password">
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:57
|
|
||||||
qw422016.E().S(lc.Get("auth.cookie_tip"))
|
|
||||||
//line auth/auth.qtpl:57
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<button class="btn" type="submit" value="Log in">`)
|
|
||||||
//line auth/auth.qtpl:58
|
|
||||||
qw422016.E().S(lc.Get("auth.login_button"))
|
|
||||||
//line auth/auth.qtpl:58
|
|
||||||
qw422016.N().S(`</button>
|
|
||||||
<a class="btn btn_weak" href="/">`)
|
|
||||||
//line auth/auth.qtpl:59
|
|
||||||
qw422016.E().S(lc.Get("ui.cancel"))
|
|
||||||
//line auth/auth.qtpl:59
|
|
||||||
qw422016.N().S(`</a>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:62
|
|
||||||
streamtelegramWidget(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:62
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:63
|
|
||||||
} else {
|
|
||||||
//line auth/auth.qtpl:63
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:64
|
|
||||||
qw422016.E().S(lc.Get("auth.noauth"))
|
|
||||||
//line auth/auth.qtpl:64
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p><a class="btn btn_weak" href="/">← `)
|
|
||||||
//line auth/auth.qtpl:65
|
|
||||||
qw422016.E().S(lc.Get("auth.go_home"))
|
|
||||||
//line auth/auth.qtpl:65
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:66
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:66
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
func WriteLogin(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
StreamLogin(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
func Login(lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
WriteLogin(qb422016, lc)
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:69
|
|
||||||
}
|
|
||||||
|
|
||||||
// Telegram auth widget was requested by Yogurt. As you can see, we don't offer user administrators control over it. Of course we don't.
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:72
|
|
||||||
func streamtelegramWidget(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:72
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:73
|
|
||||||
if cfg.TelegramEnabled {
|
|
||||||
//line auth/auth.qtpl:73
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p class="telegram-notice">`)
|
|
||||||
//line auth/auth.qtpl:74
|
|
||||||
qw422016.E().S(lc.Get("auth.telegram_tip"))
|
|
||||||
//line auth/auth.qtpl:74
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<script async src="https://telegram.org/js/telegram-widget.js?15" data-telegram-login="`)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
qw422016.E().S(cfg.TelegramBotName)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
qw422016.N().S(`" data-size="medium" data-userpic="false" data-auth-url="`)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
qw422016.E().S(cfg.URL)
|
|
||||||
//line auth/auth.qtpl:75
|
|
||||||
qw422016.N().S(`/telegram-login"></script>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:76
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:76
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
func writetelegramWidget(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
streamtelegramWidget(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
func telegramWidget(lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
writetelegramWidget(qb422016, lc)
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:77
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:79
|
|
||||||
func StreamLoginError(qw422016 *qt422016.Writer, err string, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:79
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:82
|
|
||||||
switch err {
|
|
||||||
//line auth/auth.qtpl:83
|
|
||||||
case "unknown username":
|
|
||||||
//line auth/auth.qtpl:83
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p class="error">`)
|
|
||||||
//line auth/auth.qtpl:84
|
|
||||||
qw422016.E().S(lc.Get("auth.error_username"))
|
|
||||||
//line auth/auth.qtpl:84
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:85
|
|
||||||
case "wrong password":
|
|
||||||
//line auth/auth.qtpl:85
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p class="error">`)
|
|
||||||
//line auth/auth.qtpl:86
|
|
||||||
qw422016.E().S(lc.Get("auth.error_password"))
|
|
||||||
//line auth/auth.qtpl:86
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:87
|
|
||||||
default:
|
|
||||||
//line auth/auth.qtpl:87
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p class="error">`)
|
|
||||||
//line auth/auth.qtpl:88
|
|
||||||
qw422016.E().S(err)
|
|
||||||
//line auth/auth.qtpl:88
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:89
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:89
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p><a href="/login">← `)
|
|
||||||
//line auth/auth.qtpl:90
|
|
||||||
qw422016.E().S(lc.Get("auth.try_again"))
|
|
||||||
//line auth/auth.qtpl:90
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
func WriteLoginError(qq422016 qtio422016.Writer, err string, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
StreamLoginError(qw422016, err, lc)
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
func LoginError(err string, lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
WriteLoginError(qb422016, err, lc)
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:93
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:95
|
|
||||||
func StreamLogout(qw422016 *qt422016.Writer, can bool, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:95
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:98
|
|
||||||
if can {
|
|
||||||
//line auth/auth.qtpl:98
|
|
||||||
qw422016.N().S(`
|
|
||||||
<h1>`)
|
|
||||||
//line auth/auth.qtpl:99
|
|
||||||
qw422016.E().S(lc.Get("auth.logout_header"))
|
|
||||||
//line auth/auth.qtpl:99
|
|
||||||
qw422016.N().S(`</h1>
|
|
||||||
<form method="POST" action="/logout">
|
|
||||||
<input class="btn btn_accent" type="submit" value="`)
|
|
||||||
//line auth/auth.qtpl:101
|
|
||||||
qw422016.E().S(lc.Get("auth.logout_button"))
|
|
||||||
//line auth/auth.qtpl:101
|
|
||||||
qw422016.N().S(`"/>
|
|
||||||
<a class="btn btn_weak" href="/">`)
|
|
||||||
//line auth/auth.qtpl:102
|
|
||||||
qw422016.E().S(lc.Get("auth.go_home"))
|
|
||||||
//line auth/auth.qtpl:102
|
|
||||||
qw422016.N().S(`</a>
|
|
||||||
</form>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:104
|
|
||||||
} else {
|
|
||||||
//line auth/auth.qtpl:104
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p>`)
|
|
||||||
//line auth/auth.qtpl:105
|
|
||||||
qw422016.E().S(lc.Get("auth.logout_anon"))
|
|
||||||
//line auth/auth.qtpl:105
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p><a href="/login">`)
|
|
||||||
//line auth/auth.qtpl:106
|
|
||||||
qw422016.E().S(lc.Get("auth.login_title"))
|
|
||||||
//line auth/auth.qtpl:106
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
<p><a href="/">← `)
|
|
||||||
//line auth/auth.qtpl:107
|
|
||||||
qw422016.E().S(lc.Get("auth.go_home"))
|
|
||||||
//line auth/auth.qtpl:107
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:108
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:108
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
func WriteLogout(qq422016 qtio422016.Writer, can bool, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
StreamLogout(qw422016, can, lc)
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
func Logout(can bool, lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
WriteLogout(qb422016, can, lc)
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:111
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:113
|
|
||||||
func StreamLock(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:113
|
|
||||||
qw422016.N().S(`
|
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>🔒 `)
|
|
||||||
//line auth/auth.qtpl:119
|
|
||||||
qw422016.E().S(lc.Get("auth.lock_title"))
|
|
||||||
//line auth/auth.qtpl:119
|
|
||||||
qw422016.N().S(`</title>
|
|
||||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main class="locked-notice main-width">
|
|
||||||
<section class="locked-notice__message">
|
|
||||||
<p class="locked-notice__lock">🔒</p>
|
|
||||||
<h1 class="locked-notice__title">`)
|
|
||||||
//line auth/auth.qtpl:127
|
|
||||||
qw422016.E().S(lc.Get("auth.lock_title"))
|
|
||||||
//line auth/auth.qtpl:127
|
|
||||||
qw422016.N().S(`</h1>
|
|
||||||
<form class="locked-notice__login-form" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
|
||||||
<label for="login-form__username">`)
|
|
||||||
//line auth/auth.qtpl:129
|
|
||||||
qw422016.E().S(lc.Get("auth.username"))
|
|
||||||
//line auth/auth.qtpl:129
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
|
||||||
<br>
|
|
||||||
<label for="login-form__password">`)
|
|
||||||
//line auth/auth.qtpl:133
|
|
||||||
qw422016.E().S(lc.Get("auth.password"))
|
|
||||||
//line auth/auth.qtpl:133
|
|
||||||
qw422016.N().S(`</label>
|
|
||||||
<br>
|
|
||||||
<input type="password" required name="password" autocomplete="current-password">
|
|
||||||
<br>
|
|
||||||
<button class="btn" type="submit" value="Log in">`)
|
|
||||||
//line auth/auth.qtpl:137
|
|
||||||
qw422016.E().S(lc.Get("auth.login_button"))
|
|
||||||
//line auth/auth.qtpl:137
|
|
||||||
qw422016.N().S(`</button>
|
|
||||||
</form>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:139
|
|
||||||
streamtelegramWidget(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:139
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
func WriteLock(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
StreamLock(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
func Lock(lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
WriteLock(qb422016, lc)
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:144
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:147
|
|
||||||
var userListL10n = map[string]L10nEntry{
|
|
||||||
"heading": En("List of users").Ru("Список пользователей"),
|
|
||||||
"administrators": En("Administrators").Ru("Администраторы"),
|
|
||||||
"moderators": En("Moderators").Ru("Модераторы"),
|
|
||||||
"editors": En("Editors").Ru("Редакторы"),
|
|
||||||
"readers": En("Readers").Ru("Читатели"),
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:156
|
|
||||||
func StreamUserList(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:156
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width user-list">
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:159
|
|
||||||
var get = func(key string) string {
|
|
||||||
return userListL10n[key].Get(lc.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
admins = make([]string, 0)
|
|
||||||
moderators = make([]string, 0)
|
|
||||||
editors = make([]string, 0)
|
|
||||||
readers = make([]string, 0)
|
|
||||||
)
|
|
||||||
for u := range user.YieldUsers() {
|
|
||||||
switch u.Group {
|
|
||||||
// What if we place the users into sorted slices?
|
|
||||||
case "admin":
|
|
||||||
admins = append(admins, u.Name)
|
|
||||||
case "moderator":
|
|
||||||
moderators = append(moderators, u.Name)
|
|
||||||
case "editor", "trusted":
|
|
||||||
editors = append(editors, u.Name)
|
|
||||||
case "reader":
|
|
||||||
readers = append(readers, u.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(admins)
|
|
||||||
sort.Strings(moderators)
|
|
||||||
sort.Strings(editors)
|
|
||||||
sort.Strings(readers)
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:186
|
|
||||||
qw422016.N().S(`
|
|
||||||
<h1>`)
|
|
||||||
//line auth/auth.qtpl:187
|
|
||||||
qw422016.E().S(get("heading"))
|
|
||||||
//line auth/auth.qtpl:187
|
|
||||||
qw422016.N().S(`</h1>
|
|
||||||
<section>
|
|
||||||
<h2>`)
|
|
||||||
//line auth/auth.qtpl:189
|
|
||||||
qw422016.E().S(get("administrators"))
|
|
||||||
//line auth/auth.qtpl:189
|
|
||||||
qw422016.N().S(`</h2>
|
|
||||||
<ol>`)
|
|
||||||
//line auth/auth.qtpl:190
|
|
||||||
for _, name := range admins {
|
|
||||||
//line auth/auth.qtpl:190
|
|
||||||
qw422016.N().S(`
|
|
||||||
<li><a href="/hypha/`)
|
|
||||||
//line auth/auth.qtpl:191
|
|
||||||
qw422016.E().S(cfg.UserHypha)
|
|
||||||
//line auth/auth.qtpl:191
|
|
||||||
qw422016.N().S(`/`)
|
|
||||||
//line auth/auth.qtpl:191
|
|
||||||
qw422016.E().S(name)
|
|
||||||
//line auth/auth.qtpl:191
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line auth/auth.qtpl:191
|
|
||||||
qw422016.E().S(name)
|
|
||||||
//line auth/auth.qtpl:191
|
|
||||||
qw422016.N().S(`</a></li>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:192
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:192
|
|
||||||
qw422016.N().S(`</ol>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>`)
|
|
||||||
//line auth/auth.qtpl:195
|
|
||||||
qw422016.E().S(get("moderators"))
|
|
||||||
//line auth/auth.qtpl:195
|
|
||||||
qw422016.N().S(`</h2>
|
|
||||||
<ol>`)
|
|
||||||
//line auth/auth.qtpl:196
|
|
||||||
for _, name := range moderators {
|
|
||||||
//line auth/auth.qtpl:196
|
|
||||||
qw422016.N().S(`
|
|
||||||
<li><a href="/hypha/`)
|
|
||||||
//line auth/auth.qtpl:197
|
|
||||||
qw422016.E().S(cfg.UserHypha)
|
|
||||||
//line auth/auth.qtpl:197
|
|
||||||
qw422016.N().S(`/`)
|
|
||||||
//line auth/auth.qtpl:197
|
|
||||||
qw422016.E().S(name)
|
|
||||||
//line auth/auth.qtpl:197
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line auth/auth.qtpl:197
|
|
||||||
qw422016.E().S(name)
|
|
||||||
//line auth/auth.qtpl:197
|
|
||||||
qw422016.N().S(`</a></li>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:198
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:198
|
|
||||||
qw422016.N().S(`</ol>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>`)
|
|
||||||
//line auth/auth.qtpl:201
|
|
||||||
qw422016.E().S(get("editors"))
|
|
||||||
//line auth/auth.qtpl:201
|
|
||||||
qw422016.N().S(`</h2>
|
|
||||||
<ol>`)
|
|
||||||
//line auth/auth.qtpl:202
|
|
||||||
for _, name := range editors {
|
|
||||||
//line auth/auth.qtpl:202
|
|
||||||
qw422016.N().S(`
|
|
||||||
<li><a href="/hypha/`)
|
|
||||||
//line auth/auth.qtpl:203
|
|
||||||
qw422016.E().S(cfg.UserHypha)
|
|
||||||
//line auth/auth.qtpl:203
|
|
||||||
qw422016.N().S(`/`)
|
|
||||||
//line auth/auth.qtpl:203
|
|
||||||
qw422016.E().S(name)
|
|
||||||
//line auth/auth.qtpl:203
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line auth/auth.qtpl:203
|
|
||||||
qw422016.E().S(name)
|
|
||||||
//line auth/auth.qtpl:203
|
|
||||||
qw422016.N().S(`</a></li>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:204
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:204
|
|
||||||
qw422016.N().S(`</ol>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>`)
|
|
||||||
//line auth/auth.qtpl:207
|
|
||||||
qw422016.E().S(get("readers"))
|
|
||||||
//line auth/auth.qtpl:207
|
|
||||||
qw422016.N().S(`</h2>
|
|
||||||
<ol>`)
|
|
||||||
//line auth/auth.qtpl:208
|
|
||||||
for _, name := range readers {
|
|
||||||
//line auth/auth.qtpl:208
|
|
||||||
qw422016.N().S(`
|
|
||||||
<li><a href="/hypha/`)
|
|
||||||
//line auth/auth.qtpl:209
|
|
||||||
qw422016.E().S(cfg.UserHypha)
|
|
||||||
//line auth/auth.qtpl:209
|
|
||||||
qw422016.N().S(`/`)
|
|
||||||
//line auth/auth.qtpl:209
|
|
||||||
qw422016.E().S(name)
|
|
||||||
//line auth/auth.qtpl:209
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line auth/auth.qtpl:209
|
|
||||||
qw422016.E().S(name)
|
|
||||||
//line auth/auth.qtpl:209
|
|
||||||
qw422016.N().S(`</a></li>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:210
|
|
||||||
}
|
|
||||||
//line auth/auth.qtpl:210
|
|
||||||
qw422016.N().S(`</ol>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
func WriteUserList(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
StreamUserList(qw422016, lc)
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
}
|
|
||||||
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
func UserList(lc *l18n.Localizer) string {
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
WriteUserList(qb422016, lc)
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
return qs422016
|
|
||||||
//line auth/auth.qtpl:213
|
|
||||||
}
|
|
@@ -1,22 +0,0 @@
|
|||||||
package auth
|
|
||||||
|
|
||||||
type L10nEntry struct {
|
|
||||||
_en string
|
|
||||||
_ru string
|
|
||||||
}
|
|
||||||
|
|
||||||
func En(v string) L10nEntry {
|
|
||||||
return L10nEntry{_en: v}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e L10nEntry) Ru(v string) L10nEntry {
|
|
||||||
e._ru = v
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e L10nEntry) Get(lang string) string {
|
|
||||||
if lang == "ru" && e._ru != "" {
|
|
||||||
return e._ru
|
|
||||||
}
|
|
||||||
return e._en
|
|
||||||
}
|
|
225
auth/web.go
@@ -1,225 +0,0 @@
|
|||||||
package auth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"mime"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/l18n"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
func InitAuth(r *mux.Router) {
|
|
||||||
r.HandleFunc("/user-list", handlerUserList)
|
|
||||||
r.HandleFunc("/lock", handlerLock)
|
|
||||||
// The check below saves a lot of extra checks and lines of codes in other places in this file.
|
|
||||||
if !cfg.UseAuth {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if cfg.AllowRegistration {
|
|
||||||
r.HandleFunc("/register", handlerRegister).Methods(http.MethodPost, http.MethodGet)
|
|
||||||
}
|
|
||||||
if cfg.TelegramEnabled {
|
|
||||||
r.HandleFunc("/telegram-login", handlerTelegramLogin)
|
|
||||||
}
|
|
||||||
r.HandleFunc("/login", handlerLogin)
|
|
||||||
r.HandleFunc("/logout", handlerLogout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerUserList(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte(viewutil.Base(viewutil.MetaFrom(w, rq), lc.Get("ui.users_title"), UserList(lc), map[string]string{})))
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerLock(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
_, _ = io.WriteString(w, Lock(l18n.FromRequest(rq)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// handlerRegister displays the register form (GET) or registers the user (POST).
|
|
||||||
func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
util.PrepareRq(rq)
|
|
||||||
if rq.Method == http.MethodGet {
|
|
||||||
_, _ = io.WriteString(
|
|
||||||
w,
|
|
||||||
viewutil.Base(
|
|
||||||
viewutil.MetaFrom(w, rq),
|
|
||||||
lc.Get("auth.register_title"),
|
|
||||||
Register(rq),
|
|
||||||
map[string]string{},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
username = rq.PostFormValue("username")
|
|
||||||
password = rq.PostFormValue("password")
|
|
||||||
err = user.Register(username, password, "editor", "local", false)
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to register ‘%s’: %s", username, err.Error())
|
|
||||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
_, _ = io.WriteString(
|
|
||||||
w,
|
|
||||||
viewutil.Base(
|
|
||||||
viewutil.MetaFrom(w, rq),
|
|
||||||
lc.Get("auth.register_title"),
|
|
||||||
fmt.Sprintf(
|
|
||||||
`<main class="main-width"><p>%s</p><p><a href="/register">%s<a></p></main>`,
|
|
||||||
err.Error(),
|
|
||||||
lc.Get("auth.try_again"),
|
|
||||||
),
|
|
||||||
map[string]string{},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Successfully registered ‘%s’", username)
|
|
||||||
if err := user.LoginDataHTTP(w, username, password); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handlerLogout shows the logout form (GET) or logs the user out (POST).
|
|
||||||
func handlerLogout(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
if rq.Method == http.MethodGet {
|
|
||||||
var (
|
|
||||||
u = user.FromRequest(rq)
|
|
||||||
can = u != nil
|
|
||||||
lc = l18n.FromRequest(rq)
|
|
||||||
)
|
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
||||||
if can {
|
|
||||||
log.Println("User", u.Name, "tries to log out")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
} else {
|
|
||||||
log.Println("Unknown user tries to log out")
|
|
||||||
w.WriteHeader(http.StatusForbidden)
|
|
||||||
}
|
|
||||||
_, _ = io.WriteString(
|
|
||||||
w,
|
|
||||||
viewutil.Base(viewutil.MetaFrom(w, rq), lc.Get("auth.logout_title"), Logout(can, lc), map[string]string{}),
|
|
||||||
)
|
|
||||||
} else if rq.Method == http.MethodPost {
|
|
||||||
user.LogoutFromRequest(w, rq)
|
|
||||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handlerLogin shows the login form (GET) or logs the user in (POST).
|
|
||||||
func handlerLogin(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
if rq.Method == http.MethodGet {
|
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
_, _ = io.WriteString(
|
|
||||||
w,
|
|
||||||
viewutil.Base(
|
|
||||||
viewutil.MetaFrom(w, rq),
|
|
||||||
lc.Get("auth.login_title"),
|
|
||||||
Login(lc),
|
|
||||||
map[string]string{},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else if rq.Method == http.MethodPost {
|
|
||||||
var (
|
|
||||||
username = util.CanonicalName(rq.PostFormValue("username"))
|
|
||||||
password = rq.PostFormValue("password")
|
|
||||||
err = user.LoginDataHTTP(w, username, password)
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
_, _ = io.WriteString(w, viewutil.Base(viewutil.MetaFrom(w, rq), err.Error(), LoginError(err.Error(), lc), map[string]string{}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
// Note there is no lock here.
|
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
||||||
rq.ParseForm()
|
|
||||||
var (
|
|
||||||
values = rq.URL.Query()
|
|
||||||
username = strings.ToLower(values.Get("username"))
|
|
||||||
seemsValid = user.TelegramAuthParamsAreValid(values)
|
|
||||||
err = user.Register(
|
|
||||||
username,
|
|
||||||
"", // Password matters not
|
|
||||||
"editor",
|
|
||||||
"telegram",
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// If registering a user via Telegram failed, because a Telegram user with this name
|
|
||||||
// has already registered, then everything is actually ok!
|
|
||||||
if user.HasUsername(username) && user.ByName(username).Source == "telegram" {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !seemsValid {
|
|
||||||
err = errors.New("Wrong parameters")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to register ‘%s’ using Telegram: %s", username, err.Error())
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
_, _ = io.WriteString(
|
|
||||||
w,
|
|
||||||
viewutil.Base(
|
|
||||||
viewutil.MetaFrom(w, rq),
|
|
||||||
lc.Get("ui.error"),
|
|
||||||
fmt.Sprintf(
|
|
||||||
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
|
|
||||||
lc.Get("auth.error_telegram"),
|
|
||||||
err.Error(),
|
|
||||||
lc.Get("auth.go_login"),
|
|
||||||
),
|
|
||||||
map[string]string{},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
errmsg := user.LoginDataHTTP(w, username, "")
|
|
||||||
if errmsg != nil {
|
|
||||||
log.Printf("Failed to login ‘%s’ using Telegram: %s", username, err.Error())
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
_, _ = io.WriteString(
|
|
||||||
w,
|
|
||||||
viewutil.Base(
|
|
||||||
viewutil.MetaFrom(w, rq),
|
|
||||||
"Error",
|
|
||||||
fmt.Sprintf(
|
|
||||||
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
|
|
||||||
lc.Get("auth.error_telegram"),
|
|
||||||
err.Error(),
|
|
||||||
lc.Get("auth.go_login"),
|
|
||||||
),
|
|
||||||
map[string]string{},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("Authorize ‘%s’ from Telegram", username)
|
|
||||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
|
||||||
}
|
|
@@ -1,85 +0,0 @@
|
|||||||
package backlinks
|
|
||||||
|
|
||||||
import (
|
|
||||||
"embed"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
func InitHandlers(rtr *mux.Router) {
|
|
||||||
rtr.PathPrefix("/backlinks/").HandlerFunc(handlerBacklinks)
|
|
||||||
rtr.PathPrefix("/orphans").HandlerFunc(handlerOrphans)
|
|
||||||
chainBacklinks = viewutil.CopyEnRuWith(fs, "view_backlinks.html", ruTranslation)
|
|
||||||
chainOrphans = viewutil.CopyEnRuWith(fs, "view_orphans.html", ruTranslation)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handlerBacklinks lists all backlinks to a hypha.
|
|
||||||
func handlerBacklinks(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
var (
|
|
||||||
hyphaName = util.HyphaNameFromRq(rq, "backlinks")
|
|
||||||
backlinks []string
|
|
||||||
)
|
|
||||||
for b := range yieldHyphaBacklinks(hyphaName) {
|
|
||||||
backlinks = append(backlinks, b)
|
|
||||||
}
|
|
||||||
viewBacklinks(viewutil.MetaFrom(w, rq), hyphaName, backlinks)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerOrphans(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
var orphans []string
|
|
||||||
for h := range hyphae.YieldExistingHyphae() {
|
|
||||||
if BacklinksCount(h.CanonicalName()) == 0 {
|
|
||||||
orphans = append(orphans, h.CanonicalName())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(orphans)
|
|
||||||
viewOrphans(viewutil.MetaFrom(w, rq), orphans)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
//go:embed *.html
|
|
||||||
fs embed.FS
|
|
||||||
ruTranslation = `
|
|
||||||
{{define "backlinks to text"}}Обратные ссылки на {{.}}{{end}}
|
|
||||||
{{define "backlinks to link"}}Обратные ссылки на <a href="/hypha/{{.}}">{{beautifulName .}}</a>{{end}}
|
|
||||||
{{define "description"}}Ниже перечислены гифы, на которых есть ссылка на эту гифу, трансклюзия этой гифы или эта гифа вставлена как изображение.{{end}}
|
|
||||||
{{define "orphaned hyphae"}}Гифы-сироты{{end}}
|
|
||||||
{{define "orphan description"}}Ниже перечислены гифы без ссылок на них.{{end}}
|
|
||||||
`
|
|
||||||
chainBacklinks viewutil.Chain
|
|
||||||
chainOrphans viewutil.Chain
|
|
||||||
)
|
|
||||||
|
|
||||||
type backlinksData struct {
|
|
||||||
*viewutil.BaseData
|
|
||||||
HyphaName string
|
|
||||||
Backlinks []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func viewBacklinks(meta viewutil.Meta, hyphaName string, backlinks []string) {
|
|
||||||
viewutil.ExecutePage(meta, chainBacklinks, backlinksData{
|
|
||||||
BaseData: &viewutil.BaseData{
|
|
||||||
Addr: "/backlinks/" + hyphaName,
|
|
||||||
},
|
|
||||||
HyphaName: hyphaName,
|
|
||||||
Backlinks: backlinks,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type orphansData struct {
|
|
||||||
*viewutil.BaseData
|
|
||||||
Orphans []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func viewOrphans(meta viewutil.Meta, orphans []string) {
|
|
||||||
viewutil.ExecutePage(meta, chainOrphans, orphansData{
|
|
||||||
BaseData: &viewutil.BaseData{
|
|
||||||
Addr: "/orphans",
|
|
||||||
},
|
|
||||||
Orphans: orphans,
|
|
||||||
})
|
|
||||||
}
|
|
@@ -1,37 +0,0 @@
|
|||||||
{{define "category card"}}
|
|
||||||
{{if or .GivenPermissionToModify (len .Categories)}}
|
|
||||||
{{$hyphaName := .HyphaName}}
|
|
||||||
{{$givenPermission := .GivenPermissionToModify}}
|
|
||||||
<aside class="layout-card categories-card">
|
|
||||||
<h2 class="layout-card__title">{{block `categories` .}}Categories{{end}}</h2>
|
|
||||||
<ul class="categories-card__entries">
|
|
||||||
{{range .Categories}}
|
|
||||||
<li class="categories-card__entry">
|
|
||||||
<a class="categories-card__link" href="/category/{{.}}">{{beautifulName .}}</a>
|
|
||||||
<form method="POST" action="/remove-from-category" class="categories-card__remove-form">
|
|
||||||
<input type="hidden" name="cat" value="{{.}}">
|
|
||||||
<input type="hidden" name="hypha" value="{{$hyphaName}}">
|
|
||||||
<input type="hidden" name="redirect-to" value="/hypha/{{$hyphaName}}">
|
|
||||||
{{if $givenPermission}}
|
|
||||||
<input type="submit" value="x" class="btn categories-card__btn"
|
|
||||||
title="{{block `remove from category title` .}}Remove the hypha from this category{{end}}">
|
|
||||||
{{end}}
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
{{end}}
|
|
||||||
{{if .GivenPermissionToModify}}
|
|
||||||
<li class="categories-card__entry categories-card__add-to-cat">
|
|
||||||
<form method="POST" action="/add-to-category" class="categories-card__add-form js-add-cat-form">
|
|
||||||
<input type="text" name="cat" id="_cat-input" class="js-add-cat-name" autocomplete="off"
|
|
||||||
placeholder="{{block `placeholder` .}}Category n‌ame...{{end}}">
|
|
||||||
<datalist class="js-add-cat-list" id="cat-name-options"></datalist>
|
|
||||||
<input type="hidden" name="hypha" value="{{$hyphaName}}">
|
|
||||||
<input type="hidden" name="redirect-to" value="/hypha/{{$hyphaName}}">
|
|
||||||
<input type="submit" class="btn categories-card__btn" value="+"
|
|
||||||
title="{{block `add to category title` .}}Add the hypha to this category{{end}}">
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
{{end}}
|
|
||||||
</ul>
|
|
||||||
</aside>
|
|
||||||
{{end}}{{end}}
|
|
@@ -1,37 +0,0 @@
|
|||||||
{{define "edit category x"}}Edit category {{beautifulName .}}{{end}}
|
|
||||||
{{define "title"}}{{template "edit category x" .CatName}}{{end}}
|
|
||||||
{{define "body"}}
|
|
||||||
<main class="main-width category">
|
|
||||||
<h1>{{block "edit category heading" .CatName}}Edit category <a href="/category/{{.}}">{{beautifulName .}}</a>{{end}}</h1>
|
|
||||||
{{if len .Hyphae | not}}
|
|
||||||
<p>{{block "empty cat" .}}This category is empty{{end}}</p>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .GivenPermissionToModify}}
|
|
||||||
<h2>{{block "add to category title" .}}Add a hypha to the category{{end}}</h2>
|
|
||||||
<form method="POST" action="/add-to-category" class="add-to-category">
|
|
||||||
<input type="text" name="hypha" id="_hypha-name"
|
|
||||||
placeholder="{{block `hypha name` .}}Hypha name{{end}}">
|
|
||||||
<input type="hidden" name="cat" value="{{.CatName}}">
|
|
||||||
<input type="hidden" name="redirect-to" value="/category/{{.CatName}}">
|
|
||||||
<input type="submit" class="btn" value="{{block `add` .}}Add{{end}}">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{{if len .Hyphae}}
|
|
||||||
<h2>{{block "remove hyphae" .}}Remove hyphae from the category{{end}}</h2>
|
|
||||||
<form method="POST" action="/remove-from-category" class="multi-remove-from-category">
|
|
||||||
<ol>
|
|
||||||
{{range .Hyphae}}
|
|
||||||
<li>
|
|
||||||
<input type="checkbox" name="_{{.}}" id="_{{.}}">
|
|
||||||
<label for="_{{.}}"><a href="/hypha/{{.}}">{{beautifulName .}}</a></label>
|
|
||||||
</li>
|
|
||||||
{{end}}
|
|
||||||
</ol>
|
|
||||||
<input type="hidden" name="cat" value="{{.CatName}}">
|
|
||||||
<input type="hidden" name="redirect-to" value="/edit-category/{{.CatName}}">
|
|
||||||
<input type="submit" class="btn" value="{{block `remove` .}}Remove{{end}}">
|
|
||||||
</form>
|
|
||||||
{{end}}{{end}}
|
|
||||||
</main>
|
|
||||||
{{end}}
|
|
@@ -1,107 +0,0 @@
|
|||||||
package categories
|
|
||||||
|
|
||||||
import (
|
|
||||||
"embed"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
"log"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ruTranslation = `
|
|
||||||
{{define "empty cat"}}Эта категория пуста.{{end}}
|
|
||||||
{{define "cat"}}Категория{{end}}
|
|
||||||
{{define "hypha name"}}Название гифы{{end}}
|
|
||||||
{{define "categories"}}Категории{{end}}
|
|
||||||
{{define "placeholder"}}Название категории...{{end}}
|
|
||||||
{{define "remove from category title"}}Убрать гифу из этой категории{{end}}
|
|
||||||
{{define "add to category title"}}Добавить гифу в эту категорию{{end}}
|
|
||||||
{{define "category list"}}Список категорий{{end}}
|
|
||||||
{{define "no categories"}}В этой вики нет категорий.{{end}}
|
|
||||||
{{define "category x"}}Категория {{. | beautifulName}}{{end}}
|
|
||||||
|
|
||||||
{{define "edit category x"}}Редактирование категории {{beautifulName .}}{{end}}
|
|
||||||
{{define "edit category heading"}}Редактирование категории <a href="/category/{{.}}">{{beautifulName .}}</a>{{end}}
|
|
||||||
{{define "add"}}Добавить{{end}}
|
|
||||||
{{define "remove hyphae"}}Убрать гифы из этой категории{{end}}
|
|
||||||
{{define "remove"}}Убрать{{end}}
|
|
||||||
{{define "edit"}}Редактировать{{end}}
|
|
||||||
`
|
|
||||||
|
|
||||||
var (
|
|
||||||
//go:embed *.html
|
|
||||||
fs embed.FS
|
|
||||||
viewListChain, viewPageChain, viewCardChain, viewEditChain viewutil.Chain
|
|
||||||
)
|
|
||||||
|
|
||||||
func prepareViews() {
|
|
||||||
viewCardChain = viewutil.CopyEnRuWith(fs, "view_card.html", ruTranslation)
|
|
||||||
viewListChain = viewutil.CopyEnRuWith(fs, "view_list.html", ruTranslation)
|
|
||||||
viewPageChain = viewutil.CopyEnRuWith(fs, "view_page.html", ruTranslation)
|
|
||||||
viewEditChain = viewutil.CopyEnRuWith(fs, "view_edit.html", ruTranslation)
|
|
||||||
}
|
|
||||||
|
|
||||||
type cardData struct {
|
|
||||||
HyphaName string
|
|
||||||
Categories []string
|
|
||||||
GivenPermissionToModify bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// CategoryCard is the little sidebar that is shown nearby the hypha view.
|
|
||||||
func CategoryCard(meta viewutil.Meta, hyphaName string) string {
|
|
||||||
var buf strings.Builder
|
|
||||||
err := viewCardChain.Get(meta).ExecuteTemplate(&buf, "category card", cardData{
|
|
||||||
HyphaName: hyphaName,
|
|
||||||
Categories: CategoriesWithHypha(hyphaName),
|
|
||||||
GivenPermissionToModify: meta.U.CanProceed("add-to-category"),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type catData struct {
|
|
||||||
*viewutil.BaseData
|
|
||||||
CatName string
|
|
||||||
Hyphae []string
|
|
||||||
GivenPermissionToModify bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func categoryEdit(meta viewutil.Meta, catName string) {
|
|
||||||
viewutil.ExecutePage(meta, viewEditChain, catData{
|
|
||||||
BaseData: &viewutil.BaseData{
|
|
||||||
Addr: "/edit-category/" + catName,
|
|
||||||
},
|
|
||||||
CatName: catName,
|
|
||||||
Hyphae: hyphaeInCategory(catName),
|
|
||||||
GivenPermissionToModify: meta.U.CanProceed("add-to-category"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func categoryPage(meta viewutil.Meta, catName string) {
|
|
||||||
viewutil.ExecutePage(meta, viewPageChain, catData{
|
|
||||||
BaseData: &viewutil.BaseData{
|
|
||||||
Addr: "/category/" + catName,
|
|
||||||
},
|
|
||||||
CatName: catName,
|
|
||||||
Hyphae: hyphaeInCategory(catName),
|
|
||||||
GivenPermissionToModify: meta.U.CanProceed("add-to-category"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type listData struct {
|
|
||||||
*viewutil.BaseData
|
|
||||||
Categories []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func categoryList(meta viewutil.Meta) {
|
|
||||||
cats := listOfCategories()
|
|
||||||
sort.Strings(cats)
|
|
||||||
viewutil.ExecutePage(meta, viewListChain, listData{
|
|
||||||
BaseData: &viewutil.BaseData{
|
|
||||||
Addr: "/category",
|
|
||||||
},
|
|
||||||
Categories: cats,
|
|
||||||
})
|
|
||||||
}
|
|
14
flag.go
@@ -12,17 +12,17 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
user2 "github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/version"
|
"github.com/bouncepaw/mycorrhiza/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CLI options are read and parsed here.
|
// CLI options are read and parsed here.
|
||||||
|
|
||||||
// printHelp prints the help message.
|
// printHelp prints the help message.
|
||||||
func printHelp() {
|
func printHelp() {
|
||||||
fmt.Fprintf(
|
_, _ = fmt.Fprintf(
|
||||||
flag.CommandLine.Output(),
|
flag.CommandLine.Output(),
|
||||||
"Usage: %s WIKI_PATH\n",
|
"Usage: %s WIKI_PATH\n",
|
||||||
os.Args[0],
|
os.Args[0],
|
||||||
@@ -70,13 +70,13 @@ func createAdminCommand(name string) {
|
|||||||
}
|
}
|
||||||
cfg.UseAuth = true
|
cfg.UseAuth = true
|
||||||
cfg.AllowRegistration = true
|
cfg.AllowRegistration = true
|
||||||
user.InitUserDatabase()
|
user2.InitUserDatabase()
|
||||||
|
|
||||||
password, err := askPass("Password")
|
password, err := askPass("Password")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := user.Register(name, password, "admin", "local", true); err != nil {
|
if err := user2.Register(name, password, "admin", "local", true); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
help/web.go
@@ -2,15 +2,17 @@ package help
|
|||||||
|
|
||||||
// stuff.go is used for meta stuff about the wiki or all hyphae at once.
|
// stuff.go is used for meta stuff about the wiki or all hyphae at once.
|
||||||
import (
|
import (
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/mycoopts"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/mycoopts"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
|
|
||||||
|
"git.sr.ht/~bouncepaw/mycomarkup/v5"
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -3,12 +3,11 @@ package history
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
|
|
||||||
"github.com/gorilla/feeds"
|
"github.com/gorilla/feeds"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -4,12 +4,12 @@ package histweb
|
|||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
|
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
@@ -30,9 +30,9 @@ func InitHandlers(rtr *mux.Router) {
|
|||||||
rtr.HandleFunc("/recent-changes-atom", handlerRecentChangesAtom)
|
rtr.HandleFunc("/recent-changes-atom", handlerRecentChangesAtom)
|
||||||
rtr.HandleFunc("/recent-changes-json", handlerRecentChangesJSON)
|
rtr.HandleFunc("/recent-changes-json", handlerRecentChangesJSON)
|
||||||
|
|
||||||
chainPrimitiveDiff = viewutil.CopyEnRuWith(fs, "view_primitive_diff.html", ruTranslation)
|
chainPrimitiveDiff = viewutil2.CopyEnRuWith(fs, "view_primitive_diff.html", ruTranslation)
|
||||||
chainRecentChanges = viewutil.CopyEnRuWith(fs, "view_recent_changes.html", ruTranslation)
|
chainRecentChanges = viewutil2.CopyEnRuWith(fs, "view_recent_changes.html", ruTranslation)
|
||||||
chainHistory = viewutil.CopyEnRuWith(fs, "view_history.html", ruTranslation)
|
chainHistory = viewutil2.CopyEnRuWith(fs, "view_history.html", ruTranslation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
||||||
@@ -45,12 +45,12 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
mycoFilePath string
|
mycoFilePath string
|
||||||
h = hyphae.ByName(util.CanonicalName(slug))
|
h = hyphae2.ByName(util.CanonicalName(slug))
|
||||||
)
|
)
|
||||||
switch h := h.(type) {
|
switch h := h.(type) {
|
||||||
case hyphae.ExistingHypha:
|
case hyphae2.ExistingHypha:
|
||||||
mycoFilePath = h.TextFilePath()
|
mycoFilePath = h.TextFilePath()
|
||||||
case *hyphae.EmptyHypha:
|
case *hyphae2.EmptyHypha:
|
||||||
mycoFilePath = filepath.Join(files.HyphaeDir(), h.CanonicalName()+".myco")
|
mycoFilePath = filepath.Join(files.HyphaeDir(), h.CanonicalName()+".myco")
|
||||||
}
|
}
|
||||||
text, err := history.PrimitiveDiffAtRevision(mycoFilePath, revHash)
|
text, err := history.PrimitiveDiffAtRevision(mycoFilePath, revHash)
|
||||||
@@ -58,7 +58,7 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
|||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
primitiveDiff(viewutil.MetaFrom(w, rq), h, revHash, text)
|
primitiveDiff(viewutil2.MetaFrom(w, rq), h, revHash, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerRecentChanges displays the /recent-changes/ page.
|
// handlerRecentChanges displays the /recent-changes/ page.
|
||||||
@@ -68,7 +68,7 @@ func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
|
|||||||
if editCount > 100 {
|
if editCount > 100 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
recentChanges(viewutil.MetaFrom(w, rq), editCount, history.RecentChanges(editCount))
|
recentChanges(viewutil2.MetaFrom(w, rq), editCount, history.RecentChanges(editCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerHistory lists all revisions of a hypha.
|
// handlerHistory lists all revisions of a hypha.
|
||||||
@@ -83,7 +83,7 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
log.Println("Found", len(revs), "revisions for", hyphaName)
|
log.Println("Found", len(revs), "revisions for", hyphaName)
|
||||||
|
|
||||||
historyView(viewutil.MetaFrom(w, rq), hyphaName, list)
|
historyView(viewutil2.MetaFrom(w, rq), hyphaName, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
// genericHandlerOfFeeds is a helper function for the web feed handlers.
|
// genericHandlerOfFeeds is a helper function for the web feed handlers.
|
||||||
@@ -135,20 +135,20 @@ var (
|
|||||||
{{define "n recent changes"}}{{.}} свеж{{if eq . 1}}ая правка{{else if le . 4}}их правок{{else}}их правок{{end}}{{end}}
|
{{define "n recent changes"}}{{.}} свеж{{if eq . 1}}ая правка{{else if le . 4}}их правок{{else}}их правок{{end}}{{end}}
|
||||||
{{define "recent empty"}}Правки не найдены.{{end}}
|
{{define "recent empty"}}Правки не найдены.{{end}}
|
||||||
`
|
`
|
||||||
chainPrimitiveDiff, chainRecentChanges, chainHistory viewutil.Chain
|
chainPrimitiveDiff, chainRecentChanges, chainHistory viewutil2.Chain
|
||||||
)
|
)
|
||||||
|
|
||||||
type recentChangesData struct {
|
type recentChangesData struct {
|
||||||
*viewutil.BaseData
|
*viewutil2.BaseData
|
||||||
EditCount int
|
EditCount int
|
||||||
Changes []history.Revision
|
Changes []history.Revision
|
||||||
UserHypha string
|
UserHypha string
|
||||||
Stops []int
|
Stops []int
|
||||||
}
|
}
|
||||||
|
|
||||||
func recentChanges(meta viewutil.Meta, editCount int, changes []history.Revision) {
|
func recentChanges(meta viewutil2.Meta, editCount int, changes []history.Revision) {
|
||||||
viewutil.ExecutePage(meta, chainRecentChanges, recentChangesData{
|
viewutil2.ExecutePage(meta, chainRecentChanges, recentChangesData{
|
||||||
BaseData: &viewutil.BaseData{},
|
BaseData: &viewutil2.BaseData{},
|
||||||
EditCount: editCount,
|
EditCount: editCount,
|
||||||
Changes: changes,
|
Changes: changes,
|
||||||
UserHypha: cfg.UserHypha,
|
UserHypha: cfg.UserHypha,
|
||||||
@@ -157,13 +157,13 @@ func recentChanges(meta viewutil.Meta, editCount int, changes []history.Revision
|
|||||||
}
|
}
|
||||||
|
|
||||||
type primitiveDiffData struct {
|
type primitiveDiffData struct {
|
||||||
*viewutil.BaseData
|
*viewutil2.BaseData
|
||||||
HyphaName string
|
HyphaName string
|
||||||
Hash string
|
Hash string
|
||||||
Text template.HTML
|
Text template.HTML
|
||||||
}
|
}
|
||||||
|
|
||||||
func primitiveDiff(meta viewutil.Meta, h hyphae.Hypha, hash, text string) {
|
func primitiveDiff(meta viewutil2.Meta, h hyphae2.Hypha, hash, text string) {
|
||||||
hunks := history.SplitPrimitiveDiff(text)
|
hunks := history.SplitPrimitiveDiff(text)
|
||||||
if len(hunks) > 0 {
|
if len(hunks) > 0 {
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
@@ -198,8 +198,8 @@ func primitiveDiff(meta viewutil.Meta, h hyphae.Hypha, hash, text string) {
|
|||||||
text = fmt.Sprintf(
|
text = fmt.Sprintf(
|
||||||
`<pre class="codeblock"><code>%s</code></pre>`, text)
|
`<pre class="codeblock"><code>%s</code></pre>`, text)
|
||||||
}
|
}
|
||||||
viewutil.ExecutePage(meta, chainPrimitiveDiff, primitiveDiffData{
|
viewutil2.ExecutePage(meta, chainPrimitiveDiff, primitiveDiffData{
|
||||||
BaseData: &viewutil.BaseData{},
|
BaseData: &viewutil2.BaseData{},
|
||||||
HyphaName: h.CanonicalName(),
|
HyphaName: h.CanonicalName(),
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
Text: template.HTML(text),
|
Text: template.HTML(text),
|
||||||
@@ -207,14 +207,14 @@ func primitiveDiff(meta viewutil.Meta, h hyphae.Hypha, hash, text string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type historyData struct {
|
type historyData struct {
|
||||||
*viewutil.BaseData
|
*viewutil2.BaseData
|
||||||
HyphaName string
|
HyphaName string
|
||||||
Contents string
|
Contents string
|
||||||
}
|
}
|
||||||
|
|
||||||
func historyView(meta viewutil.Meta, hyphaName, contents string) {
|
func historyView(meta viewutil2.Meta, hyphaName, contents string) {
|
||||||
viewutil.ExecutePage(meta, chainHistory, historyData{
|
viewutil2.ExecutePage(meta, chainHistory, historyData{
|
||||||
BaseData: &viewutil.BaseData{
|
BaseData: &viewutil2.BaseData{
|
||||||
Addr: "/history/" + util.CanonicalName(hyphaName),
|
Addr: "/history/" + util.CanonicalName(hyphaName),
|
||||||
},
|
},
|
||||||
HyphaName: hyphaName,
|
HyphaName: hyphaName,
|
||||||
|
@@ -4,11 +4,11 @@ package history
|
|||||||
// Things related to writing history.
|
// Things related to writing history.
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Revision represents a revision, duh. Hash is usually short. Username is extracted from email.
|
// Revision represents a revision, duh. Hash is usually short. Username is extracted from email.
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{% import "fmt" %}
|
{% import "fmt" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
{% import "github.com/bouncepaw/mycorrhiza/internal/cfg" %}
|
||||||
|
|
||||||
HyphaeLinksHTML returns a comma-separated list of hyphae that were affected by this revision as HTML string.
|
HyphaeLinksHTML returns a comma-separated list of hyphae that were affected by this revision as HTML string.
|
||||||
{% func (rev Revision) HyphaeLinksHTML() %}
|
{% func (rev Revision) HyphaeLinksHTML() %}
|
||||||
@@ -56,22 +56,18 @@ WithRevisions returns an html representation of `revs` that is meant to be inser
|
|||||||
</a>
|
</a>
|
||||||
<ul class="history__entries">
|
<ul class="history__entries">
|
||||||
{% for _, rev := range grp %}
|
{% for _, rev := range grp %}
|
||||||
{%= rev.asHistoryEntry(hyphaName) %}
|
<li class="history__entry">
|
||||||
|
<a class="history-entry" href="/rev/{%s rev.Hash %}/{%s hyphaName %}">
|
||||||
|
<time class="history-entry__time">{%s rev.timeToDisplay() %}</time>
|
||||||
|
</a>
|
||||||
|
<span class="history-entry__hash"><a href="/primitive-diff/{%s rev.Hash %}/{%s hyphaName %}">{%s rev.Hash %}</a></span>
|
||||||
|
<span class="history-entry__msg">{%s rev.Message %}</span>
|
||||||
|
{% if rev.Username != "anon" %}
|
||||||
|
<span class="history-entry__author">by <a href="/hypha/{%s cfg.UserHypha %}/{%s rev.Username %}" rel="author">{%s rev.Username %}</a></span>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
{% func (rev *Revision) asHistoryEntry(hyphaName string) %}
|
|
||||||
<li class="history__entry">
|
|
||||||
<a class="history-entry" href="/rev/{%s rev.Hash %}/{%s hyphaName %}">
|
|
||||||
<time class="history-entry__time">{%s rev.timeToDisplay() %}</time>
|
|
||||||
</a>
|
|
||||||
<span class="history-entry__hash"><a href="/primitive-diff/{%s rev.Hash %}/{%s hyphaName %}">{%s rev.Hash %}</a></span>
|
|
||||||
<span class="history-entry__msg">{%s rev.Message %}</span>
|
|
||||||
{% if rev.Username != "anon" %}
|
|
||||||
<span class="history-entry__author">by <a href="/hypha/{%s cfg.UserHypha %}/{%s rev.Username %}" rel="author">{%s rev.Username %}</a></span>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
{% endfunc %}
|
|
@@ -8,7 +8,7 @@ package history
|
|||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
//line history/view.qtpl:2
|
//line history/view.qtpl:2
|
||||||
import "github.com/bouncepaw/mycorrhiza/cfg"
|
import "github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
|
|
||||||
// HyphaeLinksHTML returns a comma-separated list of hyphae that were affected by this revision as HTML string.
|
// HyphaeLinksHTML returns a comma-separated list of hyphae that were affected by this revision as HTML string.
|
||||||
|
|
||||||
@@ -283,141 +283,102 @@ func StreamWithRevisions(qw422016 *qt422016.Writer, hyphaName string, revs []Rev
|
|||||||
for _, rev := range grp {
|
for _, rev := range grp {
|
||||||
//line history/view.qtpl:58
|
//line history/view.qtpl:58
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
<li class="history__entry">
|
||||||
//line history/view.qtpl:59
|
<a class="history-entry" href="/rev/`)
|
||||||
rev.streamasHistoryEntry(qw422016, hyphaName)
|
//line history/view.qtpl:60
|
||||||
//line history/view.qtpl:59
|
qw422016.E().S(rev.Hash)
|
||||||
|
//line history/view.qtpl:60
|
||||||
|
qw422016.N().S(`/`)
|
||||||
|
//line history/view.qtpl:60
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line history/view.qtpl:60
|
||||||
|
qw422016.N().S(`">
|
||||||
|
<time class="history-entry__time">`)
|
||||||
|
//line history/view.qtpl:61
|
||||||
|
qw422016.E().S(rev.timeToDisplay())
|
||||||
|
//line history/view.qtpl:61
|
||||||
|
qw422016.N().S(`</time>
|
||||||
|
</a>
|
||||||
|
<span class="history-entry__hash"><a href="/primitive-diff/`)
|
||||||
|
//line history/view.qtpl:63
|
||||||
|
qw422016.E().S(rev.Hash)
|
||||||
|
//line history/view.qtpl:63
|
||||||
|
qw422016.N().S(`/`)
|
||||||
|
//line history/view.qtpl:63
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line history/view.qtpl:63
|
||||||
|
qw422016.N().S(`">`)
|
||||||
|
//line history/view.qtpl:63
|
||||||
|
qw422016.E().S(rev.Hash)
|
||||||
|
//line history/view.qtpl:63
|
||||||
|
qw422016.N().S(`</a></span>
|
||||||
|
<span class="history-entry__msg">`)
|
||||||
|
//line history/view.qtpl:64
|
||||||
|
qw422016.E().S(rev.Message)
|
||||||
|
//line history/view.qtpl:64
|
||||||
|
qw422016.N().S(`</span>
|
||||||
|
`)
|
||||||
|
//line history/view.qtpl:65
|
||||||
|
if rev.Username != "anon" {
|
||||||
|
//line history/view.qtpl:65
|
||||||
|
qw422016.N().S(`
|
||||||
|
<span class="history-entry__author">by <a href="/hypha/`)
|
||||||
|
//line history/view.qtpl:66
|
||||||
|
qw422016.E().S(cfg.UserHypha)
|
||||||
|
//line history/view.qtpl:66
|
||||||
|
qw422016.N().S(`/`)
|
||||||
|
//line history/view.qtpl:66
|
||||||
|
qw422016.E().S(rev.Username)
|
||||||
|
//line history/view.qtpl:66
|
||||||
|
qw422016.N().S(`" rel="author">`)
|
||||||
|
//line history/view.qtpl:66
|
||||||
|
qw422016.E().S(rev.Username)
|
||||||
|
//line history/view.qtpl:66
|
||||||
|
qw422016.N().S(`</a></span>
|
||||||
|
`)
|
||||||
|
//line history/view.qtpl:67
|
||||||
|
}
|
||||||
|
//line history/view.qtpl:67
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
|
</li>
|
||||||
`)
|
`)
|
||||||
//line history/view.qtpl:60
|
//line history/view.qtpl:69
|
||||||
}
|
}
|
||||||
//line history/view.qtpl:60
|
//line history/view.qtpl:69
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
`)
|
`)
|
||||||
//line history/view.qtpl:63
|
//line history/view.qtpl:72
|
||||||
}
|
}
|
||||||
//line history/view.qtpl:63
|
//line history/view.qtpl:72
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line history/view.qtpl:64
|
//line history/view.qtpl:73
|
||||||
}
|
}
|
||||||
|
|
||||||
//line history/view.qtpl:64
|
//line history/view.qtpl:73
|
||||||
func WriteWithRevisions(qq422016 qtio422016.Writer, hyphaName string, revs []Revision) {
|
func WriteWithRevisions(qq422016 qtio422016.Writer, hyphaName string, revs []Revision) {
|
||||||
//line history/view.qtpl:64
|
//line history/view.qtpl:73
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line history/view.qtpl:64
|
//line history/view.qtpl:73
|
||||||
StreamWithRevisions(qw422016, hyphaName, revs)
|
StreamWithRevisions(qw422016, hyphaName, revs)
|
||||||
//line history/view.qtpl:64
|
//line history/view.qtpl:73
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line history/view.qtpl:64
|
//line history/view.qtpl:73
|
||||||
}
|
}
|
||||||
|
|
||||||
//line history/view.qtpl:64
|
//line history/view.qtpl:73
|
||||||
func WithRevisions(hyphaName string, revs []Revision) string {
|
func WithRevisions(hyphaName string, revs []Revision) string {
|
||||||
//line history/view.qtpl:64
|
//line history/view.qtpl:73
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line history/view.qtpl:64
|
//line history/view.qtpl:73
|
||||||
WriteWithRevisions(qb422016, hyphaName, revs)
|
WriteWithRevisions(qb422016, hyphaName, revs)
|
||||||
//line history/view.qtpl:64
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line history/view.qtpl:64
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line history/view.qtpl:64
|
|
||||||
return qs422016
|
|
||||||
//line history/view.qtpl:64
|
|
||||||
}
|
|
||||||
|
|
||||||
//line history/view.qtpl:66
|
|
||||||
func (rev *Revision) streamasHistoryEntry(qw422016 *qt422016.Writer, hyphaName string) {
|
|
||||||
//line history/view.qtpl:66
|
|
||||||
qw422016.N().S(`
|
|
||||||
<li class="history__entry">
|
|
||||||
<a class="history-entry" href="/rev/`)
|
|
||||||
//line history/view.qtpl:68
|
|
||||||
qw422016.E().S(rev.Hash)
|
|
||||||
//line history/view.qtpl:68
|
|
||||||
qw422016.N().S(`/`)
|
|
||||||
//line history/view.qtpl:68
|
|
||||||
qw422016.E().S(hyphaName)
|
|
||||||
//line history/view.qtpl:68
|
|
||||||
qw422016.N().S(`">
|
|
||||||
<time class="history-entry__time">`)
|
|
||||||
//line history/view.qtpl:69
|
|
||||||
qw422016.E().S(rev.timeToDisplay())
|
|
||||||
//line history/view.qtpl:69
|
|
||||||
qw422016.N().S(`</time>
|
|
||||||
</a>
|
|
||||||
<span class="history-entry__hash"><a href="/primitive-diff/`)
|
|
||||||
//line history/view.qtpl:71
|
|
||||||
qw422016.E().S(rev.Hash)
|
|
||||||
//line history/view.qtpl:71
|
|
||||||
qw422016.N().S(`/`)
|
|
||||||
//line history/view.qtpl:71
|
|
||||||
qw422016.E().S(hyphaName)
|
|
||||||
//line history/view.qtpl:71
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line history/view.qtpl:71
|
|
||||||
qw422016.E().S(rev.Hash)
|
|
||||||
//line history/view.qtpl:71
|
|
||||||
qw422016.N().S(`</a></span>
|
|
||||||
<span class="history-entry__msg">`)
|
|
||||||
//line history/view.qtpl:72
|
|
||||||
qw422016.E().S(rev.Message)
|
|
||||||
//line history/view.qtpl:72
|
|
||||||
qw422016.N().S(`</span>
|
|
||||||
`)
|
|
||||||
//line history/view.qtpl:73
|
//line history/view.qtpl:73
|
||||||
if rev.Username != "anon" {
|
|
||||||
//line history/view.qtpl:73
|
|
||||||
qw422016.N().S(`
|
|
||||||
<span class="history-entry__author">by <a href="/hypha/`)
|
|
||||||
//line history/view.qtpl:74
|
|
||||||
qw422016.E().S(cfg.UserHypha)
|
|
||||||
//line history/view.qtpl:74
|
|
||||||
qw422016.N().S(`/`)
|
|
||||||
//line history/view.qtpl:74
|
|
||||||
qw422016.E().S(rev.Username)
|
|
||||||
//line history/view.qtpl:74
|
|
||||||
qw422016.N().S(`" rel="author">`)
|
|
||||||
//line history/view.qtpl:74
|
|
||||||
qw422016.E().S(rev.Username)
|
|
||||||
//line history/view.qtpl:74
|
|
||||||
qw422016.N().S(`</a></span>
|
|
||||||
`)
|
|
||||||
//line history/view.qtpl:75
|
|
||||||
}
|
|
||||||
//line history/view.qtpl:75
|
|
||||||
qw422016.N().S(`
|
|
||||||
</li>
|
|
||||||
`)
|
|
||||||
//line history/view.qtpl:77
|
|
||||||
}
|
|
||||||
|
|
||||||
//line history/view.qtpl:77
|
|
||||||
func (rev *Revision) writeasHistoryEntry(qq422016 qtio422016.Writer, hyphaName string) {
|
|
||||||
//line history/view.qtpl:77
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line history/view.qtpl:77
|
|
||||||
rev.streamasHistoryEntry(qw422016, hyphaName)
|
|
||||||
//line history/view.qtpl:77
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line history/view.qtpl:77
|
|
||||||
}
|
|
||||||
|
|
||||||
//line history/view.qtpl:77
|
|
||||||
func (rev *Revision) asHistoryEntry(hyphaName string) string {
|
|
||||||
//line history/view.qtpl:77
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line history/view.qtpl:77
|
|
||||||
rev.writeasHistoryEntry(qb422016, hyphaName)
|
|
||||||
//line history/view.qtpl:77
|
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line history/view.qtpl:77
|
//line history/view.qtpl:73
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line history/view.qtpl:77
|
//line history/view.qtpl:73
|
||||||
return qs422016
|
return qs422016
|
||||||
//line history/view.qtpl:77
|
//line history/view.qtpl:73
|
||||||
}
|
}
|
||||||
|
3
httpd.go
@@ -1,14 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func serveHTTP(handler http.Handler) {
|
func serveHTTP(handler http.Handler) {
|
||||||
|
@@ -2,72 +2,19 @@ package hypview
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"github.com/bouncepaw/mycorrhiza/backlinks"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
//go:embed *.html
|
//go:embed *.html
|
||||||
fs embed.FS
|
fs embed.FS
|
||||||
ruTranslation = `
|
ruTranslation = `
|
||||||
{{define "editing hypha"}}Редактирование {{beautifulName .}}{{end}}
|
|
||||||
{{define "editing [[hypha]]"}}Редактирование <a href="/hypha/{{.}}">{{beautifulName .}}</a>{{end}}
|
|
||||||
{{define "creating [[hypha]]"}}Создание <a href="/hypha/{{.}}">{{beautifulName .}}</a>{{end}}
|
|
||||||
{{define "you're creating a new hypha"}}Вы создаёте новую гифу.{{end}}
|
|
||||||
{{define "describe your changes"}}Опишите ваши правки{{end}}
|
|
||||||
{{define "save"}}Сохранить{{end}}
|
|
||||||
{{define "preview"}}Предпросмотр{{end}}
|
|
||||||
{{define "previewing hypha"}}Предпросмотр «{{beautifulName .}}»{{end}}
|
|
||||||
{{define "preview tip"}}Заметьте, эта гифа ещё не сохранена. Вот её предпросмотр:{{end}}
|
|
||||||
|
|
||||||
{{define "markup"}}Разметка{{end}}
|
|
||||||
{{define "link"}}Ссылка{{end}}
|
|
||||||
{{define "link title"}}Текст{{end}}
|
|
||||||
{{define "heading"}}Заголовок{{end}}
|
|
||||||
{{define "bold"}}Жирный{{end}}
|
|
||||||
{{define "italic"}}Курсив{{end}}
|
|
||||||
{{define "highlight"}}Выделение{{end}}
|
|
||||||
{{define "underline"}}Подчеркивание{{end}}
|
|
||||||
{{define "mono"}}Моноширинный{{end}}
|
|
||||||
{{define "super"}}Надстрочный{{end}}
|
|
||||||
{{define "sub"}}Подстрочный{{end}}
|
|
||||||
{{define "strike"}}Зачёркнутый{{end}}
|
|
||||||
{{define "rocket"}}Ссылка-ракета{{end}}
|
|
||||||
{{define "transclude"}}Трансклюзия{{end}}
|
|
||||||
{{define "hr"}}Гориз. черта{{end}}
|
|
||||||
{{define "code"}}Код-блок{{end}}
|
|
||||||
{{define "bullets"}}Маркир. список{{end}}
|
|
||||||
{{define "numbers"}}Нумер. список{{end}}
|
|
||||||
{{define "mycomarkup help"}}<a href="/help/en/mycomarkup" class="shy-link">Подробнее</a> о Микоразметке{{end}}
|
|
||||||
{{define "actions"}}Действия{{end}}
|
|
||||||
{{define "current date utc"}}Дата UTC{{end}}
|
|
||||||
{{define "current time utc"}}Время UTC{{end}}
|
|
||||||
{{define "current date local"}}Местная дата{{end}}
|
|
||||||
{{define "current time local"}}Местное время{{end}}
|
|
||||||
{{define "selflink"}}Ссылка на вас{{end}}
|
|
||||||
|
|
||||||
{{define "empty heading"}}Эта гифа не существует{{end}}
|
|
||||||
{{define "empty no rights"}}У вас нет прав для создания новых гиф. Вы можете:{{end}}
|
|
||||||
{{define "empty log in"}}Войти в свою учётную запись, если она у вас есть{{end}}
|
|
||||||
{{define "empty register"}}Создать новую учётную запись{{end}}
|
|
||||||
{{define "write a text"}}Написать текст{{end}}
|
|
||||||
{{define "write a text tip"}}Напишите заметку, дневник, статью, рассказ или иной текст с помощью <a href="/help/en/mycomarkup" class="shy-link">Микоразметки</a>. Сохраняется полная история правок документа.{{end}}
|
|
||||||
{{define "write a text writing conventions"}}Не забывайте следовать правилам оформления этой вики, если они имеются.{{end}}
|
|
||||||
{{define "write a text btn"}}Создать{{end}}
|
|
||||||
{{define "upload a media"}}Загрузить медиа{{end}}
|
|
||||||
{{define "upload a media tip"}}Загрузите изображение, видео или аудио. Распространённые форматы можно просматривать из браузера, остальные можно только скачать и просмотреть локально. Позже вы можете дописать пояснение к этому медиа.{{end}}
|
|
||||||
{{define "upload a media btn"}}Загрузить{{end}}
|
|
||||||
|
|
||||||
{{define "delete hypha?"}}Удалить {{beautifulName .}}?{{end}}
|
|
||||||
{{define "delete [[hypha]]?"}}Удалить <a href="/hypha/{{.}}">{{beautifulName .}}</a>?{{end}}
|
|
||||||
{{define "want to delete?"}}Вы действительно хотите удалить эту гифу?{{end}}
|
|
||||||
{{define "delete tip"}}Нельзя отменить удаление гифы, но её история останется доступной.{{end}}
|
|
||||||
|
|
||||||
{{define "rename hypha?"}}Переименовать {{beautifulName .}}?{{end}}
|
{{define "rename hypha?"}}Переименовать {{beautifulName .}}?{{end}}
|
||||||
{{define "rename [[hypha]]?"}}Переименовать <a href="/hypha/{{.}}">{{beautifulName .}}</a>?{{end}}
|
{{define "rename [[hypha]]?"}}Переименовать <a href="/hypha/{{.}}">{{beautifulName .}}</a>?{{end}}
|
||||||
{{define "new name"}}Новое название:{{end}}
|
{{define "new name"}}Новое название:{{end}}
|
||||||
@@ -75,48 +22,15 @@ var (
|
|||||||
{{define "rename tip"}}Переименовывайте аккуратно. <a href="/help/en/rename">Документация на английском.</a>{{end}}
|
{{define "rename tip"}}Переименовывайте аккуратно. <a href="/help/en/rename">Документация на английском.</a>{{end}}
|
||||||
{{define "leave redirection"}}Оставить перенаправление{{end}}
|
{{define "leave redirection"}}Оставить перенаправление{{end}}
|
||||||
|
|
||||||
{{define "remove media from x?"}}Убрать медиа у {{beautifulName .}}?{{end}}
|
|
||||||
{{define "remove media from [[x]]?"}}Убрать медиа у <a href="/hypha/{{.MatchedHyphaName}}">{{beautifulName .MatchedHyphaName}}</a>?{{end}}
|
|
||||||
{{define "remove media for real?"}}Вы точно хотите убрать медиа у гифы «{{beautifulName .MatchedHyphaName}}»?{{end}}
|
|
||||||
`
|
`
|
||||||
chainNaviTitle viewutil.Chain
|
chainNaviTitle viewutil.Chain
|
||||||
chainEditHypha viewutil.Chain
|
|
||||||
chainEmptyHypha viewutil.Chain
|
|
||||||
chainDeleteHypha viewutil.Chain
|
|
||||||
chainRenameHypha viewutil.Chain
|
chainRenameHypha viewutil.Chain
|
||||||
chainRemoveMedia viewutil.Chain
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
chainNaviTitle = viewutil.CopyEnRuWith(fs, "view_navititle.html", "")
|
chainNaviTitle = viewutil.CopyEnRuWith(fs, "view_navititle.html", "")
|
||||||
chainEditHypha = viewutil.CopyEnRuWith(fs, "view_edit.html", ruTranslation)
|
|
||||||
chainEmptyHypha = viewutil.CopyEnRuWith(fs, "view_empty_hypha.html", ruTranslation)
|
|
||||||
chainDeleteHypha = viewutil.CopyEnRuWith(fs, "view_delete.html", ruTranslation)
|
|
||||||
chainRenameHypha = viewutil.CopyEnRuWith(fs, "view_rename.html", ruTranslation)
|
chainRenameHypha = viewutil.CopyEnRuWith(fs, "view_rename.html", ruTranslation)
|
||||||
chainRemoveMedia = viewutil.CopyEnRuWith(fs, "view_remove_media.html", ruTranslation)
|
|
||||||
}
|
|
||||||
|
|
||||||
type editData struct {
|
|
||||||
*viewutil.BaseData
|
|
||||||
HyphaName string
|
|
||||||
IsNew bool
|
|
||||||
Content string
|
|
||||||
Message string
|
|
||||||
Preview template.HTML
|
|
||||||
}
|
|
||||||
|
|
||||||
func EditHypha(meta viewutil.Meta, hyphaName string, isNew bool, content string, message string, preview template.HTML) {
|
|
||||||
viewutil.ExecutePage(meta, chainEditHypha, editData{
|
|
||||||
BaseData: &viewutil.BaseData{
|
|
||||||
Addr: "/edit/" + hyphaName,
|
|
||||||
EditScripts: cfg.EditScripts,
|
|
||||||
},
|
|
||||||
HyphaName: hyphaName,
|
|
||||||
IsNew: isNew,
|
|
||||||
Content: content,
|
|
||||||
Message: message,
|
|
||||||
Preview: preview,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type renameData struct {
|
type renameData struct {
|
||||||
@@ -135,49 +49,6 @@ func RenameHypha(meta viewutil.Meta, hyphaName string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type deleteRemoveMediaData struct {
|
|
||||||
*viewutil.BaseData
|
|
||||||
HyphaName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteHypha(meta viewutil.Meta, hyphaName string) {
|
|
||||||
viewutil.ExecutePage(meta, chainDeleteHypha, deleteRemoveMediaData{
|
|
||||||
BaseData: &viewutil.BaseData{
|
|
||||||
Addr: "/delete/" + hyphaName,
|
|
||||||
},
|
|
||||||
HyphaName: hyphaName,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveMedia(meta viewutil.Meta, hyphaName string) {
|
|
||||||
viewutil.ExecutePage(meta, chainRemoveMedia, deleteRemoveMediaData{
|
|
||||||
BaseData: &viewutil.BaseData{
|
|
||||||
Addr: "/remove-media/" + hyphaName,
|
|
||||||
},
|
|
||||||
HyphaName: hyphaName,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type emptyHyphaData struct {
|
|
||||||
Meta viewutil.Meta
|
|
||||||
HyphaName string
|
|
||||||
AllowRegistration bool
|
|
||||||
UseAuth bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func EmptyHypha(meta viewutil.Meta, hyphaName string) string {
|
|
||||||
var buf strings.Builder
|
|
||||||
if err := chainEmptyHypha.Get(meta).ExecuteTemplate(&buf, "empty hypha card", emptyHyphaData{
|
|
||||||
Meta: meta,
|
|
||||||
HyphaName: hyphaName,
|
|
||||||
AllowRegistration: cfg.AllowRegistration,
|
|
||||||
UseAuth: cfg.UseAuth,
|
|
||||||
}); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type naviTitleData struct {
|
type naviTitleData struct {
|
||||||
HyphaNameParts []string
|
HyphaNameParts []string
|
||||||
HyphaNamePartsWithParents []string
|
HyphaNamePartsWithParents []string
|
||||||
@@ -185,7 +56,7 @@ type naviTitleData struct {
|
|||||||
HomeHypha string
|
HomeHypha string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NaviTitle(meta viewutil.Meta, hyphaName string) string {
|
func NaviTitle(meta viewutil.Meta, hyphaName string) template.HTML {
|
||||||
parts, partsWithParents := naviTitleify(hyphaName)
|
parts, partsWithParents := naviTitleify(hyphaName)
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
err := chainNaviTitle.Get(meta).ExecuteTemplate(&buf, "navititle", naviTitleData{
|
err := chainNaviTitle.Get(meta).ExecuteTemplate(&buf, "navititle", naviTitleData{
|
||||||
@@ -197,7 +68,7 @@ func NaviTitle(meta viewutil.Meta, hyphaName string) string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
return buf.String()
|
return template.HTML(buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func naviTitleify(hyphaName string) ([]string, []string) {
|
func naviTitleify(hyphaName string) ([]string, []string) {
|
||||||
|
@@ -1,50 +0,0 @@
|
|||||||
{% import "github.com/bouncepaw/mycorrhiza/backlinks" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/viewutil" %}
|
|
||||||
|
|
||||||
{% func hyphaInfoEntry(h hyphae.Hypha, u *user.User, action string, hasToExist bool, displayText string) %}
|
|
||||||
{% code flag := true %}
|
|
||||||
{% switch h.(type) %}
|
|
||||||
{% case *hyphae.EmptyHypha %}
|
|
||||||
{% code flag = !hasToExist %}
|
|
||||||
{% endswitch %}
|
|
||||||
{% if u.CanProceed(action) && flag %}
|
|
||||||
<li class="hypha-info__entry hypha-info__entry_{%s action %}">
|
|
||||||
<a class="hypha-info__link" href="/{%s action %}/{%s h.CanonicalName() %}">{%s displayText %}</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func hyphaInfo(meta viewutil.Meta, h hyphae.Hypha) %}
|
|
||||||
{% code
|
|
||||||
u := meta.U
|
|
||||||
lc := meta.Lc
|
|
||||||
backs := backlinks.BacklinksCount(h.CanonicalName())
|
|
||||||
%}
|
|
||||||
<nav class="hypha-info">
|
|
||||||
<ul class="hypha-info__list">
|
|
||||||
{%= hyphaInfoEntry(h, u, "history", false, lc.Get("ui.history_link")) %}
|
|
||||||
{%= hyphaInfoEntry(h, u, "rename", true, lc.Get("ui.rename_link")) %}
|
|
||||||
{%= hyphaInfoEntry(h, u, "delete", true, lc.Get("ui.delete_link")) %}
|
|
||||||
{%= hyphaInfoEntry(h, u, "text", true, lc.Get("ui.text_link")) %}
|
|
||||||
{% switch h := h.(type) %}
|
|
||||||
{% case *hyphae.TextualHypha %}
|
|
||||||
{%= hyphaInfoEntry(h, u, "media", true, lc.Get("ui.media_link_for_textual")) %}
|
|
||||||
{% default %}
|
|
||||||
{%= hyphaInfoEntry(h, u, "media", true, lc.Get("ui.media_link")) %}
|
|
||||||
{% endswitch %}
|
|
||||||
{%= hyphaInfoEntry(h, u, "backlinks", false, lc.GetPlural("ui.backlinks_link", backs)) %}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func commonScripts() %}
|
|
||||||
{% for _, scriptPath := range cfg.CommonScripts %}
|
|
||||||
<script src="{%s scriptPath %}"></script>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func beautifulLink(hyphaName string) %}<a href="/hypha/{%s= hyphaName %}">{%s util.BeautifulName(hyphaName) %}</a>{% endfunc %}
|
|
@@ -1,311 +0,0 @@
|
|||||||
// Code generated by qtc from "nav.qtpl". DO NOT EDIT.
|
|
||||||
// See https://github.com/valyala/quicktemplate for details.
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:1
|
|
||||||
package hypview
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:1
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/backlinks"
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:2
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:3
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:4
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:5
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:6
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:8
|
|
||||||
import (
|
|
||||||
qtio422016 "io"
|
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
|
||||||
)
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:8
|
|
||||||
var (
|
|
||||||
_ = qtio422016.Copy
|
|
||||||
_ = qt422016.AcquireByteBuffer
|
|
||||||
)
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:8
|
|
||||||
func streamhyphaInfoEntry(qw422016 *qt422016.Writer, h hyphae.Hypha, u *user.User, action string, hasToExist bool, displayText string) {
|
|
||||||
//line hypview/nav.qtpl:8
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:9
|
|
||||||
flag := true
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:9
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:10
|
|
||||||
switch h.(type) {
|
|
||||||
//line hypview/nav.qtpl:11
|
|
||||||
case *hyphae.EmptyHypha:
|
|
||||||
//line hypview/nav.qtpl:11
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:12
|
|
||||||
flag = !hasToExist
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:12
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:13
|
|
||||||
}
|
|
||||||
//line hypview/nav.qtpl:13
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:14
|
|
||||||
if u.CanProceed(action) && flag {
|
|
||||||
//line hypview/nav.qtpl:14
|
|
||||||
qw422016.N().S(`
|
|
||||||
<li class="hypha-info__entry hypha-info__entry_`)
|
|
||||||
//line hypview/nav.qtpl:15
|
|
||||||
qw422016.E().S(action)
|
|
||||||
//line hypview/nav.qtpl:15
|
|
||||||
qw422016.N().S(`">
|
|
||||||
<a class="hypha-info__link" href="/`)
|
|
||||||
//line hypview/nav.qtpl:16
|
|
||||||
qw422016.E().S(action)
|
|
||||||
//line hypview/nav.qtpl:16
|
|
||||||
qw422016.N().S(`/`)
|
|
||||||
//line hypview/nav.qtpl:16
|
|
||||||
qw422016.E().S(h.CanonicalName())
|
|
||||||
//line hypview/nav.qtpl:16
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line hypview/nav.qtpl:16
|
|
||||||
qw422016.E().S(displayText)
|
|
||||||
//line hypview/nav.qtpl:16
|
|
||||||
qw422016.N().S(`</a>
|
|
||||||
</li>
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:18
|
|
||||||
}
|
|
||||||
//line hypview/nav.qtpl:18
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
func writehyphaInfoEntry(qq422016 qtio422016.Writer, h hyphae.Hypha, u *user.User, action string, hasToExist bool, displayText string) {
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
streamhyphaInfoEntry(qw422016, h, u, action, hasToExist, displayText)
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
func hyphaInfoEntry(h hyphae.Hypha, u *user.User, action string, hasToExist bool, displayText string) string {
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
writehyphaInfoEntry(qb422016, h, u, action, hasToExist, displayText)
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
return qs422016
|
|
||||||
//line hypview/nav.qtpl:19
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:21
|
|
||||||
func streamhyphaInfo(qw422016 *qt422016.Writer, meta viewutil.Meta, h hyphae.Hypha) {
|
|
||||||
//line hypview/nav.qtpl:21
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:23
|
|
||||||
u := meta.U
|
|
||||||
lc := meta.Lc
|
|
||||||
backs := backlinks.BacklinksCount(h.CanonicalName())
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:26
|
|
||||||
qw422016.N().S(`
|
|
||||||
<nav class="hypha-info">
|
|
||||||
<ul class="hypha-info__list">
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:29
|
|
||||||
streamhyphaInfoEntry(qw422016, h, u, "history", false, lc.Get("ui.history_link"))
|
|
||||||
//line hypview/nav.qtpl:29
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:30
|
|
||||||
streamhyphaInfoEntry(qw422016, h, u, "rename", true, lc.Get("ui.rename_link"))
|
|
||||||
//line hypview/nav.qtpl:30
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:31
|
|
||||||
streamhyphaInfoEntry(qw422016, h, u, "delete", true, lc.Get("ui.delete_link"))
|
|
||||||
//line hypview/nav.qtpl:31
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:32
|
|
||||||
streamhyphaInfoEntry(qw422016, h, u, "text", true, lc.Get("ui.text_link"))
|
|
||||||
//line hypview/nav.qtpl:32
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:33
|
|
||||||
switch h := h.(type) {
|
|
||||||
//line hypview/nav.qtpl:34
|
|
||||||
case *hyphae.TextualHypha:
|
|
||||||
//line hypview/nav.qtpl:34
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:35
|
|
||||||
streamhyphaInfoEntry(qw422016, h, u, "media", true, lc.Get("ui.media_link_for_textual"))
|
|
||||||
//line hypview/nav.qtpl:35
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:36
|
|
||||||
default:
|
|
||||||
//line hypview/nav.qtpl:36
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:37
|
|
||||||
streamhyphaInfoEntry(qw422016, h, u, "media", true, lc.Get("ui.media_link"))
|
|
||||||
//line hypview/nav.qtpl:37
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:38
|
|
||||||
}
|
|
||||||
//line hypview/nav.qtpl:38
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:39
|
|
||||||
streamhyphaInfoEntry(qw422016, h, u, "backlinks", false, lc.GetPlural("ui.backlinks_link", backs))
|
|
||||||
//line hypview/nav.qtpl:39
|
|
||||||
qw422016.N().S(`
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
func writehyphaInfo(qq422016 qtio422016.Writer, meta viewutil.Meta, h hyphae.Hypha) {
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
streamhyphaInfo(qw422016, meta, h)
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
func hyphaInfo(meta viewutil.Meta, h hyphae.Hypha) string {
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
writehyphaInfo(qb422016, meta, h)
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
return qs422016
|
|
||||||
//line hypview/nav.qtpl:42
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:44
|
|
||||||
func streamcommonScripts(qw422016 *qt422016.Writer) {
|
|
||||||
//line hypview/nav.qtpl:44
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:45
|
|
||||||
for _, scriptPath := range cfg.CommonScripts {
|
|
||||||
//line hypview/nav.qtpl:45
|
|
||||||
qw422016.N().S(`
|
|
||||||
<script src="`)
|
|
||||||
//line hypview/nav.qtpl:46
|
|
||||||
qw422016.E().S(scriptPath)
|
|
||||||
//line hypview/nav.qtpl:46
|
|
||||||
qw422016.N().S(`"></script>
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:47
|
|
||||||
}
|
|
||||||
//line hypview/nav.qtpl:47
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
func writecommonScripts(qq422016 qtio422016.Writer) {
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
streamcommonScripts(qw422016)
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
func commonScripts() string {
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
writecommonScripts(qb422016)
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
return qs422016
|
|
||||||
//line hypview/nav.qtpl:48
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
func streambeautifulLink(qw422016 *qt422016.Writer, hyphaName string) {
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
qw422016.N().S(`<a href="/hypha/`)
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
qw422016.N().S(hyphaName)
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
qw422016.E().S(util.BeautifulName(hyphaName))
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
qw422016.N().S(`</a>`)
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
func writebeautifulLink(qq422016 qtio422016.Writer, hyphaName string) {
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
streambeautifulLink(qw422016, hyphaName)
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
func beautifulLink(hyphaName string) string {
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
writebeautifulLink(qb422016, hyphaName)
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
return qs422016
|
|
||||||
//line hypview/nav.qtpl:50
|
|
||||||
}
|
|
@@ -1,161 +0,0 @@
|
|||||||
{% import "net/http" %}
|
|
||||||
{% import "strings" %}
|
|
||||||
{% import "path" %}
|
|
||||||
{% import "os" %}
|
|
||||||
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/categories" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/mimetype" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/tree" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/viewutil" %}
|
|
||||||
|
|
||||||
{% func MediaMenu(rq *http.Request, h hyphae.Hypha, u *user.User) %}
|
|
||||||
{% code
|
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
%}
|
|
||||||
<main class="main-width media-tab">
|
|
||||||
<h1>{%s= lc.Get("ui.media_title", &l18n.Replacements{"name": beautifulLink(h.CanonicalName())}) %}</h1>
|
|
||||||
{% switch h.(type) %}
|
|
||||||
{% case *hyphae.MediaHypha %}
|
|
||||||
<p class="explanation">{%s lc.Get("ui.media_tip") %} <a href="/help/en/media" class="shy-link">{%s lc.Get("ui.media_what_is") %}</a></p>
|
|
||||||
{% default %}
|
|
||||||
<p class="explanation">{%s lc.Get("ui.media_empty") %} <a href="/help/en/media" class="shy-link">{%s lc.Get("ui.media_what_is") %}</a></p>
|
|
||||||
{% endswitch %}
|
|
||||||
|
|
||||||
<section class="amnt-grid">
|
|
||||||
{% switch h := h.(type) %}
|
|
||||||
{% case *hyphae.MediaHypha %}
|
|
||||||
{% code
|
|
||||||
mime := mimetype.FromExtension(path.Ext(h.MediaFilePath()))
|
|
||||||
fileinfo, err := os.Stat(h.MediaFilePath()) %}
|
|
||||||
{% if err == nil %}
|
|
||||||
<fieldset class="amnt-menu-block">
|
|
||||||
<legend class="modal__title modal__title_small">{%s lc.Get("ui.media_stat") %}</legend>
|
|
||||||
<p class="modal__confirmation-msg"><b>{%s lc.Get("ui.media_stat_size") %}</b> {%s lc.GetPlural64("ui.media_size_value", fileinfo.Size())%}</p>
|
|
||||||
<p><b>{%s lc.Get("ui.media_stat_mime") %}</b> {%s mime %}</p>
|
|
||||||
</fieldset>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if strings.HasPrefix(mime, "image/") %}
|
|
||||||
<fieldset class="amnt-menu-block">
|
|
||||||
<legend class="modal__title modal__title_small">{%s lc.Get("ui.media_include") %}</legend>
|
|
||||||
<p class="modal__confirmation-msg">{%s lc.Get("ui.media_include_tip") %}</p>
|
|
||||||
<pre class="codeblock"><code>img { {%s h.CanonicalName() %} }</code></pre>
|
|
||||||
</fieldset>
|
|
||||||
{% endif %}
|
|
||||||
{% endswitch %}
|
|
||||||
|
|
||||||
{% if u.CanProceed("upload-binary") %}
|
|
||||||
<form action="/upload-binary/{%s h.CanonicalName() %}"
|
|
||||||
method="post" enctype="multipart/form-data"
|
|
||||||
class="upload-binary modal amnt-menu-block">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title modal__title_small">{%s lc.Get("ui.media_new") %}</legend>
|
|
||||||
<p class="modal__confirmation-msg">{%s lc.Get("ui.media_new_tip") %}</p>
|
|
||||||
<label for="upload-binary__input"></label>
|
|
||||||
<input type="file" id="upload-binary__input" name="binary">
|
|
||||||
|
|
||||||
<button type="submit" class="btn stick-to-bottom" value="Upload">{%s lc.Get("ui.media_upload")%}</button>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
{% switch h := h.(type) %}
|
|
||||||
{% case *hyphae.MediaHypha %}
|
|
||||||
{% if u.CanProceed("remove-media") %}
|
|
||||||
<form action="/remove-media/{%s h.CanonicalName() %}" method="post" class="modal amnt-menu-block" method="POST">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title modal__title_small">{%s lc.Get("ui.media_remove") %}</legend>
|
|
||||||
<p class="modal__confirmation-msg">{%s lc.Get("ui.media_remove_tip") %}</p>
|
|
||||||
<button type="submit" class="btn" value="Remove media">{%s lc.Get("ui.media_remove_button") %}</button>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% endswitch %}
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
If `contents` == "", a helpful message is shown instead.
|
|
||||||
|
|
||||||
If you rename .prevnext, change the docs too.
|
|
||||||
{% func Hypha(meta viewutil.Meta, h hyphae.Hypha, contents string) %}
|
|
||||||
{% code
|
|
||||||
subhyphae, prevHyphaName, nextHyphaName := tree.Tree(h.CanonicalName())
|
|
||||||
lc := meta.Lc
|
|
||||||
%}
|
|
||||||
<main class="main-width">
|
|
||||||
<section id="hypha">
|
|
||||||
{% if meta.U.CanProceed("edit") %}
|
|
||||||
<div class="btn btn_navititle">
|
|
||||||
<a class="btn__link_navititle" href="/edit/{%s h.CanonicalName() %}">{%s lc.Get("ui.edit_link") %}</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if cfg.UseAuth && util.IsProfileName(h.CanonicalName()) && meta.U.Name == strings.TrimPrefix(h.CanonicalName(), cfg.UserHypha + "/") %}
|
|
||||||
<div class="btn btn_navititle">
|
|
||||||
<a class="btn__link_navititle" href="/logout">{%s lc.Get("ui.logout_link") %}</a>
|
|
||||||
</div>
|
|
||||||
{% if meta.U.Group == "admin" %}
|
|
||||||
<div class="btn btn_navititle">
|
|
||||||
<a class="btn__link_navititle" href="/admin">{%s lc.Get("ui.admin_panel") %}<a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{%s= NaviTitle(meta, h.CanonicalName()) %}
|
|
||||||
{% switch h.(type) %}
|
|
||||||
{% case *hyphae.EmptyHypha %}
|
|
||||||
{%s= EmptyHypha(meta, h.CanonicalName()) %}
|
|
||||||
{% default %}
|
|
||||||
{%s= contents %}
|
|
||||||
{% endswitch %}
|
|
||||||
</section>
|
|
||||||
<section class="prevnext">
|
|
||||||
{% if prevHyphaName != "" %}
|
|
||||||
<a class="prevnext__el prevnext__prev" href="/hypha/{%s prevHyphaName %}" rel="prev">← {%s util.BeautifulName(path.Base(prevHyphaName)) %}</a>
|
|
||||||
{% endif %}
|
|
||||||
{% if nextHyphaName != "" %}
|
|
||||||
<a class="prevnext__el prevnext__next" href="/hypha/{%s nextHyphaName %}" rel="next">{%s util.BeautifulName(path.Base(nextHyphaName)) %} →</a>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
{% if strings.TrimSpace(subhyphae) != "" %}
|
|
||||||
<section class="subhyphae">
|
|
||||||
<h2 class="subhyphae__title">{%s lc.Get("ui.subhyphae") %}</h2>
|
|
||||||
<nav class="subhyphae__nav">
|
|
||||||
<ul class="subhyphae__list">
|
|
||||||
{%s= subhyphae %}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
<section id="hypha-bottom">
|
|
||||||
{%= hyphaInfo(meta, h) %}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{%s= categories.CategoryCard(meta, h.CanonicalName()) %}
|
|
||||||
{%= viewScripts() %}
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func Revision(meta viewutil.Meta, h hyphae.Hypha, contents, revHash string) %}
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
<p>{%s meta.Lc.Get("ui.revision_warning") %} <a href="/rev-text/{%s revHash %}/{%s h.CanonicalName() %}">{%s meta.Lc.Get("ui.revision_link") %}</a></p>
|
|
||||||
{%s= NaviTitle(meta, h.CanonicalName()) %}
|
|
||||||
{%s= contents %}
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
{%= viewScripts() %}
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
||||||
{% func viewScripts() %}
|
|
||||||
{% for _, scriptPath := range cfg.ViewScripts %}
|
|
||||||
<script src="{%s scriptPath %}"></script>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfunc %}
|
|
@@ -1,651 +0,0 @@
|
|||||||
// Code generated by qtc from "readers.qtpl". DO NOT EDIT.
|
|
||||||
// See https://github.com/valyala/quicktemplate for details.
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:1
|
|
||||||
package hypview
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:1
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:2
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:3
|
|
||||||
import "path"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:4
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:6
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:7
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:8
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/categories"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:9
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/l18n"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:10
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/mimetype"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:11
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/tree"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:12
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:13
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:14
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:16
|
|
||||||
import (
|
|
||||||
qtio422016 "io"
|
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
|
||||||
)
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:16
|
|
||||||
var (
|
|
||||||
_ = qtio422016.Copy
|
|
||||||
_ = qt422016.AcquireByteBuffer
|
|
||||||
)
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:16
|
|
||||||
func StreamMediaMenu(qw422016 *qt422016.Writer, rq *http.Request, h hyphae.Hypha, u *user.User) {
|
|
||||||
//line hypview/readers.qtpl:16
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:18
|
|
||||||
lc := l18n.FromRequest(rq)
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:19
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width media-tab">
|
|
||||||
<h1>`)
|
|
||||||
//line hypview/readers.qtpl:21
|
|
||||||
qw422016.N().S(lc.Get("ui.media_title", &l18n.Replacements{"name": beautifulLink(h.CanonicalName())}))
|
|
||||||
//line hypview/readers.qtpl:21
|
|
||||||
qw422016.N().S(`</h1>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:22
|
|
||||||
switch h.(type) {
|
|
||||||
//line hypview/readers.qtpl:23
|
|
||||||
case *hyphae.MediaHypha:
|
|
||||||
//line hypview/readers.qtpl:23
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p class="explanation">`)
|
|
||||||
//line hypview/readers.qtpl:24
|
|
||||||
qw422016.E().S(lc.Get("ui.media_tip"))
|
|
||||||
//line hypview/readers.qtpl:24
|
|
||||||
qw422016.N().S(` <a href="/help/en/media" class="shy-link">`)
|
|
||||||
//line hypview/readers.qtpl:24
|
|
||||||
qw422016.E().S(lc.Get("ui.media_what_is"))
|
|
||||||
//line hypview/readers.qtpl:24
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:25
|
|
||||||
default:
|
|
||||||
//line hypview/readers.qtpl:25
|
|
||||||
qw422016.N().S(`
|
|
||||||
<p class="explanation">`)
|
|
||||||
//line hypview/readers.qtpl:26
|
|
||||||
qw422016.E().S(lc.Get("ui.media_empty"))
|
|
||||||
//line hypview/readers.qtpl:26
|
|
||||||
qw422016.N().S(` <a href="/help/en/media" class="shy-link">`)
|
|
||||||
//line hypview/readers.qtpl:26
|
|
||||||
qw422016.E().S(lc.Get("ui.media_what_is"))
|
|
||||||
//line hypview/readers.qtpl:26
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:27
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:27
|
|
||||||
qw422016.N().S(`
|
|
||||||
|
|
||||||
<section class="amnt-grid">
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:30
|
|
||||||
switch h := h.(type) {
|
|
||||||
//line hypview/readers.qtpl:31
|
|
||||||
case *hyphae.MediaHypha:
|
|
||||||
//line hypview/readers.qtpl:31
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:33
|
|
||||||
mime := mimetype.FromExtension(path.Ext(h.MediaFilePath()))
|
|
||||||
fileinfo, err := os.Stat(h.MediaFilePath())
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:34
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:35
|
|
||||||
if err == nil {
|
|
||||||
//line hypview/readers.qtpl:35
|
|
||||||
qw422016.N().S(`
|
|
||||||
<fieldset class="amnt-menu-block">
|
|
||||||
<legend class="modal__title modal__title_small">`)
|
|
||||||
//line hypview/readers.qtpl:37
|
|
||||||
qw422016.E().S(lc.Get("ui.media_stat"))
|
|
||||||
//line hypview/readers.qtpl:37
|
|
||||||
qw422016.N().S(`</legend>
|
|
||||||
<p class="modal__confirmation-msg"><b>`)
|
|
||||||
//line hypview/readers.qtpl:38
|
|
||||||
qw422016.E().S(lc.Get("ui.media_stat_size"))
|
|
||||||
//line hypview/readers.qtpl:38
|
|
||||||
qw422016.N().S(`</b> `)
|
|
||||||
//line hypview/readers.qtpl:38
|
|
||||||
qw422016.E().S(lc.GetPlural64("ui.media_size_value", fileinfo.Size()))
|
|
||||||
//line hypview/readers.qtpl:38
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<p><b>`)
|
|
||||||
//line hypview/readers.qtpl:39
|
|
||||||
qw422016.E().S(lc.Get("ui.media_stat_mime"))
|
|
||||||
//line hypview/readers.qtpl:39
|
|
||||||
qw422016.N().S(`</b> `)
|
|
||||||
//line hypview/readers.qtpl:39
|
|
||||||
qw422016.E().S(mime)
|
|
||||||
//line hypview/readers.qtpl:39
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
</fieldset>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:41
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:41
|
|
||||||
qw422016.N().S(`
|
|
||||||
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:43
|
|
||||||
if strings.HasPrefix(mime, "image/") {
|
|
||||||
//line hypview/readers.qtpl:43
|
|
||||||
qw422016.N().S(`
|
|
||||||
<fieldset class="amnt-menu-block">
|
|
||||||
<legend class="modal__title modal__title_small">`)
|
|
||||||
//line hypview/readers.qtpl:45
|
|
||||||
qw422016.E().S(lc.Get("ui.media_include"))
|
|
||||||
//line hypview/readers.qtpl:45
|
|
||||||
qw422016.N().S(`</legend>
|
|
||||||
<p class="modal__confirmation-msg">`)
|
|
||||||
//line hypview/readers.qtpl:46
|
|
||||||
qw422016.E().S(lc.Get("ui.media_include_tip"))
|
|
||||||
//line hypview/readers.qtpl:46
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<pre class="codeblock"><code>img { `)
|
|
||||||
//line hypview/readers.qtpl:47
|
|
||||||
qw422016.E().S(h.CanonicalName())
|
|
||||||
//line hypview/readers.qtpl:47
|
|
||||||
qw422016.N().S(` }</code></pre>
|
|
||||||
</fieldset>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:49
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:49
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:50
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:50
|
|
||||||
qw422016.N().S(`
|
|
||||||
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:52
|
|
||||||
if u.CanProceed("upload-binary") {
|
|
||||||
//line hypview/readers.qtpl:52
|
|
||||||
qw422016.N().S(`
|
|
||||||
<form action="/upload-binary/`)
|
|
||||||
//line hypview/readers.qtpl:53
|
|
||||||
qw422016.E().S(h.CanonicalName())
|
|
||||||
//line hypview/readers.qtpl:53
|
|
||||||
qw422016.N().S(`"
|
|
||||||
method="post" enctype="multipart/form-data"
|
|
||||||
class="upload-binary modal amnt-menu-block">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title modal__title_small">`)
|
|
||||||
//line hypview/readers.qtpl:57
|
|
||||||
qw422016.E().S(lc.Get("ui.media_new"))
|
|
||||||
//line hypview/readers.qtpl:57
|
|
||||||
qw422016.N().S(`</legend>
|
|
||||||
<p class="modal__confirmation-msg">`)
|
|
||||||
//line hypview/readers.qtpl:58
|
|
||||||
qw422016.E().S(lc.Get("ui.media_new_tip"))
|
|
||||||
//line hypview/readers.qtpl:58
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<label for="upload-binary__input"></label>
|
|
||||||
<input type="file" id="upload-binary__input" name="binary">
|
|
||||||
|
|
||||||
<button type="submit" class="btn stick-to-bottom" value="Upload">`)
|
|
||||||
//line hypview/readers.qtpl:62
|
|
||||||
qw422016.E().S(lc.Get("ui.media_upload"))
|
|
||||||
//line hypview/readers.qtpl:62
|
|
||||||
qw422016.N().S(`</button>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:65
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:65
|
|
||||||
qw422016.N().S(`
|
|
||||||
|
|
||||||
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:68
|
|
||||||
switch h := h.(type) {
|
|
||||||
//line hypview/readers.qtpl:69
|
|
||||||
case *hyphae.MediaHypha:
|
|
||||||
//line hypview/readers.qtpl:69
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:70
|
|
||||||
if u.CanProceed("remove-media") {
|
|
||||||
//line hypview/readers.qtpl:70
|
|
||||||
qw422016.N().S(`
|
|
||||||
<form action="/remove-media/`)
|
|
||||||
//line hypview/readers.qtpl:71
|
|
||||||
qw422016.E().S(h.CanonicalName())
|
|
||||||
//line hypview/readers.qtpl:71
|
|
||||||
qw422016.N().S(`" method="post" class="modal amnt-menu-block" method="POST">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title modal__title_small">`)
|
|
||||||
//line hypview/readers.qtpl:73
|
|
||||||
qw422016.E().S(lc.Get("ui.media_remove"))
|
|
||||||
//line hypview/readers.qtpl:73
|
|
||||||
qw422016.N().S(`</legend>
|
|
||||||
<p class="modal__confirmation-msg">`)
|
|
||||||
//line hypview/readers.qtpl:74
|
|
||||||
qw422016.E().S(lc.Get("ui.media_remove_tip"))
|
|
||||||
//line hypview/readers.qtpl:74
|
|
||||||
qw422016.N().S(`</p>
|
|
||||||
<button type="submit" class="btn" value="Remove media">`)
|
|
||||||
//line hypview/readers.qtpl:75
|
|
||||||
qw422016.E().S(lc.Get("ui.media_remove_button"))
|
|
||||||
//line hypview/readers.qtpl:75
|
|
||||||
qw422016.N().S(`</button>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:78
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:78
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:79
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:79
|
|
||||||
qw422016.N().S(`
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
func WriteMediaMenu(qq422016 qtio422016.Writer, rq *http.Request, h hyphae.Hypha, u *user.User) {
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
StreamMediaMenu(qw422016, rq, h, u)
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
func MediaMenu(rq *http.Request, h hyphae.Hypha, u *user.User) string {
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
WriteMediaMenu(qb422016, rq, h, u)
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
return qs422016
|
|
||||||
//line hypview/readers.qtpl:83
|
|
||||||
}
|
|
||||||
|
|
||||||
// If `contents` == "", a helpful message is shown instead.
|
|
||||||
//
|
|
||||||
// If you rename .prevnext, change the docs too.
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:88
|
|
||||||
func StreamHypha(qw422016 *qt422016.Writer, meta viewutil.Meta, h hyphae.Hypha, contents string) {
|
|
||||||
//line hypview/readers.qtpl:88
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:90
|
|
||||||
subhyphae, prevHyphaName, nextHyphaName := tree.Tree(h.CanonicalName())
|
|
||||||
lc := meta.Lc
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:92
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width">
|
|
||||||
<section id="hypha">
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:95
|
|
||||||
if meta.U.CanProceed("edit") {
|
|
||||||
//line hypview/readers.qtpl:95
|
|
||||||
qw422016.N().S(`
|
|
||||||
<div class="btn btn_navititle">
|
|
||||||
<a class="btn__link_navititle" href="/edit/`)
|
|
||||||
//line hypview/readers.qtpl:97
|
|
||||||
qw422016.E().S(h.CanonicalName())
|
|
||||||
//line hypview/readers.qtpl:97
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line hypview/readers.qtpl:97
|
|
||||||
qw422016.E().S(lc.Get("ui.edit_link"))
|
|
||||||
//line hypview/readers.qtpl:97
|
|
||||||
qw422016.N().S(`</a>
|
|
||||||
</div>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:99
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:99
|
|
||||||
qw422016.N().S(`
|
|
||||||
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:101
|
|
||||||
if cfg.UseAuth && util.IsProfileName(h.CanonicalName()) && meta.U.Name == strings.TrimPrefix(h.CanonicalName(), cfg.UserHypha+"/") {
|
|
||||||
//line hypview/readers.qtpl:101
|
|
||||||
qw422016.N().S(`
|
|
||||||
<div class="btn btn_navititle">
|
|
||||||
<a class="btn__link_navititle" href="/logout">`)
|
|
||||||
//line hypview/readers.qtpl:103
|
|
||||||
qw422016.E().S(lc.Get("ui.logout_link"))
|
|
||||||
//line hypview/readers.qtpl:103
|
|
||||||
qw422016.N().S(`</a>
|
|
||||||
</div>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:105
|
|
||||||
if meta.U.Group == "admin" {
|
|
||||||
//line hypview/readers.qtpl:105
|
|
||||||
qw422016.N().S(`
|
|
||||||
<div class="btn btn_navititle">
|
|
||||||
<a class="btn__link_navititle" href="/admin">`)
|
|
||||||
//line hypview/readers.qtpl:107
|
|
||||||
qw422016.E().S(lc.Get("ui.admin_panel"))
|
|
||||||
//line hypview/readers.qtpl:107
|
|
||||||
qw422016.N().S(`<a>
|
|
||||||
</div>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:109
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:109
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:110
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:110
|
|
||||||
qw422016.N().S(`
|
|
||||||
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:112
|
|
||||||
qw422016.N().S(NaviTitle(meta, h.CanonicalName()))
|
|
||||||
//line hypview/readers.qtpl:112
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:113
|
|
||||||
switch h.(type) {
|
|
||||||
//line hypview/readers.qtpl:114
|
|
||||||
case *hyphae.EmptyHypha:
|
|
||||||
//line hypview/readers.qtpl:114
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:115
|
|
||||||
qw422016.N().S(EmptyHypha(meta, h.CanonicalName()))
|
|
||||||
//line hypview/readers.qtpl:115
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:116
|
|
||||||
default:
|
|
||||||
//line hypview/readers.qtpl:116
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:117
|
|
||||||
qw422016.N().S(contents)
|
|
||||||
//line hypview/readers.qtpl:117
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:118
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:118
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
<section class="prevnext">
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:121
|
|
||||||
if prevHyphaName != "" {
|
|
||||||
//line hypview/readers.qtpl:121
|
|
||||||
qw422016.N().S(`
|
|
||||||
<a class="prevnext__el prevnext__prev" href="/hypha/`)
|
|
||||||
//line hypview/readers.qtpl:122
|
|
||||||
qw422016.E().S(prevHyphaName)
|
|
||||||
//line hypview/readers.qtpl:122
|
|
||||||
qw422016.N().S(`" rel="prev">← `)
|
|
||||||
//line hypview/readers.qtpl:122
|
|
||||||
qw422016.E().S(util.BeautifulName(path.Base(prevHyphaName)))
|
|
||||||
//line hypview/readers.qtpl:122
|
|
||||||
qw422016.N().S(`</a>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:123
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:123
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:124
|
|
||||||
if nextHyphaName != "" {
|
|
||||||
//line hypview/readers.qtpl:124
|
|
||||||
qw422016.N().S(`
|
|
||||||
<a class="prevnext__el prevnext__next" href="/hypha/`)
|
|
||||||
//line hypview/readers.qtpl:125
|
|
||||||
qw422016.E().S(nextHyphaName)
|
|
||||||
//line hypview/readers.qtpl:125
|
|
||||||
qw422016.N().S(`" rel="next">`)
|
|
||||||
//line hypview/readers.qtpl:125
|
|
||||||
qw422016.E().S(util.BeautifulName(path.Base(nextHyphaName)))
|
|
||||||
//line hypview/readers.qtpl:125
|
|
||||||
qw422016.N().S(` →</a>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:126
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:126
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:128
|
|
||||||
if strings.TrimSpace(subhyphae) != "" {
|
|
||||||
//line hypview/readers.qtpl:128
|
|
||||||
qw422016.N().S(`
|
|
||||||
<section class="subhyphae">
|
|
||||||
<h2 class="subhyphae__title">`)
|
|
||||||
//line hypview/readers.qtpl:130
|
|
||||||
qw422016.E().S(lc.Get("ui.subhyphae"))
|
|
||||||
//line hypview/readers.qtpl:130
|
|
||||||
qw422016.N().S(`</h2>
|
|
||||||
<nav class="subhyphae__nav">
|
|
||||||
<ul class="subhyphae__list">
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:133
|
|
||||||
qw422016.N().S(subhyphae)
|
|
||||||
//line hypview/readers.qtpl:133
|
|
||||||
qw422016.N().S(`
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</section>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:137
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:137
|
|
||||||
qw422016.N().S(`
|
|
||||||
<section id="hypha-bottom">
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:139
|
|
||||||
streamhyphaInfo(qw422016, meta, h)
|
|
||||||
//line hypview/readers.qtpl:139
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:142
|
|
||||||
qw422016.N().S(categories.CategoryCard(meta, h.CanonicalName()))
|
|
||||||
//line hypview/readers.qtpl:142
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:143
|
|
||||||
streamviewScripts(qw422016)
|
|
||||||
//line hypview/readers.qtpl:143
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
func WriteHypha(qq422016 qtio422016.Writer, meta viewutil.Meta, h hyphae.Hypha, contents string) {
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
StreamHypha(qw422016, meta, h, contents)
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
func Hypha(meta viewutil.Meta, h hyphae.Hypha, contents string) string {
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
WriteHypha(qb422016, meta, h, contents)
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
return qs422016
|
|
||||||
//line hypview/readers.qtpl:144
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:146
|
|
||||||
func StreamRevision(qw422016 *qt422016.Writer, meta viewutil.Meta, h hyphae.Hypha, contents, revHash string) {
|
|
||||||
//line hypview/readers.qtpl:146
|
|
||||||
qw422016.N().S(`
|
|
||||||
<main class="main-width">
|
|
||||||
<section>
|
|
||||||
<p>`)
|
|
||||||
//line hypview/readers.qtpl:149
|
|
||||||
qw422016.E().S(meta.Lc.Get("ui.revision_warning"))
|
|
||||||
//line hypview/readers.qtpl:149
|
|
||||||
qw422016.N().S(` <a href="/rev-text/`)
|
|
||||||
//line hypview/readers.qtpl:149
|
|
||||||
qw422016.E().S(revHash)
|
|
||||||
//line hypview/readers.qtpl:149
|
|
||||||
qw422016.N().S(`/`)
|
|
||||||
//line hypview/readers.qtpl:149
|
|
||||||
qw422016.E().S(h.CanonicalName())
|
|
||||||
//line hypview/readers.qtpl:149
|
|
||||||
qw422016.N().S(`">`)
|
|
||||||
//line hypview/readers.qtpl:149
|
|
||||||
qw422016.E().S(meta.Lc.Get("ui.revision_link"))
|
|
||||||
//line hypview/readers.qtpl:149
|
|
||||||
qw422016.N().S(`</a></p>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:150
|
|
||||||
qw422016.N().S(NaviTitle(meta, h.CanonicalName()))
|
|
||||||
//line hypview/readers.qtpl:150
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:151
|
|
||||||
qw422016.N().S(contents)
|
|
||||||
//line hypview/readers.qtpl:151
|
|
||||||
qw422016.N().S(`
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:154
|
|
||||||
streamviewScripts(qw422016)
|
|
||||||
//line hypview/readers.qtpl:154
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
func WriteRevision(qq422016 qtio422016.Writer, meta viewutil.Meta, h hyphae.Hypha, contents, revHash string) {
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
StreamRevision(qw422016, meta, h, contents, revHash)
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
func Revision(meta viewutil.Meta, h hyphae.Hypha, contents, revHash string) string {
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
WriteRevision(qb422016, meta, h, contents, revHash)
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
return qs422016
|
|
||||||
//line hypview/readers.qtpl:155
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:157
|
|
||||||
func streamviewScripts(qw422016 *qt422016.Writer) {
|
|
||||||
//line hypview/readers.qtpl:157
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:158
|
|
||||||
for _, scriptPath := range cfg.ViewScripts {
|
|
||||||
//line hypview/readers.qtpl:158
|
|
||||||
qw422016.N().S(`
|
|
||||||
<script src="`)
|
|
||||||
//line hypview/readers.qtpl:159
|
|
||||||
qw422016.E().S(scriptPath)
|
|
||||||
//line hypview/readers.qtpl:159
|
|
||||||
qw422016.N().S(`"></script>
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:160
|
|
||||||
}
|
|
||||||
//line hypview/readers.qtpl:160
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
func writeviewScripts(qq422016 qtio422016.Writer) {
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
streamviewScripts(qw422016)
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
}
|
|
||||||
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
func viewScripts() string {
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
writeviewScripts(qb422016)
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
return qs422016
|
|
||||||
//line hypview/readers.qtpl:161
|
|
||||||
}
|
|
@@ -1,32 +0,0 @@
|
|||||||
{{define "empty hypha card"}}
|
|
||||||
<section class="non-existent-hypha">
|
|
||||||
<h2 class="non-existent-hypha__title">{{block "empty heading" .}}This hypha does not exist{{end}}</h2>
|
|
||||||
{{if and .UseAuth (eq .Meta.U.Group "anon")}}
|
|
||||||
<p>{{block "empty no rights" .}}You are not authorized to create new hyphae. Here is what you can do:{{end}}</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/login">{{block "empty log in" .}}Log in to your account, if you have one{{end}}</a></li>
|
|
||||||
{{if .AllowRegistration}}<li><a href="/register">{{block "empty register" .}}Register a new account{{end}}</a></li>{{end}}
|
|
||||||
</ul>
|
|
||||||
{{else}}
|
|
||||||
<div class="non-existent-hypha__ways">
|
|
||||||
<section class="non-existent-hypha__way">
|
|
||||||
<h3 class="non-existent-hypha__subtitle">📝 {{block "write a text" .}}Write a text{{end}}</h3>
|
|
||||||
<p>{{block "write a text tip" .}}Write a note, a diary, an article, a story or anything textual using <a href="/help/en/mycomarkup" class="shy-link">Mycomarkup</a>. Full history of edits to the document will be saved.{{end}}</p>
|
|
||||||
<p>{{block "write a text writing conventions" .}}Make sure to follow this wiki's writing conventions if there are any.{{end}}</p>
|
|
||||||
<a class="btn btn_accent stick-to-bottom" href="/edit/{{.HyphaName}}">{{block "write a text btn" .}}Create{{end}}</a>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="non-existent-hypha__way">
|
|
||||||
<h3 class="non-existent-hypha__subtitle">🖼 {{block "upload a media" .}}Upload a media{{end}}</h3>
|
|
||||||
<p>{{block "upload a media tip" .}}Upload a picture, a video or an audio. Most common formats can be viewed from the browser, others can only be downloaded and viewed locally. You can write a description for the media later.{{end}}</p>
|
|
||||||
<form action="/upload-binary/{{.HyphaName}}"
|
|
||||||
method="post" enctype="multipart/form-data"
|
|
||||||
class="upload-binary">
|
|
||||||
<input type="file" id="upload-binary__input" name="binary">
|
|
||||||
<button type="submit" class="btn stick-to-bottom" value="Upload">{{block "upload a media btn" .}}Upload{{end}}</button>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
</section>
|
|
||||||
{{end}}
|
|
@@ -1,22 +0,0 @@
|
|||||||
{{define "remove media from x?"}}Remove media from {{beautifulName .}}?{{end}}
|
|
||||||
{{define "title"}}{{template "remove media from x?" .HyphaName}}{{end}}
|
|
||||||
{{define "body"}}
|
|
||||||
<main class="main-width">
|
|
||||||
<form class="modal" action="/remove-media/{{.HyphaName}}" method="post">
|
|
||||||
<fieldset class="modal__fieldset">
|
|
||||||
<legend class="modal__title">
|
|
||||||
{{block "remove media from [[x]]?" .}}Remove media from <a href="/hypha/{{.HyphaName}}">{{beautifulName .HyphaName}}</a>?{{end}}
|
|
||||||
</legend>
|
|
||||||
<p class="modal__confirmation-msg">
|
|
||||||
{{block "remove media for real?" .}}Do you really want to remove media from hypha ‘{{beautifulName .HyphaName}}’?{{end}}
|
|
||||||
</p>
|
|
||||||
<button type="submit" value="Confirm" class="btn" autofocus>
|
|
||||||
{{template "confirm"}}
|
|
||||||
</button>
|
|
||||||
<a href="/hypha/{%s hyphaName %}" class="btn btn_weak">
|
|
||||||
{{template "cancel"}}
|
|
||||||
</a>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
||||||
{{end}}
|
|
@@ -2,10 +2,11 @@
|
|||||||
package backlinks
|
package backlinks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ import (
|
|||||||
func yieldHyphaBacklinks(hyphaName string) <-chan string {
|
func yieldHyphaBacklinks(hyphaName string) <-chan string {
|
||||||
hyphaName = util.CanonicalName(hyphaName)
|
hyphaName = util.CanonicalName(hyphaName)
|
||||||
out := make(chan string)
|
out := make(chan string)
|
||||||
sorted := hyphae.PathographicSort(out)
|
sorted := hyphae2.PathographicSort(out)
|
||||||
go func() {
|
go func() {
|
||||||
backlinks, exists := backlinkIndex[hyphaName]
|
backlinks, exists := backlinkIndex[hyphaName]
|
||||||
if exists {
|
if exists {
|
||||||
@@ -42,7 +43,7 @@ var backlinkIndex = make(map[string]linkSet)
|
|||||||
// IndexBacklinks traverses all text hyphae, extracts links from them and forms an initial index. Call it when indexing and reindexing hyphae.
|
// IndexBacklinks traverses all text hyphae, extracts links from them and forms an initial index. Call it when indexing and reindexing hyphae.
|
||||||
func IndexBacklinks() {
|
func IndexBacklinks() {
|
||||||
// It is safe to ignore the mutex, because there is only one worker.
|
// It is safe to ignore the mutex, because there is only one worker.
|
||||||
for h := range hyphae.FilterHyphaeWithText(hyphae.YieldExistingHyphae()) {
|
for h := range hyphae2.FilterHyphaeWithText(hyphae2.YieldExistingHyphae()) {
|
||||||
foundLinks := extractHyphaLinksFromContent(h.CanonicalName(), fetchText(h))
|
foundLinks := extractHyphaLinksFromContent(h.CanonicalName(), fetchText(h))
|
||||||
for _, link := range foundLinks {
|
for _, link := range foundLinks {
|
||||||
if _, exists := backlinkIndex[link]; !exists {
|
if _, exists := backlinkIndex[link]; !exists {
|
||||||
@@ -61,6 +62,25 @@ func BacklinksCount(hyphaName string) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BacklinksFor(hyphaName string) []string {
|
||||||
|
var backlinks []string
|
||||||
|
for b := range yieldHyphaBacklinks(hyphaName) {
|
||||||
|
backlinks = append(backlinks, b)
|
||||||
|
}
|
||||||
|
return backlinks
|
||||||
|
}
|
||||||
|
|
||||||
|
func Orphans() []string {
|
||||||
|
var orphans []string
|
||||||
|
for h := range hyphae2.YieldExistingHyphae() {
|
||||||
|
if BacklinksCount(h.CanonicalName()) == 0 {
|
||||||
|
orphans = append(orphans, h.CanonicalName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(orphans)
|
||||||
|
return orphans
|
||||||
|
}
|
||||||
|
|
||||||
// Using set here seems like the most appropriate solution
|
// Using set here seems like the most appropriate solution
|
||||||
type linkSet map[string]struct{}
|
type linkSet map[string]struct{}
|
||||||
|
|
||||||
@@ -72,14 +92,14 @@ func toLinkSet(xs []string) linkSet {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchText(h hyphae.Hypha) string {
|
func fetchText(h hyphae2.Hypha) string {
|
||||||
var path string
|
var path string
|
||||||
switch h := h.(type) {
|
switch h := h.(type) {
|
||||||
case *hyphae.EmptyHypha:
|
case *hyphae2.EmptyHypha:
|
||||||
return ""
|
return ""
|
||||||
case *hyphae.TextualHypha:
|
case *hyphae2.TextualHypha:
|
||||||
path = h.TextFilePath()
|
path = h.TextFilePath()
|
||||||
case *hyphae.MediaHypha:
|
case *hyphae2.MediaHypha:
|
||||||
if !h.HasTextFile() {
|
if !h.HasTextFile() {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
@@ -5,7 +5,7 @@ import (
|
|||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/links"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/links"
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/mycoopts"
|
"github.com/bouncepaw/mycorrhiza/mycoopts"
|
||||||
)
|
)
|
||||||
|
|
@@ -23,8 +23,8 @@ package categories
|
|||||||
|
|
||||||
import "sync"
|
import "sync"
|
||||||
|
|
||||||
// listOfCategories returns unsorted names of all categories.
|
// ListOfCategories returns unsorted names of all categories.
|
||||||
func listOfCategories() (categoryList []string) {
|
func ListOfCategories() (categoryList []string) {
|
||||||
mutex.RLock()
|
mutex.RLock()
|
||||||
for cat, _ := range categoryToHyphae {
|
for cat, _ := range categoryToHyphae {
|
||||||
categoryList = append(categoryList, cat)
|
categoryList = append(categoryList, cat)
|
||||||
@@ -44,8 +44,8 @@ func CategoriesWithHypha(hyphaName string) (categoryList []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hyphaeInCategory returns what hyphae are in the category. If the returned slice is empty, the category does not exist, and vice versa. The category name must be canonical.
|
// HyphaeInCategory returns what hyphae are in the category. If the returned slice is empty, the category does not exist, and vice versa. The category name must be canonical.
|
||||||
func hyphaeInCategory(catName string) (hyphaList []string) {
|
func HyphaeInCategory(catName string) (hyphaList []string) {
|
||||||
mutex.RLock()
|
mutex.RLock()
|
||||||
defer mutex.RUnlock()
|
defer mutex.RUnlock()
|
||||||
if node, ok := categoryToHyphae[catName]; ok {
|
if node, ok := categoryToHyphae[catName]; ok {
|
||||||
@@ -75,8 +75,8 @@ func AddHyphaToCategory(hyphaName, catName string) {
|
|||||||
go saveToDisk()
|
go saveToDisk()
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeHyphaFromCategory removes the hypha from the category and updates the records on the disk. If the hypha is not in the category, nothing happens. Pass canonical names.
|
// RemoveHyphaFromCategory removes the hypha from the category and updates the records on the disk. If the hypha is not in the category, nothing happens. Pass canonical names.
|
||||||
func removeHyphaFromCategory(hyphaName, catName string) {
|
func RemoveHyphaFromCategory(hyphaName, catName string) {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
if node, ok := hyphaToCategories[hyphaName]; ok {
|
if node, ok := hyphaToCategories[hyphaName]; ok {
|
||||||
node.removeCategory(catName)
|
node.removeCategory(catName)
|
@@ -2,13 +2,14 @@ package categories
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var categoryToHyphae = map[string]*categoryNode{}
|
var categoryToHyphae = map[string]*categoryNode{}
|
@@ -2,12 +2,11 @@
|
|||||||
package files
|
package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/static"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/static"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var paths struct {
|
var paths struct {
|
@@ -1,12 +1,11 @@
|
|||||||
package hyphae
|
package hyphae
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/mimetype"
|
||||||
"log"
|
"log"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Index finds all hypha files in the full `path` and saves them to the hypha storage.
|
// Index finds all hypha files in the full `path` and saves them to the hypha storage.
|
@@ -1,9 +1,10 @@
|
|||||||
package hyphae
|
package hyphae
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MediaHypha struct {
|
type MediaHypha struct {
|
@@ -2,7 +2,7 @@ package migration
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
@@ -8,13 +8,14 @@
|
|||||||
package migration
|
package migration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
)
|
)
|
||||||
|
|
||||||
func genericLineMigrator(
|
func genericLineMigrator(
|
@@ -2,7 +2,7 @@ package migration
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
@@ -2,23 +2,23 @@ package shroom
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/l18n"
|
"github.com/bouncepaw/mycorrhiza/l18n"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: get rid of this abomination
|
// TODO: get rid of this abomination
|
||||||
|
|
||||||
func canFactory(
|
func canFactory(
|
||||||
rejectLogger func(hyphae.Hypha, *user.User, string),
|
rejectLogger func(hyphae2.Hypha, *user.User, string),
|
||||||
action string,
|
action string,
|
||||||
dispatcher func(hyphae.Hypha, *user.User, *l18n.Localizer) (string, string),
|
dispatcher func(hyphae2.Hypha, *user.User, *l18n.Localizer) (string, string),
|
||||||
noRightsMsg string,
|
noRightsMsg string,
|
||||||
notExistsMsg string,
|
notExistsMsg string,
|
||||||
mustExist bool,
|
mustExist bool,
|
||||||
) func(*user.User, hyphae.Hypha, *l18n.Localizer) error {
|
) func(*user.User, hyphae2.Hypha, *l18n.Localizer) error {
|
||||||
return func(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) error {
|
return func(u *user.User, h hyphae2.Hypha, lc *l18n.Localizer) error {
|
||||||
if !u.CanProceed(action) {
|
if !u.CanProceed(action) {
|
||||||
rejectLogger(h, u, "no rights")
|
rejectLogger(h, u, "no rights")
|
||||||
return errors.New(noRightsMsg)
|
return errors.New(noRightsMsg)
|
||||||
@@ -26,7 +26,7 @@ func canFactory(
|
|||||||
|
|
||||||
if mustExist {
|
if mustExist {
|
||||||
switch h.(type) {
|
switch h.(type) {
|
||||||
case *hyphae.EmptyHypha:
|
case *hyphae2.EmptyHypha:
|
||||||
rejectLogger(h, u, "does not exist")
|
rejectLogger(h, u, "does not exist")
|
||||||
return errors.New(notExistsMsg)
|
return errors.New(notExistsMsg)
|
||||||
}
|
}
|
@@ -2,29 +2,29 @@ package shroom
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bouncepaw/mycorrhiza/backlinks"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/categories"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/internal/categories"
|
||||||
|
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Delete deletes the hypha and makes a history record about that.
|
// Delete deletes the hypha and makes a history record about that.
|
||||||
func Delete(u *user.User, h hyphae.ExistingHypha) error {
|
func Delete(u *user.User, h hyphae2.ExistingHypha) error {
|
||||||
hop := history.
|
hop := history.
|
||||||
Operation(history.TypeDeleteHypha).
|
Operation(history.TypeDeleteHypha).
|
||||||
WithMsg(fmt.Sprintf("Delete ‘%s’", h.CanonicalName())).
|
WithMsg(fmt.Sprintf("Delete ‘%s’", h.CanonicalName())).
|
||||||
WithUser(u)
|
WithUser(u)
|
||||||
|
|
||||||
originalText, _ := hyphae.FetchMycomarkupFile(h)
|
originalText, _ := hyphae2.FetchMycomarkupFile(h)
|
||||||
switch h := h.(type) {
|
switch h := h.(type) {
|
||||||
case *hyphae.MediaHypha:
|
case *hyphae2.MediaHypha:
|
||||||
if h.HasTextFile() {
|
if h.HasTextFile() {
|
||||||
hop.WithFilesRemoved(h.MediaFilePath(), h.TextFilePath())
|
hop.WithFilesRemoved(h.MediaFilePath(), h.TextFilePath())
|
||||||
} else {
|
} else {
|
||||||
hop.WithFilesRemoved(h.MediaFilePath())
|
hop.WithFilesRemoved(h.MediaFilePath())
|
||||||
}
|
}
|
||||||
case *hyphae.TextualHypha:
|
case *hyphae2.TextualHypha:
|
||||||
hop.WithFilesRemoved(h.TextFilePath())
|
hop.WithFilesRemoved(h.TextFilePath())
|
||||||
}
|
}
|
||||||
if hop.Apply().HasErrors() {
|
if hop.Apply().HasErrors() {
|
||||||
@@ -32,6 +32,6 @@ func Delete(u *user.User, h hyphae.ExistingHypha) error {
|
|||||||
}
|
}
|
||||||
backlinks.UpdateBacklinksAfterDelete(h, originalText)
|
backlinks.UpdateBacklinksAfterDelete(h, originalText)
|
||||||
categories.RemoveHyphaFromAllCategories(h.CanonicalName())
|
categories.RemoveHyphaFromAllCategories(h.CanonicalName())
|
||||||
hyphae.DeleteHypha(h)
|
hyphae2.DeleteHypha(h)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
@@ -4,19 +4,19 @@ import (
|
|||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5"
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/blocks"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/blocks"
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/mycoopts"
|
"github.com/bouncepaw/mycorrhiza/mycoopts"
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetHeaderLinks initializes header links by reading the configured hypha, if there is any, or resorting to default values.
|
// SetHeaderLinks initializes header links by reading the configured hypha, if there is any, or resorting to default values.
|
||||||
func SetHeaderLinks() {
|
func SetHeaderLinks() {
|
||||||
switch userLinksHypha := hyphae.ByName(cfg.HeaderLinksHypha).(type) {
|
switch userLinksHypha := hyphae2.ByName(cfg.HeaderLinksHypha).(type) {
|
||||||
case *hyphae.EmptyHypha:
|
case *hyphae2.EmptyHypha:
|
||||||
setDefaultHeaderLinks()
|
setDefaultHeaderLinks()
|
||||||
case hyphae.ExistingHypha:
|
case hyphae2.ExistingHypha:
|
||||||
contents, err := os.ReadFile(userLinksHypha.TextFilePath())
|
contents, err := os.ReadFile(userLinksHypha.TextFilePath())
|
||||||
if err != nil || len(contents) == 0 {
|
if err != nil || len(contents) == 0 {
|
||||||
setDefaultHeaderLinks()
|
setDefaultHeaderLinks()
|
@@ -1,10 +1,9 @@
|
|||||||
package shroom
|
package shroom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func rejectRenameLog(h hyphae.Hypha, u *user.User, errmsg string) {
|
func rejectRenameLog(h hyphae.Hypha, u *user.User, errmsg string) {
|
@@ -3,36 +3,36 @@ package shroom
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bouncepaw/mycorrhiza/backlinks"
|
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
|
||||||
"github.com/bouncepaw/mycorrhiza/categories"
|
"github.com/bouncepaw/mycorrhiza/internal/categories"
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
|
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Rename renames the old hypha to the new name and makes a history record about that. Call if and only if the user has the permission to rename.
|
// Rename renames the old hypha to the new name and makes a history record about that. Call if and only if the user has the permission to rename.
|
||||||
func Rename(oldHypha hyphae.ExistingHypha, newName string, recursive bool, leaveRedirections bool, u *user.User) error {
|
func Rename(oldHypha hyphae2.ExistingHypha, newName string, recursive bool, leaveRedirections bool, u *user.User) error {
|
||||||
// * bouncepaw hates this function and related renaming functions
|
// * bouncepaw hates this function and related renaming functions
|
||||||
if newName == "" {
|
if newName == "" {
|
||||||
rejectRenameLog(oldHypha, u, "no new name given")
|
rejectRenameLog(oldHypha, u, "no new name given")
|
||||||
return errors.New("ui.rename_noname_tip")
|
return errors.New("ui.rename_noname_tip")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hyphae.IsValidName(newName) {
|
if !hyphae2.IsValidName(newName) {
|
||||||
rejectRenameLog(oldHypha, u, fmt.Sprintf("new name ‘%s’ invalid", newName))
|
rejectRenameLog(oldHypha, u, fmt.Sprintf("new name ‘%s’ invalid", newName))
|
||||||
return errors.New("ui.rename_badname_tip") // FIXME: There is a bug related to this.
|
return errors.New("ui.rename_badname_tip") // FIXME: There is a bug related to this.
|
||||||
}
|
}
|
||||||
|
|
||||||
switch targetHypha := hyphae.ByName(newName); targetHypha.(type) {
|
switch targetHypha := hyphae2.ByName(newName); targetHypha.(type) {
|
||||||
case hyphae.ExistingHypha:
|
case hyphae2.ExistingHypha:
|
||||||
if targetHypha.CanonicalName() == oldHypha.CanonicalName() {
|
if targetHypha.CanonicalName() == oldHypha.CanonicalName() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ func Rename(oldHypha hyphae.ExistingHypha, newName string, recursive bool, leave
|
|||||||
oldName = h.CanonicalName()
|
oldName = h.CanonicalName()
|
||||||
newName = re.ReplaceAllString(oldName, newName)
|
newName = re.ReplaceAllString(oldName, newName)
|
||||||
)
|
)
|
||||||
hyphae.RenameHyphaTo(h, newName, replaceName)
|
hyphae2.RenameHyphaTo(h, newName, replaceName)
|
||||||
backlinks.UpdateBacklinksAfterRename(h, oldName)
|
backlinks.UpdateBacklinksAfterRename(h, oldName)
|
||||||
categories.RenameHyphaInAllCategories(oldName, newName)
|
categories.RenameHyphaInAllCategories(oldName, newName)
|
||||||
if leaveRedirections {
|
if leaveRedirections {
|
||||||
@@ -104,12 +104,12 @@ const redirectionTemplate = `=> %[1]s | 👁️➡️ %[2]s
|
|||||||
func leaveRedirection(oldName, newName string, hop *history.Op) error {
|
func leaveRedirection(oldName, newName string, hop *history.Op) error {
|
||||||
var (
|
var (
|
||||||
text = fmt.Sprintf(redirectionTemplate, newName, util.BeautifulName(newName))
|
text = fmt.Sprintf(redirectionTemplate, newName, util.BeautifulName(newName))
|
||||||
emptyHypha = hyphae.ByName(oldName)
|
emptyHypha = hyphae2.ByName(oldName)
|
||||||
)
|
)
|
||||||
switch emptyHypha := emptyHypha.(type) {
|
switch emptyHypha := emptyHypha.(type) {
|
||||||
case *hyphae.EmptyHypha:
|
case *hyphae2.EmptyHypha:
|
||||||
h := hyphae.ExtendEmptyToTextual(emptyHypha, filepath.Join(files.HyphaeDir(), oldName+".myco"))
|
h := hyphae2.ExtendEmptyToTextual(emptyHypha, filepath.Join(files.HyphaeDir(), oldName+".myco"))
|
||||||
hyphae.Insert(h)
|
hyphae2.Insert(h)
|
||||||
categories.AddHyphaToCategory(oldName, cfg.RedirectionCategory)
|
categories.AddHyphaToCategory(oldName, cfg.RedirectionCategory)
|
||||||
defer backlinks.UpdateBacklinksAfterEdit(h, "")
|
defer backlinks.UpdateBacklinksAfterEdit(h, "")
|
||||||
return writeTextToDisk(h, []byte(text), hop)
|
return writeTextToDisk(h, []byte(text), hop)
|
||||||
@@ -118,15 +118,15 @@ func leaveRedirection(oldName, newName string, hop *history.Op) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func findHyphaeToRename(superhypha hyphae.ExistingHypha, recursive bool) []hyphae.ExistingHypha {
|
func findHyphaeToRename(superhypha hyphae2.ExistingHypha, recursive bool) []hyphae2.ExistingHypha {
|
||||||
hyphaList := []hyphae.ExistingHypha{superhypha}
|
hyphaList := []hyphae2.ExistingHypha{superhypha}
|
||||||
if recursive {
|
if recursive {
|
||||||
hyphaList = append(hyphaList, hyphae.Subhyphae(superhypha)...)
|
hyphaList = append(hyphaList, hyphae2.Subhyphae(superhypha)...)
|
||||||
}
|
}
|
||||||
return hyphaList
|
return hyphaList
|
||||||
}
|
}
|
||||||
|
|
||||||
func renamingPairs(hyphaeToRename []hyphae.ExistingHypha, replaceName func(string) string) (map[string]string, error) {
|
func renamingPairs(hyphaeToRename []hyphae2.ExistingHypha, replaceName func(string) string) (map[string]string, error) {
|
||||||
var (
|
var (
|
||||||
renameMap = make(map[string]string)
|
renameMap = make(map[string]string)
|
||||||
newNames = make([]string, len(hyphaeToRename))
|
newNames = make([]string, len(hyphaeToRename))
|
||||||
@@ -138,12 +138,12 @@ func renamingPairs(hyphaeToRename []hyphae.ExistingHypha, replaceName func(strin
|
|||||||
renameMap[h.TextFilePath()] = replaceName(h.TextFilePath())
|
renameMap[h.TextFilePath()] = replaceName(h.TextFilePath())
|
||||||
}
|
}
|
||||||
switch h := h.(type) {
|
switch h := h.(type) {
|
||||||
case *hyphae.MediaHypha:
|
case *hyphae2.MediaHypha:
|
||||||
renameMap[h.MediaFilePath()] = replaceName(h.MediaFilePath())
|
renameMap[h.MediaFilePath()] = replaceName(h.MediaFilePath())
|
||||||
}
|
}
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
}
|
}
|
||||||
if firstFailure, ok := hyphae.AreFreeNames(newNames...); !ok {
|
if firstFailure, ok := hyphae2.AreFreeNames(newNames...); !ok {
|
||||||
return nil, errors.New("Hypha " + firstFailure + " already exists")
|
return nil, errors.New("Hypha " + firstFailure + " already exists")
|
||||||
}
|
}
|
||||||
return renameMap, nil
|
return renameMap, nil
|
@@ -1,9 +1,9 @@
|
|||||||
package shroom
|
package shroom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
@@ -2,14 +2,14 @@ package shroom
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RemoveMedia removes media from the media hypha and makes a history record about that. If it only had media, the hypha will be deleted. If it also had text, the hypha will become textual.
|
// RemoveMedia removes media from the media hypha and makes a history record about that. If it only had media, the hypha will be deleted. If it also had text, the hypha will become textual.
|
||||||
func RemoveMedia(u *user.User, h *hyphae.MediaHypha) error {
|
func RemoveMedia(u *user.User, h *hyphae2.MediaHypha) error {
|
||||||
hop := history.
|
hop := history.
|
||||||
Operation(history.TypeRemoveMedia).
|
Operation(history.TypeRemoveMedia).
|
||||||
WithFilesRemoved(h.MediaFilePath()).
|
WithFilesRemoved(h.MediaFilePath()).
|
||||||
@@ -24,9 +24,9 @@ func RemoveMedia(u *user.User, h *hyphae.MediaHypha) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if h.HasTextFile() {
|
if h.HasTextFile() {
|
||||||
hyphae.Insert(hyphae.ShrinkMediaToTextual(h))
|
hyphae2.Insert(hyphae2.ShrinkMediaToTextual(h))
|
||||||
} else {
|
} else {
|
||||||
hyphae.DeleteHypha(h)
|
hyphae2.DeleteHypha(h)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
@@ -4,18 +4,19 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bouncepaw/mycorrhiza/backlinks"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/mimetype"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
func historyMessageForTextUpload(h hyphae.Hypha, userMessage string) string {
|
func historyMessageForTextUpload(h hyphae.Hypha, userMessage string) string {
|
@@ -1,14 +1,18 @@
|
|||||||
package tree
|
package tree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tree returns the subhypha matrix as HTML and names of the next and previous hyphae (or empty strings).
|
// Tree returns the subhypha matrix as HTML and names of the next and previous hyphae (or empty strings).
|
||||||
func Tree(hyphaName string) (childrenHTML, prev, next string) {
|
func Tree(hyphaName string) (childrenHTML template.HTML, prev, next string) {
|
||||||
var (
|
var (
|
||||||
root = child{hyphaName, true, make([]child, 0)}
|
root = child{hyphaName, true, make([]child, 0)}
|
||||||
descendantPrefix = hyphaName + "/"
|
descendantPrefix = hyphaName + "/"
|
||||||
@@ -44,6 +48,41 @@ type child struct {
|
|||||||
children []child
|
children []child
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Subhyphae links are recursive. It may end up looking like that if drawn with
|
||||||
|
pseudographics:
|
||||||
|
╔══════════════╗
|
||||||
|
║Foo ║ The presented hyphae are ./foo and ./foo/bar
|
||||||
|
║╔════════════╗║
|
||||||
|
║║Bar ║║
|
||||||
|
║╚════════════╝║
|
||||||
|
╚══════════════╝
|
||||||
|
*/
|
||||||
|
func childHTML(c *child, w io.Writer) {
|
||||||
|
sort.Slice(c.children, func(i, j int) bool {
|
||||||
|
return c.children[i].name < c.children[j].name
|
||||||
|
})
|
||||||
|
|
||||||
|
_, _ = io.WriteString(w, "<li class=\"subhyphae__entry\">\n<a class=\"subhyphae__link")
|
||||||
|
if !c.exists {
|
||||||
|
_, _ = io.WriteString(w, " wikilink_new")
|
||||||
|
}
|
||||||
|
_, _ = io.WriteString(w, fmt.Sprintf(
|
||||||
|
"\" href=\"/hypha/%s\">%s</a>\n",
|
||||||
|
c.name,
|
||||||
|
util.BeautifulName(path.Base(c.name)),
|
||||||
|
))
|
||||||
|
|
||||||
|
if len(c.children) > 0 {
|
||||||
|
_, _ = io.WriteString(w, "<ul>\n")
|
||||||
|
for _, child := range c.children {
|
||||||
|
childHTML(&child, w)
|
||||||
|
}
|
||||||
|
_, _ = io.WriteString(w, "</ul>\n")
|
||||||
|
}
|
||||||
|
_, _ = io.WriteString(w, "</li>\n")
|
||||||
|
}
|
||||||
|
|
||||||
func addHyphaToChild(hyphaName, subPath string, child *child) {
|
func addHyphaToChild(hyphaName, subPath string, child *child) {
|
||||||
// when hyphaName = "root/a/b", subPath = "a/b", and child.name = "root"
|
// when hyphaName = "root/a/b", subPath = "a/b", and child.name = "root"
|
||||||
// addHyphaToChild("root/a/b", "b", child{"root/a"})
|
// addHyphaToChild("root/a/b", "b", child{"root/a"})
|
||||||
@@ -78,12 +117,13 @@ func findOrCreateSubchild(name string, baseChild *child) *child {
|
|||||||
return &baseChild.children[len(baseChild.children)-1]
|
return &baseChild.children[len(baseChild.children)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func subhyphaeMatrix(children []child) (html string) {
|
func subhyphaeMatrix(children []child) template.HTML {
|
||||||
sort.Slice(children, func(i, j int) bool {
|
sort.Slice(children, func(i, j int) bool {
|
||||||
return children[i].name < children[j].name
|
return children[i].name < children[j].name
|
||||||
})
|
})
|
||||||
|
var buf strings.Builder
|
||||||
for _, child := range children {
|
for _, child := range children {
|
||||||
html += childHTML(&child)
|
childHTML(&child, &buf)
|
||||||
}
|
}
|
||||||
return html
|
return template.HTML(buf.String())
|
||||||
}
|
}
|
@@ -3,11 +3,11 @@ package user
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -14,7 +15,6 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -79,6 +79,11 @@ func Register(username, password, group, source string, force bool) error {
|
|||||||
return SaveUserDatabase()
|
return SaveUserDatabase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnknownUsername = errors.New("unknown username")
|
||||||
|
ErrWrongPassword = errors.New("wrong password")
|
||||||
|
)
|
||||||
|
|
||||||
// LoginDataHTTP logs such user in and returns string representation of an error if there is any.
|
// LoginDataHTTP logs such user in and returns string representation of an error if there is any.
|
||||||
//
|
//
|
||||||
// The HTTP parameters are used for setting header status (bad request, if it is bad) and saving a cookie.
|
// The HTTP parameters are used for setting header status (bad request, if it is bad) and saving a cookie.
|
||||||
@@ -87,12 +92,12 @@ func LoginDataHTTP(w http.ResponseWriter, username, password string) error {
|
|||||||
if !HasUsername(username) {
|
if !HasUsername(username) {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
log.Println("Unknown username", username, "was entered")
|
log.Println("Unknown username", username, "was entered")
|
||||||
return errors.New("unknown username")
|
return ErrUnknownUsername
|
||||||
}
|
}
|
||||||
if !CredentialsOK(username, password) {
|
if !CredentialsOK(username, password) {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
log.Println("A wrong password was entered for username", username)
|
log.Println("A wrong password was entered for username", username)
|
||||||
return errors.New("wrong password")
|
return ErrWrongPassword
|
||||||
}
|
}
|
||||||
token, err := AddSession(username)
|
token, err := AddSession(username)
|
||||||
if err != nil {
|
if err != nil {
|
@@ -2,12 +2,12 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@ var minimalRights = map[string]int{
|
|||||||
"upload-binary": 1,
|
"upload-binary": 1,
|
||||||
"rename": 1,
|
"rename": 1,
|
||||||
"upload-text": 1,
|
"upload-text": 1,
|
||||||
"add-to-category": 2,
|
"add-to-category": 1,
|
||||||
"remove-from-category": 2,
|
"remove-from-category": 1,
|
||||||
"remove-media": 2,
|
"remove-media": 2,
|
||||||
"update-header-links": 3,
|
"update-header-links": 3,
|
||||||
"delete": 3,
|
"delete": 3,
|
@@ -1,6 +1,9 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
var users sync.Map
|
var users sync.Map
|
||||||
var tokens sync.Map
|
var tokens sync.Map
|
||||||
@@ -99,3 +102,24 @@ func terminateSession(token string) {
|
|||||||
tokens.Delete(token)
|
tokens.Delete(token)
|
||||||
dumpTokens()
|
dumpTokens()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UsersInGroups() (admins []string, moderators []string, editors []string, readers []string) {
|
||||||
|
for u := range YieldUsers() {
|
||||||
|
switch u.Group {
|
||||||
|
// What if we place the users into sorted slices?
|
||||||
|
case "admin":
|
||||||
|
admins = append(admins, u.Name)
|
||||||
|
case "moderator":
|
||||||
|
moderators = append(moderators, u.Name)
|
||||||
|
case "editor", "trusted":
|
||||||
|
editors = append(editors, u.Name)
|
||||||
|
case "reader":
|
||||||
|
readers = append(readers, u.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(admins)
|
||||||
|
sort.Strings(moderators)
|
||||||
|
sort.Strings(editors)
|
||||||
|
sort.Strings(readers)
|
||||||
|
return
|
||||||
|
}
|
@@ -5,7 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/options"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/options"
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@@ -2,7 +2,7 @@ package interwiki
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@@ -3,33 +3,29 @@
|
|||||||
"password": "Password",
|
"password": "Password",
|
||||||
|
|
||||||
"register_title": "Register",
|
"register_title": "Register",
|
||||||
"register_header": "Register on {{.name}}",
|
"register_header": "",
|
||||||
"register_button": "Register",
|
"register_button": "Register",
|
||||||
|
|
||||||
"login_title": "Login",
|
|
||||||
"login_header": "Log in to {{.name}}",
|
|
||||||
"login_button": "Log in",
|
|
||||||
|
|
||||||
"logout_title": "Logout?",
|
"logout_title": "",
|
||||||
"logout_header": "Log out?",
|
"logout_header": "Log out?",
|
||||||
"logout_button": "Confirm",
|
"logout_button": "Confirm",
|
||||||
"logout_anon": "You cannot log out because you are not logged in.",
|
"logout_anon": "",
|
||||||
|
|
||||||
"lock_title": "Locked",
|
"lock_title": "Locked",
|
||||||
|
|
||||||
"password_tip": "The server stores your password in an encrypted form; even administrators cannot read it.",
|
"password_tip": "",
|
||||||
"cookie_tip": "By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you. You will stay logged in until you log out.",
|
"cookie_tip": "",
|
||||||
"telegram_tip": "You can log in using Telegram. It only works if you have set your @username in Telegram and this username is free on this wiki.",
|
"telegram_tip": "",
|
||||||
|
|
||||||
"noauth": "Authentication is disabled. You can make edits anonymously.",
|
"noauth": "",
|
||||||
"noregister": "Registrations are currently closed. Administrators can make an account for you by hand; contact them.",
|
"noregister": "Registrations are currently closed. Administrators can make an account for you by hand; contact them.",
|
||||||
|
|
||||||
"error_username": "Unknown username.",
|
"error_username": "",
|
||||||
"error_password": "Wrong password.",
|
"error_password": "",
|
||||||
"error_telegram": "Could not authorize using Telegram.",
|
"error_telegram": "",
|
||||||
|
|
||||||
"go_back": "Go back",
|
"go_back": "Go back",
|
||||||
"go_home": "Go home",
|
"go_home": "",
|
||||||
"go_login": "Go to the login page",
|
"go_login": "Go to the login page",
|
||||||
"try_again": "Try again"
|
"try_again": "Try again"
|
||||||
}
|
}
|
||||||
|
@@ -4,18 +4,6 @@
|
|||||||
"title_search": "Search by title",
|
"title_search": "Search by title",
|
||||||
"admin_panel": "Admin panel",
|
"admin_panel": "Admin panel",
|
||||||
|
|
||||||
"edit_link": "Edit text",
|
|
||||||
"logout_link": "Log out",
|
|
||||||
"history_link": "View history",
|
|
||||||
"rename_link": "Rename",
|
|
||||||
"delete_link": "Delete",
|
|
||||||
"text_link": "View markup",
|
|
||||||
"media_link": "Manage media",
|
|
||||||
"media_link_for_textual": "Turn to media hypha",
|
|
||||||
"backlinks_link": "{{.n}} backlink%s",
|
|
||||||
"backlinks_link+one": "",
|
|
||||||
"backlinks_link+other": "s",
|
|
||||||
|
|
||||||
"subhyphae": "Subhyphae",
|
"subhyphae": "Subhyphae",
|
||||||
|
|
||||||
"random_no_hyphae": "There are no hyphae",
|
"random_no_hyphae": "There are no hyphae",
|
||||||
@@ -57,9 +45,9 @@
|
|||||||
|
|
||||||
"diff_title": "Diff of {{.name}} at {{.rev}}",
|
"diff_title": "Diff of {{.name}} at {{.rev}}",
|
||||||
|
|
||||||
"revision_title": "{{.name}} at {{.rev}}",
|
"revision_title": "",
|
||||||
"revision_warning": "Please note that viewing media is not supported in history for now.",
|
"revision_warning": "",
|
||||||
"revision_link": "Get Mycomarkup source of this revision",
|
"revision_link": "",
|
||||||
"revision_no_text": "This hypha had no text at this revision.",
|
"revision_no_text": "This hypha had no text at this revision.",
|
||||||
|
|
||||||
"about_title": "About {{.name}}",
|
"about_title": "About {{.name}}",
|
||||||
@@ -76,25 +64,6 @@
|
|||||||
"media_noaudio": "Your browser does not support audio.",
|
"media_noaudio": "Your browser does not support audio.",
|
||||||
"media_noaudio_link": "Download audio",
|
"media_noaudio_link": "Download audio",
|
||||||
|
|
||||||
"media_title": "Media of {{.name}}",
|
|
||||||
"media_empty": "This hypha has no media, you can upload it here.",
|
|
||||||
"media_tip": "You can manage the hypha's media on this page.",
|
|
||||||
"media_what_is": "What is media?",
|
|
||||||
"media_upload": "Upload",
|
|
||||||
"media_stat": "Stat",
|
|
||||||
"media_stat_size": "File size:",
|
|
||||||
"media_size_value": "{{.n}} byte%s",
|
|
||||||
"media_size_value+one": "",
|
|
||||||
"media_size_value+other": "s",
|
|
||||||
"media_stat_mime": "MIME type:",
|
|
||||||
"media_include": "Include",
|
|
||||||
"media_include_tip": "This media is an image. To include it in a hypha, use a syntax like this:",
|
|
||||||
"media_new": "media",
|
|
||||||
"media_new_tip": "You can upload a new media. Please do not upload too big pictures unless you need to because may not want to wait for big pictures to load.",
|
|
||||||
"media_remove": "Remove media",
|
|
||||||
"media_remove_tip": "Please note that you don't have to remove media before uploading a new media.",
|
|
||||||
"media_remove_button": "Remove media",
|
|
||||||
|
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"cancel": "Cancel"
|
"cancel": "Cancel"
|
||||||
}
|
}
|
||||||
|
@@ -2,31 +2,31 @@
|
|||||||
"username": "Логин",
|
"username": "Логин",
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
|
|
||||||
"register_title": "Регистрация",
|
"register_title": "",
|
||||||
"register_header": "Регистрация на «{{.name}}»",
|
"register_header": "",
|
||||||
"register_button": "Зарегистрироваться",
|
"register_button": "",
|
||||||
|
|
||||||
"login_title": "Вход",
|
"login_title": "Вход",
|
||||||
"login_header": "Вход в «{{.name}}»",
|
"login_header": "",
|
||||||
"login_button": "Войти",
|
"login_button": "",
|
||||||
|
|
||||||
"logout_title": "Выйти?",
|
"logout_title": "",
|
||||||
"logout_header": "Выйти?",
|
"logout_header": "?",
|
||||||
"logout_button": "Подтвердить",
|
"logout_button": "Подтвердить",
|
||||||
"logout_anon": "Вы не можете выйти, потому что ещё не вошли.",
|
"logout_anon": "",
|
||||||
|
|
||||||
"lock_title": "Доступ закрыт",
|
"lock_title": "",
|
||||||
|
|
||||||
"password_tip": "Сервер хранит ваш пароль в зашифрованном виде, даже администраторы не смогут его прочесть.",
|
"password_tip": "",
|
||||||
"cookie_tip": "Отправляя эту форму, вы разрешаете вики хранить cookie в вашем браузере. Это позволит движку связывать ваши правки с вашей учётной записью. Вы будете авторизованы, пока не выйдете из учётной записи.",
|
"cookie_tip": "",
|
||||||
"telegram_tip": "Вы можете войти с помощью Телеграм. Это сработает, если у вашего профиля есть @имя, и оно не занято в этой вики.",
|
"telegram_tip": "Вы можете войти с помощью Телеграм. Это сработает, если у вашего профиля есть @имя, и оно не занято в этой вики.",
|
||||||
|
|
||||||
"noauth": "Аутентификация отключена. Вы можете делать правки анонимно.",
|
"noauth": "",
|
||||||
"noregister": "Регистрация в текущее время недоступна. Администраторы могут вручную создать вам учётную запись, свяжитесь с ними.",
|
"noregister": "Регистрация в текущее время недоступна. Администраторы могут вручную создать вам учётную запись, свяжитесь с ними.",
|
||||||
|
|
||||||
"error_username": "Неизвестное имя пользователя.",
|
"error_username": "",
|
||||||
"error_password": "Неверный пароль.",
|
"error_password": "Неверный пароль.",
|
||||||
"error_telegram": "Не удалось авторизоваться через Телеграм.",
|
"error_telegram": "",
|
||||||
|
|
||||||
"go_back": "Назад",
|
"go_back": "Назад",
|
||||||
"go_home": "Домой",
|
"go_home": "Домой",
|
||||||
|
@@ -8,14 +8,14 @@
|
|||||||
"backlinks_heading": "Обратные ссылки на {{.hypha_link}}",
|
"backlinks_heading": "Обратные ссылки на {{.hypha_link}}",
|
||||||
"backlinks_desc": "Ниже перечислены гифы, на которых есть ссылка на эту гифу, трансклюзия этой гифы или эта гифа вставлена как изображение.",
|
"backlinks_desc": "Ниже перечислены гифы, на которых есть ссылка на эту гифу, трансклюзия этой гифы или эта гифа вставлена как изображение.",
|
||||||
|
|
||||||
"edit_link": "Редактировать",
|
"edit_link": "",
|
||||||
"logout_link": "Выйти",
|
"logout_link": "",
|
||||||
"history_link": "История",
|
"history_link": "",
|
||||||
"rename_link": "Переименовать",
|
"rename_link": "",
|
||||||
"delete_link": "Удалить",
|
"delete_link": "",
|
||||||
"text_link": "Посмотреть разметку",
|
"text_link": "",
|
||||||
"media_link": "Медиа",
|
"media_link": "",
|
||||||
"media_link_for_textual": "Превратить в медиа-гифу",
|
"media_link_for_textual": "",
|
||||||
"backlinks_link": "{{.n}} %s сюда",
|
"backlinks_link": "{{.n}} %s сюда",
|
||||||
"backlinks_link+one": "ссылка",
|
"backlinks_link+one": "ссылка",
|
||||||
"backlinks_link+few": "ссылки",
|
"backlinks_link+few": "ссылки",
|
||||||
@@ -59,9 +59,9 @@
|
|||||||
"ask_really": "Вы действительно хотите {{.verb}} гифу «{{.name}}»?",
|
"ask_really": "Вы действительно хотите {{.verb}} гифу «{{.name}}»?",
|
||||||
"ask_remove_media_verb": "убрать медиа",
|
"ask_remove_media_verb": "убрать медиа",
|
||||||
|
|
||||||
"revision_title": "{{.name}} из {{.rev}}",
|
"revision_title": "",
|
||||||
"revision_warning": "Обратите внимание, просмотр медиа в истории пока что недоступен.",
|
"revision_warning": "",
|
||||||
"revision_link": "Посмотреть Микоразметку для этой ревизии",
|
"revision_link": "",
|
||||||
"revision_no_text": "В этой ревизии гифы не было текста.",
|
"revision_no_text": "В этой ревизии гифы не было текста.",
|
||||||
|
|
||||||
"about_title": "О {{.name}}",
|
"about_title": "О {{.name}}",
|
||||||
@@ -78,26 +78,6 @@
|
|||||||
"media_noaudio": "Ваш браузер не поддерживает аудио.",
|
"media_noaudio": "Ваш браузер не поддерживает аудио.",
|
||||||
"media_noaudio_link": "Скачать аудио",
|
"media_noaudio_link": "Скачать аудио",
|
||||||
|
|
||||||
"media_title": "Медиа «{{.name}}»",
|
|
||||||
"media_empty": "Эта гифа не имеет медиа, здесь вы можете его загрузить.",
|
|
||||||
"media_tip": "На этой странице вы можете управлять медиа.",
|
|
||||||
"media_what_is": "Что такое медиа?",
|
|
||||||
"media_upload": "Загрузить",
|
|
||||||
"media_stat": "Свойства",
|
|
||||||
"media_stat_size": "Размер файла:",
|
|
||||||
"media_size_value": "{{.n}} %s",
|
|
||||||
"media_size_value+one": "байт",
|
|
||||||
"media_size_value+few": "байта",
|
|
||||||
"media_size_value+many": "байт",
|
|
||||||
"media_stat_mime": "MIME-тип:",
|
|
||||||
"media_include": "Добавление",
|
|
||||||
"media_include_tip": "Это медиа – изображение. Чтобы добавить его в текст гифы, используйте синтаксис ниже:",
|
|
||||||
"media_new": "Прикрепить",
|
|
||||||
"media_new_tip": "Вы можете загрузить новое медиа. Пожалуйста, не загружайте слишком большие изображения без необходимости, чтобы впоследствии не ждать её долгую загрузку.",
|
|
||||||
"media_remove": "Открепить",
|
|
||||||
"media_remove_tip": "Заметьте, чтобы заменить медиа, вам не нужно его перед этим откреплять.",
|
|
||||||
"media_remove_button": "Открепить",
|
|
||||||
|
|
||||||
"confirm": "Применить",
|
"confirm": "Применить",
|
||||||
"cancel": "Отмена"
|
"cancel": "Отмена"
|
||||||
}
|
}
|
||||||
|
27
main.go
@@ -1,30 +1,27 @@
|
|||||||
// Command mycorrhiza is a program that runs a mycorrhiza wiki.
|
// Command mycorrhiza is a program that runs a mycorrhiza wiki.
|
||||||
//
|
//
|
||||||
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=tree
|
|
||||||
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=history
|
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=history
|
||||||
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=mycoopts
|
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=mycoopts
|
||||||
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=auth
|
|
||||||
//go:generate go run github.com/valyala/quicktemplate/qtc -dir=hypview
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bouncepaw/mycorrhiza/backlinks"
|
"github.com/bouncepaw/mycorrhiza/internal/categories"
|
||||||
"github.com/bouncepaw/mycorrhiza/categories"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/interwiki"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/migration"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/version"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
|
||||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/static"
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/migration"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/shroom"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/version"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/interwiki"
|
||||||
"github.com/bouncepaw/mycorrhiza/web"
|
"github.com/bouncepaw/mycorrhiza/web"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/static"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
package misc
|
package misc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/version"
|
||||||
"github.com/bouncepaw/mycorrhiza/l18n"
|
"github.com/bouncepaw/mycorrhiza/l18n"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/version"
|
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template" // sic!
|
"text/template" // sic!
|
||||||
|
@@ -11,16 +11,16 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/backlinks"
|
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/shroom"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/l18n"
|
"github.com/bouncepaw/mycorrhiza/l18n"
|
||||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/static"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
"github.com/bouncepaw/mycorrhiza/web/static"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitAssetHandlers(rtr *mux.Router) {
|
func InitAssetHandlers(rtr *mux.Router) {
|
||||||
|
@@ -2,8 +2,8 @@ package misc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -3,8 +3,8 @@ package mycoopts
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/options"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/options"
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/interwiki"
|
"github.com/bouncepaw/mycorrhiza/interwiki"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{% import "path/filepath" %}
|
{% import "path/filepath" %}
|
||||||
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
{% import "github.com/bouncepaw/mycorrhiza/internal/hyphae" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
|
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
|
||||||
|
|
||||||
{% func mediaRaw(h *hyphae.MediaHypha) %}{%= Media(h, l18n.New("en", "en")) %}{% endfunc %}
|
{% func mediaRaw(h *hyphae.MediaHypha) %}{%= Media(h, l18n.New("en", "en")) %}{% endfunc %}
|
||||||
|
@@ -8,7 +8,7 @@ package mycoopts
|
|||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
|
|
||||||
//line mycoopts/view.qtpl:3
|
//line mycoopts/view.qtpl:3
|
||||||
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
import "github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
|
||||||
//line mycoopts/view.qtpl:4
|
//line mycoopts/view.qtpl:4
|
||||||
import "github.com/bouncepaw/mycorrhiza/l18n"
|
import "github.com/bouncepaw/mycorrhiza/l18n"
|
||||||
|
@@ -1,47 +0,0 @@
|
|||||||
package settings
|
|
||||||
|
|
||||||
import (
|
|
||||||
"embed"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: translate untranslated strings
|
|
||||||
const settingsTranslationRu = `
|
|
||||||
{{define "change password"}}Change password{{end}}
|
|
||||||
{{define "confirm password"}}Confirm password{{end}}
|
|
||||||
{{define "current password"}}Current password{{end}}
|
|
||||||
{{define "non local password change"}}Non-local accounts cannot have their passwords changed.{{end}}
|
|
||||||
{{define "password"}}Password{{end}}
|
|
||||||
{{define "submit"}}Submit{{end}}
|
|
||||||
`
|
|
||||||
|
|
||||||
var (
|
|
||||||
//go:embed *.html
|
|
||||||
fs embed.FS
|
|
||||||
changePassowrdChain viewutil.Chain
|
|
||||||
)
|
|
||||||
|
|
||||||
func Init(rtr *mux.Router) {
|
|
||||||
rtr.HandleFunc("/change-password", handlerUserChangePassword).Methods(http.MethodGet, http.MethodPost)
|
|
||||||
|
|
||||||
changePassowrdChain = viewutil.CopyEnRuWith(fs, "view_change_password.html", settingsTranslationRu)
|
|
||||||
}
|
|
||||||
|
|
||||||
func changePasswordPage(meta viewutil.Meta, form util.FormData, u *user.User) {
|
|
||||||
viewutil.ExecutePage(meta, changePassowrdChain, changePasswordData{
|
|
||||||
BaseData: &viewutil.BaseData{},
|
|
||||||
Form: form,
|
|
||||||
U: u,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type changePasswordData struct {
|
|
||||||
*viewutil.BaseData
|
|
||||||
Form util.FormData
|
|
||||||
U *user.User
|
|
||||||
}
|
|
@@ -1,32 +0,0 @@
|
|||||||
{% import "sort" %}
|
|
||||||
{% import "path" %}
|
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
|
||||||
|
|
||||||
Subhyphae links are recursive. It may end up looking like that if drawn with
|
|
||||||
pseudographics:
|
|
||||||
╔══════════════╗
|
|
||||||
║Foo ║ The presented hyphae are ./foo and ./foo/bar
|
|
||||||
║╔════════════╗║
|
|
||||||
║║Bar ║║
|
|
||||||
║╚════════════╝║
|
|
||||||
╚══════════════╝
|
|
||||||
{% func childHTML(c *child) %}
|
|
||||||
{% code
|
|
||||||
sort.Slice(c.children, func(i, j int) bool {
|
|
||||||
return c.children[i].name < c.children[j].name
|
|
||||||
})
|
|
||||||
%}
|
|
||||||
<li class="subhyphae__entry">
|
|
||||||
<a class="subhyphae__link {% if !c.exists %}wikilink_new{% endif %}" href="/hypha/{%s c.name %}">
|
|
||||||
{%s util.BeautifulName(path.Base(c.name)) %}
|
|
||||||
</a>
|
|
||||||
{% if len(c.children) > 0 %}
|
|
||||||
<ul>
|
|
||||||
{% for _, child := range c.children %}
|
|
||||||
{%s= childHTML(&child) %}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
{% endfunc %}
|
|
||||||
|
|
@@ -1,126 +0,0 @@
|
|||||||
// Code generated by qtc from "view.qtpl". DO NOT EDIT.
|
|
||||||
// See https://github.com/valyala/quicktemplate for details.
|
|
||||||
|
|
||||||
//line tree/view.qtpl:1
|
|
||||||
package tree
|
|
||||||
|
|
||||||
//line tree/view.qtpl:1
|
|
||||||
import "sort"
|
|
||||||
|
|
||||||
//line tree/view.qtpl:2
|
|
||||||
import "path"
|
|
||||||
|
|
||||||
//line tree/view.qtpl:3
|
|
||||||
import "github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
|
|
||||||
// Subhyphae links are recursive. It may end up looking like that if drawn with
|
|
||||||
// pseudographics:
|
|
||||||
// ╔══════════════╗
|
|
||||||
// ║Foo ║ The presented hyphae are ./foo and ./foo/bar
|
|
||||||
// ║╔════════════╗║
|
|
||||||
// ║║Bar ║║
|
|
||||||
// ║╚════════════╝║
|
|
||||||
// ╚══════════════╝
|
|
||||||
|
|
||||||
//line tree/view.qtpl:13
|
|
||||||
import (
|
|
||||||
qtio422016 "io"
|
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
|
||||||
)
|
|
||||||
|
|
||||||
//line tree/view.qtpl:13
|
|
||||||
var (
|
|
||||||
_ = qtio422016.Copy
|
|
||||||
_ = qt422016.AcquireByteBuffer
|
|
||||||
)
|
|
||||||
|
|
||||||
//line tree/view.qtpl:13
|
|
||||||
func streamchildHTML(qw422016 *qt422016.Writer, c *child) {
|
|
||||||
//line tree/view.qtpl:13
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line tree/view.qtpl:15
|
|
||||||
sort.Slice(c.children, func(i, j int) bool {
|
|
||||||
return c.children[i].name < c.children[j].name
|
|
||||||
})
|
|
||||||
|
|
||||||
//line tree/view.qtpl:18
|
|
||||||
qw422016.N().S(`
|
|
||||||
<li class="subhyphae__entry">
|
|
||||||
<a class="subhyphae__link `)
|
|
||||||
//line tree/view.qtpl:20
|
|
||||||
if !c.exists {
|
|
||||||
//line tree/view.qtpl:20
|
|
||||||
qw422016.N().S(`wikilink_new`)
|
|
||||||
//line tree/view.qtpl:20
|
|
||||||
}
|
|
||||||
//line tree/view.qtpl:20
|
|
||||||
qw422016.N().S(`" href="/hypha/`)
|
|
||||||
//line tree/view.qtpl:20
|
|
||||||
qw422016.E().S(c.name)
|
|
||||||
//line tree/view.qtpl:20
|
|
||||||
qw422016.N().S(`">
|
|
||||||
`)
|
|
||||||
//line tree/view.qtpl:21
|
|
||||||
qw422016.E().S(util.BeautifulName(path.Base(c.name)))
|
|
||||||
//line tree/view.qtpl:21
|
|
||||||
qw422016.N().S(`
|
|
||||||
</a>
|
|
||||||
`)
|
|
||||||
//line tree/view.qtpl:23
|
|
||||||
if len(c.children) > 0 {
|
|
||||||
//line tree/view.qtpl:23
|
|
||||||
qw422016.N().S(`
|
|
||||||
<ul>
|
|
||||||
`)
|
|
||||||
//line tree/view.qtpl:25
|
|
||||||
for _, child := range c.children {
|
|
||||||
//line tree/view.qtpl:25
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line tree/view.qtpl:26
|
|
||||||
qw422016.N().S(childHTML(&child))
|
|
||||||
//line tree/view.qtpl:26
|
|
||||||
qw422016.N().S(`
|
|
||||||
`)
|
|
||||||
//line tree/view.qtpl:27
|
|
||||||
}
|
|
||||||
//line tree/view.qtpl:27
|
|
||||||
qw422016.N().S(`
|
|
||||||
</ul>
|
|
||||||
`)
|
|
||||||
//line tree/view.qtpl:29
|
|
||||||
}
|
|
||||||
//line tree/view.qtpl:29
|
|
||||||
qw422016.N().S(`
|
|
||||||
</li>
|
|
||||||
`)
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
}
|
|
||||||
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
func writechildHTML(qq422016 qtio422016.Writer, c *child) {
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
streamchildHTML(qw422016, c)
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
}
|
|
||||||
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
func childHTML(c *child) string {
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
writechildHTML(qb422016, c)
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
return qs422016
|
|
||||||
//line tree/view.qtpl:31
|
|
||||||
}
|
|
@@ -3,13 +3,14 @@ package util
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
|
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/util"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/util"
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrepareRq strips the trailing / in rq.URL.Path. In the future it might do more stuff for making all request structs uniform.
|
// PrepareRq strips the trailing / in rq.URL.Path. In the future it might do more stuff for making all request structs uniform.
|
||||||
@@ -31,6 +32,7 @@ func ShorterPath(path string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HTTP404Page writes a 404 error in the status, needed when no content is found on the page.
|
// HTTP404Page writes a 404 error in the status, needed when no content is found on the page.
|
||||||
|
// TODO: demolish
|
||||||
func HTTP404Page(w http.ResponseWriter, page string) {
|
func HTTP404Page(w http.ResponseWriter, page string) {
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
@@ -38,6 +40,7 @@ func HTTP404Page(w http.ResponseWriter, page string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HTTP200Page wraps some frequently used things for successful 200 responses.
|
// HTTP200Page wraps some frequently used things for successful 200 responses.
|
||||||
|
// TODO: demolish
|
||||||
func HTTP200Page(w http.ResponseWriter, page string) {
|
func HTTP200Page(w http.ResponseWriter, page string) {
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
@@ -1,20 +1,110 @@
|
|||||||
package admin
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"log"
|
"log"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const adminTranslationRu = `
|
||||||
|
{{define "panel title"}}Панель админстратора{{end}}
|
||||||
|
{{define "panel safe section title"}}Безопасная секция{{end}}
|
||||||
|
{{define "panel link about"}}Об этой вики{{end}}
|
||||||
|
{{define "panel update header"}}Обновить ссылки в верхней панели{{end}}
|
||||||
|
{{define "panel link user list"}}Список пользователей{{end}}
|
||||||
|
{{define "panel users"}}Управление пользователями{{end}}
|
||||||
|
{{define "panel unsafe section title"}}Опасная секция{{end}}
|
||||||
|
{{define "panel shutdown"}}Выключить вики{{end}}
|
||||||
|
{{define "panel reindex hyphae"}}Переиндексировать гифы{{end}}
|
||||||
|
{{define "panel interwiki"}}Интервики{{end}}
|
||||||
|
|
||||||
|
{{define "manage users"}}Управление пользователями{{end}}
|
||||||
|
{{define "create user"}}Создать пользователя{{end}}
|
||||||
|
{{define "reindex users"}}Переиндексировать пользователей{{end}}
|
||||||
|
{{define "name"}}Имя{{end}}
|
||||||
|
{{define "group"}}Группа{{end}}
|
||||||
|
{{define "registered at"}}Зарегистрирован{{end}}
|
||||||
|
{{define "actions"}}Действия{{end}}
|
||||||
|
{{define "edit"}}Изменить{{end}}
|
||||||
|
|
||||||
|
{{define "new user"}}Новый пользователь{{end}}
|
||||||
|
{{define "password"}}Пароль{{end}}
|
||||||
|
{{define "confirm password"}}Подтвердить пароль{{end}}
|
||||||
|
{{define "change password"}}Изменить пароль{{end}}
|
||||||
|
{{define "non local password change"}}Поменять пароль можно только у локальных пользователей.{{end}}
|
||||||
|
{{define "create"}}Создать{{end}}
|
||||||
|
|
||||||
|
{{define "change group"}}Изменить группу{{end}}
|
||||||
|
{{define "user x"}}Пользователь {{.}}{{end}}
|
||||||
|
{{define "update"}}Обновить{{end}}
|
||||||
|
{{define "delete user"}}Удалить пользователя{{end}}
|
||||||
|
{{define "delete user tip"}}Удаляет пользователя из базы данных. Правки пользователя будут сохранены. Имя пользователя освободится для повторной регистрации.{{end}}
|
||||||
|
|
||||||
|
{{define "delete user?"}}Удалить пользователя {{.}}?{{end}}
|
||||||
|
{{define "delete user warning"}}Вы уверены, что хотите удалить этого пользователя из базы данных? Это действие нельзя отменить.{{end}}
|
||||||
|
`
|
||||||
|
|
||||||
|
func viewPanel(meta viewutil.Meta) {
|
||||||
|
viewutil.ExecutePage(meta, panelChain, &viewutil.BaseData{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type listData struct {
|
||||||
|
*viewutil.BaseData
|
||||||
|
UserHypha string
|
||||||
|
Users []*user.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewList(meta viewutil.Meta, users []*user.User) {
|
||||||
|
viewutil.ExecutePage(meta, listChain, listData{
|
||||||
|
BaseData: &viewutil.BaseData{},
|
||||||
|
UserHypha: cfg.UserHypha,
|
||||||
|
Users: users,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type newUserData struct {
|
||||||
|
*viewutil.BaseData
|
||||||
|
Form util.FormData
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewNewUser(meta viewutil.Meta, form util.FormData) {
|
||||||
|
viewutil.ExecutePage(meta, newUserChain, newUserData{
|
||||||
|
BaseData: &viewutil.BaseData{},
|
||||||
|
Form: form,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type editDeleteUserData struct {
|
||||||
|
*viewutil.BaseData
|
||||||
|
Form util.FormData
|
||||||
|
U *user.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewEditUser(meta viewutil.Meta, form util.FormData, u *user.User) {
|
||||||
|
viewutil.ExecutePage(meta, editUserChain, editDeleteUserData{
|
||||||
|
BaseData: &viewutil.BaseData{},
|
||||||
|
Form: form,
|
||||||
|
U: u,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewDeleteUser(meta viewutil.Meta, form util.FormData, u *user.User) {
|
||||||
|
viewutil.ExecutePage(meta, deleteUserChain, editDeleteUserData{
|
||||||
|
BaseData: &viewutil.BaseData{},
|
||||||
|
Form: form,
|
||||||
|
U: u,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// handlerAdmin provides the admin panel.
|
// handlerAdmin provides the admin panel.
|
||||||
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
@@ -1,40 +1,46 @@
|
|||||||
package categories
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/internal/categories"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
|
||||||
|
|
||||||
// InitHandlers initializes HTTP handlers for the given router. Call somewhere in package web.
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
func InitHandlers(r *mux.Router) {
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
r.PathPrefix("/add-to-category").HandlerFunc(handlerAddToCategory).Methods("POST")
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
r.PathPrefix("/remove-from-category").HandlerFunc(handlerRemoveFromCategory).Methods("POST")
|
)
|
||||||
r.PathPrefix("/category/").HandlerFunc(handlerCategory).Methods("GET")
|
|
||||||
r.PathPrefix("/edit-category/").HandlerFunc(handlerEditCategory).Methods("GET")
|
|
||||||
r.PathPrefix("/category").HandlerFunc(handlerListCategory).Methods("GET")
|
|
||||||
prepareViews()
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerEditCategory(w http.ResponseWriter, rq *http.Request) {
|
func handlerEditCategory(w http.ResponseWriter, rq *http.Request) {
|
||||||
util.PrepareRq(rq)
|
util.PrepareRq(rq)
|
||||||
|
meta := viewutil.MetaFrom(w, rq)
|
||||||
catName := util.CanonicalName(strings.TrimPrefix(strings.TrimPrefix(rq.URL.Path, "/edit-category"), "/"))
|
catName := util.CanonicalName(strings.TrimPrefix(strings.TrimPrefix(rq.URL.Path, "/edit-category"), "/"))
|
||||||
if catName == "" {
|
if catName == "" {
|
||||||
http.Error(w, "No category name", http.StatusBadRequest)
|
viewutil.HandlerNotFound(w, rq)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Println("Editing category", catName)
|
|
||||||
categoryEdit(viewutil.MetaFrom(w, rq), catName)
|
slog.Info("Editing category", "name", catName)
|
||||||
|
_ = pageCatEdit.RenderTo(meta, map[string]any{
|
||||||
|
"Addr": "/edit-category/" + catName,
|
||||||
|
"CatName": catName,
|
||||||
|
"Hyphae": categories.HyphaeInCategory(catName),
|
||||||
|
"GivenPermissionToModify": meta.U.CanProceed("add-to-category"),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerListCategory(w http.ResponseWriter, rq *http.Request) {
|
func handlerListCategory(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println("Viewing list of categories")
|
slog.Info("Viewing list of categories")
|
||||||
categoryList(viewutil.MetaFrom(w, rq))
|
cats := categories.ListOfCategories()
|
||||||
|
sort.Strings(cats)
|
||||||
|
|
||||||
|
_ = pageCatList.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{
|
||||||
|
"Addr": "/category",
|
||||||
|
"Categories": cats,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerCategory(w http.ResponseWriter, rq *http.Request) {
|
func handlerCategory(w http.ResponseWriter, rq *http.Request) {
|
||||||
@@ -44,8 +50,15 @@ func handlerCategory(w http.ResponseWriter, rq *http.Request) {
|
|||||||
handlerListCategory(w, rq)
|
handlerListCategory(w, rq)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Println("Viewing category", catName)
|
|
||||||
categoryPage(viewutil.MetaFrom(w, rq), catName)
|
meta := viewutil.MetaFrom(w, rq)
|
||||||
|
slog.Info("Viewing category", "name", catName)
|
||||||
|
_ = pageCatPage.RenderTo(meta, map[string]any{
|
||||||
|
"Addr": "/category/" + catName,
|
||||||
|
"CatName": catName,
|
||||||
|
"Hyphae": categories.HyphaeInCategory(catName),
|
||||||
|
"GivenPermissionToModify": meta.U.CanProceed("add-to-category"),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// A request for removal of hyphae can either remove one hypha (used in the card on /hypha) or many hyphae (used in /edit-category). Both approaches are handled by /remove-from-category. This function finds all passed hyphae.
|
// A request for removal of hyphae can either remove one hypha (used in the card on /hypha) or many hyphae (used in /edit-category). Both approaches are handled by /remove-from-category. This function finds all passed hyphae.
|
||||||
@@ -93,7 +106,7 @@ func handlerRemoveFromCategory(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
for _, hyphaName := range hyphaNames {
|
for _, hyphaName := range hyphaNames {
|
||||||
// TODO: Make it more effective.
|
// TODO: Make it more effective.
|
||||||
removeHyphaFromCategory(hyphaName, catName)
|
categories.RemoveHyphaFromCategory(hyphaName, catName)
|
||||||
}
|
}
|
||||||
log.Printf("%s removed %q from category %s\n", u.Name, hyphaNames, catName)
|
log.Printf("%s removed %q from category %s\n", u.Name, hyphaNames, catName)
|
||||||
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
|
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
|
||||||
@@ -115,7 +128,7 @@ func handlerAddToCategory(w http.ResponseWriter, rq *http.Request) {
|
|||||||
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
|
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Println(user.FromRequest(rq).Name, "added", hyphaName, "to", catName)
|
slog.Info(user.FromRequest(rq).Name, "added", hyphaName, "to", catName)
|
||||||
AddHyphaToCategory(hyphaName, catName)
|
categories.AddHyphaToCategory(hyphaName, catName)
|
||||||
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
|
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
|
||||||
}
|
}
|
@@ -2,22 +2,21 @@ package web
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/shroom"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
|
||||||
"github.com/bouncepaw/mycorrhiza/hypview"
|
"github.com/bouncepaw/mycorrhiza/hypview"
|
||||||
"github.com/bouncepaw/mycorrhiza/mycoopts"
|
"github.com/bouncepaw/mycorrhiza/mycoopts"
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
|
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/l18n"
|
"github.com/bouncepaw/mycorrhiza/l18n"
|
||||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ func initMutators(r *mux.Router) {
|
|||||||
r.PathPrefix("/edit/").HandlerFunc(handlerEdit)
|
r.PathPrefix("/edit/").HandlerFunc(handlerEdit)
|
||||||
r.PathPrefix("/rename/").HandlerFunc(handlerRename).Methods("GET", "POST")
|
r.PathPrefix("/rename/").HandlerFunc(handlerRename).Methods("GET", "POST")
|
||||||
r.PathPrefix("/delete/").HandlerFunc(handlerDelete).Methods("GET", "POST")
|
r.PathPrefix("/delete/").HandlerFunc(handlerDelete).Methods("GET", "POST")
|
||||||
r.PathPrefix("/remove-media/").HandlerFunc(handlerRemoveMedia).Methods("GET", "POST")
|
r.PathPrefix("/remove-media/").HandlerFunc(handlerRemoveMedia).Methods("POST")
|
||||||
r.PathPrefix("/upload-binary/").HandlerFunc(handlerUploadBinary)
|
r.PathPrefix("/upload-binary/").HandlerFunc(handlerUploadBinary)
|
||||||
r.PathPrefix("/upload-text/").HandlerFunc(handlerUploadText)
|
r.PathPrefix("/upload-text/").HandlerFunc(handlerUploadText)
|
||||||
}
|
}
|
||||||
@@ -43,10 +42,6 @@ func handlerRemoveMedia(w http.ResponseWriter, rq *http.Request) {
|
|||||||
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "no rights")
|
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "no rights")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if rq.Method == "GET" {
|
|
||||||
hypview.RemoveMedia(viewutil.MetaFrom(w, rq), h.CanonicalName())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch h := h.(type) {
|
switch h := h.(type) {
|
||||||
case *hyphae.EmptyHypha, *hyphae.TextualHypha:
|
case *hyphae.EmptyHypha, *hyphae.TextualHypha:
|
||||||
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "no media to remove")
|
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "no media to remove")
|
||||||
@@ -83,7 +78,11 @@ func handlerDelete(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if rq.Method == "GET" {
|
if rq.Method == "GET" {
|
||||||
hypview.DeleteHypha(meta, h.CanonicalName())
|
_ = pageHyphaDelete.RenderTo(
|
||||||
|
viewutil.MetaFrom(w, rq),
|
||||||
|
map[string]any{
|
||||||
|
"HyphaName": h.CanonicalName(),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +168,15 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hypview.EditHypha(meta, hyphaName, isNew, content, "", "")
|
_ = pageHyphaEdit.RenderTo(
|
||||||
|
viewutil.MetaFrom(w, rq),
|
||||||
|
map[string]any{
|
||||||
|
"HyphaName": hyphaName,
|
||||||
|
"Content": content,
|
||||||
|
"IsNew": isNew,
|
||||||
|
"Message": "",
|
||||||
|
"Preview": "",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerUploadText uploads a new text part for the hypha.
|
// handlerUploadText uploads a new text part for the hypha.
|
||||||
@@ -191,7 +198,16 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
|||||||
if action == "preview" {
|
if action == "preview" {
|
||||||
ctx, _ := mycocontext.ContextFromStringInput(textData, mycoopts.MarkupOptions(hyphaName))
|
ctx, _ := mycocontext.ContextFromStringInput(textData, mycoopts.MarkupOptions(hyphaName))
|
||||||
preview := template.HTML(mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx)))
|
preview := template.HTML(mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx)))
|
||||||
hypview.EditHypha(meta, hyphaName, isNew, textData, message, preview)
|
|
||||||
|
_ = pageHyphaEdit.RenderTo(
|
||||||
|
viewutil.MetaFrom(w, rq),
|
||||||
|
map[string]any{
|
||||||
|
"HyphaName": hyphaName,
|
||||||
|
"Content": textData,
|
||||||
|
"IsNew": isNew,
|
||||||
|
"Message": message,
|
||||||
|
"Preview": preview,
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
118
web/newtmpl/newtmpl.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
package newtmpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
|
"html/template"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed *.html
|
||||||
|
var fs embed.FS
|
||||||
|
|
||||||
|
var base = template.Must(template.ParseFS(fs, "base.html"))
|
||||||
|
|
||||||
|
type Page struct {
|
||||||
|
TemplateEnglish *template.Template
|
||||||
|
TemplateRussian *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPage(fs embed.FS, russianTranslation map[string]string, tmpls ...string) *Page {
|
||||||
|
must := template.Must
|
||||||
|
en := must(must(must(
|
||||||
|
base.Clone()).
|
||||||
|
Funcs(template.FuncMap{
|
||||||
|
"beautifulName": util.BeautifulName,
|
||||||
|
"inc": func(i int) int { return i + 1 },
|
||||||
|
"base": func(hyphaName string) string {
|
||||||
|
parts := strings.Split(hyphaName, "/")
|
||||||
|
return parts[len(parts)-1]
|
||||||
|
},
|
||||||
|
"beautifulLink": func(hyphaName string) template.HTML {
|
||||||
|
return template.HTML(
|
||||||
|
fmt.Sprintf(
|
||||||
|
`<a href="/hypha/%s">%s</a>`, hyphaName, hyphaName))
|
||||||
|
},
|
||||||
|
}).
|
||||||
|
Parse(fmt.Sprintf(`
|
||||||
|
{{define "wiki name"}}%s{{end}}
|
||||||
|
{{define "user hypha"}}%s{{end}}
|
||||||
|
`, cfg.WikiName, cfg.UserHypha))).
|
||||||
|
ParseFS(fs, tmpls...))
|
||||||
|
|
||||||
|
if cfg.UseAuth {
|
||||||
|
en = must(en.Parse(`
|
||||||
|
{{define "auth"}}
|
||||||
|
<ul class="top-bar__auth auth-links">
|
||||||
|
<li class="auth-links__box auth-links__user-box">
|
||||||
|
{{if .Meta.U.Group | eq "anon" }}
|
||||||
|
<a href="/login" class="auth-links__link auth-links__login-link">
|
||||||
|
{{block "login" .}}Login{{end}}
|
||||||
|
</a>
|
||||||
|
{{else}}
|
||||||
|
<a href="/hypha/{{block "user hypha" .}}{{end}}/{{.Meta.U.Name}}" class="auth-links__link auth-links__user-link">
|
||||||
|
{{beautifulName .Meta.U.Name}}
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
|
</li>
|
||||||
|
{{block "registration" .}}{{end}}
|
||||||
|
</ul>
|
||||||
|
{{end}}
|
||||||
|
`))
|
||||||
|
}
|
||||||
|
if cfg.AllowRegistration {
|
||||||
|
must(en.Parse(`{{define "registration"}}
|
||||||
|
{{if .Meta.U.Group | eq "anon"}}
|
||||||
|
<li class="auth-links__box auth-links__register-box">
|
||||||
|
<a href="/register" class="auth-links__link auth-links__register-link">
|
||||||
|
{{block "register" .}}Register{{end}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{end}}
|
||||||
|
{{end}}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
russianTranslation["search by title"] = "Поиск по названию"
|
||||||
|
russianTranslation["login"] = "Войти"
|
||||||
|
russianTranslation["register"] = "Регистрация"
|
||||||
|
russianTranslation["cancel"] = "Отмена"
|
||||||
|
russianTranslation["categories"] = "Категории"
|
||||||
|
russianTranslation["remove from category title"] = "Убрать гифу из этой категории"
|
||||||
|
russianTranslation["placeholder"] = "Название категории..."
|
||||||
|
russianTranslation["add to category title"] = "Добавить гифу в эту категорию"
|
||||||
|
|
||||||
|
return &Page{
|
||||||
|
TemplateEnglish: en,
|
||||||
|
TemplateRussian: must(must(en.Clone()).
|
||||||
|
Parse(translationsIntoTemplates(russianTranslation))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func translationsIntoTemplates(m map[string]string) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
for k, v := range m {
|
||||||
|
sb.WriteString(fmt.Sprintf(`{{define "%s"}}%s{{end}}
|
||||||
|
`, k, v))
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Page) RenderTo(meta viewutil.Meta, data map[string]any) error {
|
||||||
|
data["Meta"] = meta
|
||||||
|
data["HeadElements"] = meta.HeadElements
|
||||||
|
data["BodyAttributes"] = meta.BodyAttributes
|
||||||
|
data["CommonScripts"] = cfg.CommonScripts
|
||||||
|
data["EditScripts"] = cfg.EditScripts
|
||||||
|
data["HeaderLinks"] = viewutil.HeaderLinks
|
||||||
|
data["UseAuth"] = cfg.UseAuth
|
||||||
|
|
||||||
|
tmpl := p.TemplateEnglish
|
||||||
|
if meta.LocaleIsRussian() {
|
||||||
|
tmpl = p.TemplateRussian
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmpl.ExecuteTemplate(meta.W, "page", data)
|
||||||
|
}
|
202
web/pages.go
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/newtmpl"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed views/*.html
|
||||||
|
var fs embed.FS
|
||||||
|
|
||||||
|
var pageOrphans, pageBacklinks, pageUserList, pageChangePassword *newtmpl.Page
|
||||||
|
var pageHyphaDelete, pageHyphaEdit, pageHyphaEmpty, pageHypha *newtmpl.Page
|
||||||
|
var pageRevision, pageMedia *newtmpl.Page
|
||||||
|
var pageAuthLock, pageAuthLogin, pageAuthLogout, pageAuthRegister *newtmpl.Page
|
||||||
|
var pageCatPage, pageCatList, pageCatEdit *newtmpl.Page
|
||||||
|
|
||||||
|
var panelChain, listChain, newUserChain, editUserChain, deleteUserChain viewutil.Chain
|
||||||
|
|
||||||
|
func initPages() {
|
||||||
|
|
||||||
|
panelChain = viewutil.CopyEnRuWith(fs, "views/admin-panel.html", adminTranslationRu)
|
||||||
|
listChain = viewutil.CopyEnRuWith(fs, "views/admin-user-list.html", adminTranslationRu)
|
||||||
|
newUserChain = viewutil.CopyEnRuWith(fs, "views/admin-new-user.html", adminTranslationRu)
|
||||||
|
editUserChain = viewutil.CopyEnRuWith(fs, "views/admin-edit-user.html", adminTranslationRu)
|
||||||
|
deleteUserChain = viewutil.CopyEnRuWith(fs, "views/admin-delete-user.html", adminTranslationRu)
|
||||||
|
|
||||||
|
pageOrphans = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"orphaned hyphae": "Гифы-сироты",
|
||||||
|
"orphan description": "Ниже перечислены гифы без ссылок на них.",
|
||||||
|
}, "views/orphans.html")
|
||||||
|
pageBacklinks = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"backlinks to text": `Обратные ссылки на {{.}}`,
|
||||||
|
"backlinks to link": `Обратные ссылки на <a href="/hypha/{{.}}">{{beautifulName .}}</a>`,
|
||||||
|
"description": `Ниже перечислены гифы, на которых есть ссылка на эту гифу, трансклюзия этой гифы или эта гифа вставлена как изображение.`,
|
||||||
|
}, "views/backlinks.html")
|
||||||
|
pageUserList = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"title": "Список пользователей",
|
||||||
|
"administrators": "Администраторы",
|
||||||
|
"moderators": "Модераторы",
|
||||||
|
"editors": "Редакторы",
|
||||||
|
"readers": "Читатели",
|
||||||
|
}, "views/user-list.html")
|
||||||
|
pageChangePassword = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"change password": "Сменить пароль",
|
||||||
|
"confirm password": "Повторите пароль",
|
||||||
|
"current password": "Текущий пароль",
|
||||||
|
"non local password change": "Пароль можно поменять только местным аккаунтам. Telegram-аккаунтам нельзя.",
|
||||||
|
"password": "Пароль",
|
||||||
|
"submit": "Поменять",
|
||||||
|
}, "views/change-password.html")
|
||||||
|
pageHyphaDelete = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"delete hypha?": "Удалить {{beautifulName .}}?",
|
||||||
|
"delete [[hypha]]?": "Удалить <a href=\"/hypha/{{.}}\">{{beautifulName .}}</a>?",
|
||||||
|
"want to delete?": "Вы действительно хотите удалить эту гифу?",
|
||||||
|
"delete tip": "Нельзя отменить удаление гифы, но её история останется доступной.",
|
||||||
|
}, "views/hypha-delete.html")
|
||||||
|
pageHyphaEdit = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"editing hypha": `Редактирование {{beautifulName .}}`,
|
||||||
|
"editing [[hypha]]": `Редактирование <a href="/hypha/{{.}}">{{beautifulName .}}</a>`,
|
||||||
|
"creating [[hypha]]": `Создание <a href="/hypha/{{.}}">{{beautifulName .}}</a>`,
|
||||||
|
"you're creating a new hypha": `Вы создаёте новую гифу.`,
|
||||||
|
"describe your changes": `Опишите ваши правки`,
|
||||||
|
"save": `Сохранить`,
|
||||||
|
"preview": `Предпросмотр`,
|
||||||
|
"previewing hypha": `Предпросмотр {{beautifulName .}}`,
|
||||||
|
"preview tip": `Заметьте, эта гифа ещё не сохранена. Вот её предпросмотр:`,
|
||||||
|
|
||||||
|
"markup": `Разметка`,
|
||||||
|
"link": `Ссылка`,
|
||||||
|
"link title": `Текст`,
|
||||||
|
"heading": `Заголовок`,
|
||||||
|
"bold": `Жирный`,
|
||||||
|
"italic": `Курсив`,
|
||||||
|
"highlight": `Выделение`,
|
||||||
|
"underline": `Подчеркивание`,
|
||||||
|
"mono": `Моноширинный`,
|
||||||
|
"super": `Надстрочный`,
|
||||||
|
"sub": `Подстрочный`,
|
||||||
|
"strike": `Зачёркнутый`,
|
||||||
|
"rocket": `Ссылка-ракета`,
|
||||||
|
"transclude": `Трансклюзия`,
|
||||||
|
"hr": `Гориз. черта`,
|
||||||
|
"code": `Код-блок`,
|
||||||
|
"bullets": `Маркир. список`,
|
||||||
|
"numbers": `Нумер. список`,
|
||||||
|
"mycomarkup help": `<a href="/help/en/mycomarkup" class="shy-link">Подробнее</a> о Микоразметке`,
|
||||||
|
"actions": `Действия`,
|
||||||
|
"current date local": `Местная дата`,
|
||||||
|
"current time local": `Местное время`,
|
||||||
|
"current date utc": "Дата UTC",
|
||||||
|
"current time utc": "Время UTC",
|
||||||
|
"selflink": `Ссылка на вас`,
|
||||||
|
}, "views/hypha-edit.html")
|
||||||
|
pageHypha = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"edit text": "Редактировать",
|
||||||
|
"log out": "Выйти",
|
||||||
|
"admin panel": "Админка",
|
||||||
|
"subhyphae": "Подгифы",
|
||||||
|
"history": "История",
|
||||||
|
"rename": "Переименовать",
|
||||||
|
"delete": "Удалить",
|
||||||
|
"view markup": "Посмотреть разметку",
|
||||||
|
"manage media": "Медиа",
|
||||||
|
"turn to media": "Превратить в медиа-гифу",
|
||||||
|
"backlinks": "{{.BacklinkCount}} обратн{{if eq .BacklinkCount 1}}ая ссылка{{else if and (le .BacklinkCount 4) (gt .BacklinkCount 1)}}ые ссылки{{else}}ых ссылок{{end}}",
|
||||||
|
|
||||||
|
"empty heading": `Эта гифа не существует`,
|
||||||
|
"empty no rights": `У вас нет прав для создания новых гиф. Вы можете:`,
|
||||||
|
"empty log in": `Войти в свою учётную запись, если она у вас есть`,
|
||||||
|
"empty register": `Создать новую учётную запись`,
|
||||||
|
"write a text": `Написать текст`,
|
||||||
|
"write a text tip": `Напишите заметку, дневник, статью, рассказ или иной текст с помощью <a href="/help/en/mycomarkup" class="shy-link">Микоразметки</a>. Сохраняется полная история правок документа.`,
|
||||||
|
"write a text writing conventions": `Не забывайте следовать правилам оформления этой вики, если они имеются.`,
|
||||||
|
"write a text btn": `Создать`,
|
||||||
|
"upload a media": `Загрузить медиа`,
|
||||||
|
"upload a media tip": `Загрузите изображение, видео или аудио. Распространённые форматы можно просматривать из браузера, остальные можно только скачать и просмотреть локально. Позже вы можете дописать пояснение к этому медиа.`,
|
||||||
|
"upload a media btn": `Загрузить`,
|
||||||
|
}, "views/hypha.html")
|
||||||
|
pageRevision = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"revision warning": "Обратите внимание, просмотр медиа в истории пока что недоступен.",
|
||||||
|
"revision link": "Посмотреть Микоразметку для этой ревизии",
|
||||||
|
"hypha at rev": "{{.HyphaName}} на {{.RevHash}}",
|
||||||
|
}, "views/hypha-revision.html")
|
||||||
|
pageMedia = newtmpl.NewPage(fs, map[string]string{ // TODO: сделать новый перевод
|
||||||
|
"media title": "Медиа «{{.HyphaName | beautifulLink}}»",
|
||||||
|
"tip": "На этой странице вы можете управлять медиа.",
|
||||||
|
"empty": "Эта гифа не имеет медиа, здесь вы можете его загрузить.",
|
||||||
|
"what is media?": "Что такое медиа?",
|
||||||
|
"stat": "Свойства",
|
||||||
|
"stat size": "Размер файла:",
|
||||||
|
"stat mime": "MIME-тип:",
|
||||||
|
|
||||||
|
"upload title": "Прикрепить",
|
||||||
|
"upload tip": "Вы можете загрузить новое медиа. Пожалуйста, не загружайте слишком большие изображения без необходимости, чтобы впоследствии не ждать её долгую загрузку.",
|
||||||
|
"upload btn": "Загрузить",
|
||||||
|
|
||||||
|
"remove title": "Открепить",
|
||||||
|
"remove tip": "Заметьте, чтобы заменить медиа, вам не нужно его перед этим откреплять.",
|
||||||
|
"remove btn": "Открепить",
|
||||||
|
}, "views/hypha-media.html")
|
||||||
|
|
||||||
|
pageAuthLock = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"lock title": "Доступ закрыт",
|
||||||
|
"username": "Логин",
|
||||||
|
"password": "Пароль",
|
||||||
|
"log in": "Войти",
|
||||||
|
}, "views/auth-telegram.html", "views/auth-lock.html")
|
||||||
|
|
||||||
|
pageAuthLogin = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"username": "Логин",
|
||||||
|
"password": "Пароль",
|
||||||
|
"log in": "Войти",
|
||||||
|
"cookie tip": "Отправляя эту форму, вы разрешаете вики хранить cookie в вашем браузере. Это позволит движку связывать ваши правки с вашей учётной записью. Вы будете авторизованы, пока не выйдете из учётной записи.",
|
||||||
|
"log in to x": "Войти в {{.}}",
|
||||||
|
"auth disabled": "Аутентификация отключена. Вы можете делать правки анонимно.",
|
||||||
|
"error username": "Неизвестное имя пользователя.",
|
||||||
|
"error password": "Неправильный пароль.",
|
||||||
|
"error telegram": "Не удалось войти через Телеграм.",
|
||||||
|
"go home": "Домой",
|
||||||
|
}, "views/auth-telegram.html", "views/auth-login.html")
|
||||||
|
|
||||||
|
pageAuthLogout = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"log out?": "Выйти?",
|
||||||
|
"log out": "Выйти",
|
||||||
|
"cannot log out anon": "Вы не можете выйти, потому что ещё не вошли.",
|
||||||
|
"log in": "Войти",
|
||||||
|
"go home": "Домой",
|
||||||
|
}, "views/auth-logout.html")
|
||||||
|
|
||||||
|
pageAuthRegister = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"username": "Логин",
|
||||||
|
"password": "Пароль",
|
||||||
|
"cookie tip": "Отправляя эту форму, вы разрешаете вики хранить cookie в вашем браузере. Это позволит движку связывать ваши правки с вашей учётной записью. Вы будете авторизованы, пока не выйдете из учётной записи.",
|
||||||
|
"password tip": "Сервер хранит ваш пароль в зашифрованном виде, даже администраторы не смогут его прочесть.",
|
||||||
|
"register btn": "Зарегистрироваться",
|
||||||
|
"register on x": "Регистрация на {{.}}",
|
||||||
|
}, "views/auth-telegram.html", "views/auth-register.html")
|
||||||
|
|
||||||
|
pageCatPage = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"category x": "Категория {{. | beautifulName}}",
|
||||||
|
"edit": "Редактировать",
|
||||||
|
"cat": "Категория",
|
||||||
|
"empty cat": "Эта категория пуста.",
|
||||||
|
}, "views/cat-page.html")
|
||||||
|
|
||||||
|
pageCatEdit = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"edit category x": "Редактирование категории {{beautifulName .}}",
|
||||||
|
"edit category heading": "Редактирование категории <a href=\"/category/{{.}}\">{{beautifulName .}}</a>",
|
||||||
|
"empty cat": "Эта категория пуста.",
|
||||||
|
"add to category title": "Добавить гифу в эту категорию",
|
||||||
|
"hypha name": "Название гифы",
|
||||||
|
"add": "Добавить",
|
||||||
|
"remove hyphae": "Убрать гифы из этой категории",
|
||||||
|
"remove": "Убрать",
|
||||||
|
}, "views/cat-edit.html")
|
||||||
|
|
||||||
|
pageCatList = newtmpl.NewPage(fs, map[string]string{
|
||||||
|
"category list": "Список категорий",
|
||||||
|
"no categories": "В этой вики нет категорий.",
|
||||||
|
}, "views/cat-list.html")
|
||||||
|
}
|
@@ -1,15 +1,13 @@
|
|||||||
package settings
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func handlerUserChangePassword(w http.ResponseWriter, rq *http.Request) {
|
func handlerUserChangePassword(w http.ResponseWriter, rq *http.Request) {
|
||||||
@@ -49,6 +47,7 @@ func handlerUserChangePassword(w http.ResponseWriter, rq *http.Request) {
|
|||||||
f = f.WithError(err)
|
f = f.WithError(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: handle first attempt different
|
||||||
err := fmt.Errorf("incorrect password")
|
err := fmt.Errorf("incorrect password")
|
||||||
f = f.WithError(err)
|
f = f.WithError(err)
|
||||||
}
|
}
|
||||||
@@ -58,5 +57,11 @@ func handlerUserChangePassword(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
||||||
|
|
||||||
changePasswordPage(viewutil.MetaFrom(w, rq), f, u)
|
_ = pageChangePassword.RenderTo(
|
||||||
|
viewutil.MetaFrom(w, rq),
|
||||||
|
map[string]any{
|
||||||
|
"Form": f,
|
||||||
|
"U": u,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
174
web/readers.go
@@ -3,15 +3,24 @@ package web
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5"
|
||||||
"github.com/bouncepaw/mycorrhiza/categories"
|
"github.com/bouncepaw/mycorrhiza/hypview"
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
|
||||||
views2 "github.com/bouncepaw/mycorrhiza/hypview"
|
"github.com/bouncepaw/mycorrhiza/internal/categories"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/cfg"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/files"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/mimetype"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/tree"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/internal/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/mycoopts"
|
"github.com/bouncepaw/mycorrhiza/mycoopts"
|
||||||
"github.com/bouncepaw/mycorrhiza/viewutil"
|
"github.com/bouncepaw/mycorrhiza/web/viewutil"
|
||||||
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -21,10 +30,7 @@ import (
|
|||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
|
||||||
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/l18n"
|
"github.com/bouncepaw/mycorrhiza/l18n"
|
||||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,6 +44,10 @@ func initReaders(r *mux.Router) {
|
|||||||
r.PathPrefix("/media/").HandlerFunc(handlerMedia)
|
r.PathPrefix("/media/").HandlerFunc(handlerMedia)
|
||||||
r.Path("/today").HandlerFunc(handlerToday)
|
r.Path("/today").HandlerFunc(handlerToday)
|
||||||
r.Path("/edit-today").HandlerFunc(handlerEditToday)
|
r.Path("/edit-today").HandlerFunc(handlerEditToday)
|
||||||
|
|
||||||
|
// Backlinks
|
||||||
|
r.PathPrefix("/backlinks/").HandlerFunc(handlerBacklinks)
|
||||||
|
r.PathPrefix("/orphans").HandlerFunc(handlerOrphans)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerEditToday(w http.ResponseWriter, rq *http.Request) {
|
func handlerEditToday(w http.ResponseWriter, rq *http.Request) {
|
||||||
@@ -56,15 +66,31 @@ func handlerMedia(w http.ResponseWriter, rq *http.Request) {
|
|||||||
hyphaName = util.HyphaNameFromRq(rq, "media")
|
hyphaName = util.HyphaNameFromRq(rq, "media")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
lc = l18n.FromRequest(rq)
|
isMedia = false
|
||||||
|
|
||||||
|
mime string
|
||||||
|
fileSize int64
|
||||||
)
|
)
|
||||||
util.HTTP200Page(w,
|
switch h := h.(type) {
|
||||||
viewutil.Base(
|
case *hyphae.MediaHypha:
|
||||||
viewutil.MetaFrom(w, rq),
|
isMedia = true
|
||||||
lc.Get("ui.media_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName)}),
|
mime = mimetype.FromExtension(path.Ext(h.MediaFilePath()))
|
||||||
views2.MediaMenu(rq, h, u),
|
|
||||||
map[string]string{},
|
fileinfo, err := os.Stat(h.MediaFilePath())
|
||||||
))
|
if err != nil {
|
||||||
|
slog.Error("failed to stat media file", "err", err)
|
||||||
|
// no return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileSize = fileinfo.Size()
|
||||||
|
}
|
||||||
|
_ = pageMedia.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{
|
||||||
|
"HyphaName": h.CanonicalName(),
|
||||||
|
"U": u,
|
||||||
|
"IsMediaHypha": isMedia,
|
||||||
|
"MimeType": mime,
|
||||||
|
"FileSize": fileSize,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerRevisionText sends Mycomarkup text of the hypha at the given revision. See also: handlerRevision, handlerText.
|
// handlerRevisionText sends Mycomarkup text of the hypha at the given revision. See also: handlerRevision, handlerText.
|
||||||
@@ -129,7 +155,7 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
|||||||
var (
|
var (
|
||||||
hyphaName = util.CanonicalName(slug)
|
hyphaName = util.CanonicalName(slug)
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
contents = fmt.Sprintf(`<p>%s</p>`, lc.Get("ui.revision_no_text"))
|
contents = template.HTML(fmt.Sprintf(`<p>%s</p>`, lc.Get("ui.revision_no_text")))
|
||||||
textContents string
|
textContents string
|
||||||
err error
|
err error
|
||||||
mycoFilePath string
|
mycoFilePath string
|
||||||
@@ -143,26 +169,17 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
|||||||
textContents, err = history.FileAtRevision(mycoFilePath, revHash)
|
textContents, err = history.FileAtRevision(mycoFilePath, revHash)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ctx, _ := mycocontext.ContextFromStringInput(textContents, mycoopts.MarkupOptions(hyphaName))
|
ctx, _ := mycocontext.ContextFromStringInput(textContents, mycoopts.MarkupOptions(hyphaName))
|
||||||
contents = mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx))
|
contents = template.HTML(mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx)))
|
||||||
}
|
}
|
||||||
|
|
||||||
page := views2.Revision(
|
meta := viewutil.MetaFrom(w, rq)
|
||||||
viewutil.MetaFrom(w, rq),
|
_ = pageRevision.RenderTo(meta, map[string]any{
|
||||||
h,
|
"ViewScripts": cfg.ViewScripts,
|
||||||
contents,
|
"Contents": contents,
|
||||||
revHash,
|
"RevHash": revHash,
|
||||||
)
|
"NaviTitle": hypview.NaviTitle(meta, h.CanonicalName()),
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
"HyphaName": h.CanonicalName(),
|
||||||
w.WriteHeader(http.StatusOK)
|
})
|
||||||
_, _ = fmt.Fprint(
|
|
||||||
w,
|
|
||||||
viewutil.Base(
|
|
||||||
viewutil.MetaFrom(w, rq),
|
|
||||||
lc.Get("ui.revision_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName), "rev": revHash}),
|
|
||||||
page,
|
|
||||||
map[string]string{},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerText serves raw source text of the hypha.
|
// handlerText serves raw source text of the hypha.
|
||||||
@@ -182,8 +199,7 @@ func handlerBinary(w http.ResponseWriter, rq *http.Request) {
|
|||||||
util.PrepareRq(rq)
|
util.PrepareRq(rq)
|
||||||
hyphaName := util.HyphaNameFromRq(rq, "binary")
|
hyphaName := util.HyphaNameFromRq(rq, "binary")
|
||||||
switch h := hyphae.ByName(hyphaName).(type) {
|
switch h := hyphae.ByName(hyphaName).(type) {
|
||||||
case *hyphae.EmptyHypha:
|
case *hyphae.EmptyHypha, *hyphae.TextualHypha:
|
||||||
case *hyphae.TextualHypha:
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
log.Printf("Textual hypha ‘%s’ has no media, cannot serve\n", h.CanonicalName())
|
log.Printf("Textual hypha ‘%s’ has no media, cannot serve\n", h.CanonicalName())
|
||||||
case *hyphae.MediaHypha:
|
case *hyphae.MediaHypha:
|
||||||
@@ -197,44 +213,80 @@ func handlerBinary(w http.ResponseWriter, rq *http.Request) {
|
|||||||
func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
||||||
util.PrepareRq(rq)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = util.HyphaNameFromRq(rq, "page", "hypha")
|
hyphaName = util.HyphaNameFromRq(rq, "page", "hypha")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
contents string
|
contents template.HTML
|
||||||
openGraph string
|
openGraph template.HTML
|
||||||
lc = l18n.FromRequest(rq)
|
lc = l18n.FromRequest(rq)
|
||||||
|
meta = viewutil.MetaFrom(w, rq)
|
||||||
|
subhyphae, prevHyphaName, nextHyphaName = tree.Tree(h.CanonicalName())
|
||||||
|
cats = categories.CategoriesWithHypha(h.CanonicalName())
|
||||||
|
category_list = ":" + strings.Join(cats, ":") + ":"
|
||||||
|
isMyProfile = cfg.UseAuth && util.IsProfileName(h.CanonicalName()) && meta.U.Name == strings.TrimPrefix(h.CanonicalName(), cfg.UserHypha+"/")
|
||||||
|
|
||||||
|
data = map[string]any{
|
||||||
|
"HyphaName": h.CanonicalName(),
|
||||||
|
"SubhyphaeHTML": subhyphae,
|
||||||
|
"PrevHyphaName": prevHyphaName,
|
||||||
|
"NextHyphaName": nextHyphaName,
|
||||||
|
"IsMyProfile": isMyProfile,
|
||||||
|
"NaviTitle": hypview.NaviTitle(meta, h.CanonicalName()),
|
||||||
|
"BacklinkCount": backlinks.BacklinksCount(h.CanonicalName()),
|
||||||
|
"GivenPermissionToModify": user.CanProceed(rq, "edit"),
|
||||||
|
"Categories": cats,
|
||||||
|
"IsMediaHypha": false,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
slog.Info("reading hypha", "name", h.CanonicalName(), "can edit", data["GivenPermissionToModify"])
|
||||||
|
meta.BodyAttributes = map[string]string{
|
||||||
|
"cats": category_list,
|
||||||
|
}
|
||||||
|
|
||||||
switch h := h.(type) {
|
switch h := h.(type) {
|
||||||
case *hyphae.EmptyHypha:
|
case *hyphae.EmptyHypha:
|
||||||
util.HTTP404Page(w,
|
w.WriteHeader(http.StatusNotFound)
|
||||||
viewutil.Base(
|
data["Contents"] = ""
|
||||||
viewutil.MetaFrom(w, rq),
|
_ = pageHypha.RenderTo(meta, data)
|
||||||
util.BeautifulName(hyphaName),
|
|
||||||
views2.Hypha(viewutil.MetaFrom(w, rq), h, contents),
|
|
||||||
map[string]string{},
|
|
||||||
openGraph))
|
|
||||||
case hyphae.ExistingHypha:
|
case hyphae.ExistingHypha:
|
||||||
fileContentsT, errT := os.ReadFile(h.TextFilePath())
|
fileContentsT, err := os.ReadFile(h.TextFilePath())
|
||||||
if errT == nil {
|
if err == nil {
|
||||||
ctx, _ := mycocontext.ContextFromStringInput(string(fileContentsT), mycoopts.MarkupOptions(hyphaName))
|
ctx, _ := mycocontext.ContextFromStringInput(string(fileContentsT), mycoopts.MarkupOptions(hyphaName))
|
||||||
getOpenGraph, descVisitor, imgVisitor := tools.OpenGraphVisitors(ctx)
|
getOpenGraph, descVisitor, imgVisitor := tools.OpenGraphVisitors(ctx)
|
||||||
|
openGraph = template.HTML(getOpenGraph())
|
||||||
ast := mycomarkup.BlockTree(ctx, descVisitor, imgVisitor)
|
ast := mycomarkup.BlockTree(ctx, descVisitor, imgVisitor)
|
||||||
contents = mycomarkup.BlocksToHTML(ctx, ast)
|
contents = template.HTML(mycomarkup.BlocksToHTML(ctx, ast))
|
||||||
openGraph = getOpenGraph()
|
|
||||||
}
|
}
|
||||||
switch h := h.(type) {
|
switch h := h.(type) {
|
||||||
case *hyphae.MediaHypha:
|
case *hyphae.MediaHypha:
|
||||||
contents = mycoopts.Media(h, lc) + contents
|
contents = template.HTML(mycoopts.Media(h, lc)) + contents
|
||||||
|
data["IsMediaHypha"] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
category_list := ":" + strings.Join(categories.CategoriesWithHypha(h.CanonicalName()), ":") + ":"
|
data["Contents"] = contents
|
||||||
|
meta.HeadElements = append(meta.HeadElements, openGraph)
|
||||||
|
_ = pageHypha.RenderTo(meta, data)
|
||||||
|
|
||||||
util.HTTP200Page(w,
|
// TODO: check head cats
|
||||||
viewutil.Base(
|
// TODO: check opengraph
|
||||||
viewutil.MetaFrom(w, rq),
|
|
||||||
util.BeautifulName(hyphaName),
|
|
||||||
views2.Hypha(viewutil.MetaFrom(w, rq), h, contents),
|
|
||||||
map[string]string{"cats": category_list},
|
|
||||||
openGraph))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlerBacklinks lists all backlinks to a hypha.
|
||||||
|
func handlerBacklinks(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
hyphaName := util.HyphaNameFromRq(rq, "backlinks")
|
||||||
|
|
||||||
|
_ = pageBacklinks.RenderTo(viewutil.MetaFrom(w, rq),
|
||||||
|
map[string]any{
|
||||||
|
"Addr": "/backlinks/" + hyphaName,
|
||||||
|
"HyphaName": hyphaName,
|
||||||
|
"Backlinks": backlinks.BacklinksFor(hyphaName),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlerOrphans(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
_ = pageOrphans.RenderTo(viewutil.MetaFrom(w, rq),
|
||||||
|
map[string]any{
|
||||||
|
"Addr": "/orphans",
|
||||||
|
"Orphans": backlinks.Orphans(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -354,7 +354,7 @@ kbd {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border: none;
|
border: none;
|
||||||
background: url(/static/icon/x.svg) no-repeat 8px 8px / 16px 16px;
|
background: url(/web/static/icon/x.svg) no-repeat 8px 8px / 16px 16px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
Before Width: | Height: | Size: 531 B After Width: | Height: | Size: 531 B |
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 477 B |
Before Width: | Height: | Size: 955 B After Width: | Height: | Size: 955 B |
Before Width: | Height: | Size: 636 B After Width: | Height: | Size: 636 B |
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 270 B |
Before Width: | Height: | Size: 888 B After Width: | Height: | Size: 888 B |
Before Width: | Height: | Size: 139 B After Width: | Height: | Size: 139 B |