diff options
author | Azul <azul@riseup.net> | 2016-09-14 11:11:48 +0200 |
---|---|---|
committer | Azul <azul@riseup.net> | 2016-09-14 11:11:48 +0200 |
commit | f575f57915d5369269849352a70b0e4704c3fd19 (patch) | |
tree | 7b13646448e64e560101493eeac1a03c3a25dbd8 | |
parent | 92481029074f34342cc35937f3aab94aca7887c7 (diff) | |
parent | 22478d315af3590d2a344eb1aa8cf2aec0730506 (diff) |
Merge remote-tracking branch 'pr/237' into develop
Twitter Feature on Main-View from @loadtocode
Twitter feature within home/index of LEAP web app as feed of tweets of one user's twitter account
How to use it: set up an Application-only authentication by twitter (https://dev.twitter.com/oauth/application-only) and then create a config/secrets.yml including:
```
development:
twitter:
enabled: false # set to true for usage
twitter_handle: XXXXX #put your twitter handle here
bearer_token: XXXXX #put your bearer token here
test:
twitter:
enabled: false # set to true for usage
twitter_handle: XXXXX #put your twitter handle here
bearer_token: XXXXX #put your bearer token here
```
things in the PR:
- [x] gemfile:twitter
- [x] twitter helper (caching (to avoid reaching twitter rate limits)
- [x] index twitter view
- [x] twitter css (twitter design as discussed in call: Profil-Header: Picture, Link to twitter + twitter_handle; removed picture and twitter_handle from individual tweet, heading aligns at the top, only the last 3 tweets are shown)
- [x] script to generate bearer token
- [x] script to invalidate generated bearer token
- [x] documentation (Readme + Doc)
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Gemfile | 1 | ||||
-rw-r--r-- | Gemfile.lock | 35 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | app/assets/images/Twitter_Logo_Blue.png | bin | 0 -> 4298 bytes | |||
-rw-r--r-- | app/assets/stylesheets/application.scss | 1 | ||||
-rw-r--r-- | app/assets/stylesheets/twitter.scss | 72 | ||||
-rw-r--r-- | app/helpers/twitter_helper.rb | 46 | ||||
-rw-r--r-- | app/views/home/_content.html.haml | 31 | ||||
-rw-r--r-- | app/views/twitter/_index.html.erb | 26 | ||||
-rw-r--r-- | doc/TWITTER_FEED.md | 35 | ||||
-rwxr-xr-x | script/generate_bearer_token | 84 | ||||
-rwxr-xr-x | script/invalidate_bearer_token | 47 |
13 files changed, 368 insertions, 14 deletions
@@ -34,6 +34,7 @@ public/ca.crt public/config/* public/provider.json config/config.yml +config/secrets.yml public/1/* vendor/bundle/* public/img @@ -98,6 +98,7 @@ end ## ## OPTIONAL GEMS AND ENGINES ## +gem 'twitter' enabled_engines.each do |name, gem_info| gem gem_info[:name], :path => gem_info[:path], :groups => gem_info[:env] diff --git a/Gemfile.lock b/Gemfile.lock index ab97719..8601b75 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -76,6 +76,7 @@ GEM sass (>= 3.3.4) braintree (2.65.0) builder (>= 2.0.0) + buftok (0.2.0) builder (3.2.2) byebug (9.0.5) capybara (2.7.1) @@ -126,6 +127,9 @@ GEM cucumber-wire (0.0.1) debug_inspector (0.0.2) diff-lcs (1.2.5) + domain_name (0.5.20160615) + unf (>= 0.0.5, < 1.0.0) + equalizer (0.0.10) erubis (2.7.0) execjs (2.7.0) factory_girl (4.7.0) @@ -140,6 +144,8 @@ GEM sinatra faker (1.6.6) i18n (~> 0.5) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) ffi (1.9.14) gherkin (4.0.0) globalid (0.3.7) @@ -157,7 +163,16 @@ GEM haml (~> 4.0.0) nokogiri (~> 1.6.0) ruby_parser (~> 3.5) + http (1.0.4) + addressable (~> 2.3) + http-cookie (~> 1.0) + http-form_data (~> 1.0.1) + http_parser.rb (~> 0.6.0) + http-cookie (1.0.2) + domain_name (~> 0.5) + http-form_data (1.0.1) http_accept_language (2.0.5) + http_parser.rb (0.6.0) httpclient (2.8.1) i18n (0.7.0) i18n-missing_translations (0.0.1) @@ -178,6 +193,8 @@ GEM nokogiri (>= 1.5.9) mail (2.6.4) mime-types (>= 1.16, < 4) + memoizable (0.4.2) + thread_safe (~> 0.3, >= 0.3.1) metaclass (0.0.4) method_source (0.8.2) mime-types (3.1) @@ -190,6 +207,8 @@ GEM metaclass (~> 0.0.1) multi_json (1.12.1) multi_test (0.1.2) + multipart-post (2.0.0) + naught (1.1.0) nokogiri (1.6.8) mini_portile2 (~> 2.1.0) pkg-config (~> 1.1.7) @@ -258,6 +277,7 @@ GEM simple_form (3.2.1) actionpack (> 4, < 5.1) activemodel (> 4, < 5.1) + simple_oauth (0.3.1) sinatra (1.4.7) rack (~> 1.5) rack-protection (~> 1.4) @@ -278,10 +298,22 @@ GEM thor (0.19.1) thread_safe (0.3.5) tilt (2.0.5) + twitter (5.16.0) + addressable (~> 2.3) + buftok (~> 0.2.0) + equalizer (= 0.0.10) + faraday (~> 0.9.0) + http (~> 1.0) + http_parser.rb (~> 0.6.0) + json (~> 1.8) + memoizable (~> 0.4.0) + naught (~> 1.0) + simple_oauth (~> 0.3.0) tzinfo (1.2.2) thread_safe (~> 0.1) uglifier (3.0.1) execjs (>= 0.3.0, < 3) + unf (0.2.0.beta2) valid_email (0.0.13) activemodel mail (~> 2.6.1) @@ -337,8 +369,9 @@ DEPENDENCIES sass-rails simple_form therubyracer + twitter uglifier valid_email BUNDLED WITH - 1.11.2 + 1.12.5 @@ -9,6 +9,7 @@ The LEAP Web App provides the following functions: * Webfinger access to user’s public keys * Email aliases and forwarding * Localized and Customizable documentation +* Display of status updates from Twitter (access to tweets via Twitter API) Written in: Ruby, Rails. @@ -36,6 +37,7 @@ For more information, see these files in the ``doc`` directory: * DEPLOY -- for notes on deployment. * DEVELOP -- for developer notes. * CUSTOM -- how to customize. +* TWITTER_FEED -- how to use it. External docs: @@ -186,4 +188,3 @@ Known problems attacks. These are very hard to prevent, because our protocol is designed to allow query of a user database via proxy in order to provide network perspective. - diff --git a/app/assets/images/Twitter_Logo_Blue.png b/app/assets/images/Twitter_Logo_Blue.png Binary files differnew file mode 100644 index 0000000..b5eebc8 --- /dev/null +++ b/app/assets/images/Twitter_Logo_Blue.png diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 856a559..f42044b 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -16,6 +16,7 @@ // LEAP web app specific overrides // @import "leap"; +@import "twitter"; // And finally bootswatch style itself // @import "bootswatch/cerulean/bootswatch"; diff --git a/app/assets/stylesheets/twitter.scss b/app/assets/stylesheets/twitter.scss new file mode 100644 index 0000000..b2233d0 --- /dev/null +++ b/app/assets/stylesheets/twitter.scss @@ -0,0 +1,72 @@ +.twitter { + position: relative; +} + +.twitter_header { + font-size: 16px; + text-align: left; + margin-bottom: 55px; + padding: 10px 8px; +} + +.twitter_id { + position: absolute; +} + +.twitter_image_frame { + display: block; + width: 40px; + height: 40px; + overflow: hidden; + position: absolute; + left: 0; + top: 0; + } + +.twitter_image_frame > img { + display: block; + position: absolute; + top: 0; + bottom: 0; + left: 30; + width: 100%; + margin: auto; + } + +.twitter_name { + padding-left: 55px; + line-height: 20px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 16px; +} + +// Body = displays tweets +.twitter_list { + box-sizing: border-box; +} + +.tweet { + border-top-style: solid; + border-color: lightgrey; + padding: 10px 8px; +} + +.tweet_text { +box-sizing: border-box; +} + +.tweet_text_date { + text-align: right; + padding-top: 4px; + font-size: 12px ; +} + +.twitter_footer { + border-top-style: solid; + border-color: lightgrey; + padding: 10px 8px; + font-style: italic; + font-size: 12px; +} diff --git a/app/helpers/twitter_helper.rb b/app/helpers/twitter_helper.rb new file mode 100644 index 0000000..f824a03 --- /dev/null +++ b/app/helpers/twitter_helper.rb @@ -0,0 +1,46 @@ +module TwitterHelper + def twitter_enabled + if Rails.application.secrets.twitter + Rails.application.secrets.twitter['enabled'] == true + end + end + + def twitter_client + Twitter::REST::Client.new do |config| + config.bearer_token = Rails.application.secrets.twitter['bearer_token'] + end + end + + def twitter_handle + Rails.application.secrets.twitter['twitter_handle'] + end + + def twitter_user_info + $twitter_user_info ||= [] + end + + def update_twitter_info + twitter_user_info[0] = Time.now + twitter_user_info[1] = twitter_client.user(twitter_handle).name + twitter_user_info[2] = twitter_client.user_timeline(twitter_handle).select{ |tweet| tweet.text.start_with?('RT','@')==false}.take(3) + end + + def cached_info + if twitter_user_info[0] == nil + update_twitter_info + else + if Time.now > twitter_user_info[0] + 15.minutes + update_twitter_info + end + end + twitter_user_info + end + + def twitter_name + cached_info[1] + end + + def tweets + cached_info[2] + end +end diff --git a/app/views/home/_content.html.haml b/app/views/home/_content.html.haml index 67e4533..5341189 100644 --- a/app/views/home/_content.html.haml +++ b/app/views/home/_content.html.haml @@ -1,12 +1,19 @@ -.row - %h1= t(:welcome, :provider => APP_CONFIG[:domain]) - .p=t(:welcome_message_html) - -.row - = home_page_buttons - - - if Rails.env == 'development' - .row - %hr - %p - = link_to "make donation", new_payment_path if APP_CONFIG[:payment].present? +.col-md-8 + .row + %h1= t(:welcome, :provider => APP_CONFIG[:domain]) + .p=t(:welcome_message_html) + + .row + = home_page_buttons + +.col-md-1 + +.col-md-3 + .row + = render 'twitter/index' + + - if Rails.env == 'development' + .row + %hr + %p + = link_to "make donation", new_payment_path if APP_CONFIG[:payment].present? diff --git a/app/views/twitter/_index.html.erb b/app/views/twitter/_index.html.erb new file mode 100644 index 0000000..a7ebd1b --- /dev/null +++ b/app/views/twitter/_index.html.erb @@ -0,0 +1,26 @@ +<% if twitter_enabled == true %> + <div class="twitter"> + + <div class="twitter_header"> + <div class="twitter_id"> + <div class="twitter_image_frame"><%= image_tag("Twitter_Logo_Blue.png") %></div> + <div class="twitter_name"><%= twitter_name%><br><a href="https://twitter.com/loadtocode">@<%= twitter_handle %></a></div> + </div> + </div> + + <div class="twitter_list"> + <% tweets.each do |e| %> + <div class="tweet"> + <div class="tweet_text"><%= " #{e.text}" %> + </div> + <div class="tweet_text_date">tweeted on <% t = e.created_at%> <%= t.strftime("%m/%d/%y").to_s %> + </div> + </div> + <% end %> + </div> + + <div class="twitter_footer"> + <p>This feed uses a Ruby interface to access the Twitter API. Within LEAP Twitter does not track you.</p> + </div> +</div> +<% end %> diff --git a/doc/TWITTER_FEED.md b/doc/TWITTER_FEED.md new file mode 100644 index 0000000..dd3954c --- /dev/null +++ b/doc/TWITTER_FEED.md @@ -0,0 +1,35 @@ +# Display of status updates from twitter on main view # + +This is a feature to include status updates that displays most recent tweets +of a (determined) twitter account (accessed via Twitter API). +If you chose to use it, the feature gets included in `home/index` of +LEAP web app (as part of the main view). + +## How to use it ## + +* Create Twitter Application on https://apps.twitter.com/ + * Visit https://apps.twitter.com/ and log in with the twitter account you want to use + * Make sure you have a mobile phone number registered with your account to be able to proceed + * Choose the option to `Create New App` + * Fill in Application Details and Developer Agreement and `Create your Twitter application` + * Choose the section "Keys and Access Tokens" to get your consumer key and consumer secret + * Optional: Go to section "Permissions" and change the "Access" from `Read and Write` (by default) to `Read only` + * Have your consumer key and secret by hand for one of the next steps + +* Activate the feature within your local LEAP Web Application + * If not already existing create a secrets-file in /config with the name secrets.yml (`/config/secrets.yml`) + * Secrets-file should contain the following, make sure its in YAML: {"development"=> {"twitter"=>{"enabled"=>false, "twitter_handle"=>"", "bearer_token"=>"", "twitter_picture"=>nil}}, "test"=>{"twitter"=>{"enabled"=>false, "twitter_handle"=>"", "bearer_token"=>"", "twitter_picture"=>nil}}} + * To have your bearer token created, run script in terminal being in the file of leap_web: `script/generate_bearer_token` + * To have the script run properly you have to add before running: `--key your_consumerkey --secret your_consumersecret` + * Add also `--projectroot your_projectroot --twitterhandle your_twitterhandle` as well to not have manually put the data in your secrets-file + * The full command looks like this: `script/generate_bearer_token --key your_consumerkey --secret your_consumersecret --projectroot your_projectroot --twitterhandle your_twitterhandle` + * If you didn't give all your information to the script, had a typo or want to change anything else, please do so by finding the secrets-file at `/config/secrets.yml` + * Make sure that the correct twitterhandle and bearer-token is included + +* Deactivate your bearer token + * To deactivate your generated bearer token you can run script/invalidate_bearer_token + * The full command looks like this: script/invalidate_bearer_token --key your_consumerkey --secret your_consumersecret --token your_bearer_token + +### Default avatar image ### + +This feature uses by default the twitter bird as avatar picture (Twitter_Logo_Blue.png). By using the Twitter trademarks, you agree to follow the Twitter Trademark Guidelines as well as Twitter's Terms of Service and all other Twitter rules and policies. Please find more details here: https://brand.twitter.com/. diff --git a/script/generate_bearer_token b/script/generate_bearer_token new file mode 100755 index 0000000..7091a8d --- /dev/null +++ b/script/generate_bearer_token @@ -0,0 +1,84 @@ +#!/usr/bin/env ruby + +require "net/http" +require "uri" +require "json" +require "base64" +require "optparse" +require "yaml" + +options = {} + +option_parser = OptionParser.new do |opts| + opts.banner = "Create your bearer_token for twitter by including following two [options], feel free to have your secrets-file created/filled giving the other information as well:" + + opts.on("--key KEY", "consumer_key of your twitter application") do |key| + options[:conkey] = key + end + + opts.on("--secret SECRET", "consumer_secret of your twitter application") do |secret| + options[:consec] = secret + end + + opts.on("--projectroot DIR", "directory where leapweb is") do |projectroot| + options[:projectroot] = projectroot + end + + opts.on("--twitterhandle TWI", "twitterhandle without @ which will be passed into secrets-file") do |twitterhandle| + options[:twitterhandle] = twitterhandle + end + +end + +option_parser.parse! + +if options[:conkey].nil? || options[:consec].nil? then + puts option_parser + exit +else + consumer_key = options[:conkey] + consumer_secret = options[:consec] +end + +uri = URI("https://api.twitter.com/oauth2/token") +data = "grant_type=client_credentials" +cre = Base64.strict_encode64("#{consumer_key}:#{consumer_secret}") +authorization_headers = { "Authorization" => "Basic #{cre}"} + +Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| + response = http.request_post(uri, data, authorization_headers) + token_hash = JSON.parse(response.body) + $bearer_token = token_hash["access_token"] +end + +if options[:projectroot].nil? then + puts "You didn't tell us the directory to have your secrets-file created or being filled. Feel free to copy/paste your bearer_token:" + puts $bearer_token +else + if File.exist?("#{options[:projectroot]}/leap_web/config/secrets.yml") + secrets = YAML.load_file("#{options[:projectroot]}/leap_web/config/secrets.yml") + else + puts "Please make sure that you created a secrets-file as described in the documentation or have given the correct directory. No secrets-file could be found." + exit + # secrets_content = {"twitter"=>{"enabled"=>false, "twitter_handle"=>"", "bearer_token"=>"", "twitter_picture"=>nil}} + # secrets = {"development"=> secrets_content, "test"=>secrets_content} + # secrets = {"development"=> {"twitter"=>{"enabled"=>false, "twitter_handle"=>"", "bearer_token"=>"", "twitter_picture"=>nil}}, "test"=>{"twitter"=>{"enabled"=>false, "twitter_handle"=>"", "bearer_token"=>"", "twitter_picture"=>nil}}} + # File.new("#{options[:projectroot]}/leap_web/config/secrets.yml", "w") + end + + if options[:twitterhandle].nil? then + if secrets["development"]["twitter"]["twitter_handle"] == "" then + puts "You didn't put your twitter-handle neither in the secrets-file nor passed it as a flag. Don't forget that you can't use the twitter-feature without your twitter-handle." + end + else + secrets["development"]["twitter"]["twitter_handle"] = options[:twitterhandle] + secrets["test"]["twitter"]["twitter_handle"] = options[:twitterhandle] + end + + secrets["development"]["twitter"]["bearer_token"] = $bearer_token + secrets["test"]["twitter"]["bearer_token"] = $bearer_token + + File.open("#{options[:projectroot]}/leap_web/config/secrets.yml", "r+") do |file| + file.write(secrets.to_yaml) + end +end diff --git a/script/invalidate_bearer_token b/script/invalidate_bearer_token new file mode 100755 index 0000000..eda1c7d --- /dev/null +++ b/script/invalidate_bearer_token @@ -0,0 +1,47 @@ +#!/usr/bin/env ruby + +require "net/http" +require "uri" +require "json" +require "base64" +require "optparse" + +options = {} + +option_parser = OptionParser.new do |opts| + opts.banner = "Invalidate your bearer_token for twitter by including the following [options]. The bearer token can't be used afterwards anymore. Please create a new bearer-token if you want to activate the twitter feature again." + + opts.on("--key KEY", "consumer_key of your twitter application") do |key| + options[:conkey] = key + end + + opts.on("--secret SECRET", "consumer_secret of your twitter application") do |secret| + options[:consec] = secret + end + + opts.on("--token TOKEN", "bearer token for twitter") do |token| + options[:token] = token + end + +end + +option_parser.parse! + +if options[:conkey].nil? || options[:consec].nil? || options[:token].nil? then + puts option_parser + exit +else + consumer_key = options[:conkey] + consumer_secret = options[:consec] + bearer_token = options[:token] +end + +uri = URI("https://api.twitter.com/oauth2/invalidate_token") +data = "access_token=#{bearer_token}" +cre = Base64.strict_encode64("#{consumer_key}:#{consumer_secret}") +authorization_headers = { "Authorization" => "Basic #{cre}"} + +Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| + response = http.request_post(uri, data, authorization_headers) + puts JSON.parse(response.body) +end |