🚧 Reorganize some packages
This commit is contained in:
parent
a509c797ba
commit
b1beb17e0e
|
@ -0,0 +1,72 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-ap/activitypub"
|
||||
|
||||
"source.toby3d.me/toby3d/home/internal/common"
|
||||
"source.toby3d.me/toby3d/home/internal/domain"
|
||||
)
|
||||
|
||||
type (
|
||||
Handler struct{}
|
||||
|
||||
/* TODO(toby3d)
|
||||
Profile struct {
|
||||
*activitypub.Person
|
||||
|
||||
// Mastodon related
|
||||
|
||||
// Pinned posts. See [Featured collection].
|
||||
//
|
||||
// [Featured collection]: https://docs.joinmastodon.org/spec/activitypub/#featured
|
||||
Featured []interface{} `json:"featured"`
|
||||
|
||||
// Required for Move activity.
|
||||
AlsoKnownAs []string `json:"alsoKnownAs"`
|
||||
|
||||
// Will be shown as a locked account.
|
||||
ManuallyApprovesFollowers bool `json:"manuallyApprovesFollowers"`
|
||||
|
||||
// Will be shown in the profile directory.
|
||||
// See [Discoverability flag].
|
||||
//
|
||||
// [Discoverability flag]: https://docs.joinmastodon.org/spec/activitypub/#discoverable
|
||||
Discoverable bool `json:"discoverable"`
|
||||
}
|
||||
*/
|
||||
)
|
||||
|
||||
func NewHandler() *Handler {
|
||||
return &Handler{}
|
||||
}
|
||||
|
||||
func (Handler) HandleProfile(site *domain.Site) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
langRef := activitypub.LangRef(site.DefaultLanguage.Lang())
|
||||
person := activitypub.PersonNew(activitypub.IRI(site.BaseURL.String()))
|
||||
person.URL = person.ID
|
||||
person.Name.Add(activitypub.LangRefValueNew(langRef, "Maxim Lebedev"))
|
||||
person.Summary.Add(activitypub.LangRefValueNew(langRef, "Creative dude from russia"))
|
||||
person.PreferredUsername.Add(activitypub.LangRefValueNew(langRef, "toby3d"))
|
||||
person.Published = time.Date(2009, time.February, 0, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
w.Header().Set(common.HeaderContentType, common.MIMEApplicationActivityJSONCharsetUTF8)
|
||||
_ = json.NewEncoder(w).Encode(person)
|
||||
})
|
||||
}
|
||||
|
||||
func (Handler) HandleEntry(site *domain.Site, entry *domain.Entry) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
resp := activitypub.ObjectNew(activitypub.NoteType)
|
||||
resp.ID = activitypub.ID(site.BaseURL.JoinPath(entry.File.Path()).String())
|
||||
resp.Content.Add(activitypub.LangRefValueNew(activitypub.LangRef(entry.Language.Lang()),
|
||||
string(entry.Content)))
|
||||
|
||||
w.Header().Set(common.HeaderContentType, common.MIMEApplicationActivityJSONCharsetUTF8)
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
})
|
||||
}
|
|
@ -12,9 +12,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
activitypubhttpdelivery "source.toby3d.me/toby3d/home/internal/activitypub/delivery/http"
|
||||
"source.toby3d.me/toby3d/home/internal/common"
|
||||
"source.toby3d.me/toby3d/home/internal/domain"
|
||||
entryhttpdelivery "source.toby3d.me/toby3d/home/internal/entry/delivery/http"
|
||||
pagefsrepo "source.toby3d.me/toby3d/home/internal/entry/repository/fs"
|
||||
pageucase "source.toby3d.me/toby3d/home/internal/entry/usecase"
|
||||
"source.toby3d.me/toby3d/home/internal/middleware"
|
||||
|
@ -28,6 +28,7 @@ import (
|
|||
statichttpdelivery "source.toby3d.me/toby3d/home/internal/static/delivery/http"
|
||||
staticfsrepo "source.toby3d.me/toby3d/home/internal/static/repository/fs"
|
||||
staticucase "source.toby3d.me/toby3d/home/internal/static/usecase"
|
||||
themehttpdelivery "source.toby3d.me/toby3d/home/internal/theme/delivery/http"
|
||||
themefsrepo "source.toby3d.me/toby3d/home/internal/theme/repository/fs"
|
||||
themeucase "source.toby3d.me/toby3d/home/internal/theme/usecase"
|
||||
"source.toby3d.me/toby3d/home/internal/urlutil"
|
||||
|
@ -64,7 +65,8 @@ func NewApp(logger *log.Logger, config *domain.Config) (*App, error) {
|
|||
resourceHandler := resourcehttpdelivery.NewHandler(resourcer, contentDir)
|
||||
staticHandler := statichttpdelivery.NewHandler(staticer)
|
||||
siteHandler := sitehttpdelivery.NewHandler(siter)
|
||||
entryHandler := entryhttpdelivery.NewHandler(themer)
|
||||
themeHandler := themehttpdelivery.NewHandler(themer)
|
||||
activityPubHadnler := activitypubhttpdelivery.NewHandler()
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// NOTE(toby3d): any file in $HOME_STATIC_DIR is public and
|
||||
// unprotected by design, so it's safe to search it first before
|
||||
|
@ -97,10 +99,11 @@ func NewApp(logger *log.Logger, config *domain.Config) (*App, error) {
|
|||
return
|
||||
}
|
||||
|
||||
mediaType, _, _ := mime.ParseMediaType(r.Header.Get(common.HeaderAccept))
|
||||
|
||||
if handler != nil {
|
||||
mediaType, _, _ := mime.ParseMediaType(r.Header.Get(common.HeaderAccept))
|
||||
if strings.EqualFold(mediaType, common.MIMEApplicationLdJSON) {
|
||||
entryHandler.ActivityPubHandler.HandleProfile(site).ServeHTTP(w, r)
|
||||
activityPubHadnler.HandleProfile(site).ServeHTTP(w, r)
|
||||
} else {
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
@ -125,7 +128,12 @@ func NewApp(logger *log.Logger, config *domain.Config) (*App, error) {
|
|||
return
|
||||
}
|
||||
|
||||
entryHandler.Handle(site, entry).ServeHTTP(w, r)
|
||||
switch strings.ToLower(mediaType) {
|
||||
default:
|
||||
themeHandler.Handle(site, entry).ServeHTTP(w, r)
|
||||
case common.MIMEApplicationLdJSON:
|
||||
activityPubHadnler.HandleEntry(site, entry).ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
chain := middleware.Chain{
|
||||
// middleware.LogFmt(),
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-ap/activitypub"
|
||||
|
||||
"source.toby3d.me/toby3d/home/internal/common"
|
||||
"source.toby3d.me/toby3d/home/internal/domain"
|
||||
"source.toby3d.me/toby3d/home/internal/theme"
|
||||
)
|
||||
|
||||
type (
|
||||
Handler struct {
|
||||
*ActivityPubHandler
|
||||
*TemplateHandler
|
||||
}
|
||||
|
||||
ActivityPubHandler struct{}
|
||||
|
||||
TemplateHandler struct {
|
||||
themes theme.UseCase
|
||||
}
|
||||
|
||||
/* TODO(toby3d)
|
||||
Profile struct {
|
||||
*activitypub.Person
|
||||
|
||||
// Mastodon related
|
||||
|
||||
// Pinned posts. See [Featured collection].
|
||||
//
|
||||
// [Featured collection]: https://docs.joinmastodon.org/spec/activitypub/#featured
|
||||
Featured []interface{} `json:"featured"`
|
||||
|
||||
// Required for Move activity.
|
||||
AlsoKnownAs []string `json:"alsoKnownAs"`
|
||||
|
||||
// Will be shown as a locked account.
|
||||
ManuallyApprovesFollowers bool `json:"manuallyApprovesFollowers"`
|
||||
|
||||
// Will be shown in the profile directory.
|
||||
// See [Discoverability flag].
|
||||
//
|
||||
// [Discoverability flag]: https://docs.joinmastodon.org/spec/activitypub/#discoverable
|
||||
Discoverable bool `json:"discoverable"`
|
||||
}
|
||||
*/
|
||||
)
|
||||
|
||||
func NewHandler(themes theme.UseCase) *Handler {
|
||||
return &Handler{
|
||||
ActivityPubHandler: NewActivityPubHandler(),
|
||||
TemplateHandler: NewTemplateHandler(themes),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Handle(site *domain.Site, entry *domain.Entry) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mediaType, _, _ := mime.ParseMediaType(r.Header.Get(common.HeaderAccept))
|
||||
|
||||
switch strings.ToLower(mediaType) {
|
||||
default:
|
||||
h.TemplateHandler.Handle(site, entry).ServeHTTP(w, r)
|
||||
case common.MIMEApplicationLdJSON:
|
||||
h.ActivityPubHandler.Handle(site, entry).ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func NewTemplateHandler(themes theme.UseCase) *TemplateHandler {
|
||||
return &TemplateHandler{themes: themes}
|
||||
}
|
||||
|
||||
func (h *TemplateHandler) Handle(site *domain.Site, entry *domain.Entry) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO(toby3d): handle home page.
|
||||
// TODO(toby3d): handle sections.
|
||||
// TODO(toby3d): handle errors.
|
||||
|
||||
// NOTE(toby3d): wrap founded entry into theme template and
|
||||
// answer to client.
|
||||
contentLanguage := make([]string, len(entry.Translations))
|
||||
for i := range entry.Translations {
|
||||
contentLanguage[i] = entry.Translations[i].Language.Code()
|
||||
}
|
||||
|
||||
w.Header().Set(common.HeaderContentLanguage, strings.Join(contentLanguage, ", "))
|
||||
|
||||
template, err := h.themes.Do(r.Context(), site, entry)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set(common.HeaderContentType, common.MIMETextHTMLCharsetUTF8)
|
||||
if err = template(w); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func NewActivityPubHandler() *ActivityPubHandler {
|
||||
return &ActivityPubHandler{}
|
||||
}
|
||||
|
||||
func (ActivityPubHandler) HandleProfile(site *domain.Site) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set(common.HeaderContentType, common.MIMEApplicationActivityJSONCharsetUTF8)
|
||||
encoder := json.NewEncoder(w)
|
||||
|
||||
langRef := activitypub.LangRef(site.DefaultLanguage.Lang())
|
||||
person := activitypub.PersonNew(activitypub.IRI(site.BaseURL.String()))
|
||||
person.URL = person.ID
|
||||
person.Name.Add(activitypub.LangRefValueNew(langRef, "Maxim Lebedev"))
|
||||
person.Summary.Add(activitypub.LangRefValueNew(langRef, "Creative dude from russia"))
|
||||
person.PreferredUsername.Add(activitypub.LangRefValueNew(langRef, "toby3d"))
|
||||
person.Published = time.Date(2009, time.February, 0, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
_ = encoder.Encode(person)
|
||||
})
|
||||
}
|
||||
|
||||
func (ActivityPubHandler) Handle(site *domain.Site, entry *domain.Entry) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set(common.HeaderContentType, common.MIMEApplicationActivityJSONCharsetUTF8)
|
||||
encoder := json.NewEncoder(w)
|
||||
|
||||
resp := activitypub.ObjectNew(activitypub.NoteType)
|
||||
resp.ID = activitypub.ID(site.BaseURL.JoinPath(entry.File.Path()).String())
|
||||
resp.Content.Add(activitypub.LangRefValueNew(activitypub.NilLangRef, string(entry.Content)))
|
||||
|
||||
_ = encoder.Encode(resp)
|
||||
})
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package http_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"source.toby3d.me/toby3d/home/internal/common"
|
||||
"source.toby3d.me/toby3d/home/internal/domain"
|
||||
delivery "source.toby3d.me/toby3d/home/internal/entry/delivery/http"
|
||||
entryucase "source.toby3d.me/toby3d/home/internal/entry/usecase"
|
||||
siteucase "source.toby3d.me/toby3d/home/internal/site/usecase"
|
||||
"source.toby3d.me/toby3d/home/internal/testutil"
|
||||
)
|
||||
|
||||
func TestHandler_ServeHTTP(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testEntry := domain.TestEntry(t)
|
||||
|
||||
for name, path := range map[string]string{
|
||||
"person": "/",
|
||||
"note": testEntry.File.Path(),
|
||||
} {
|
||||
name, path := name, path
|
||||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "https://example.com/"+path, nil)
|
||||
req.Header.Set(common.HeaderAccept, common.MIMEApplicationLdJSONProfile)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
delivery.NewHandler(
|
||||
siteucase.NewStubSiteUseCase(domain.TestSite(t), nil),
|
||||
entryucase.NewStubEntryUseCase(domain.TestEntry(t), nil),
|
||||
).ServeHTTP(w, req)
|
||||
testutil.GoldenEqual(t, w.Result().Body)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{"id":"https://example.com/en/sample-page","type":"Note","content":"Hello, world!"}
|
|
@ -1 +0,0 @@
|
|||
{"id":"https://example.com/","type":"Person","name":"Maxim Lebedev","summary":"Creative dude from russia","url":"https://example.com/","published":"2009-01-31T00:00:00Z","preferredUsername":"toby3d"}
|
|
@ -0,0 +1,73 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"sync"
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/adrg/frontmatter"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"source.toby3d.me/toby3d/home/internal/domain"
|
||||
"source.toby3d.me/toby3d/home/internal/entry"
|
||||
)
|
||||
|
||||
type (
|
||||
Page struct {
|
||||
Title string `yaml:"title"`
|
||||
Description string `yaml:"description"`
|
||||
Params map[string]any `yaml:",inline"`
|
||||
Content []byte `yaml:"-"`
|
||||
}
|
||||
|
||||
memoryEntryRepository struct {
|
||||
mutex *sync.RWMutex
|
||||
files fstest.MapFS
|
||||
}
|
||||
)
|
||||
|
||||
var FrontMatterFormats = []*frontmatter.Format{
|
||||
frontmatter.NewFormat(`---`, `---`, yaml.Unmarshal),
|
||||
}
|
||||
|
||||
func (repo *memoryEntryRepository) Get(_ context.Context, lang domain.Language, p string) (*domain.Entry, error) {
|
||||
f, err := repo.files.Open(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get entry from memory: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data := &Page{
|
||||
Params: make(map[string]any),
|
||||
}
|
||||
if data.Content, err = frontmatter.Parse(f, data, FrontMatterFormats...); err != nil {
|
||||
return nil, fmt.Errorf("cannot parse entry content as FrontMatter: %w", err)
|
||||
}
|
||||
|
||||
return &domain.Entry{
|
||||
File: domain.NewPath(p),
|
||||
Language: lang,
|
||||
Title: data.Title,
|
||||
Content: data.Content,
|
||||
Description: data.Description,
|
||||
Params: data.Params,
|
||||
Resources: make([]*domain.Resource, 0),
|
||||
Translations: make([]*domain.Entry, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (repo *memoryEntryRepository) Stat(_ context.Context, l domain.Language, p string) (bool, error) {
|
||||
_, err := fs.Stat(repo.files, p)
|
||||
|
||||
return errors.Is(err, fs.ErrExist), nil
|
||||
}
|
||||
|
||||
func NewMemoryEntryRepository(files fstest.MapFS) entry.Repository {
|
||||
return &memoryEntryRepository{
|
||||
mutex: new(sync.RWMutex),
|
||||
files: files,
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"sync"
|
||||
"testing/fstest"
|
||||
|
||||
"source.toby3d.me/toby3d/home/internal/domain"
|
||||
"source.toby3d.me/toby3d/home/internal/entry"
|
||||
)
|
||||
|
||||
type memoryEntryRepository struct {
|
||||
mutex *sync.RWMutex
|
||||
files fstest.MapFS
|
||||
}
|
||||
|
||||
func (repo *memoryEntryRepository) Get(_ context.Context, _ domain.Language, p string) (*domain.Page, error) {
|
||||
f, err := repo.files.Open(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get entry from memory: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (repo *memoryEntryRepository) Stat(_ context.Context, l domain.Language, p string) (bool, error) {
|
||||
_, err := fs.Stat(repo.files, p)
|
||||
|
||||
return errors.Is(err, fs.ErrExist), nil
|
||||
}
|
||||
|
||||
func NewMemoryEntryRepository(files fstest.MapFS) entry.Repository {
|
||||
return &memoryEntryRepository{
|
||||
mutex: new(sync.RWMutex),
|
||||
files: files,
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ func TestDo(t *testing.T) {
|
|||
|
||||
site := domain.TestSite(t)
|
||||
|
||||
actual, err := usecase.NewServerUseCase().Do(context.Background(), *site)
|
||||
actual, err := usecase.NewServerUseCase().Do(context.Background(), site)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"source.toby3d.me/toby3d/home/internal/common"
|
||||
"source.toby3d.me/toby3d/home/internal/domain"
|
||||
"source.toby3d.me/toby3d/home/internal/theme"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
themes theme.UseCase
|
||||
}
|
||||
|
||||
func NewHandler(themes theme.UseCase) *Handler {
|
||||
return &Handler{
|
||||
themes: themes,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Handle(site *domain.Site, entry *domain.Entry) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO(toby3d): handle home page.
|
||||
// TODO(toby3d): handle sections.
|
||||
// TODO(toby3d): handle errors.
|
||||
|
||||
// NOTE(toby3d): wrap founded entry into theme template and
|
||||
// answer to client.
|
||||
contentLanguage := make([]string, len(entry.Translations))
|
||||
for i := range entry.Translations {
|
||||
contentLanguage[i] = entry.Translations[i].Language.Code()
|
||||
}
|
||||
|
||||
w.Header().Set(common.HeaderContentLanguage, strings.Join(contentLanguage, ", "))
|
||||
|
||||
template, err := h.themes.Do(r.Context(), site, entry)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set(common.HeaderContentType, common.MIMETextHTMLCharsetUTF8)
|
||||
if err = template(w); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -43,7 +43,7 @@ func NewBaseOf(site *domain.Site) BaseOf {
|
|||
{% endfunc %}
|
||||
|
||||
{% func (b BaseOf) Dir() %}
|
||||
{%s b.site.Language.Dir() %}
|
||||
{%s b.site.Language.Dir().String() %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func Template(p Pager) %}
|
||||
|
|
|
@ -206,7 +206,7 @@ func (b BaseOf) Lang() string {
|
|||
//line web/template/baseof.qtpl:45
|
||||
func (b BaseOf) StreamDir(qw422016 *qt422016.Writer) {
|
||||
//line web/template/baseof.qtpl:46
|
||||
qw422016.E().S(b.site.Language.Dir())
|
||||
qw422016.E().S(b.site.Language.Dir().String())
|
||||
//line web/template/baseof.qtpl:47
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
{% code
|
||||
type Page struct {
|
||||
BaseOf
|
||||
page *domain.Page
|
||||
page *domain.Entry
|
||||
}
|
||||
|
||||
func NewPage(base BaseOf, page *domain.Page) Page {
|
||||
func NewPage(base BaseOf, page *domain.Entry) Page {
|
||||
return Page{
|
||||
BaseOf: base,
|
||||
page: page,
|
||||
|
@ -35,7 +35,7 @@ func NewPage(base BaseOf, page *domain.Page) Page {
|
|||
|
||||
{% func (p Page) Dir() %}
|
||||
{% if p.page.Language != domain.LanguageUnd %}
|
||||
{%s p.page.Language.Dir() %}
|
||||
{%s p.page.Language.Dir().String() %}
|
||||
{% else %}
|
||||
{%= p.BaseOf.Lang() %}
|
||||
{% endif %}
|
||||
|
|
|
@ -25,10 +25,10 @@ var (
|
|||
//line web/template/page.qtpl:6
|
||||
type Page struct {
|
||||
BaseOf
|
||||
page *domain.Page
|
||||
page *domain.Entry
|
||||
}
|
||||
|
||||
func NewPage(base BaseOf, page *domain.Page) Page {
|
||||
func NewPage(base BaseOf, page *domain.Entry) Page {
|
||||
return Page{
|
||||
BaseOf: base,
|
||||
page: page,
|
||||
|
@ -122,7 +122,7 @@ func (p Page) StreamDir(qw422016 *qt422016.Writer) {
|
|||
//line web/template/page.qtpl:37
|
||||
if p.page.Language != domain.LanguageUnd {
|
||||
//line web/template/page.qtpl:38
|
||||
qw422016.E().S(p.page.Language.Dir())
|
||||
qw422016.E().S(p.page.Language.Dir().String())
|
||||
//line web/template/page.qtpl:39
|
||||
} else {
|
||||
//line web/template/page.qtpl:40
|
||||
|
|
Loading…
Reference in New Issue