diff --git a/src/main/java/com/commafeed/backend/feeds/OPMLExporter.java b/src/main/java/com/commafeed/backend/feeds/OPMLExporter.java new file mode 100644 index 00000000..10234d37 --- /dev/null +++ b/src/main/java/com/commafeed/backend/feeds/OPMLExporter.java @@ -0,0 +1,87 @@ +package com.commafeed.backend.feeds; + +import java.util.Calendar; +import java.util.List; + +import javax.ejb.Stateless; +import javax.inject.Inject; + +import com.commafeed.backend.dao.FeedCategoryDAO; +import com.commafeed.backend.dao.FeedSubscriptionDAO; +import com.commafeed.backend.model.FeedCategory; +import com.commafeed.backend.model.FeedSubscription; +import com.commafeed.backend.model.User; +import com.sun.syndication.feed.opml.Attribute; +import com.sun.syndication.feed.opml.Opml; +import com.sun.syndication.feed.opml.Outline; + +@Stateless +public class OPMLExporter { + + @Inject + FeedCategoryDAO feedCategoryDAO; + + @Inject + FeedSubscriptionDAO feedSubscriptionDAO; + + @SuppressWarnings("unchecked") + public Opml export(User user) { + Opml opml = new Opml(); + opml.setFeedType("opml_1.0"); + opml.setTitle(String.format("%s subscriptions in CommaFeed", + user.getName())); + opml.setCreated(Calendar.getInstance().getTime()); + + List categories = feedCategoryDAO.findAll(user); + List subscriptions = feedSubscriptionDAO + .findAll(user); + + for (FeedCategory cat : categories) { + opml.getOutlines().add(buildCategoryOutline(cat, subscriptions)); + } + for (FeedSubscription sub : subscriptions) { + if (sub.getCategory() == null) { + opml.getOutlines().add(buildSubscriptionOutline(sub)); + } + } + + return opml; + + } + + @SuppressWarnings("unchecked") + private Outline buildCategoryOutline(FeedCategory cat, + List subscriptions) { + Outline outline = new Outline(); + outline.setText(cat.getName()); + outline.setTitle(cat.getName()); + + for (FeedCategory child : cat.getChildren()) { + outline.getChildren().add( + buildCategoryOutline(child, subscriptions)); + } + + for (FeedSubscription sub : subscriptions) { + if (sub.getCategory() != null + && sub.getCategory().getId().equals(cat.getId())) { + outline.getChildren().add(buildSubscriptionOutline(sub)); + } + } + return outline; + } + + @SuppressWarnings("unchecked") + private Outline buildSubscriptionOutline(FeedSubscription sub) { + Outline outline = new Outline(); + outline.setText(sub.getTitle()); + outline.setTitle(sub.getTitle()); + outline.setType("rss"); + outline.getAttributes().add( + new Attribute("xmlUrl", sub.getFeed().getUrl())); + if (sub.getFeed().getLink() != null) { + outline.getAttributes().add( + new Attribute("htmlUrl", sub.getFeed().getLink())); + } + return outline; + } +} diff --git a/src/main/java/com/commafeed/frontend/rest/resources/AbstractREST.java b/src/main/java/com/commafeed/frontend/rest/resources/AbstractREST.java index 0c9e8093..9e7830be 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/AbstractREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/AbstractREST.java @@ -36,6 +36,7 @@ import com.commafeed.backend.dao.UserRoleDAO; import com.commafeed.backend.dao.UserSettingsDAO; import com.commafeed.backend.feeds.FeedFetcher; import com.commafeed.backend.feeds.FeedRefreshTaskGiver; +import com.commafeed.backend.feeds.OPMLExporter; import com.commafeed.backend.feeds.OPMLImporter; import com.commafeed.backend.model.User; import com.commafeed.backend.model.UserRole.Role; @@ -96,6 +97,9 @@ public abstract class AbstractREST { @Inject OPMLImporter opmlImporter; + + @Inject + OPMLExporter opmlExporter; @Inject PasswordEncryptionService encryptionService; diff --git a/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java b/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java index 968a3415..20846f35 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java @@ -9,6 +9,7 @@ import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; @@ -36,6 +37,8 @@ import com.commafeed.frontend.model.request.RenameRequest; import com.commafeed.frontend.model.request.SubscribeRequest; import com.commafeed.frontend.rest.Enums.ReadType; import com.google.common.base.Preconditions; +import com.sun.syndication.feed.opml.Opml; +import com.sun.syndication.io.WireFeedOutput; import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; @@ -203,7 +206,7 @@ public class FeedREST extends AbstractResourceREST { @POST @Path("/import") @Consumes(MediaType.MULTIPART_FORM_DATA) - @ApiOperation(value = "OPML Import", notes = "Import an OPML file, posted as a FORM with the 'file' name") + @ApiOperation(value = "OPML import", notes = "Import an OPML file, posted as a FORM with the 'file' name") public Response importOpml() { try { FileItemFactory factory = new DiskFileItemFactory(1000000, null); @@ -223,4 +226,21 @@ public class FeedREST extends AbstractResourceREST { return Response.ok(Status.OK).build(); } + @GET + @Path("/export") + @Produces(MediaType.APPLICATION_XML) + @ApiOperation(value = "OPML export", notes = "Export an OPML file of the user's subscriptions") + public Response exportOpml() { + Opml opml = opmlExporter.export(getUser()); + WireFeedOutput output = new WireFeedOutput(); + String opmlString = null; + try { + opmlString = output.outputString(opml); + } catch (Exception e) { + return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e) + .build(); + } + return Response.ok(opmlString).build(); + } + }