Beispiel #1
0
func removeFolder(pfc *PFContext) (interface{}, error) {
	r := pfc.R

	folderID := r.PostFormValue("folder")
	if folderID == "" {
		return nil, NewReadableError(_l("Folder not found"), nil)
	}

	folderRef := storage.FolderRef{
		UserID:   pfc.UserID,
		FolderID: folderID,
	}

	if exists, err := storage.FolderExists(pfc.C, folderRef); err != nil {
		return nil, err
	} else if !exists {
		return nil, NewReadableError(_l("Folder not found"), nil)
	}

	// Delete the folder and subscriptions
	if err := storage.DeleteFolder(pfc.C, folderRef); err != nil {
		return nil, err
	}

	// Start a task to purge the articles
	params := taskParams{
		"folderID": folderID,
	}
	if err := startTask(pfc, "removeFolder", params, modificationQueue); err != nil {
		return nil, NewReadableError(_l("Cannot unsubscribe - too busy"), &err)
	}

	return storage.NewUserSubscriptions(pfc.C, pfc.UserID)
}
Beispiel #2
0
func removeTag(pfc *PFContext) (interface{}, error) {
	r := pfc.R

	tagID := r.PostFormValue("tag")
	if tagID == "" {
		return nil, NewReadableError(_l("Tag not found"), nil)
	}

	if exists, err := storage.TagExists(pfc.C, pfc.UserID, tagID); err != nil {
		return nil, err
	} else if !exists {
		return nil, NewReadableError(_l("Tag not found"), nil)
	}

	// Delete the tag
	if err := storage.DeleteTag(pfc.C, pfc.UserID, tagID); err != nil {
		return nil, err
	}

	// Start a task to remove existing tag
	params := taskParams{
		"tagID": tagID,
	}
	if err := startTask(pfc, "removeTag", params, modificationQueue); err != nil {
		return nil, NewReadableError(_l("Cannot remove tag - too busy"), &err)
	}

	return storage.NewUserSubscriptions(pfc.C, pfc.UserID)
}
Beispiel #3
0
func unsubscribe(pfc *PFContext) (interface{}, error) {
	r := pfc.R

	subscriptionID := r.PostFormValue("subscription")
	folderID := r.PostFormValue("folder")

	// Remove a subscription
	ref := storage.SubscriptionRef{
		FolderRef: storage.FolderRef{
			UserID:   pfc.UserID,
			FolderID: folderID,
		},
		SubscriptionID: subscriptionID,
	}

	if exists, err := storage.SubscriptionExists(pfc.C, ref); err != nil {
		return nil, err
	} else if !exists {
		return nil, NewReadableError(_l("Subscription not found"), nil)
	}

	if err := storage.Unsubscribe(pfc.C, ref); err != nil {
		return nil, err
	}

	params := taskParams{
		"subscriptionID": subscriptionID,
		"folderID":       folderID,
	}
	if err := startTask(pfc, "unsubscribe", params, modificationQueue); err != nil {
		return nil, NewReadableError(_l("Cannot unsubscribe - too busy"), &err)
	}

	return storage.NewUserSubscriptions(pfc.C, pfc.UserID)
}
Beispiel #4
0
func moveSubscription(pfc *PFContext) (interface{}, error) {
	r := pfc.R

	subscriptionID := r.PostFormValue("subscription")
	folderID := r.PostFormValue("folder")
	destinationID := r.PostFormValue("destination")

	destination := storage.FolderRef{
		UserID:   pfc.UserID,
		FolderID: destinationID,
	}

	if destinationID != "" {
		if exists, err := storage.FolderExists(pfc.C, destination); err != nil {
			return nil, err
		} else if !exists {
			return nil, NewReadableError(_l("Folder not found"), nil)
		}
	}

	ref := storage.SubscriptionRef{
		FolderRef: storage.FolderRef{
			UserID:   pfc.UserID,
			FolderID: folderID,
		},
		SubscriptionID: subscriptionID,
	}

	if exists, err := storage.SubscriptionExists(pfc.C, ref); err != nil {
		return nil, err
	} else if !exists {
		return nil, NewReadableError(_l("Subscription not found"), nil)
	}

	if err := storage.MoveSubscription(pfc.C, ref, destination); err != nil {
		return nil, err
	}

	params := taskParams{
		"subscriptionID": subscriptionID,
		"folderID":       folderID,
		"destinationID":  destinationID,
	}

	if err := startTask(pfc, "moveSubscription", params, modificationQueue); err != nil {
		return nil, err
	}

	return storage.NewUserSubscriptions(pfc.C, pfc.UserID)
}
Beispiel #5
0
func syncFeedsTask(pfc *PFContext) (TaskMessage, error) {
	if err := storage.UpdateAllSubscriptions(pfc.C, pfc.UserID); err != nil {
		return TaskMessage{}, err
	}

	userSubscriptions, err := storage.NewUserSubscriptions(pfc.C, pfc.UserID)
	if err != nil {
		return TaskMessage{}, err
	}

	return TaskMessage{
		Refresh:       false,
		Subscriptions: userSubscriptions,
	}, nil
}
Beispiel #6
0
func setTags(pfc *PFContext) (interface{}, error) {
	r := pfc.R

	folderID := r.PostFormValue("folder")
	subscriptionID := r.PostFormValue("subscription")
	articleID := r.PostFormValue("article")
	tagsAsString := r.PostFormValue("tags")

	tags := make([]string, 0, 10)
OuterLoop:
	for _, tag := range strings.Split(tagsAsString, ",") {
		if trimmedTag := strings.TrimSpace(tag); len(trimmedTag) > 0 {
			for _, existingTag := range tags {
				if existingTag == trimmedTag {
					continue OuterLoop
				}
			}

			tags = append(tags, trimmedTag)
		}
	}

	if articleID == "" || subscriptionID == "" {
		return nil, NewReadableError(_l("Article not found"), nil)
	}

	ref := storage.ArticleRef{
		SubscriptionRef: storage.SubscriptionRef{
			FolderRef: storage.FolderRef{
				UserID:   pfc.UserID,
				FolderID: folderID,
			},
			SubscriptionID: subscriptionID,
		},
		ArticleID: articleID,
	}

	if updatedTags, err := storage.SetTags(pfc.C, ref, tags); err != nil {
		return nil, NewReadableError(_l("Error updating article"), &err)
	} else {
		subs, err := storage.NewUserSubscriptions(pfc.C, pfc.UserID)
		return map[string]interface{}{
			"tags":          updatedTags,
			"subscriptions": subs,
		}, err
	}
}
Beispiel #7
0
func rename(pfc *PFContext) (interface{}, error) {
	r := pfc.R

	title := r.PostFormValue("title")
	if title == "" {
		return nil, NewReadableError(_l("Name not specified"), nil)
	}

	ref, err := storage.SubscriptionRefFromJSON(pfc.UserID, r.PostFormValue("ref"))
	if err != nil {
		return nil, err
	}

	if ref.IsSubscriptionExplicit() {
		if exists, err := storage.SubscriptionExists(pfc.C, ref); err != nil {
			return nil, err
		} else if !exists {
			return nil, NewReadableError(_l("Subscription not found"), nil)
		}

		if err := storage.RenameSubscription(pfc.C, ref, title); err != nil {
			return nil, NewReadableError(_l("Error renaming subscription"), &err)
		}
	} else {
		if exists, err := storage.FolderExists(pfc.C, ref.FolderRef); err != nil {
			return nil, err
		} else if !exists {
			return nil, NewReadableError(_l("Folder not found"), nil)
		}

		if isDupe, err := storage.IsFolderDuplicate(pfc.C, pfc.UserID, title); err != nil {
			return nil, err
		} else if isDupe {
			return nil, NewReadableError(_l("A folder with that name already exists"), nil)
		}

		if err := storage.RenameFolder(pfc.C, ref.FolderRef, title); err != nil {
			return nil, NewReadableError(_l("Error renaming folder"), &err)
		}
	}

	return storage.NewUserSubscriptions(pfc.C, pfc.UserID)
}
Beispiel #8
0
func syncFeeds(pfc *PFContext) (interface{}, error) {
	c := pfc.C

	staleDuration := time.Duration(subscriptionStalePeriodInMinutes) * time.Minute
	if appengine.IsDevAppServer() {
		// On dev server, stale period is 1 minute
		staleDuration = time.Duration(1) * time.Minute
	}

	userSubscriptions, err := storage.NewUserSubscriptions(c, pfc.UserID)
	if err != nil {
		return nil, err
	}

	if time.Since(pfc.User.LastSubscriptionUpdate) > staleDuration {
		pfc.User.LastSubscriptionUpdate = time.Now()
		if err := pfc.User.Save(c); err != nil {
			c.Warningf("Could not write user object back to store: %s", err)
		} else {
			started := time.Now()

			// Determine if new feeds are available
			if needRefresh, err := storage.AreNewEntriesAvailable(c, userSubscriptions.Subscriptions); err != nil {
				c.Warningf("Could not determine if new entries are available: %s", err)
			} else if needRefresh {
				if appengine.IsDevAppServer() {
					c.Debugf("Subscriptions need update; initiating a refresh (took %s)", time.Since(started))
				}

				if err := startTask(pfc, "syncFeeds", nil, refreshQueue); err != nil {
					c.Warningf("Could not initiate the refresh task: %s", err)
				}
			} else {
				if appengine.IsDevAppServer() {
					c.Debugf("Subscriptions are up to date (took %s)", time.Since(started))
				}
			}
		}
	}

	return userSubscriptions, nil
}
Beispiel #9
0
func createFolder(pfc *PFContext) (interface{}, error) {
	r := pfc.R

	title := r.PostFormValue("folderName")
	if title == "" {
		return nil, NewReadableError(_l("Missing folder name"), nil)
	}

	if utf8.RuneCountInString(title) > 200 {
		return nil, NewReadableError(_l("Folder name is too long"), nil)
	}

	if exists, err := storage.IsFolderDuplicate(pfc.C, pfc.UserID, title); err != nil {
		return nil, err
	} else if exists {
		return nil, NewReadableError(_l("A folder with that name already exists"), nil)
	}

	if _, err := storage.CreateFolder(pfc.C, pfc.UserID, title); err != nil {
		return nil, NewReadableError(_l("An error occurred while adding the new folder"), &err)
	}

	return storage.NewUserSubscriptions(pfc.C, pfc.UserID)
}
Beispiel #10
0
func subscriptions(pfc *PFContext) (interface{}, error) {
	return storage.NewUserSubscriptions(pfc.C, pfc.UserID)
}
Beispiel #11
0
func subscribe(pfc *PFContext) (interface{}, error) {
	c := pfc.C
	r := pfc.R

	subscriptionURL := r.PostFormValue("url")
	folderId := r.PostFormValue("folder")

	if subscriptionURL == "" {
		return nil, NewReadableError(_l("Missing URL"), nil)
	} else if _, err := url.ParseRequestURI(subscriptionURL); err != nil {
		return nil, NewReadableError(_l("URL is not valid"), &err)
	}

	folderRef := storage.FolderRef{
		UserID:   pfc.UserID,
		FolderID: folderId,
	}

	if folderId != "" {
		if exists, err := storage.FolderExists(pfc.C, folderRef); err != nil {
			return nil, err
		} else if !exists {
			return nil, NewReadableError(_l("Folder not found"), nil)
		}
	}

	feedTitle := _l("New Subscription")

	if exists, err := storage.IsFeedAvailable(pfc.C, subscriptionURL); err != nil {
		return nil, err
	} else if !exists {
		// Not a known feed URL
		// Match it against a list of known WWW links
		if feedURL, err := storage.WebToFeedURL(pfc.C, subscriptionURL, &feedTitle); err != nil {
			return nil, err
		} else if feedURL != "" {
			subscriptionURL = feedURL
		} else {
			// Still nothing
			// Add/remove 'www' to/from URL and try again
			var modifiedURL string
			if re := regexp.MustCompile(`://www\.`); re.MatchString(subscriptionURL) {
				modifiedURL = re.ReplaceAllString(subscriptionURL, "://")
			} else {
				re = regexp.MustCompile(`://`)
				modifiedURL = re.ReplaceAllString(subscriptionURL, "://www.")
			}

			if feedURL, err := storage.WebToFeedURL(pfc.C, modifiedURL, &feedTitle); err != nil {
				return nil, err
			} else if feedURL != "" {
				subscriptionURL = feedURL
			}
		}
	} else if feed, err := storage.FeedByURL(pfc.C, subscriptionURL); err == nil {
		if feed.Title != "" {
			feedTitle = feed.Title
		}
	}

	if subscribed, err := storage.IsSubscriptionDuplicate(pfc.C, pfc.UserID, subscriptionURL); err != nil {
		return nil, err
	} else if subscribed {
		return nil, NewReadableError(_l("You are already subscribed to %s", feedTitle), nil)
	}

	// At this point, the URL may have been re-written, so we check again
	if exists, err := storage.IsFeedAvailable(pfc.C, subscriptionURL); err != nil {
		return nil, err
	} else if !exists {
		// Don't have the feed locally - fetch it
		client := createHttpClient(c)
		if response, err := client.Get(subscriptionURL); err != nil {
			return nil, NewReadableError(_l("An error occurred while downloading the feed"), &err)
		} else {
			defer response.Body.Close()

			var body string
			if bytes, err := ioutil.ReadAll(response.Body); err != nil {
				return nil, NewReadableError(_l("An error occurred while reading the feed"), &err)
			} else {
				body = string(bytes)
			}

			reader := strings.NewReader(body)
			if feed, err := rss.UnmarshalStream(subscriptionURL, reader); err != nil {
				c.Warningf("Error parsing RSS (URL %s): %s", subscriptionURL, err)

				// Parse failed. Assume it's an HTML document and
				// try to pull out an RSS <link />
				if linkURL, err := rss.ExtractRSSLink(c, subscriptionURL, body); linkURL == "" || err != nil {
					return nil, NewReadableError(_l("RSS content not found (and no RSS links to follow)"), &err)
				} else {
					// Validate the RSS file
					if response, err := client.Get(linkURL); err != nil {
						return nil, NewReadableError(_l("An error occurred while downloading the feed"), &err)
					} else {
						defer response.Body.Close()

						if feed, err := rss.UnmarshalStream(linkURL, response.Body); err != nil {
							return nil, NewReadableError(_l("RSS content not found"), &err)
						} else {
							feedTitle = feed.Title
						}

						subscriptionURL = linkURL
					}
				}
			} else {
				feedTitle = feed.Title
			}
		}
	} else if feed, err := storage.FeedByURL(pfc.C, subscriptionURL); err == nil {
		if feed.Title != "" {
			feedTitle = feed.Title
		}
	}

	// Create subscription entry
	if _, err := storage.Subscribe(pfc.C, folderRef, subscriptionURL, feedTitle); err != nil {
		return nil, NewReadableError(_l("Cannot subscribe"), &err)
	}

	params := taskParams{
		"url":      subscriptionURL,
		"folderID": folderId,
	}
	if err := startTask(pfc, "subscribe", params, subscriptionQueue); err != nil {
		return nil, NewReadableError(_l("Cannot subscribe - too busy"), &err)
	}

	return storage.NewUserSubscriptions(c, pfc.UserID)
}