From a1ab76d1769dd0458391def5b927ac7faed6fdcb Mon Sep 17 00:00:00 2001 From: Athou Date: Fri, 5 Apr 2013 16:31:42 +0200 Subject: [PATCH] import from google reader --- pom.xml | 11 +++ .../com/commafeed/backend/StartupBean.java | 7 ++ .../dao/ApplicationSettingsService.java | 34 +++++++ .../backend/model/ApplicationSettings.java | 48 +++++++++ .../com/commafeed/backend/model/User.java | 4 + .../frontend/CommaFeedApplication.java | 4 + .../pages/GoogleImportCallbackPage.java | 98 +++++++++++++++++++ .../pages/GoogleImportRedirectPage.java | 52 ++++++++++ .../frontend/rest/RESTApplication.java | 2 + .../rest/resources/AdminSettingsREST.java | 33 +++++++ .../utils/exception/DisplayExceptionPage.java | 8 ++ src/main/webapp/js/controllers.js | 14 +++ src/main/webapp/js/main.js | 5 + src/main/webapp/js/services.js | 19 ++++ src/main/webapp/templates/_subscribe.html | 3 +- src/main/webapp/templates/admin.settings.html | 47 +++++++++ 16 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/commafeed/backend/dao/ApplicationSettingsService.java create mode 100644 src/main/java/com/commafeed/backend/model/ApplicationSettings.java create mode 100644 src/main/java/com/commafeed/frontend/pages/GoogleImportCallbackPage.java create mode 100644 src/main/java/com/commafeed/frontend/pages/GoogleImportRedirectPage.java create mode 100644 src/main/java/com/commafeed/frontend/rest/resources/AdminSettingsREST.java create mode 100644 src/main/webapp/templates/admin.settings.html diff --git a/pom.xml b/pom.xml index 0a76b27e..712a37ed 100644 --- a/pom.xml +++ b/pom.xml @@ -173,6 +173,17 @@ 1.0 + + com.google.oauth-client + google-oauth-client-servlet + 1.14.1-beta + + + com.google.http-client + google-http-client-jackson2 + 1.14.1-beta + + org.apache.httpcomponents httpclient diff --git a/src/main/java/com/commafeed/backend/StartupBean.java b/src/main/java/com/commafeed/backend/StartupBean.java index f02253e2..d032485d 100644 --- a/src/main/java/com/commafeed/backend/StartupBean.java +++ b/src/main/java/com/commafeed/backend/StartupBean.java @@ -11,10 +11,12 @@ import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.commafeed.backend.dao.ApplicationSettingsService; import com.commafeed.backend.dao.FeedCategoryService; import com.commafeed.backend.dao.FeedService; import com.commafeed.backend.dao.FeedSubscriptionService; import com.commafeed.backend.dao.UserService; +import com.commafeed.backend.model.ApplicationSettings; import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.FeedCategory; import com.commafeed.backend.model.FeedSubscription; @@ -44,6 +46,9 @@ public class StartupBean { @Inject PasswordEncryptionService encryptionService; + @Inject + ApplicationSettingsService applicationSettingsService; + private long startupTime; @PostConstruct @@ -52,6 +57,8 @@ public class StartupBean { if (userService.getCount() == 0) { log.info("Populating database with default values"); + applicationSettingsService.save(new ApplicationSettings()); + User user = userService.register(ADMIN_NAME, "admin", Arrays.asList(Role.ADMIN, Role.USER)); userService.register("test", "test", Arrays.asList(Role.USER)); diff --git a/src/main/java/com/commafeed/backend/dao/ApplicationSettingsService.java b/src/main/java/com/commafeed/backend/dao/ApplicationSettingsService.java new file mode 100644 index 00000000..2b03b660 --- /dev/null +++ b/src/main/java/com/commafeed/backend/dao/ApplicationSettingsService.java @@ -0,0 +1,34 @@ +package com.commafeed.backend.dao; + +import java.util.List; + +import javax.ejb.Stateless; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import com.commafeed.backend.model.ApplicationSettings; +import com.google.common.collect.Iterables; +import com.uaihebert.factory.EasyCriteriaFactory; +import com.uaihebert.model.EasyCriteria; + +@Stateless +public class ApplicationSettingsService { + + @PersistenceContext + protected EntityManager em; + + public void save(ApplicationSettings settings) { + if (settings.getId() == null) { + em.persist(settings); + } else { + em.merge(settings); + } + } + + public ApplicationSettings get() { + EasyCriteria criteria = EasyCriteriaFactory + .createQueryCriteria(em, ApplicationSettings.class); + List list = criteria.getResultList(); + return Iterables.getFirst(list, null); + } +} diff --git a/src/main/java/com/commafeed/backend/model/ApplicationSettings.java b/src/main/java/com/commafeed/backend/model/ApplicationSettings.java new file mode 100644 index 00000000..69ba5ddc --- /dev/null +++ b/src/main/java/com/commafeed/backend/model/ApplicationSettings.java @@ -0,0 +1,48 @@ +package com.commafeed.backend.model; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Table(name = "APPLICATIONSETTINGS") +@SuppressWarnings("serial") +public class ApplicationSettings extends AbstractModel { + + private String publicUrl; + private boolean allowRegistrations = false; + private String googleClientId; + private String googleClientSecret; + + public String getPublicUrl() { + return publicUrl; + } + + public void setPublicUrl(String publicUrl) { + this.publicUrl = publicUrl; + } + + public boolean isAllowRegistrations() { + return allowRegistrations; + } + + public void setAllowRegistrations(boolean allowRegistrations) { + this.allowRegistrations = allowRegistrations; + } + + public String getGoogleClientId() { + return googleClientId; + } + + public void setGoogleClientId(String googleClientId) { + this.googleClientId = googleClientId; + } + + public String getGoogleClientSecret() { + return googleClientSecret; + } + + public void setGoogleClientSecret(String googleClientSecret) { + this.googleClientSecret = googleClientSecret; + } + +} diff --git a/src/main/java/com/commafeed/backend/model/User.java b/src/main/java/com/commafeed/backend/model/User.java index e8d759d4..28a0c593 100644 --- a/src/main/java/com/commafeed/backend/model/User.java +++ b/src/main/java/com/commafeed/backend/model/User.java @@ -21,6 +21,10 @@ public class User extends AbstractModel { @Index(name = "username_index") private String name; + @Column(length = 256, unique = true) + @Index(name = "useremail_index") + private String email; + @Column(length = 256, nullable = false) private byte[] password; diff --git a/src/main/java/com/commafeed/frontend/CommaFeedApplication.java b/src/main/java/com/commafeed/frontend/CommaFeedApplication.java index a882c22b..fe1603ac 100644 --- a/src/main/java/com/commafeed/frontend/CommaFeedApplication.java +++ b/src/main/java/com/commafeed/frontend/CommaFeedApplication.java @@ -39,6 +39,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.commafeed.frontend.pages.FaviconPage; +import com.commafeed.frontend.pages.GoogleImportCallbackPage; +import com.commafeed.frontend.pages.GoogleImportRedirectPage; import com.commafeed.frontend.pages.HomePage; import com.commafeed.frontend.pages.LoginPage; import com.commafeed.frontend.pages.LogoutPage; @@ -59,6 +61,8 @@ public class CommaFeedApplication extends AuthenticatedWebApplication { mountPage("logout", LogoutPage.class); mountPage("error", DisplayExceptionPage.class); mountPage("favicon", FaviconPage.class); + mountPage("google/import/redirect", GoogleImportRedirectPage.class); + mountPage("google/import/callback", GoogleImportCallbackPage.class); setupInjection(); diff --git a/src/main/java/com/commafeed/frontend/pages/GoogleImportCallbackPage.java b/src/main/java/com/commafeed/frontend/pages/GoogleImportCallbackPage.java new file mode 100644 index 00000000..95c09666 --- /dev/null +++ b/src/main/java/com/commafeed/frontend/pages/GoogleImportCallbackPage.java @@ -0,0 +1,98 @@ +package com.commafeed.frontend.pages; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; + +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.request.Url; +import org.apache.wicket.request.UrlRenderer; +import org.apache.wicket.request.cycle.RequestCycle; +import org.apache.wicket.request.mapper.parameter.PageParameters; + +import com.commafeed.backend.dao.ApplicationSettingsService; +import com.commafeed.backend.dao.UserService; +import com.commafeed.backend.feeds.OPMLImporter; +import com.commafeed.backend.model.ApplicationSettings; +import com.commafeed.frontend.utils.WicketUtils; +import com.commafeed.frontend.utils.exception.DisplayException; +import com.google.api.client.auth.oauth2.AuthorizationCodeResponseUrl; +import com.google.api.client.auth.oauth2.AuthorizationCodeTokenRequest; +import com.google.api.client.auth.oauth2.BearerToken; +import com.google.api.client.auth.oauth2.TokenResponse; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; + +@SuppressWarnings("serial") +public class GoogleImportCallbackPage extends WebPage { + + @Inject + ApplicationSettingsService applicationSettingsService; + + @Inject + OPMLImporter importer; + @Inject + UserService userService; + + private static final String TOKEN_URL = "https://accounts.google.com/o/oauth2/token"; + private static final String EXPORT_URL = "https://www.google.com/reader/subscriptions/export"; + + public static String getCallbackUrl() { + RequestCycle cycle = RequestCycle.get(); + UrlRenderer renderer = cycle.getUrlRenderer(); + return renderer.renderFullUrl(Url.parse(cycle.urlFor( + GoogleImportCallbackPage.class, null).toString())); + } + + public GoogleImportCallbackPage(PageParameters params) { + + HttpServletRequest request = WicketUtils.getHttpServletRequest(); + StringBuffer urlBuffer = request.getRequestURL(); + if (request.getQueryString() != null) { + urlBuffer.append('?').append(request.getQueryString()); + } + AuthorizationCodeResponseUrl responseUrl = new AuthorizationCodeResponseUrl( + urlBuffer.toString()); + String code = responseUrl.getCode(); + + if (responseUrl.getError() != null) { + throw new DisplayException(responseUrl.getError()); + } else if (code == null) { + throw new DisplayException("Missing authorization code"); + } else { + ApplicationSettings settings = applicationSettingsService.get(); + String redirectUri = getCallbackUrl(); + String clientId = settings.getGoogleClientId(); + String clientSecret = settings.getGoogleClientSecret(); + + HttpTransport httpTransport = new NetHttpTransport(); + JacksonFactory jsonFactory = new JacksonFactory(); + + AuthorizationCodeTokenRequest tokenRequest = new AuthorizationCodeTokenRequest( + httpTransport, jsonFactory, new GenericUrl(TOKEN_URL), code); + tokenRequest.setRedirectUri(redirectUri); + tokenRequest.put("client_id", clientId); + tokenRequest.put("client_secret", clientSecret); + tokenRequest.setGrantType("authorization_code"); + + try { + TokenResponse tokenResponse = tokenRequest.execute(); + String accessToken = tokenResponse.getAccessToken(); + + HttpRequest httpRequest = httpTransport.createRequestFactory() + .buildGetRequest(new GenericUrl(EXPORT_URL)); + BearerToken.authorizationHeaderAccessMethod().intercept( + httpRequest, accessToken); + String opml = httpRequest.execute().parseAsString(); + String state = responseUrl.getState(); + importer.importOpml(userService.findById(Long.valueOf(state)), + opml); + } catch (Exception e) { + throw new DisplayException(e); + } + } + setResponsePage(getApplication().getHomePage()); + } +} diff --git a/src/main/java/com/commafeed/frontend/pages/GoogleImportRedirectPage.java b/src/main/java/com/commafeed/frontend/pages/GoogleImportRedirectPage.java new file mode 100644 index 00000000..4e0f8a53 --- /dev/null +++ b/src/main/java/com/commafeed/frontend/pages/GoogleImportRedirectPage.java @@ -0,0 +1,52 @@ +package com.commafeed.frontend.pages; + +import java.net.URISyntaxException; + +import javax.inject.Inject; + +import org.apache.http.client.utils.URIBuilder; +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.request.flow.RedirectToUrlException; +import org.jboss.logging.Logger; + +import com.commafeed.backend.dao.ApplicationSettingsService; +import com.commafeed.backend.model.ApplicationSettings; +import com.commafeed.frontend.CommaFeedSession; + +@SuppressWarnings("serial") +public class GoogleImportRedirectPage extends WebPage { + + private static Logger log = Logger + .getLogger(GoogleImportRedirectPage.class); + + private static final String SCOPE = "https://www.google.com/reader/subscriptions/export"; + private static final String AUTH_URL = "https://accounts.google.com/o/oauth2/auth"; + + @Inject + ApplicationSettingsService applicationSettingsService; + + public GoogleImportRedirectPage() { + + ApplicationSettings settings = applicationSettingsService.get(); + + String clientId = settings.getGoogleClientId(); + + String redirectUri = GoogleImportCallbackPage.getCallbackUrl(); + try { + URIBuilder builder = new URIBuilder(AUTH_URL); + + builder.addParameter("redirect_uri", redirectUri); + builder.addParameter("response_type", "code"); + builder.addParameter("scope", SCOPE); + builder.addParameter("approval_prompt", "force"); + builder.addParameter("client_id", clientId); + builder.addParameter("state", + String.valueOf(CommaFeedSession.get().getUser().getId())); + + throw new RedirectToUrlException(builder.build().toString()); + } catch (URISyntaxException e) { + log.error(e.getMessage(), e); + } + + } +} diff --git a/src/main/java/com/commafeed/frontend/rest/RESTApplication.java b/src/main/java/com/commafeed/frontend/rest/RESTApplication.java index 09916954..70318355 100644 --- a/src/main/java/com/commafeed/frontend/rest/RESTApplication.java +++ b/src/main/java/com/commafeed/frontend/rest/RESTApplication.java @@ -5,6 +5,7 @@ import java.util.Set; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; +import com.commafeed.frontend.rest.resources.AdminSettingsREST; import com.commafeed.frontend.rest.resources.AdminUsersREST; import com.commafeed.frontend.rest.resources.EntriesREST; import com.commafeed.frontend.rest.resources.SessionREST; @@ -24,6 +25,7 @@ public class RESTApplication extends Application { set.add(EntriesREST.class); set.add(SettingsREST.class); set.add(AdminUsersREST.class); + set.add(AdminSettingsREST.class); set.add(SessionREST.class); return set; } diff --git a/src/main/java/com/commafeed/frontend/rest/resources/AdminSettingsREST.java b/src/main/java/com/commafeed/frontend/rest/resources/AdminSettingsREST.java new file mode 100644 index 00000000..0c8c90ec --- /dev/null +++ b/src/main/java/com/commafeed/frontend/rest/resources/AdminSettingsREST.java @@ -0,0 +1,33 @@ +package com.commafeed.frontend.rest.resources; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +import com.commafeed.backend.dao.ApplicationSettingsService; +import com.commafeed.backend.model.ApplicationSettings; +import com.commafeed.backend.model.UserRole.Role; +import com.commafeed.frontend.SecurityCheck; + +@SecurityCheck(Role.ADMIN) +@Path("admin/settings") +public class AdminSettingsREST { + + @Inject + ApplicationSettingsService applicationSettingsService; + + @Path("get") + @GET + public ApplicationSettings get() { + return applicationSettingsService.get(); + } + + @Path("save") + @POST + public Response save(ApplicationSettings settings) { + applicationSettingsService.save(settings); + return Response.ok().build(); + } +} diff --git a/src/main/java/com/commafeed/frontend/utils/exception/DisplayExceptionPage.java b/src/main/java/com/commafeed/frontend/utils/exception/DisplayExceptionPage.java index 71b03535..716f4d1d 100644 --- a/src/main/java/com/commafeed/frontend/utils/exception/DisplayExceptionPage.java +++ b/src/main/java/com/commafeed/frontend/utils/exception/DisplayExceptionPage.java @@ -3,10 +3,13 @@ package com.commafeed.frontend.utils.exception; import java.io.PrintWriter; import java.io.StringWriter; +import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import de.agilecoders.wicket.Bootstrap; + public class DisplayExceptionPage extends WebPage { private static final long serialVersionUID = 1L; @@ -36,4 +39,9 @@ public class DisplayExceptionPage extends WebPage { return t; } + @Override + public void renderHead(IHeaderResponse response) { + Bootstrap.renderHead(response); + } + } diff --git a/src/main/webapp/js/controllers.js b/src/main/webapp/js/controllers.js index bdf25844..0f3fcf6d 100644 --- a/src/main/webapp/js/controllers.js +++ b/src/main/webapp/js/controllers.js @@ -492,4 +492,18 @@ module.controller('SettingsCtrl', function($scope, $location, SettingsService) { window.location.href.lastIndexOf('#')); }); }; +}); + +module.controller('ManageSettingsCtrl', function($scope, $location, AdminSettingsService) { + + $scope.settings = AdminSettingsService.get(); + + $scope.cancel = function() { + $location.path('/'); + }; + $scope.save = function() { + AdminSettingsService.save({}, $scope.settings, function() { + $location.path('/'); + }); + }; }); \ No newline at end of file diff --git a/src/main/webapp/js/main.js b/src/main/webapp/js/main.js index d3e9e4d6..1320d69e 100644 --- a/src/main/webapp/js/main.js +++ b/src/main/webapp/js/main.js @@ -39,6 +39,11 @@ app.config(function($routeProvider, $stateProvider, $urlRouterProvider) { templateUrl : 'templates/admin.useredit.html', controller : 'ManageUserCtrl' }); + $stateProvider.state('admin.settings', { + url : '/settings', + templateUrl : 'templates/admin.settings.html', + controller : 'ManageSettingsCtrl' + }); $stateProvider.state('settings', { url : '/settings', diff --git a/src/main/webapp/js/services.js b/src/main/webapp/js/services.js index b3034b64..0e395180 100644 --- a/src/main/webapp/js/services.js +++ b/src/main/webapp/js/services.js @@ -211,4 +211,23 @@ module.factory('AdminUsersService', function($resource) { }; var res = $resource('rest/admin/users/:_method', {}, actions); return res; +}); + +module.factory('AdminSettingsService', function($resource) { + var actions = { + get : { + method : 'GET', + params : { + _method : 'get' + } + }, + save : { + method : 'POST', + params : { + _method : 'save' + } + } + }; + var res = $resource('rest/admin/settings/:_method', {}, actions); + return res; }); \ No newline at end of file diff --git a/src/main/webapp/templates/_subscribe.html b/src/main/webapp/templates/_subscribe.html index 7804aeb6..b8929434 100644 --- a/src/main/webapp/templates/_subscribe.html +++ b/src/main/webapp/templates/_subscribe.html @@ -57,7 +57,8 @@