mirror of https://github.com/perkeep/perkeep.git
publish: show title, members, include javascript blob metadata
start of awesomeness. Change-Id: I45d63b04fa223b9aafceb9452b179e4ddc52bce5
This commit is contained in:
parent
66c0e430d0
commit
6179e508e3
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package search
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"http"
|
||||
"log"
|
||||
|
@ -242,7 +243,20 @@ type DescribeRequest struct {
|
|||
wg *sync.WaitGroup // for load requests
|
||||
}
|
||||
|
||||
// Given a blobref string returns a Description or nil.
|
||||
// dr may be nil itself.
|
||||
func (dr *DescribeRequest) DescribedBlobStr(blobstr string) *DescribedBlob {
|
||||
if dr == nil {
|
||||
return nil
|
||||
}
|
||||
dr.lk.Lock()
|
||||
defer dr.lk.Unlock()
|
||||
return dr.m[blobstr]
|
||||
}
|
||||
|
||||
type DescribedBlob struct {
|
||||
Request *DescribeRequest
|
||||
|
||||
BlobRef *blobref.BlobRef
|
||||
MimeType string
|
||||
CamliType string
|
||||
|
@ -254,6 +268,63 @@ type DescribedBlob struct {
|
|||
|
||||
// if camliType "file"
|
||||
File *FileInfo
|
||||
|
||||
Stub bool // if not loaded, but referenced
|
||||
}
|
||||
|
||||
func (b *DescribedBlob) Title() string {
|
||||
if b == nil {
|
||||
return ""
|
||||
}
|
||||
if b.Permanode != nil {
|
||||
if t := b.Permanode.Attr.Get("title"); t != "" {
|
||||
return t
|
||||
}
|
||||
if contentRef := b.Permanode.Attr.Get("camliContent"); contentRef != "" {
|
||||
return b.Request.DescribedBlobStr(contentRef).Title()
|
||||
}
|
||||
}
|
||||
if b.File != nil {
|
||||
return b.File.FileName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *DescribedBlob) Description() string {
|
||||
if b == nil {
|
||||
return ""
|
||||
}
|
||||
if b.Permanode != nil {
|
||||
return b.Permanode.Attr.Get("description")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *DescribedBlob) Members() []*DescribedBlob {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
m := make([]*DescribedBlob, 0)
|
||||
if b.Permanode != nil {
|
||||
for _, bstr := range b.Permanode.Attr["camliMember"] {
|
||||
if br := blobref.Parse(bstr); br != nil {
|
||||
m = append(m, b.peerBlob(br))
|
||||
}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (b *DescribedBlob) peerBlob(br *blobref.BlobRef) *DescribedBlob {
|
||||
if b.Request == nil {
|
||||
return &DescribedBlob{BlobRef: br, Stub: true}
|
||||
}
|
||||
b.Request.lk.Lock()
|
||||
defer b.Request.lk.Unlock()
|
||||
if peer, ok := b.Request.m[br.String()]; ok {
|
||||
return peer
|
||||
}
|
||||
return &DescribedBlob{Request: b.Request, BlobRef: br, Stub: true}
|
||||
}
|
||||
|
||||
func (b *DescribedBlob) jsonMap() map[string]interface{} {
|
||||
|
@ -276,7 +347,7 @@ func (b *DescribedBlob) jsonMap() map[string]interface{} {
|
|||
}
|
||||
|
||||
type DescribedPermanode struct {
|
||||
Attr map[string][]string
|
||||
Attr http.Values // a map[string][]string
|
||||
}
|
||||
|
||||
func (dp *DescribedPermanode) jsonMap() map[string]interface{} {
|
||||
|
@ -308,23 +379,26 @@ func (sh *Handler) NewDescribeRequest() *DescribeRequest {
|
|||
|
||||
type DescribeError map[string]os.Error
|
||||
|
||||
func (e DescribeError) String() string {
|
||||
return "one or more errors describing blobs"
|
||||
func (de DescribeError) String() string {
|
||||
var buf bytes.Buffer
|
||||
for b, err := range de {
|
||||
fmt.Fprintf(&buf, "%s: %v; ", b, err)
|
||||
}
|
||||
return fmt.Sprintf("Errors (%d) describing blobs: %s", len(de), buf.String())
|
||||
}
|
||||
|
||||
// Result waits for all outstanding lookups to complete and
|
||||
// returns the map of blobref (strings) to their described
|
||||
// results. The returned error is non-nil if any errors
|
||||
// occured.
|
||||
func (dr *DescribeRequest) Result() (map[string]*DescribedBlob, DescribeError) {
|
||||
// occured, and will be of type DescribeError.
|
||||
func (dr *DescribeRequest) Result() (desmap map[string]*DescribedBlob, err os.Error) {
|
||||
dr.wg.Wait()
|
||||
// TODO: set "done" / locked flag, so no more DescribeBlob can
|
||||
// be called.
|
||||
var err DescribeError
|
||||
if len(dr.errs) > 0 {
|
||||
err = DescribeError(dr.errs)
|
||||
return dr.m, DescribeError(dr.errs)
|
||||
}
|
||||
return dr.m, err
|
||||
return dr.m, nil
|
||||
}
|
||||
|
||||
// PopulateJSON waits for all outstanding lookups to complete and populates
|
||||
|
@ -350,7 +424,7 @@ func (dr *DescribeRequest) describedBlob(b *blobref.BlobRef) *DescribedBlob {
|
|||
if des, ok := dr.m[bs]; ok {
|
||||
return des
|
||||
}
|
||||
des := &DescribedBlob{BlobRef: b}
|
||||
des := &DescribedBlob{Request: dr, BlobRef: b}
|
||||
dr.m[bs] = des
|
||||
return des
|
||||
}
|
||||
|
@ -463,7 +537,7 @@ func (sh *Handler) serveFiles(rw http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
func (dr *DescribeRequest) populatePermanodeFields(pi *DescribedPermanode, pn, signer *blobref.BlobRef, depth int) {
|
||||
pi.Attr = make(map[string][]string)
|
||||
pi.Attr = make(http.Values)
|
||||
attr := pi.Attr
|
||||
|
||||
claims, err := dr.sh.index.GetOwnerClaims(pn, signer)
|
||||
|
|
|
@ -111,24 +111,62 @@ func (pub *PublishHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(rw, "I am publish handler at base %q, serving root %q (permanode=%s), suffix %q<hr>",
|
||||
base, pub.RootName, pn, html.EscapeString(suffix))
|
||||
if req.FormValue("debug") == "1" {
|
||||
fmt.Fprintf(rw, "I am publish handler at base %q, serving root %q (permanode=%s), suffix %q<hr>",
|
||||
base, pub.RootName, pn, html.EscapeString(suffix))
|
||||
}
|
||||
path, err := pub.Search.Index().PathLookup(pub.Search.Owner(), pn, suffix, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(rw, "<b>Error:</b> %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(rw, "<p><b>Target:</b> <a href='/ui/?p=%s'>%s</a></p>", path.Target, path.Target)
|
||||
if req.FormValue("debug") == "1" {
|
||||
fmt.Fprintf(rw, "<p><b>Target:</b> <a href='/ui/?p=%s'>%s</a></p>", path.Target, path.Target)
|
||||
return
|
||||
}
|
||||
|
||||
dr := pub.Search.NewDescribeRequest()
|
||||
dr.Describe(path.Target, 3)
|
||||
res, err := dr.Result()
|
||||
if err != nil {
|
||||
log.Printf("Errors loading %s, permanode %s: %v, %#v", req.URL, path.Target, err, err)
|
||||
fmt.Fprintf(rw, "<p>Errors loading.</p>")
|
||||
return
|
||||
}
|
||||
|
||||
subject := res[path.Target.String()]
|
||||
title := subject.Title()
|
||||
|
||||
// HTML header + Javascript
|
||||
{
|
||||
jm := make(map[string]interface{})
|
||||
dr.PopulateJSON(jm)
|
||||
fmt.Fprintf(rw, "<pre>")
|
||||
fmt.Fprintf(rw, "<html>\n<head>\n <title>%s</title>\n <script>\nvar camliPageMeta = \n",
|
||||
html.EscapeString(title))
|
||||
json, _ := json.MarshalIndent(jm, "", " ")
|
||||
rw.Write(json)
|
||||
fmt.Fprintf(rw, "</pre>")
|
||||
fmt.Fprintf(rw, ";\n </script>\n</head>\n<body>\n")
|
||||
defer fmt.Fprintf(rw, "</body>\n</html>\n")
|
||||
}
|
||||
|
||||
if title != "" {
|
||||
fmt.Fprintf(rw, "<h1>%s</h1>\n", html.EscapeString(title))
|
||||
}
|
||||
|
||||
if members := subject.Members(); len(members) > 0 {
|
||||
fmt.Fprintf(rw, "<ul>\n")
|
||||
for _, member := range members {
|
||||
des := member.Description()
|
||||
if des != "" {
|
||||
des = " - " + des
|
||||
}
|
||||
link := "#"
|
||||
fmt.Fprintf(rw, " <li><a href='%s'>%s</a>%s</li>\n",
|
||||
link,
|
||||
html.EscapeString(member.Title()),
|
||||
des)
|
||||
}
|
||||
fmt.Fprintf(rw, "</ul>\n")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,6 +197,6 @@ func (pub *PublishHandler) bootstrapPermanode(jsonSign *JSONSignHandler) (err os
|
|||
|
||||
pn := signUpload("permanode", schema.NewUnsignedPermanode())
|
||||
signUpload("set-attr camliRoot", schema.NewSetAttributeClaim(pn, "camliRoot", pub.RootName))
|
||||
signUpload("set-attr title", schema.NewSetAttributeClaim(pn, "title", "Publish root node for " + pub.RootName))
|
||||
signUpload("set-attr title", schema.NewSetAttributeClaim(pn, "title", "Publish root node for "+pub.RootName))
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue