func getFeedTags(user content.User, id data.FeedId) (resp responseError) { resp = newResponse() repo := user.Repo() tf := repo.TaggedFeed(user) if uf := user.FeedById(id); uf.HasErr() { resp.err = uf.Err() return } else { tf.Data(uf.Data()) } tags := tf.Tags() if tf.HasErr() { resp.err = tf.Err() return } t := make([]string, len(tags)) for _, tag := range tags { t = append(t, tag.String()) } resp.val["Tags"] = t return }
func setFeedTags(user content.User, id data.FeedId, tagValues []data.TagValue) (resp responseError) { resp = newResponse() feed := user.FeedById(id) if feed.HasErr() { resp.err = feed.Err() return } repo := user.Repo() tf := repo.TaggedFeed(user) tf.Data(feed.Data()) tags := make([]content.Tag, len(tagValues)) for i := range tagValues { tags[i] = repo.Tag(user) tags[i].Value(tagValues[i]) } tf.Tags(tags) if tf.UpdateTags(); tf.HasErr() { resp.err = tf.Err() return } resp.val["Success"] = true resp.val["Id"] = id return }
func createUserFeed(u content.User, d data.Feed) (uf content.UserFeed) { uf = repo.UserFeed(u) uf.Data(d) uf.Update() u.AddFeed(uf) return }
func createTaggedFeed(u content.User, d data.Feed) (tf content.TaggedFeed) { tf = repo.TaggedFeed(u) tf.Data(d) tf.Update() u.AddFeed(tf) return }
func getAuthData(user content.User) (resp responseError) { resp = newResponse() in := user.Data() resp.val["Auth"] = true resp.val["User"] = user resp.val["ProfileData"] = in.ProfileData return }
func addFeeds(user content.User, fm *readeef.FeedManager, links []string) (resp responseError) { resp = newResponse() var err error errs := make([]addFeedError, 0, len(links)) for _, link := range links { var u *url.URL if u, err = url.Parse(link); err != nil { resp.err = err errs = append(errs, addFeedError{Link: link, Error: "Error parsing link"}) continue } else if !u.IsAbs() { resp.err = errors.New("Feed has no link") errs = append(errs, addFeedError{Link: link, Error: resp.err.Error()}) continue } else { var f content.Feed if f, err = fm.AddFeedByLink(link); err != nil { resp.err = err errs = append(errs, addFeedError{Link: link, Error: "Error adding feed to the database"}) continue } uf := user.AddFeed(f) if uf.HasErr() { resp.err = f.Err() errs = append(errs, addFeedError{Link: link, Title: f.Data().Title, Error: "Error adding feed to the database"}) continue } tags := strings.SplitN(u.Fragment, ",", -1) if u.Fragment != "" && len(tags) > 0 { repo := uf.Repo() tf := repo.TaggedFeed(user) tf.Data(uf.Data()) t := make([]content.Tag, len(tags)) for i := range tags { t[i] = repo.Tag(user) t[i].Data(data.Tag{Value: data.TagValue(tags[i])}) } tf.Tags(t) if tf.UpdateTags(); tf.HasErr() { resp.err = tf.Err() errs = append(errs, addFeedError{Link: link, Title: f.Data().Title, Error: "Error adding feed to the database"}) continue } } } } resp.val["Errors"] = errs resp.val["Success"] = len(errs) < len(links) return }
func addFeed(user content.User, fm *readeef.FeedManager, links []string) (resp responseError) { resp = newResponse() success := false for _, link := range links { var u *url.URL if u, resp.err = url.Parse(link); resp.err != nil { /* TODO: non-fatal error */ return } else if !u.IsAbs() { /* TODO: non-fatal error */ resp.err = errors.New("Feed has no link") return } else { var f content.Feed if f, resp.err = fm.AddFeedByLink(link); resp.err != nil { return } uf := user.AddFeed(f) if uf.HasErr() { resp.err = f.Err() return } tags := strings.SplitN(u.Fragment, ",", -1) if u.Fragment != "" && len(tags) > 0 { repo := uf.Repo() tf := repo.TaggedFeed(user) tf.Data(uf.Data()) t := make([]content.Tag, len(tags)) for i := range tags { t[i] = repo.Tag(user) t[i].Value(data.TagValue(tags[i])) } tf.Tags(t) if tf.UpdateTags(); tf.HasErr() { resp.err = tf.Err() return } } success = true } } resp.val["Success"] = success return }
func listUsers(user content.User) (resp responseError) { resp = newResponse() if !user.Data().Admin { resp.err = errForbidden resp.errType = errTypeForbidden return } repo := user.Repo() resp.val["Users"], resp.err = repo.AllUsers(), repo.Err() return }
func readState(user content.User, id string, beforeId data.ArticleId, timestamp int64) (resp responseError) { resp = newResponse() var ar content.ArticleRepo o := data.ArticleUpdateStateOptions{} if timestamp > 0 { t := time.Unix(timestamp/1000, 0) o.BeforeDate = t } if beforeId > 0 { o.BeforeId = beforeId } switch { case id == "all": ar = user case id == "favorite": o.FavoriteOnly = true ar = user case strings.HasPrefix(id, "popular:"): // Can't bulk set state to popular articles case strings.HasPrefix(id, "tag:"): tag := user.Repo().Tag(user) tag.Data(data.Tag{Value: data.TagValue(id[4:])}) ar = tag default: var feedId int64 if feedId, resp.err = strconv.ParseInt(id, 10, 64); resp.err != nil { /* TODO: non-fatal error */ return } ar = user.FeedById(data.FeedId(feedId)) } if ar != nil { ar.ReadState(true, o) if e, ok := ar.(content.Error); ok && e.HasErr() { resp.err = e.Err() return } } resp.val["Success"] = true return }
func formatArticle(user content.User, id data.ArticleId, webfwConfig webfw.Config, readeefConfig readeef.Config) (resp responseError) { resp = newResponse() article := user.ArticleById(id) if user.HasErr() { resp.err = user.Err() return } formatting := article.Format(webfwConfig.Renderer.Dir, readeefConfig.ArticleFormatter.ReadabilityKey) if article.HasErr() { resp.err = user.Err() return } s := summarize.NewFromString(formatting.Title, readeef.StripTags(formatting.Content)) s.Language = formatting.Language keyPoints := s.KeyPoints() for i := range keyPoints { keyPoints[i] = html.UnescapeString(keyPoints[i]) } resp.val["KeyPoints"] = keyPoints resp.val["Content"] = formatting.Content resp.val["TopImage"] = formatting.TopImage resp.val["Id"] = id return }
func removeFeed(user content.User, fm *readeef.FeedManager, id data.FeedId) (resp responseError) { resp = newResponse() feed := user.FeedById(id) if feed.Delete(); feed.HasErr() { resp.err = feed.Err() return } fm.RemoveFeed(feed) resp.val["Success"] = true return }
func TestImplements(t *testing.T) { var article content.Article r := NewRepo(nil, nil) article = r.Article() article.Data() var userArticle content.UserArticle userArticle = r.UserArticle(nil) userArticle.Data() var scoredArticle content.ScoredArticle scoredArticle = r.ScoredArticle() scoredArticle.Data() var feed content.Feed feed = r.Feed() feed.Data() var userFeed content.UserFeed userFeed = r.UserFeed(nil) userFeed.Data() var taggedFeed content.TaggedFeed taggedFeed = r.TaggedFeed(nil) taggedFeed.Data() r.HasErr() var subscription content.Subscription subscription = r.Subscription() subscription.Data() var tag content.Tag tag = r.Tag(nil) tag.Value() var user content.User user = r.User() user.Data() }
func getAuthData(user content.User, sess context.Session, capabilities capabilities) (resp responseError) { resp = newResponse() if sess != nil { sess.Set(readeef.AuthNameKey, user.Data().Login) if err := sess.Write(nil); err != nil { resp.err = fmt.Errorf("Error writing session data: %v", err) } } resp.val["Auth"] = true resp.val["Capabilities"] = capabilities resp.val["User"] = user return }
func getArticles(u content.User, dbo *db.DB, logger webfw.Logger, opts data.ArticleQueryOptions, sorting content.ArticleSorting, join, where string, args []interface{}) (ua []content.UserArticle) { if u.HasErr() { return } var err error if getArticlesTemplate == nil { getArticlesTemplate, err = template.New("read-state-update-sql"). Parse(dbo.SQL().User.GetArticlesTemplate) if err != nil { u.Err(fmt.Errorf("Error generating get-articles-update template: %v", err)) return } } /* Much faster than using 'ORDER BY read' * TODO: potential overall improvement for fetching pages other than the * first by using the unread count and moving the offset based on it */ if opts.UnreadFirst && opts.Offset == 0 { originalUnreadOnly := opts.UnreadOnly opts.UnreadFirst = false opts.UnreadOnly = true ua = internalGetArticles(u, dbo, logger, opts, sorting, join, where, args) if !originalUnreadOnly && (opts.Limit == 0 || opts.Limit > len(ua)) { if opts.Limit > 0 { opts.Limit -= len(ua) } opts.UnreadOnly = false opts.ReadOnly = true readOnly := internalGetArticles(u, dbo, logger, opts, sorting, join, where, args) ua = append(ua, readOnly...) } return } return internalGetArticles(u, dbo, logger, opts, sorting, join, where, args) }
func addUser(user content.User, login data.Login, password string, secret []byte) (resp responseError) { resp = newResponse() resp.val["Login"] = login if !user.Data().Admin { resp.err = errForbidden resp.errType = errTypeForbidden return } repo := user.Repo() u := repo.UserByLogin(login) if !u.HasErr() { /* TODO: non-fatal error */ resp.err = errUserExists resp.errType = errTypeUserExists return } else { err := u.Err() if err != content.ErrNoContent { resp.err = err return } } resp.err = nil in := data.User{Login: login} u = repo.User() u.Data(in) u.Password(password, secret) u.Update() if resp.err = u.Err(); resp.err != nil { return } resp.val["Success"] = true return }
func getUserAttribute(user content.User, attr string) (resp responseError) { resp = newResponse() in := user.Data() resp.val["Login"] = in.Login switch attr { case "FirstName": resp.val[attr] = in.FirstName case "LastName": resp.val[attr] = in.LastName case "Email": resp.val[attr] = in.Email case "ProfileData": resp.val[attr] = in.ProfileData default: resp.err = errors.New("Error getting user attribute: unknown attribute " + attr) return } return }
func search(user content.User, searchIndex readeef.SearchIndex, query, highlight, feedId string) (resp responseError) { resp = newResponse() if strings.HasPrefix(feedId, "tag:") { tag := user.Repo().Tag(user) tag.Value(data.TagValue(feedId[4:])) tag.Highlight(highlight) resp.val["Articles"], resp.err = tag.Query(query, searchIndex.Index), tag.Err() } else { if id, err := strconv.ParseInt(feedId, 10, 64); err == nil { f := user.FeedById(data.FeedId(id)) resp.val["Articles"], resp.err = f.Query(query, searchIndex.Index), f.Err() } else { user.Highlight(highlight) resp.val["Articles"], resp.err = user.Query(query, searchIndex.Index), user.Err() } } return }
func markArticleAsFavorite(user content.User, id data.ArticleId, favorite bool) (resp responseError) { resp = newResponse() article := user.ArticleById(id) if user.HasErr() { resp.err = user.Err() return } in := article.Data() previouslyFavorite := in.Favorite if previouslyFavorite != favorite { article.Favorite(favorite) if article.HasErr() { resp.err = article.Err() return } } resp.val["Success"] = previouslyFavorite != favorite resp.val["Favorite"] = favorite resp.val["Id"] = in.Id return }
func removeUser(user content.User, login data.Login) (resp responseError) { resp = newResponse() resp.val["Login"] = login if !user.Data().Admin { resp.err = errForbidden resp.errType = errTypeForbidden return } if user.Data().Login == login { resp.err = errCurrentUser resp.errType = errTypeCurrentUser return } u := user.Repo().UserByLogin(login) u.Delete() if resp.err = u.Err(); resp.err != nil { return } resp.val["Success"] = true return }
func ttRssFeedListCategoryFeed(u content.User, f content.UserFeed, id data.FeedId, includeUnread bool) (c ttRssCategory) { c.BareId = id c.Id = "FEED:" + strconv.FormatInt(int64(id), 10) c.Type = "feed" copts := data.ArticleCountOptions{UnreadOnly: true} if f != nil { c.Name = f.Data().Title c.Unread = f.Count(copts) } else { c.Name = ttRssSpecialTitle(id) switch id { case TTRSS_FAVORITE_ID: copts.FavoriteOnly = true c.Unread = u.Count(copts) case TTRSS_FRESH_ID: copts.AfterDate = time.Now().Add(TTRSS_FRESH_DURATION) c.Unread = u.Count(copts) case TTRSS_ALL_ID: c.Unread = u.Count(copts) } } return }
func markArticleAsRead(user content.User, id data.ArticleId, read bool) (resp responseError) { resp = newResponse() article := user.ArticleById(id) if user.HasErr() { resp.err = user.Err() return } in := article.Data() previouslyRead := in.Read if previouslyRead != read { article.Read(read) if article.HasErr() { resp.err = article.Err() return } } resp.val["Success"] = previouslyRead != read resp.val["Read"] = read resp.val["Id"] = in.Id return }
func articleFavoriteState(user content.User, id data.ArticleId, favorite bool) (resp responseError) { resp = newResponse() article := user.ArticleById(id, data.ArticleQueryOptions{SkipProcessors: true}) if user.HasErr() { resp.err = user.Err() return } in := article.Data() previouslyFavorite := in.Favorite if previouslyFavorite != favorite { article.Favorite(favorite) if article.HasErr() { resp.err = article.Err() return } } resp.val["Success"] = previouslyFavorite != favorite resp.val["Favorite"] = favorite resp.val["Id"] = in.Id return }
func getGroups(user content.User) (g []feverGroup, fg []feverFeedsGroup, err error) { tags := user.Tags() if user.HasErr() { err = fmt.Errorf("Error getting user tags: %v", user.Err()) return } g = make([]feverGroup, len(tags)) fg = make([]feverFeedsGroup, len(tags)) for i := range tags { td := tags[i].Data() g[i] = feverGroup{Id: int64(td.Id), Title: string(td.Value)} feeds := tags[i].AllFeeds() if tags[i].HasErr() { err = fmt.Errorf("Error getting tag feeds: %v", tags[i].Err()) return } ids := make([]string, len(feeds)) for j := range feeds { ids[j] = strconv.FormatInt(int64(feeds[j].Data().Id), 10) } fg[i] = feverFeedsGroup{GroupId: int64(td.Id), FeedIds: strings.Join(ids, ",")} } return }
func articleReadState(user content.User, id data.ArticleId, read bool) (resp responseError) { resp = newResponse() article := user.ArticleById(id, data.ArticleQueryOptions{SkipProcessors: true}) if user.HasErr() { resp.err = user.Err() return } in := article.Data() previouslyRead := in.Read if previouslyRead != read { article.Read(read) if article.HasErr() { resp.err = article.Err() return } } resp.val["Success"] = previouslyRead != read resp.val["Read"] = read resp.val["Id"] = in.Id return }
func discoverFeeds(user content.User, fm *readeef.FeedManager, link string) (resp responseError) { resp = newResponse() if u, err := url.Parse(link); err != nil { resp.err = readeef.ErrNoAbsolute resp.errType = errTypeNoAbsolute return } else if !u.IsAbs() { u.Scheme = "http" if u.Host == "" { parts := strings.SplitN(u.Path, "/", 2) u.Host = parts[0] if len(parts) > 1 { u.Path = "/" + parts[1] } else { u.Path = "" } } link = u.String() } feeds, err := fm.DiscoverFeeds(link) if err != nil { resp.val["Feeds"] = []content.Feed{} return } uf := user.AllFeeds() if user.HasErr() { resp.err = user.Err() return } userFeedIdMap := make(map[data.FeedId]bool) userFeedLinkMap := make(map[string]bool) for i := range uf { in := uf[i].Data() userFeedIdMap[in.Id] = true userFeedLinkMap[in.Link] = true u, err := url.Parse(in.Link) if err == nil && strings.HasPrefix(u.Host, "www.") { u.Host = u.Host[4:] userFeedLinkMap[u.String()] = true } } respFeeds := []content.Feed{} for i := range feeds { in := feeds[i].Data() if !userFeedIdMap[in.Id] && !userFeedLinkMap[in.Link] { respFeeds = append(respFeeds, feeds[i]) } } resp.val["Feeds"] = respFeeds return }
func markFeedAsRead(user content.User, id string, timestamp int64) (resp responseError) { resp = newResponse() t := time.Unix(timestamp/1000, 0) switch { case id == "all": if user.ReadBefore(t, true); user.HasErr() { resp.err = user.Err() return } case id == "favorite" || strings.HasPrefix(id, "popular:"): // Favorites are assumbed to have been read already case strings.HasPrefix(id, "tag:"): tag := user.Repo().Tag(user) tag.Value(data.TagValue(id[4:])) if tag.ReadBefore(t, true); tag.HasErr() { resp.err = tag.Err() return } default: var feedId int64 if feedId, resp.err = strconv.ParseInt(id, 10, 64); resp.err != nil { /* TODO: non-fatal error */ return } feed := user.FeedById(data.FeedId(feedId)) if feed.ReadBefore(t, true); feed.HasErr() { resp.err = feed.Err() return } } resp.val["Success"] = true return }
func setFeedTags(user content.User, id data.FeedId, tagValues []data.TagValue) (resp responseError) { resp = newResponse() feed := user.FeedById(id) if feed.HasErr() { resp.err = feed.Err() return } repo := user.Repo() tf := repo.TaggedFeed(user) tf.Data(feed.Data()) filtered := make([]data.TagValue, 0, len(tagValues)) for _, v := range tagValues { v = data.TagValue(strings.TrimSpace(string(v))) if v != "" { filtered = append(filtered, v) } } tags := make([]content.Tag, len(filtered)) for i := range filtered { tags[i] = repo.Tag(user) tags[i].Data(data.Tag{Value: filtered[i]}) } tf.Tags(tags) if tf.UpdateTags(); tf.HasErr() { resp.err = tf.Err() return } resp.val["Success"] = true resp.val["Id"] = id return }
func fetchArticle(user content.User, id data.ArticleId) (resp responseError) { resp = newResponse() article := user.ArticleById(id) if user.HasErr() { resp.err = user.Err() return } resp.val["Article"] = article return }
func exportOpml(user content.User) (resp responseError) { resp = newResponse() o := parser.OpmlXml{ Version: "1.1", Head: parser.OpmlHead{Title: "Feed subscriptions of " + user.String() + " from readeef"}, } if feeds := user.AllTaggedFeeds(); user.HasErr() { resp.err = user.Err() return } else { body := parser.OpmlBody{} for _, f := range feeds { d := f.Data() tags := f.Tags() category := make([]string, len(tags)) for i, t := range tags { category[i] = string(t.Data().Value) } body.Outline = append(body.Outline, parser.OpmlOutline{ Text: d.Title, Title: d.Title, XmlUrl: d.Link, HtmlUrl: d.SiteLink, Category: strings.Join(category, ","), Type: "rss", }) } o.Body = body } var b []byte if b, resp.err = xml.MarshalIndent(o, "", " "); resp.err != nil { return } resp.val["opml"] = xml.Header + string(b) return }
func performSearch(user content.User, sp content.SearchProvider, query, feedId string, limit, offset int) (ua []content.UserArticle, err error) { defer func() { if rec := recover(); rec != nil { err = fmt.Errorf("Error during search: %s", rec) } }() if strings.HasPrefix(feedId, "tag:") { tag := user.Repo().Tag(user) tag.Data(data.Tag{Value: data.TagValue(feedId[4:])}) ua, err = tag.Query(query, sp, limit, offset), tag.Err() } else { if id, err := strconv.ParseInt(feedId, 10, 64); err == nil { f := user.FeedById(data.FeedId(id)) ua, err = f.Query(query, sp, limit, offset), f.Err() } else { ua, err = user.Query(query, sp, limit, offset), user.Err() } } return }