diff options
| -rw-r--r-- | exporter/exporter.go | 23 | ||||
| -rw-r--r-- | main.go | 16 | ||||
| -rw-r--r-- | static/ui.html | 10 | ||||
| -rw-r--r-- | web/rice-box.go | 8 | ||||
| -rw-r--r-- | web/web.go | 9 | 
5 files changed, 43 insertions, 23 deletions
diff --git a/exporter/exporter.go b/exporter/exporter.go index 04ced0b..013377d 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -2,37 +2,38 @@ package exporter  import (  	"adammathes.com/neko/models/feed" +	"bytes"  	"encoding/json"  	"encoding/xml"  	"fmt"  	"html/template" -	"os"  ) -func ExportFeeds(format string) { +func ExportFeeds(format string) string {  	feeds, err := feed.All()  	if err != nil {  		panic(err)  	} +	s := ""  	switch format {  	case "text":  		for _, f := range feeds { -			fmt.Printf("%s\n", f.Url) +			s = s + fmt.Sprintf("%s\n", f.Url)  		}  	case "opml": -		fmt.Printf(`<opml version="2.0"><head><title>neko feeds</title></head><body>`) -		fmt.Printf("\n") +		s = s + fmt.Sprintf(`<opml version="2.0"><head><title>neko feeds</title></head><body>`) +		s = s + fmt.Sprintf("\n")  		for _, f := range feeds {  			b, _ := xml.Marshal(f) -			fmt.Printf("%s\n", string(b)) +			s = s + fmt.Sprintf("%s\n", string(b))  		} -		fmt.Printf(`</body></opml>`) +		s = s + fmt.Sprintf(`</body></opml>`)  	case "json":  		js, _ := json.Marshal(feeds) -		fmt.Printf("%s\n", js) +		s = fmt.Sprintf("%s\n", js)  	case "html":  		htmlTemplateString := `<html> @@ -48,10 +49,14 @@ func ExportFeeds(format string) {  </ul>  </body>  </html>` +		var bts bytes.Buffer  		htmlTemplate, err := template.New("feeds").Parse(htmlTemplateString) -		err = htmlTemplate.Execute(os.Stdout, feeds) +		err = htmlTemplate.Execute(&bts, feeds)  		if err != nil {  			panic(err)  		} +		s = bts.String()  	} + +	return s  } @@ -8,6 +8,7 @@ import (  	"adammathes.com/neko/models/feed"  	"adammathes.com/neko/vlog"  	"adammathes.com/neko/web" +	"fmt"  	flag "github.com/ogier/pflag"  ) @@ -26,10 +27,7 @@ func main() {  	flag.StringVarP(&dbfile, "db", "d", "neko.db", "sqlite database file")  	flag.IntVarP(&port, "http", "s", 4994, "HTTP port to serve on")  	flag.BoolVarP(&proxyImages, "imageproxy", "i", false, "rewrite and proxy all image requests for privacy (experimental)") - -	// verbose output by default unless you know what you're doing -	// this is probably wrong but keeping it this way for debugging -	flag.BoolVarP(&verbose, "verbose", "v", true, "verbose output") +	flag.BoolVarP(&verbose, "verbose", "v", false, "verbose output")  	// passwords on command line are bad  	flag.StringVarP(&password, "password", "p", "", "password to access web interface") @@ -60,14 +58,14 @@ func main() {  		return  	}  	if export != "" { -		vlog.Printf("feed export\n") -		exporter.ExportFeeds(export) +		vlog.Printf("exporting feeds in format %s\n", export) +		fmt.Printf("%s", exporter.ExportFeeds(export))  		return  	} -	if password == "" { -		panic("Please specify an access password\n") -	} +	//	if password == "" { +	//		panic("Please specify an access password\n") +	//	}  	vlog.Printf("starting web server at 127.0.0.1:%d\n",  		config.Config.Port)  	web.Serve() diff --git a/static/ui.html b/static/ui.html index bf533df..84d6629 100644 --- a/static/ui.html +++ b/static/ui.html @@ -27,8 +27,16 @@          <h4 onclick="$('#feeds').toggle();">Feeds</h4>                  <ul id="feeds" style="display: none;">          </ul> -        </div> +        <h4 onclick="$('#export').toggle();">Export</h4> +        <ul id="export" style="display: none;"> +            <li><a href="/export/opml">opml</a></li> +            <li><a href="/export/text">text</a></li> +            <li><a href="/export/json">json</a></li> +        </ul> + +      </div> +              </div>     <div id="c"> diff --git a/web/rice-box.go b/web/rice-box.go index bb8b8b5..7a52f0e 100644 --- a/web/rice-box.go +++ b/web/rice-box.go @@ -60,8 +60,8 @@ func init() {  	}  	filec := &embedded.EmbeddedFile{  		Filename:    "ui.html", -		FileModTime: time.Unix(1529169644, 0), -		Content:     string("<!DOCTYPE html>\n<html>\n  <head>\n    <title>neko rss mode</title>\n    <link rel=\"stylesheet\" href=\"/static/style.css\" />\n    <script src=\"/static/jquery-3.3.1.min.js\"></script>\n    <script src=\"/static/jquery.tmpl.min.js\"></script>\n    <script src=\"/static/underscore-1.8.3.min.js\"></script>\n    <script src=\"/static/backbone-1.3.3.min.js\"></script>\n    <script>\n      PUBLIC_VERSION = false;\n    </script>\n    <script src=\"/static/ui.js\"></script>\n    <meta name=\"viewport\" content=\"width=device-width,height=device-height, initial-scale=1, maximum-scale=1\" />\n    <base target=\"_blank\">\n  </head>\n  <body>\n      <h1 class=\"logo\" onclick=\"$('#filters').toggleClass('hidden');\">🐱</h1>\n\n      <div id=\"filters\">\n\n        <div id=\"controls\"></div>\n        <h4 onclick=\"$('#tags').toggle();\">Tags</h4>        \n        <ul id=\"tags\" style=\"display: none;\">\n        </ul>\n        \n        <h4 onclick=\"$('#feeds').toggle();\">Feeds</h4>        \n        <ul id=\"feeds\" style=\"display: none;\">\n        </ul>\n        </div>\n\n    </div>\n\n   <div id=\"c\">\n      <div id=\"items\">\n      </div>      \n    </div>\n    \n    <script id=\"item_template\" type=\"text/jqtmp\">\n      <h2><a class=\"i\" id=\"i_${item_id}\" href=\"${item.url}\">${item.title }</a> \n    <span class={{if item.starred}}\"unstar\"{{else}}\"star\"{{/if}}>★</span>\n    </h2>\n      <p class=\"dateline\" style=\"clear: both;\">\n        <a href=\"${item.feed_url}\">${item.feed_title}</a> | <a href=\"${item.url}\">${item.p_url}</a>\n    | ${item.feed_category} |\n    <span class=\"full\">{{if item.full}}hide{{else}}scrape{{/if}} full text</span>\n    \n    </p>\n      {{if item.header_image}}\n      <div class=\"img\"><img src=\"${item.header_image}\" /></div>\n      {{/if}}\n      <div class=\"description\">\n        {{if item.full}}\n           {{html item.full_content}}\n        {{else}}\n           {{html item.description}}\n        {{/if}}\n      </div>\n    </script>\n\n    <script id=\"tag_template\" type=\"text/jqtmp\">\n      {{if tag.selected}}<b>{{/if}}\n      ${tag.title}\n      {{if tag.selected}}</b>{{/if}}\n    </script>\n\n    <script id=\"feed_template\" type=\"text/jqtmp\">\n      {{if feed.selected}}<b>{{/if}}\n      <span class=\"txt\">\n      {{if feed.title}}\n      ${feed.title}\n      {{else}}\n      ${feed.url}\n      {{/if}}\n\n      </span>\n      <span class=\"edit\">[e]</span>\n      <span class=\"delete\">[x]</span>\n      {{if feed.selected}}</b>{{/if}}\n    </script>\n\n    <script id=\"controls_template\" type=\"text/jqtmp\">\n      <ul>\n        <li>\n          <a {{if app.unreadFilter}}style=\"font-weight: bold;\"{{/if}} \n             class=\"unread_filter\">unread</a>\n        </li>\n        <li>\n          <a \n             {{if app.allFilter}}style=\"font-weight: bold;\"{{/if}} \n             class=\"all_filter\">all</a> \n        </li>\n        <li>\n         <a {{if app.starredFilter}}style=\"font-weight: bold;\"{{/if}}\n            class=\"starred_filter\">★ starred</a>\n        </li>\n        <li>\n          <button class=\"new_feed\"> + new </button>\n        </li>\n        <li>\n           <input id=\"search\" type=\"search\" /><button class=\"search_go\">search</button>\n        </li>\n\t\t<li>\n\t\t</li>\n\n\t  </ul>\n   </script>  \n   \n</body>\n</html>  \n"), +		FileModTime: time.Unix(1530741472, 0), +		Content:     string("<!DOCTYPE html>\n<html>\n  <head>\n    <title>neko rss mode</title>\n    <link rel=\"stylesheet\" href=\"/static/style.css\" />\n    <script src=\"/static/jquery-3.3.1.min.js\"></script>\n    <script src=\"/static/jquery.tmpl.min.js\"></script>\n    <script src=\"/static/underscore-1.8.3.min.js\"></script>\n    <script src=\"/static/backbone-1.3.3.min.js\"></script>\n    <script>\n      PUBLIC_VERSION = false;\n    </script>\n    <script src=\"/static/ui.js\"></script>\n    <meta name=\"viewport\" content=\"width=device-width,height=device-height, initial-scale=1, maximum-scale=1\" />\n    <base target=\"_blank\">\n  </head>\n  <body>\n      <h1 class=\"logo\" onclick=\"$('#filters').toggleClass('hidden');\">🐱</h1>\n\n      <div id=\"filters\">\n\n        <div id=\"controls\"></div>\n        <h4 onclick=\"$('#tags').toggle();\">Tags</h4>        \n        <ul id=\"tags\" style=\"display: none;\">\n        </ul>\n        \n        <h4 onclick=\"$('#feeds').toggle();\">Feeds</h4>        \n        <ul id=\"feeds\" style=\"display: none;\">\n        </ul>\n\n        <h4 onclick=\"$('#export').toggle();\">Export</h4>\n        <ul id=\"export\" style=\"display: none;\">\n            <li><a href=\"/export/opml\">opml</a></li>\n            <li><a href=\"/export/text\">text</a></li>\n            <li><a href=\"/export/json\">json</a></li>\n        </ul>\n\n      </div>\n        \n    </div>\n\n   <div id=\"c\">\n      <div id=\"items\">\n      </div>      \n    </div>\n    \n    <script id=\"item_template\" type=\"text/jqtmp\">\n      <h2><a class=\"i\" id=\"i_${item_id}\" href=\"${item.url}\">${item.title }</a> \n    <span class={{if item.starred}}\"unstar\"{{else}}\"star\"{{/if}}>★</span>\n    </h2>\n      <p class=\"dateline\" style=\"clear: both;\">\n        <a href=\"${item.feed_url}\">${item.feed_title}</a> | <a href=\"${item.url}\">${item.p_url}</a>\n    | ${item.feed_category} |\n    <span class=\"full\">{{if item.full}}hide{{else}}scrape{{/if}} full text</span>\n    \n    </p>\n      {{if item.header_image}}\n      <div class=\"img\"><img src=\"${item.header_image}\" /></div>\n      {{/if}}\n      <div class=\"description\">\n        {{if item.full}}\n           {{html item.full_content}}\n        {{else}}\n           {{html item.description}}\n        {{/if}}\n      </div>\n    </script>\n\n    <script id=\"tag_template\" type=\"text/jqtmp\">\n      {{if tag.selected}}<b>{{/if}}\n      ${tag.title}\n      {{if tag.selected}}</b>{{/if}}\n    </script>\n\n    <script id=\"feed_template\" type=\"text/jqtmp\">\n      {{if feed.selected}}<b>{{/if}}\n      <span class=\"txt\">\n      {{if feed.title}}\n      ${feed.title}\n      {{else}}\n      ${feed.url}\n      {{/if}}\n\n      </span>\n      <span class=\"edit\">[e]</span>\n      <span class=\"delete\">[x]</span>\n      {{if feed.selected}}</b>{{/if}}\n    </script>\n\n    <script id=\"controls_template\" type=\"text/jqtmp\">\n      <ul>\n        <li>\n          <a {{if app.unreadFilter}}style=\"font-weight: bold;\"{{/if}} \n             class=\"unread_filter\">unread</a>\n        </li>\n        <li>\n          <a \n             {{if app.allFilter}}style=\"font-weight: bold;\"{{/if}} \n             class=\"all_filter\">all</a> \n        </li>\n        <li>\n         <a {{if app.starredFilter}}style=\"font-weight: bold;\"{{/if}}\n            class=\"starred_filter\">★ starred</a>\n        </li>\n        <li>\n          <button class=\"new_feed\"> + new </button>\n        </li>\n        <li>\n           <input id=\"search\" type=\"search\" /><button class=\"search_go\">search</button>\n        </li>\n\t\t<li>\n\t\t</li>\n\n\t  </ul>\n   </script>  \n   \n</body>\n</html>  \n"),  	}  	filed := &embedded.EmbeddedFile{  		Filename:    "ui.js", @@ -82,7 +82,7 @@ func init() {  	// define dirs  	dir1 := &embedded.EmbeddedDir{  		Filename:   "", -		DirModTime: time.Unix(1529169644, 0), +		DirModTime: time.Unix(1530741472, 0),  		ChildFiles: []*embedded.EmbeddedFile{  			file2, // ".DS_Store"  			file3, // "backbone-1.3.3.min.js" @@ -108,7 +108,7 @@ func init() {  	// register embeddedBox  	embedded.RegisterEmbeddedBox(`../static`, &embedded.EmbeddedBox{  		Name: `../static`, -		Time: time.Unix(1529169644, 0), +		Time: time.Unix(1530741472, 0),  		Dirs: map[string]*embedded.EmbeddedDir{  			"": dir1,  		}, @@ -3,6 +3,7 @@ package web  import (  	"adammathes.com/neko/config"  	"adammathes.com/neko/crawler" +	"adammathes.com/neko/exporter"  	"adammathes.com/neko/models/feed"  	"adammathes.com/neko/models/item"  	"encoding/base64" @@ -193,6 +194,13 @@ func imageProxyHandler(w http.ResponseWriter, r *http.Request) {  	return  } +func exportHandler(w http.ResponseWriter, r *http.Request) { +	format := r.URL.String() +	w.Header().Set("content-type", "text/plain") +	w.Write([]byte(exporter.ExportFeeds(format))) +	return +} +  func fullTextHandler(w http.ResponseWriter, r *http.Request) {  	fmt.Printf("request: %v\n\n", r) @@ -289,6 +297,7 @@ func Serve() {  	http.HandleFunc("/feed/", AuthWrap(feedHandler))  	http.HandleFunc("/tag/", AuthWrap(categoryHandler))  	http.Handle("/image/", http.StripPrefix("/image/", AuthWrap(imageProxyHandler))) +	http.Handle("/export/", http.StripPrefix("/export/", AuthWrap(exportHandler)))  	http.HandleFunc("/login/", loginHandler)  	http.HandleFunc("/logout/", logoutHandler)  | 
