diff --git a/3.1/modules/twitter/controllers/twitter.php b/3.1/modules/twitter/controllers/twitter.php new file mode 100644 index 00000000..e55ed674 --- /dev/null +++ b/3.1/modules/twitter/controllers/twitter.php @@ -0,0 +1,235 @@ +id; + $token_is_set = $this->_is_token_set($user_id); + + $v = new View("twitter_dialog.html"); + $v->is_registered = twitter::is_registered(); + $v->user_token_set = $token_is_set; + + if ($token_is_set) { + $v->type = $item->type; + $v->title = $item->title; + $v->description = $item->description; + $v->form = $form; + $v->character_count = twitter::$character_count; + } else { + $item_url = urlencode(url::abs_site($item->relative_url_cache)); + $v->user_id = $user_id; + $v->twitter_auth_url = url::site("twitter/redirect?item_url=$item_url"); + } + + print $v; + } + + /** + * Check if current user's Twitter credentials have been stored locally. + * @param int $user_id + * @return boolean + */ + private function _is_token_set($user_id) { + $twitter_user = $this->_get_twitter_user($user_id); + if (!empty($twitter_user->oauth_token) && !empty($twitter_user->oauth_token_secret)) { + return true; + } + return false; + } + + /** + * Get Twitter credentials for the current user. + * @param int $user_id + * @return mixed object|false + */ + private function _get_twitter_user($user_id) { + $twitter_user = ORM::factory("twitter_user")->where("user_id", "=", $user_id)->find(); + if ($twitter_user->loaded()) { + return $twitter_user; + } + return false; + } + + /** + * Verify credentials and redirect based on response from Twitter. + */ + public function callback() { + require_once(MODPATH . "twitter/lib/twitteroauth.php"); + + $consumer_key = module::get_var("twitter", "consumer_key"); + $consumer_secret = module::get_var("twitter", "consumer_secret"); + $oauth_token = Session::instance()->get("twitter_oauth_token"); + $oauth_token_secret = Session::instance()->get("twitter_oauth_token_secret"); + $item_url = Session::instance()->get("twitter_item_redirect"); + + // If the oauth_token is old redirect to the connect page + if (isset($_REQUEST['oauth_token']) && $oauth_token !== $_REQUEST['oauth_token']) { + Session::instance()->set("twitter_oauth_status", "old_token"); + $this->clear_twitter_session(); + url::redirect(url::site("twitter/redirect")); + } + + // Create TwitteroAuth object with app key/secret and token key/secret from default phase + $connection = new TwitterOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret); + + // Request access tokens from twitter + $access_token = $connection->getAccessToken($_REQUEST['oauth_verifier']); + + // Save the access tokens + Session::instance()->set("twitter_access_token", $access_token); + + // Remove no longer needed request tokens + Session::instance()->delete("twitter_oauth_token"); + Session::instance()->delete("twitter_oauth_token_secret"); + + // If HTTP response is 200 continue otherwise send to connect page to retry + if (200 == $connection->http_code) { + // The user has been verified and the access tokens can be saved for future use + $this->save_twitter_user($access_token); + // Redirect to the tweet form + $item = ORM::factory("item", $item_id); + url::redirect(url::abs_site($item_url)); + } else { + // @todo Log HTTP status for application log and/or error message + $this->clear_twitter_session(); + url::redirect(url::site("twitter/redirect")); + } + } + + /** + * Save or update the current user's Twitter credentials. + * @param array $access_token + * @todo Ensure only one record per twitter_screen_name + */ + function save_twitter_user($access_token) { + $twitter_user = ORM::factory("twitter_user"); + $twitter_user->oauth_token = $access_token["oauth_token"]; + $twitter_user->oauth_token_secret = $access_token["oauth_token_secret"]; + $twitter_user->twitter_user_id = $access_token["user_id"]; + $twitter_user->screen_name = $access_token["screen_name"]; + $twitter_user->user_id = identity::active_user()->id; + $twitter_user->save(); + + message::success(t("Twitter access tokens saved!")); + } + + /** + * Redirect user to Twitter authorization page. + */ + function redirect() { + require_once(MODPATH . "twitter/lib/twitteroauth.php"); + + $consumer_key = module::get_var("twitter", "consumer_key"); + $consumer_secret = module::get_var("twitter", "consumer_secret"); + $oauth_callback = url::abs_site("twitter/callback"); + + // We'll want this after Twitter kicks back to our callback + if (!empty($_GET['item_url'])) { + Session::instance()->set("twitter_item_redirect", $_GET['item_url']); + } + + // Build TwitterOAuth object with client credentials + $connection = new TwitterOAuth($consumer_key, $consumer_secret); + + // Get temporary credentials. + $request_token = $connection->getRequestToken($oauth_callback); + + // Save temporary credentials to session. + Session::instance()->set("twitter_oauth_token", $request_token['oauth_token']); + Session::instance()->set("twitter_oauth_token_secret", $request_token['oauth_token_secret']); + + // If last connection failed don't display authorization link + if (200 == $connection->http_code) { + // Build authorize URL and redirect user to Twitter + $url = $connection->getAuthorizeURL($request_token["oauth_token"]); + url::redirect(url::site($url)); + } else { + // Show notification if something went wrong + message::success(t("Could not connect to Twitter. Refresh the page or try again later.")); + url::redirect(url::site($url)); + } + } + + /** + * Post a status update to Twitter + * @param string $message + */ + function tweet() { + access::verify_csrf(); + require_once(MODPATH . "twitter/lib/twitteroauth.php"); + + $form = twitter::get_tweet_form(); + + $user_id = identity::active_user()->id; + $item_url = url::abs_site($item->relative_url_cache); + $twitter_user = $this->_get_twitter_user($user_id); + $consumer_key = module::get_var("twitter", "consumer_key"); + $consumer_secret = module::get_var("twitter", "consumer_secret"); + + $connection = new TwitterOAuth( + $consumer_key, + $consumer_secret, + $twitter_user["oauth_key"], + $twitter_user["oauth_user"]); + + $connection->post('statuses/update', array('status' => $message)); + + if (200 == $connection->http_code) { + return true; + } else { + // @todo Save tweet with a status of not sent. + return false; + } + + if (request::method() == "post") { + if ($form->validate()) { + $message = $form->twitter_message->tweet->value; + if ($this->post($message, $item)) { + message::success(t("Tweet sent!")); + } else { + message::error(t("Unable to send Tweet. Try again later.")); + } + } + url::redirect(url::abs_site($item->relative_url_cache)); + } + + } + + /** + * Clear Twitter module session variables + */ + function clear_twitter_session() { + Session::instance()->delete("twitter_oauth_token"); + Session::instance()->delete("twitter_oauth_token_secret"); + Session::instance()->delete("twitter_access_token"); + } +} \ No newline at end of file diff --git a/3.1/modules/twitter/helpers/twitter.php b/3.1/modules/twitter/helpers/twitter.php new file mode 100644 index 00000000..4eb67789 --- /dev/null +++ b/3.1/modules/twitter/helpers/twitter.php @@ -0,0 +1,122 @@ + "g-configure-twitter-form")); + + $group_oauth = $form->group("twitter_oauth")->label(t("OAuth Settings")); + $group_oauth->input("consumer_key") + ->label(t("Consumer key")) + ->value(module::get_var("twitter", "consumer_key")); + $group_oauth->input("consumer_secret") + ->label(t("Consumer secret")) + ->value(module::get_var("twitter", "consumer_secret")); + + $group_tweet = $form->group("twitter_message")->label(t("Default Tweet")); + $group_tweet->input("default_tweet") + ->label("Default Tweet") + ->value(module::get_var("twitter", "default_tweet")); + // @todo Add reset default tweet button + + if (module::is_active("bitly")) { + $group_url = $form->group("urls")->label(t("Shorten URLs")); + $group_url->checkbox("shorten_urls") + ->label(t("Shorten URLs automatically with bit.ly")) + ->checked(module::get_var("twitter", "shorten_urls")); + } + + $form->submit("")->value(t("Save")); + return $form; + } + + /** + * + * @param $item + * @return Forge + */ + static function get_tweet_form($item) { + $long_url = url::abs_site($item->relative_url_cache); + $default_tweet = module::get_var("twitter", "default_tweet"); + $tweet = preg_replace("/%type/", $item->type, $default_tweet); + $tweet = preg_replace("/%title/", $item->title, $tweet); + $tweet = preg_replace("/%description/", $item->description, $tweet); + // If bit.ly module's enabled, get the item's URL and shorten it + // @todo Refactor bit.ly module so that it doesn't output a status message when called by other modules + if (module::is_active("bitly") && module::get_var("twitter", "shorten_urls")) { + $url = bitly::shorten_url($item->id); + } else { + $url = url::abs_site($item->relative_url_cache); + } + $tweet = preg_replace("/%url/", $url, $tweet); + $form = new Forge("twitter/tweet", "", "post", array("id" => "g-twitter-form")); + $group = $form->group("twitter_message")->label(t("Compose Tweet")); + $group->textarea("tweet") + ->value($tweet) + ->rules("required") + ->error_messages("required", t("Your tweet cannot be empty!")) + ->id("g-tweet"); + $group->hidden("item_id")->value($item->id); + $form->submit("")->value(t("Tweet")); + return $form; + } + + /** + * Has this Gallery been registered at dev.twitter.com/app? + * @return boolean + */ + static function is_registered() { + $consumer_key = module::get_var("twitter", "consumer_key"); + $consumer_secret = module::get_var("twitter", "consumer_secret"); + if (empty($consumer_key) || empty($consumer_secret)) { + site_status::warning( + t("Twitter module requires attention! Set the consumer key and secret.", + array("url" => html::mark_clean(url::site("admin/twitter")))), + "twitter_config"); + return false; + } else { + site_status::clear("twitter_config"); + return true; + } + } + + /** + * Reset the standard Tweet to the module default + * @return string + */ + static function reset_default_tweet() { + $default_tweet = t("Check out this %type, '%title': %description %url"); + module::set_var("twitter", "default_tweet", $default_tweet); + return $default_tweet; + } + +} diff --git a/3.1/modules/twitter/helpers/twitter_installer.php b/3.1/modules/twitter/helpers/twitter_installer.php new file mode 100644 index 00000000..7a4f77c6 --- /dev/null +++ b/3.1/modules/twitter/helpers/twitter_installer.php @@ -0,0 +1,50 @@ +query("CREATE TABLE {twitter_tweets} ( + `id` int(9) NOT NULL AUTO_INCREMENT, + `created` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + `status` tinyint(1) NOT NULL, + `tweet` varchar(140) NOT NULL, + `user_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + Database::instance() + ->query("CREATE TABLE {twitter_users} ( + `id` int(9) NOT NULL AUTO_INCREMENT, + `oauth_token` varchar(64) NOT NULL, + `oauth_token_secret` varchar(64) NOT NULL, + `screen_name` varchar(16) NOT NULL, + `twitter_user_id` int(9) NOT NULL, + `user_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("twitter", 1); + twitter::reset_default_tweet(); + } + + static function deactivate() { + site_status::clear("twitter_config"); + } +}