diff options
-rw-r--r-- | init.sql | 2 | ||||
-rw-r--r-- | models/feed/feed.go | 54 | ||||
-rw-r--r-- | models/item/item.go | 22 | ||||
-rw-r--r-- | reset.sql | 2 | ||||
-rw-r--r-- | static/ui.html | 11 | ||||
-rw-r--r-- | static/ui.js | 27 | ||||
-rw-r--r-- | web/web.go | 26 |
7 files changed, 108 insertions, 36 deletions
@@ -6,6 +6,7 @@ CREATE TABLE feed ( url VARCHAR(255) NOT NULL, web_url VARCHAR(255) NOT NULL DEFAULT "", title VARCHAR(255) NOT NULL DEFAULT "", + category VARCHAR(255) NOT NULL DEFAULT "", last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY (url), PRIMARY KEY (id) @@ -27,4 +28,3 @@ CREATE TABLE item ( INDEX (publish_date), PRIMARY KEY (id) ); - diff --git a/models/feed/feed.go b/models/feed/feed.go index 3d2473b..17f344f 100644 --- a/models/feed/feed.go +++ b/models/feed/feed.go @@ -8,16 +8,21 @@ import ( ) type Feed struct { - Id int64 `json:"_id" xml:"-"` - Url string `json:"url" xml:"xmlUrl,attr"` - WebUrl string `json:"web_url" xml:"htmlUrl,attr"` - Title string `json:"title" xml:"text,attr"` + Id int64 `json:"_id" xml:"-"` + Url string `json:"url" xml:"xmlUrl,attr"` + WebUrl string `json:"web_url" xml:"htmlUrl,attr"` + Title string `json:"title" xml:"text,attr"` + Category string `json:"category"` // for OPML output purposes XMLName string `json:"-" xml:"outline"` Type string `json:"-" xml:"type,attr"` } +type Category struct { + Title string `json:"title"` +} + func NewFeed(url string) error { url = ResolveFeedURL(url) stmt, err := models.DB.Prepare("INSERT INTO feed(url) VALUES(?)") @@ -37,7 +42,8 @@ func All() ([]*Feed, error) { func filter(where string) ([]*Feed, error) { // todo: add back in title - rows, err := models.DB.Query(`SELECT id, url, web_url, title + rows, err := models.DB.Query(`SELECT + id, url, web_url, title, category FROM feed ` + where) if err != nil { return nil, err @@ -47,7 +53,7 @@ func filter(where string) ([]*Feed, error) { feeds := make([]*Feed, 0) for rows.Next() { f := new(Feed) - err := rows.Scan(&f.Id, &f.Url, &f.WebUrl, &f.Title) + err := rows.Scan(&f.Id, &f.Url, &f.WebUrl, &f.Title, &f.Category) f.Type = "rss" if err != nil { return nil, err @@ -74,23 +80,23 @@ func (f *Feed) Update() { } models.DB.Query(`UPDATE feed - SET title=?, url=?, web_url=? - WHERE id=?`, f.Title, f.Url, f.WebUrl, f.Id) + SET title=?, url=?, web_url=?, category=? + WHERE id=?`, f.Title, f.Url, f.WebUrl, f.Category, f.Id) } func (f *Feed) Delete() { log.Println("lets delete some shiteeee") _, err := models.DB.Exec(`DELETE FROM feed - WHERE id=?`, f.Id) + WHERE id=?`, f.Id) if err != nil { log.Println(err) } } func (f *Feed) ByUrl(url string) error { - err := models.DB.QueryRow(`SELECT id, url, title + err := models.DB.QueryRow(`SELECT id, url, title, category FROM feed - WHERE url = ?`, url).Scan(&f.Id, &f.Url, &f.Title) + WHERE url = ?`, url).Scan(&f.Id, &f.Url, &f.Title, &f.Category) if err != nil { return err } @@ -167,3 +173,29 @@ func ResolveFeedURL(url string) string { } return f } + +func Categories() ([]*Category, error) { + rows, err := models.DB.Query(`SELECT + DISTINCT category + FROM feed + WHERE category!="" + ORDER BY lower(category) ASC`) + if err != nil { + return nil, err + } + defer rows.Close() + + categories := make([]*Category, 0) + for rows.Next() { + c := new(Category) + err := rows.Scan(&c.Title) + if err != nil { + return nil, err + } + categories = append(categories, c) + } + if err = rows.Err(); err != nil { + return nil, err + } + return categories, nil +} diff --git a/models/item/item.go b/models/item/item.go index c5ffff9..b3dc5eb 100644 --- a/models/item/item.go +++ b/models/item/item.go @@ -19,9 +19,10 @@ type Item struct { Description string `json:"description"` PublishDate string `json:"publish_date"` - FeedId int64 - FeedTitle string `json:"feed_title"` - FeedUrl string `json:"feed_url"` + FeedId int64 + FeedTitle string `json:"feed_title"` + FeedUrl string `json:"feed_url"` + FeedCategory string `json:"feed_category"` ReadState bool `json:"read"` Starred bool `json:"starred"` @@ -112,16 +113,16 @@ func (i *Item) GetFullContent() { } } -func Filter(max_id int64, feed_id int64, unread_only bool, starred_only bool) ([]*Item, error) { +func Filter(max_id int64, feed_id int64, category string, unread_only bool, starred_only bool) ([]*Item, error) { var args []interface{} query := `SELECT item.id, item.title, item.url, item.description, item.read_state, item.starred, item.publish_date, item.full_content, item.header_image, - feed.url, feed.title - FROM item,feed - WHERE item.feed_id=feed.id ` + feed.url, feed.title, feed.category + FROM item,feed + WHERE item.feed_id=feed.id ` if max_id != 0 { query = query + "AND item.id < ? " @@ -133,6 +134,11 @@ func Filter(max_id int64, feed_id int64, unread_only bool, starred_only bool) ([ args = append(args, feed_id) } + if category != "" { + query = query + " AND feed.category=? " + args = append(args, category) + } + if unread_only { query = query + " AND item.read_state=0 " } @@ -157,7 +163,7 @@ func Filter(max_id int64, feed_id int64, unread_only bool, starred_only bool) ([ items := make([]*Item, 0) for rows.Next() { i := new(Item) - err := rows.Scan(&i.Id, &i.Title, &i.Url, &i.Description, &i.ReadState, &i.Starred, &i.PublishDate, &i.FullContent, &i.HeaderImage, &i.FeedUrl, &i.FeedTitle) + err := rows.Scan(&i.Id, &i.Title, &i.Url, &i.Description, &i.ReadState, &i.Starred, &i.PublishDate, &i.FullContent, &i.HeaderImage, &i.FeedUrl, &i.FeedTitle, &i.FeedCategory) if err != nil { log.Println(err) return nil, err @@ -1,2 +1,2 @@ -DROP TABLE feed; DROP TABLE item; +DROP TABLE feed; diff --git a/static/ui.html b/static/ui.html index 2e002b9..a7bd3a4 100644 --- a/static/ui.html +++ b/static/ui.html @@ -20,6 +20,10 @@ <div id="controls"></div> + <h4 onclick="$('#tags').toggle();">Tags</h4> + <ul id="tags" style="display: none;"> + </ul> + <h4 onclick="$('#feeds').toggle();">Feeds</h4> <ul id="feeds" style="display: none;"> <li><a href="/import/">import</a></li> @@ -39,6 +43,7 @@ </h2> <p class="dateline" style="clear: both;"> <a href="${item.feed_url}">${item.feed_title}</a> | <a href="${item.url}">${item.p_url}</a> + | ${item.feed_category} </p> {{if item.header_image}} <div class="img"><img src="${item.header_image}" /></div> @@ -53,7 +58,9 @@ </script> <script id="tag_template" type="text/jqtmp"> - ${tag.name} ${tag.unread} + {{if tag.selected}}[{{/if}} + ${tag.title} + {{if tag.selected}}]{{/if}} </script> <script id="feed_template" type="text/jqtmp"> @@ -69,8 +76,6 @@ </script> <script id="controls_template" type="text/jqtmp"> - - <ul> <li> <a {{if app.unreadFilter}}style="font-weight: bold;"{{/if}} diff --git a/static/ui.js b/static/ui.js index d76602e..49f6ad0 100644 --- a/static/ui.js +++ b/static/ui.js @@ -21,7 +21,7 @@ var AppModel = Backbone.Model.extend({ boot: function() { this.items.boot(); -// this.tags.boot(); + this.tags.boot(); this.feeds.fetch({set: true, remove: false}) window.setInterval(function() { App.update_read_status() }, 5000); }, @@ -239,9 +239,6 @@ var ItemCollection = Backbone.Collection.extend({ App.loading = true; url = '/stream/'; - if(App.tag != undefined) { - url = url + 'tag/' + App.tag + '/'; - } url=url+'?foo=bar' if(App.get('searchFilter')) { url = url + '&q=' + App.get('searchFilter'); @@ -251,7 +248,10 @@ var ItemCollection = Backbone.Collection.extend({ } if(App.get('starredFilter')) { url = url + '&starred=1'; - } + } + if(App.tag != undefined) { + url = url + '&tag=' + App.tag; + } if(App.items.last()) { url = url + '&max_id=' + App.items.last().get('_id'); } @@ -375,7 +375,7 @@ var TagCollection = Backbone.Collection.extend({ boot: function() { var t = this; - $.getJSON('/tags/', function(data) { + $.getJSON('/tag/', function(data) { $.each(data, function(i,v) { var tag = new Tag(v); t.add(tag); @@ -399,13 +399,20 @@ var TagView = Backbone.View.extend({ render: function() { var h = $.tmpl(templates.tag_template, { 'tag': this.model.toJSON() }); $(this.el).html(h); - if(this.model.get('unread')) { - $(this.el).addClass('hasunread'); - } return this; }, filterTo: function() { - App.tag = this.model.get('name'); + App.tag = null; + if (this.model.get('selected')) { + this.model.set('selected', false); + } + else { + App.tags.models.forEach ( function (tag) { + tag.set('selected', false); + }); + this.model.set('selected', true); + App.tag = this.model.get('title'); + } App.items.reboot(); } }); @@ -2,9 +2,9 @@ package web import ( "adammathes.com/neko/config" + "adammathes.com/neko/crawler" "adammathes.com/neko/models/feed" "adammathes.com/neko/models/item" - "adammathes.com/neko/crawler" "encoding/json" "fmt" "golang.org/x/crypto/bcrypt" @@ -33,6 +33,11 @@ func streamHandler(w http.ResponseWriter, r *http.Request) { feed_id = f.Id } + category := "" + if r.FormValue("tag") != "" { + category = r.FormValue("tag") + } + unread_only := true if r.FormValue("read_filter") != "" { unread_only = false @@ -44,7 +49,7 @@ func streamHandler(w http.ResponseWriter, r *http.Request) { } var items []*item.Item - items, err := item.Filter(int64(max_id), feed_id, unread_only, starred_only) + items, err := item.Filter(int64(max_id), feed_id, category, unread_only, starred_only) if err != nil { log.Println(err) } @@ -107,6 +112,22 @@ func feedHandler(w http.ResponseWriter, r *http.Request) { } } +func categoryHandler(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + categories, err := feed.Categories() + if err != nil { + log.Println(err) + } + + js, err := json.Marshal(categories) + if err != nil { + log.Println(err) + } + w.Write(js) + return + } +} + var AuthCookie = "auth" var SecondsInAYear = 60 * 60 * 24 * 365 @@ -164,6 +185,7 @@ func Serve() { http.HandleFunc("/stream/", AuthWrap(streamHandler)) http.HandleFunc("/item/", AuthWrap(itemHandler)) http.HandleFunc("/feed/", AuthWrap(feedHandler)) + http.HandleFunc("/tag/", AuthWrap(categoryHandler)) http.HandleFunc("/login/", loginHandler) http.HandleFunc("/logout/", logoutHandler) |