public
Description: This contains various plugins for Feather
Clone URL: git://github.com/eldiablo/feather-plugins.git
Search Repo:
Click here to lend your support to: feather-plugins and make a donation at www.pledgie.com !
added a Twitter plugin, to allow integration of Twitter messages (tweets) 
into your Feather blog; also updated snippets to use the array of 
available view hooks so that it doesn't have to be altered every time a 
new view hook is added to Feather core
eldiablo (author)
Sun Apr 20 13:28:58 -0700 2008
commit  a975f8f9978e3fdad3eb971aa61e1974487badf2
tree    2bbe2710db5e74b31cfd165efec4f683e29cf226
parent  c7071408ba171885735b8f74bceb25a19f057ce7
0
...
10
11
12
 
13
...
10
11
12
13
14
0
@@ -10,5 +10,6 @@
0
 feather-snippets: this allows custom and dynamic creation of snippets to add to the blog template
0
 feather-styles: this allows custom stylesheets to be defined and automatically included so as to override default blog styling
0
 feather-tagging: this provides tagging for articles, and a tag cloud for the blog sidebar
0
+feather-twitter: this polls for twitter updates, and inserts them in to your blog at the appropriate place
0
 feather-uploads: this allows for custom files to be uploaded and served
...
1
 
2
...
 
1
2
0
@@ -1,3 +1,3 @@
0
-<%= select_control :location, :collection => ["before_article", "before_article_in_list", "after_article", "after_article_in_list", "meta_section", "head", "header", "before_layout", "after_layout", "sidebar", "footer"], :label => 'Location:' %>
0
+<%= select_control :location, :collection => Hooks::View.available_hooks, :label => 'Location:' %>
0
 <%= text_area_control :content, :rows => 20, :cols => 100, :label => 'Snippet:' %>
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
0
@@ -1 +1,28 @@
0
+module Admin
0
+ class Tweets < Base
0
+ include_plugin_views __FILE__
0
+
0
+ before :find_tweet, :only => %w(delete show)
0
+
0
+ def index
0
+ @tweets = Tweet.all
0
+ display @tweets
0
+ end
0
+
0
+ def delete
0
+ @tweet.destroy!
0
+ expire_index
0
+ redirect url(:admin_tweets)
0
+ end
0
+
0
+ def show
0
+ display @tweet
0
+ end
0
+
0
+ private
0
+ def find_tweet
0
+ @tweet = Tweet[params[:id]]
0
+ end
0
+ end
0
+end
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
0
@@ -1 +1,29 @@
0
+module Admin
0
+ class TwitterSettings < Base
0
+ include_plugin_views __FILE__
0
+
0
+ def show
0
+ @twitter_setting = TwitterSetting.current
0
+ display @twitter_setting
0
+ end
0
+
0
+ def edit
0
+ @twitter_setting = TwitterSetting.current
0
+ display @twitter_setting
0
+ end
0
+
0
+ def update
0
+ @twitter_setting = TwitterSetting.current
0
+ res = true
0
+ res = @twitter_setting.update_attributes(params[:twitter_setting]) unless params[:twitter_setting].nil?
0
+ if res
0
+ @twitter_setting.scan if params[:force] && params[:force] == "true"
0
+ expire_index
0
+ redirect url(:admin_twitter_settings, @twitter_setting)
0
+ else
0
+ render :edit
0
+ end
0
+ end
0
+ end
0
+end
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0
@@ -1 +1,19 @@
0
+require File.join(File.join(File.join(File.dirname(__FILE__), "controllers"), "admin"), "tweets")
0
+require File.join(File.join(File.join(File.dirname(__FILE__), "controllers"), "admin"), "twitter_settings")
0
+require File.join(File.join(File.dirname(__FILE__), "models"), "tweet")
0
+require File.join(File.join(File.dirname(__FILE__), "models"), "twitter_setting")
0
+
0
+Merb::Router.prepend do |r|
0
+ r.namespace :admin do |admin|
0
+ admin.resources :tweets
0
+ admin.resource :twitter_settings
0
+ end
0
+end
0
+
0
+Hooks::View.register_partial_view "between_articles", "tweets"
0
+
0
+Hooks::Menu.add_menu_item "Tweets", "/admin/tweets"
0
+
0
+Hooks::Events.register_event(:after_article_index) { TwitterSetting.current.rescan }
0
+Hooks::Events.register_event(:after_article_show) { TwitterSetting.current.rescan }
...
 
 
...
1
2
0
@@ -1 +1,3 @@
0
+Database::migrate(Tweet)
0
+Database::migrate(TwitterSetting)
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
0
@@ -1 +1,36 @@
0
+plugin:
0
+ name: feather-twitter
0
+ author: El Draper
0
+ version: 1.0
0
+ homepage: http://featherblog.com
0
+ about: This plugin provides Twitter integration for your blog
0
+ contents:
0
+ .:
0
+ - init.rb
0
+ - install.rb
0
+ controllers:
0
+ .:
0
+ admin:
0
+ .:
0
+ - twitter_settings.rb
0
+ - tweets.rb
0
+ models:
0
+ .:
0
+ - tweet.rb
0
+ - twitter_setting.rb
0
+ views:
0
+ admin:
0
+ twitter_settings:
0
+ .:
0
+ - edit.html.erb
0
+ - show.html.erb
0
+ tweets:
0
+ .:
0
+ - _tweet.html.erb
0
+ - index.html.erb
0
+ - show.html.erb
0
+ - update.js.erb
0
+ between_articles:
0
+ .:
0
+ - _tweets.html.erb
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
0
@@ -1 +1,51 @@
0
+class Tweet < DataMapper::Base
0
+ property :twitter_id, :string, :nullable => false
0
+ property :text, :string, :nullable => false, :length => 140
0
+ property :source, :string, :nullable => false
0
+ property :in_reply_to, :string, :length => 140
0
+ property :username, :string, :nullable => false, :length => 255
0
+ property :created_at, :datetime, :nullable => false
0
+
0
+ validates_presence_of :twitter_id, :key => "uniq_twitter_id"
0
+ validates_presence_of :text, :key => "uniq_text"
0
+ validates_presence_of :source, :key => "uniq_source"
0
+ validates_presence_of :created_at, :key => "uniq_created_at"
0
+
0
+ ##
0
+ # This returns true if the tweet is a reply to someone, false if it isn't
0
+ def reply?
0
+ !self.in_reply_to.nil?
0
+ end
0
+
0
+ ##
0
+ # This returns the user that was being replied to if this was a reply
0
+ def replying_to
0
+ return nil unless self.reply?
0
+ user = self.text[0...self.text.index(" ")]
0
+ return nil unless user[0...1] == "@"
0
+ user
0
+ end
0
+
0
+ ##
0
+ # This provides a summary of the text update, if it's longer than 30 characters
0
+ def text_summary
0
+ summary = self.text
0
+ summary = summary[(summary.index(" ") + 1)...summary.length] if self.text[0...1] == "@"
0
+ summary = (summary.length > 30 ? "#{summary[0..30]}..." : summary[0..30])
0
+ summary
0
+ end
0
+
0
+ ##
0
+ # This returns any tweets found between two articles
0
+ def self.find_between_articles(before, after)
0
+ settings = TwitterSetting.current
0
+ Tweet.all.select { |t| t.created_at < before.published_at && t.created_at > after.published_at }.select { |t| (t.reply? && settings.ignore_replies) ? false : true }
0
+ end
0
+
0
+ ##
0
+ # This builds a direct url to the tweet
0
+ def url
0
+ "http://twitter.com/#{self.username}/statuses/#{self.twitter_id}"
0
+ end
0
+end
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
0
@@ -1 +1,53 @@
0
+class TwitterSetting < DataMapper::Base
0
+ property :username, :string
0
+ property :ignore_replies, :boolean, :nullable => false, :default => true
0
+ property :last_scan_at, :datetime
0
+
0
+ after_update :rescan
0
+
0
+ ##
0
+ # This rescans for new Tweets, if the last scan time was more than 30 minutes ago
0
+ def rescan
0
+ scan if self.last_scan_at.nil? || ((self.last_scan_at.to_time + (60 * 30)) < Time.now)
0
+ true
0
+ end
0
+
0
+ ##
0
+ # This scans for new Tweets
0
+ def scan
0
+ # Ensure we have valid settings
0
+ unless self.username.nil? || self.username == ""
0
+ # Grab Twitter statuses as xml
0
+ doc = Hpricot(Net::HTTP.get(URI.parse("http://twitter.com/statuses/user_timeline/#{self.username}.xml")))
0
+ statuses = (doc/"statuses"/"status")
0
+ # Loop through statuses
0
+ statuses.each do |status|
0
+ # Ensure it's unique, and that we don't already have it
0
+ if Tweet.first(:twitter_id => (status/"id").first.innerText).nil?
0
+ # Save the new tweet
0
+ tweet = Tweet.new
0
+ tweet.twitter_id = (status/"id").first.innerText
0
+ tweet.text = (status/"text").first.innerText
0
+ tweet.source = (status/"source").first.innerText
0
+ tweet.in_reply_to = (status/"in_reply_to").first.innerText == "" ? nil : (status/"in_reply_to").first.innerText
0
+ tweet.username = (status/"user"/"screen_name").first.innerText
0
+ tweet.created_at = (status/"created_at").first.innerText
0
+ tweet.save
0
+ end
0
+ end
0
+ # Set the last scan time on the settings
0
+ self.last_scan_at = DateTime.now
0
+ self.save
0
+ end
0
+ true
0
+ end
0
+
0
+ ##
0
+ # This returns the current settings, creating them if they aren't already present
0
+ def self.current
0
+ twitter_settings = TwitterSetting.first
0
+ twitter_settings = TwitterSetting.create(:username => nil, :ignore_replies => true) if twitter_settings.nil?
0
+ twitter_settings
0
+ end
0
+end
...
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
0
@@ -1 +1,8 @@
0
+<tr>
0
+ <td><%= link_to tweet.text_summary, url(:admin_tweet, tweet) %></td>
0
+ <td><%= tweet.source %></td>
0
+ <td><%= tweet.reply? %></td>
0
+ <td><%= render_relative_date(tweet.created_at) %></td>
0
+ <td><%= link_to 'Delete', url(:delete_admin_tweet, tweet), {:method => :delete, :onclick => "return confirm('Are you sure?')"} %></td>
0
+</tr>
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
0
@@ -1 +1,24 @@
0
+<% throw_content :right do %>
0
+ <h4>View Tweets</h4>
0
+ <p>
0
+ Tweets are the messages you send to Twitter, and they can be integrated within your Feather blog.
0
+ <br />
0
+ <%= link_to "View Twitter settings", url(:admin_twitter_settings) %>
0
+ </p>
0
+<% end %>
0
+
0
+<h1>View Tweets</h1>
0
+
0
+<table>
0
+ <tr>
0
+ <th>Text</th>
0
+ <th>Source</th>
0
+ <th>Reply?</th>
0
+ <th>Created</th>
0
+ <th>&nbsp;</th>
0
+ </tr>
0
+ <%= partial('admin/tweets/tweet', :with => @tweets) %>
0
+</table>
0
+<br />
0
+<%= link_to "View Twitter settings", url(:admin_twitter_settings) %>
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
0
@@ -1 +1,37 @@
0
+<% throw_content :right do %>
0
+ <h4>View Tweet</h4>
0
+ <p>
0
+ Here you can view the details of an existing tweet.
0
+ </p>
0
+<% end %>
0
+
0
+<h1>View Tweet</h1>
0
+
0
+<p>
0
+ Twitter ID: <%= @tweet.twitter_id %>
0
+</p>
0
+
0
+<p>
0
+ Text:
0
+ <br />
0
+ <%= @tweet.text %>
0
+</p>
0
+
0
+<p>
0
+ Source: <%= @tweet.source %>
0
+</p>
0
+
0
+<p>
0
+ Reply? <%= @tweet.reply? %>
0
+ <% if @tweet.reply? %>
0
+ <br />
0
+ <%= @tweet.in_reply_to %>
0
+ <% end %>
0
+</p>
0
+
0
+<p>
0
+ Created? <%= @tweet.created_at %>
0
+</p>
0
+
0
+<%= link_to "Back to tweets", url(:admin_tweets) %>
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
0
@@ -1 +1,34 @@
0
+<% throw_content :right do %>
0
+ <h4>Twitter Settings</h4>
0
+ Update the twitter settings for this blog.
0
+ <br />
0
+ <ul>
0
+ <li>
0
+ <strong>Username</strong>
0
+ <br />
0
+ This is your username on Twitter, it'll be used to find your tweets to integrate with your blog.
0
+ </li>
0
+ <li>
0
+ <strong>Ignore Replies</strong>
0
+ <br />
0
+ This will ignore tweets that are just replies, and will not show these within your blog.
0
+ </li>
0
+ </ul>
0
+<% end %>
0
+
0
+<%= error_messages_for @twitter_setting %>
0
+<h1>Edit Twitter settings</h1>
0
+
0
+<% form_for :twitter_setting, :action => url(:admin_twitter_settings, @twitter_setting), :method => :put do %>
0
+ <p>
0
+ <span class="mock_label">Username</span>
0
+ <%= text_control :username, :value => @twitter_setting.username %>
0
+ </p>
0
+
0
+ <p>
0
+ <span class="mock_label">Ignore Replies</span>
0
+ <%= checkbox_control :ignore_replies, :value => @twitter_setting.ignore_replies %>
0
+ </p>
0
+ <%= submit_button 'Save Settings' %> or <%= link_to 'Cancel', url(:admin_twitter_settings) %>
0
+<% end %>
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
0
@@ -1 +1,37 @@
0
+<% throw_content :right do %>
0
+ <h4>View Twitter Settings</h4>
0
+ <p>
0
+ Here you can view Twitter settings.
0
+ <br />
0
+ <%= link_to "View Tweets", url(:admin_tweets) %>
0
+ </p>
0
+<% end %>
0
+
0
+<h1>View Twitter Settings</h1>
0
+
0
+<p>
0
+ Username:
0
+ <%= @twitter_setting.username %>
0
+</p>
0
+
0
+<p>
0
+ Ignore Replies:
0
+ <%= @twitter_setting.ignore_replies %>
0
+</p>
0
+
0
+<p>
0
+ Tweets last scanned at:
0
+ <%= @twitter_setting.last_scan_at.nil? ? "Never" : @twitter_setting.last_scan_at.to_s %>
0
+</p>
0
+
0
+<p>
0
+ Force re-scan for tweets:
0
+ <% form_for :twitter_setting, :action => url(:admin_twitter_settings, @twitter_setting), :method => :put do %>
0
+ <%= hidden_field :name => "force", :value => true %>
0
+ <%= submit_button "Re-scan" %>
0
+ <% end %>
0
+</p>
0
+
0
+<%= link_to "Edit Twitter settings", url(:edit_admin_twitter_settings, @twitter_setting) %> |
0
+<%= link_to "View tweets", url(:admin_tweets) %>
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
0
@@ -1 +1,20 @@
0
+<% Tweet.find_between_articles(locals[:with][:before], locals[:with][:after]).each do |tweet| %>
0
+ <div class="hentry" id="tweet-<%= tweet.id %>">
0
+ <h2 class="entry-title">
0
+ <%= link_to tweet.text_summary, tweet.url %>
0
+ </h2>
0
+ <div class="vcard">
0
+ Posted to Twitter by <span class="fn"><%= tweet.username %></span>using <%= tweet.source %>&nbsp;
0
+ </div>
0
+ <abbr class="published" title=""><%= tweet.created_at.strftime("on %b %d, %y") %></abbr>
0
+ <br class="clear" />
0
+ <div class="entry-content">
0
+ <%= tweet.text %>
0
+ <% if tweet.reply? %>
0
+ <br /><br />
0
+ <i>In reply to: <%= tweet.in_reply_to %> (by <%= tweet.replying_to %>)</i>
0
+ <% end %>
0
+ </div>
0
+ </div>
0
+<% end %>

Comments

    No one has commented yet.