/** * ============================================================================= * * ORCID (R) Open Source * http://orcid.org * * Copyright (c) 2012-2014 ORCID, Inc. * Licensed under an MIT-Style License (MIT) * http://orcid.org/open-source-license * * This copyright and license information (including a link to the full license) * shall be included in its entirety in all copies or substantial portion of * the software. * * ============================================================================= */ package org.orcid.core.manager.impl; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.apache.commons.lang.StringUtils; import org.eclipse.jetty.util.ajax.JSON; import org.orcid.core.locale.LocaleManager; import org.orcid.core.manager.EncryptionManager; import org.orcid.core.manager.OrcidSocialManager; import org.orcid.persistence.dao.OrcidSocialDao; import org.orcid.persistence.jpa.entities.OrcidSocialEntity; import org.orcid.persistence.jpa.entities.OrcidSocialType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import twitter4j.Twitter; import twitter4j.TwitterFactory; import twitter4j.auth.AccessToken; import twitter4j.auth.RequestToken; @Service("orcidSocialManager") public class OrcidSocialManagerImpl implements OrcidSocialManager { private static final Logger LOGGER = LoggerFactory.getLogger(OrcidSocialManagerImpl.class); private static String TWITTER_USER_KEY = "twitter-key"; private static String TWITTER_USER_SECRET = "twitter-secret"; @Value("${org.orcid.core.baseUri:http://orcid.org}") private String baseUri; @Value("${org.orcid.core.twitter.key}") private String twitterKey; @Value("${org.orcid.core.twitter.secret}") private String twitterSecret; @Value("${org.orcid.core.twitter.enabled:true}") private boolean isTwitterEnabled; @Resource private EncryptionManager encryptionManager; @Resource private OrcidSocialDao orcidSocialDao; @Resource private LocaleManager localeManager; private Map<String, RequestToken> requestTokenMap = new HashMap<String, RequestToken>(); /** * Get the twitter RequestToken * * @return The twitter RequestToken * */ private RequestToken getTwitterRequestToken(String orcid) throws Exception { // If it exists, use it once and discard it if (requestTokenMap.containsKey(orcid)) { RequestToken result = requestTokenMap.get(orcid); requestTokenMap.remove(orcid); return result; } else { Twitter twitter = new TwitterFactory().getInstance(); twitter.setOAuthConsumer(twitterKey, twitterSecret); RequestToken requestToken = twitter.getOAuthRequestToken(); requestTokenMap.put(orcid, requestToken); return requestToken; } } /** * Get the twitter AccessToken * * @return The twitter AccessToken * */ private AccessToken getOAuthAccessToken(String orcid, String pin) throws Exception { RequestToken requestToken = getTwitterRequestToken(orcid); Twitter twitter = new TwitterFactory().getInstance(); twitter.setOAuthConsumer(twitterKey, twitterSecret); return twitter.getOAuthAccessToken(requestToken, pin); } /** * Return the URL where the user should authorize access * * @param orcid * the user orcid * @return the twitter URL where the user should authorize access * */ public String getTwitterAuthorizationUrl(String orcid) throws Exception { RequestToken token = getTwitterRequestToken(orcid); return token.getAuthorizationURL(); } /** * Enables twitter on the user profile * * @param orcid * @param pin * oauth_verifier parameter that comes from twitter request * */ @Override public void enableTwitter(String userOrcid, String pin) throws Exception { AccessToken accessToken = getOAuthAccessToken(userOrcid, pin); String credentials = generateEncryptedTwitterCredentials(accessToken.getToken(), accessToken.getTokenSecret()); orcidSocialDao.save(userOrcid, OrcidSocialType.TWITTER, credentials); } /** * Generate a JSON string with the credentials and return the string * encrypted * * @param userKey * @param userSecret * @return an encrypted string that contains the user key and secret in a * json string * */ private String generateEncryptedTwitterCredentials(String userKey, String userSecret) { Map<String, String> twitterCredentials = new HashMap<String, String>(); twitterCredentials.put(TWITTER_USER_KEY, userKey); twitterCredentials.put(TWITTER_USER_SECRET, userSecret); String twitterJsonCredentials = JSON.toString(twitterCredentials); return encrypt(twitterJsonCredentials); } /** * Encrypts a string * * @param unencrypted * @return the string encrypted * */ private String encrypt(String unencrypted) { if (StringUtils.isNotBlank(unencrypted)) { return encryptionManager.encryptForInternalUse(unencrypted); } else { return null; } } /** * Decrypts a string * * @param encrypted * string * @return the unencrypted string * */ private String decrypt(String encrypted) { if (StringUtils.isNotBlank(encrypted)) { return encryptionManager.decryptForInternalUse(encrypted); } else { return null; } } /** * Removes the twitter access from the user profile * * @param userOrcid * the profile to disable twitter * */ @Override public void disableTwitter(String userOrcid) { orcidSocialDao.delete(userOrcid, OrcidSocialType.TWITTER); } /** * Checks if twitter is enabled in the given profile * * @param userOrcid * @return true if the profile has twitter enabled * */ @Override public boolean isTwitterEnabled(String userOrcid) { return orcidSocialDao.isEnabled(userOrcid, OrcidSocialType.TWITTER); } /** * Will be invoked by a cron job to tweet in the modified profiles * */ @Override public void tweetLatestUpdates() { LOGGER.info("Start tweeting thread"); if(isTwitterEnabled) { List<OrcidSocialEntity> toTweet = orcidSocialDao.getRecordsToTweet(); Calendar cal = Calendar.getInstance(); cal.roll(Calendar.HOUR, false); Date oneHourBack = cal.getTime(); for (OrcidSocialEntity entity : toTweet) { Date lastTimeTweeted = entity.getLastRun(); if (lastTimeTweeted == null || lastTimeTweeted.before(oneHourBack)) { LOGGER.info("Tweeting profile {}", entity.getId().getOrcid()); if(lastTimeTweeted == null || entity.getProfile().getLastModified().after(lastTimeTweeted)) { if (tweet(entity)) orcidSocialDao.updateLatestRunDate(entity.getId().getOrcid(), entity.getType()); } } } } else { LOGGER.info("Twitter is disabled in this environment"); } LOGGER.info("Finished tweeting thread"); } /** * Tweet a message to the specified profile * * @param entity * An entity containing the user information and the twitter * credentials * @return true if it was able to tweet the updates * */ @SuppressWarnings("unchecked") private boolean tweet(OrcidSocialEntity entity) { String jsonCredentials = decrypt(entity.getEncryptedCredentials()); Map<String, String> credentials = (HashMap<String, String>) JSON.parse(jsonCredentials); Twitter twitter = new TwitterFactory().getInstance(); twitter.setOAuthConsumer(twitterKey, twitterSecret); AccessToken accessToken = new AccessToken(credentials.get(TWITTER_USER_KEY), credentials.get(TWITTER_USER_SECRET)); twitter.setOAuthAccessToken(accessToken); try { twitter.updateStatus(buildUpdateMessage(entity.getId().getOrcid())); } catch (Exception e) { LOGGER.error("Unable to tweet on profile {}", entity.getId().getOrcid()); return false; } return true; } /** * Builds the message to be tweeted * * @param orcid * @return the message the will be tweeted * */ private String buildUpdateMessage(String orcid) { String path = baseUri + '/' + orcid; return localeManager.resolveMessage("orcid_social.twitter.updated_message", path); } }