diff --git a/server/handlers.go b/server/handlers.go index a5b32ec..c690ac1 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -5,16 +5,15 @@ import ( "github.com/mmcdole/gofeed" "net/http" "encoding/json" + "encoding/xml" "os" "log" "io" - "io/ioutil" "mime" "strings" "path/filepath" "strconv" "math" - "fmt" ) func IndexHandler(rw http.ResponseWriter, req *http.Request) { @@ -351,6 +350,33 @@ func SettingsHandler(rw http.ResponseWriter, req *http.Request) { } } +type opml struct { + XMLName xml.Name `xml:"opml"` + Version string `xml:"version,attr"` + Outlines []outline `xml:"body>outline"` +} + +type outline struct { + Type string `xml:"type,attr,omitempty"` + Title string `xml:"text,attr"` + FeedURL string `xml:"xmlUrl,attr,omitempty"` + SiteURL string `xml:"htmlUrl,attr,omitempty"` + Description string `xml:"description,attr,omitempty"` + Outlines []outline `xml:"outline,omitempty"` +} + +func (o outline) AllFeeds() []outline { + result := make([]outline, 0) + for _, sub := range o.Outlines { + if sub.Type == "rss" { + result = append(result, sub) + } else { + result = append(result, sub.AllFeeds()...) + } + } + return result +} + func OPMLImportHandler(rw http.ResponseWriter, req *http.Request) { if req.Method == "POST" { file, _, err := req.FormFile("opml") @@ -358,12 +384,25 @@ func OPMLImportHandler(rw http.ResponseWriter, req *http.Request) { log.Print(err) return } - content, err := ioutil.ReadAll(file) + feeds := new(opml) + decoder := xml.NewDecoder(file) + decoder.Entity = xml.HTMLEntity + decoder.Strict = false + err = decoder.Decode(&feeds) if err != nil { log.Print(err) return } - fmt.Println(string(content)) + for _, outline := range feeds.Outlines { + if outline.Type == "rss" { + db(req).CreateFeed(outline.Title, outline.Description, outline.SiteURL, outline.FeedURL, "", nil) + } else { + folder := db(req).CreateFolder(outline.Title) + for _, o := range outline.AllFeeds() { + db(req).CreateFeed(o.Title, o.Description, o.SiteURL, o.FeedURL, "", &folder.Id) + } + } + } } else { rw.WriteHeader(http.StatusMethodNotAllowed) } diff --git a/storage/folder.go b/storage/folder.go index 71bbbda..0a042c4 100644 --- a/storage/folder.go +++ b/storage/folder.go @@ -13,17 +13,34 @@ type Folder struct { func (s *Storage) CreateFolder(title string) *Folder { expanded := true result, err := s.db.Exec(` - insert into folders (title, is_expanded) values (?, ?)`, + insert into folders (title, is_expanded) values (?, ?) + on conflict (title) do nothing`, title, expanded, ) if err != nil { fmt.Println(err) return nil } - id, idErr := result.LastInsertId() - if idErr != nil { + + var id int64 + numrows, err := result.RowsAffected() + if err != nil { + s.log.Print(err) return nil } + if numrows == 1 { + id, err = result.LastInsertId() + if err != nil { + s.log.Print(err) + return nil + } + } else { + err = s.db.QueryRow(`select id, is_expanded from folders where title=?`, title).Scan(&id, &expanded) + if err != nil { + s.log.Print(err) + return nil + } + } return &Folder{Id: id, Title: title, IsExpanded: expanded} } diff --git a/storage/storage.go b/storage/storage.go index 3218036..bac9594 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -14,6 +14,8 @@ create table if not exists folders ( is_expanded boolean not null default false ); +create unique index if not exists idx_folder_title on folders(title); + create table if not exists feeds ( id integer primary key autoincrement, folder_id references folders(id), @@ -63,12 +65,10 @@ func New() (*Storage, error) { if err != nil { return nil, err } - /* _, err = db.Exec(initQuery) if err != nil { return nil, err } - */ logger := log.New(os.Stdout, "storage: ", log.Ldate | log.Ltime | log.Lshortfile) return &Storage{db: db, log: logger}, nil } diff --git a/template/static/javascripts/app.js b/template/static/javascripts/app.js index ec273d9..bea0536 100644 --- a/template/static/javascripts/app.js +++ b/template/static/javascripts/app.js @@ -241,10 +241,12 @@ var vm = new Vue({ api.items.update(item.id, {status: item.status}) }, importOPML: function(event) { + var vm = this var input = event.target var form = document.querySelector('#opml-import-form') api.upload_opml(form).then(function() { input.value = '' + vm.refreshFeeds() }) }, }