Permalink
Browse files

Added feeds integration

  • Loading branch information...
1 parent 0cf6d64 commit ccfd841f7a484ba01f10022dd4aedfd9b5c77471 Joshua Clayton committed Mar 18, 2008
Showing with 1,018 additions and 2 deletions.
  1. +4 −0 app/controllers/admin/base_controller.rb
  2. +85 −0 app/controllers/admin/feeds_controller.rb
  3. +27 −0 app/controllers/admin/users_controller.rb
  4. +3 −0 app/controllers/application.rb
  5. +24 −0 app/controllers/session_controller.rb
  6. +2 −0 app/helpers/feeds_helper.rb
  7. +2 −0 app/helpers/session_helper.rb
  8. +2 −0 app/helpers/users_helper.rb
  9. +84 −0 app/models/authenticated_base.rb
  10. +55 −0 app/models/feed.rb
  11. +22 −0 app/models/feed_item.rb
  12. +5 −0 app/models/feed_status.rb
  13. +2 −0 app/models/flickr.rb
  14. +17 −0 app/models/last_fm.rb
  15. +2 −0 app/models/tumblr.rb
  16. +21 −0 app/models/twitter.rb
  17. +8 −0 app/models/user.rb
  18. +12 −0 app/views/admin/feeds/edit.html.erb
  19. +18 −0 app/views/admin/feeds/index.html.erb
  20. +11 −0 app/views/admin/feeds/new.html.erb
  21. +3 −0 app/views/admin/feeds/show.html.erb
  22. +17 −0 app/views/layouts/feeds.html.erb
  23. +14 −0 app/views/session/new.html.erb
  24. +7 −2 config/routes.rb
  25. +17 −0 db/migrate/004_create_users.rb
  26. +11 −0 db/migrate/005_create_feeds.rb
  27. +25 −0 db/migrate/006_create_feed_items.rb
  28. +17 −0 db/migrate/007_create_feed_statuses.rb
  29. +116 −0 lib/authenticated_system.rb
  30. +10 −0 lib/authenticated_test_helper.rb
  31. +6 −0 lib/tasks/cron.rake
  32. +8 −0 lib/tasks/feeds.rake
  33. +7 −0 test/fixtures/feed_items.yml
  34. +7 −0 test/fixtures/feed_statuses.yml
  35. +7 −0 test/fixtures/feeds.yml
  36. +19 −0 test/fixtures/users.yml
  37. +45 −0 test/functional/feeds_controller_test.rb
  38. +85 −0 test/functional/session_controller_test.rb
  39. +65 −0 test/functional/users_controller_test.rb
  40. +8 −0 test/unit/feed_item_test.rb
  41. +8 −0 test/unit/feed_status_test.rb
  42. +8 −0 test/unit/feed_test.rb
  43. +101 −0 test/unit/user_test.rb
  44. +1 −0 vendor/plugins/acts_as_archivable
@@ -0,0 +1,4 @@
+class Admin::BaseController < ApplicationController
+ layout 'admin'
+ before_filter :login_required
+end
@@ -0,0 +1,85 @@
+class FeedsController < Admin::BaseController
+ # GET /feeds
+ # GET /feeds.xml
+ def index
+ @feeds = Feed.find(:all)
+
+ respond_to do |format|
+ format.html # index.html.erb
+ format.xml { render :xml => @feeds }
+ end
+ end
+
+ # GET /feeds/1
+ # GET /feeds/1.xml
+ def show
+ @feed = Feed.find(params[:id])
+
+ respond_to do |format|
+ format.html # show.html.erb
+ format.xml { render :xml => @feed }
+ end
+ end
+
+ # GET /feeds/new
+ # GET /feeds/new.xml
+ def new
+ @feed = Feed.new
+
+ respond_to do |format|
+ format.html # new.html.erb
+ format.xml { render :xml => @feed }
+ end
+ end
+
+ # GET /feeds/1/edit
+ def edit
+ @feed = Feed.find(params[:id])
+ end
+
+ # POST /feeds
+ # POST /feeds.xml
+ def create
+ @feed = Feed.new(params[:feed])
+
+ respond_to do |format|
+ if @feed.save
+ flash[:notice] = 'Feed was successfully created.'
+ format.html { redirect_to(@feed) }
+ format.xml { render :xml => @feed, :status => :created, :location => @feed }
+ else
+ format.html { render :action => "new" }
+ format.xml { render :xml => @feed.errors, :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ # PUT /feeds/1
+ # PUT /feeds/1.xml
+ def update
+ @feed = Feed.find(params[:id])
+
+ respond_to do |format|
+ if @feed.update_attributes(params[:feed])
+ flash[:notice] = 'Feed was successfully updated.'
+ format.html { redirect_to(@feed) }
+ format.xml { head :ok }
+ else
+ format.html { render :action => "edit" }
+ format.xml { render :xml => @feed.errors, :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ # DELETE /feeds/1
+ # DELETE /feeds/1.xml
+ def destroy
+ @feed = Feed.find(params[:id])
+ @feed.destroy
+
+ respond_to do |format|
+ format.html { redirect_to(feeds_url) }
+ format.xml { head :ok }
+ end
+ end
+end
@@ -0,0 +1,27 @@
+class UsersController < ApplicationController
+ # Be sure to include AuthenticationSystem in Application Controller instead
+ include AuthenticatedSystem
+
+
+ # render new.rhtml
+ def new
+ end
+
+ def create
+ cookies.delete :auth_token
+ # protects against session fixation attacks, wreaks havoc with
+ # request forgery protection.
+ # uncomment at your own risk
+ # reset_session
+ @user = User.new(params[:user])
+ @user.save
+ if @user.errors.empty?
+ self.current_user = @user
+ redirect_back_or_default('/')
+ flash[:notice] = "Thanks for signing up!"
+ else
+ render :action => 'new'
+ end
+ end
+
+end
@@ -3,6 +3,9 @@
class ApplicationController < ActionController::Base
include ExceptionLoggable
+ # Be sure to include AuthenticationSystem in Application Controller instead
+ include AuthenticatedSystem
+
helper :all # include all helpers, all the time
# See ActionController::RequestForgeryProtection for details
@@ -0,0 +1,24 @@
+# This controller handles the login/logout function of the site.
+class SessionController < ApplicationController
+ def create
+ self.current_user = User.authenticate(params[:login], params[:password])
+ if logged_in?
+ if params[:remember_me] == "1"
+ self.current_user.remember_me
+ cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
+ end
+ redirect_back_or_default('/')
+ flash[:notice] = "Logged in successfully"
+ else
+ render :action => 'new'
+ end
+ end
+
+ def destroy
+ self.current_user.forget_me if logged_in?
+ cookies.delete :auth_token
+ reset_session
+ flash[:notice] = "You have been logged out."
+ redirect_back_or_default('/')
+ end
+end
@@ -0,0 +1,2 @@
+module FeedsHelper
+end
@@ -0,0 +1,2 @@
+module SessionHelper
+end
@@ -0,0 +1,2 @@
+module UsersHelper
+end
@@ -0,0 +1,84 @@
+require 'digest/sha1'
+module AuthenticatedBase
+ def self.included(base)
+ base.set_table_name base.name.tableize
+
+ base.validates_presence_of :login, :email
+ base.validates_presence_of :password, :if => :password_required?
+ base.validates_presence_of :password_confirmation, :if => :password_required?
+ base.validates_length_of :password, :within => 4..40, :if => :password_required?
+ base.validates_confirmation_of :password, :if => :password_required?
+ base.validates_length_of :login, :within => 3..40
+ base.validates_length_of :email, :within => 3..100
+ base.validates_uniqueness_of :login, :email, :case_sensitive => false
+ base.before_save :encrypt_password
+
+ # prevents a user from submitting a crafted form that bypasses activation
+ # anything else you want your user to change should be added here.
+ base.attr_accessible :login, :email, :password, :password_confirmation
+ base.cattr_accessor :current_user
+
+ base.extend ClassMethods
+ end
+
+ attr_accessor :password
+
+ module ClassMethods
+ # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
+ def authenticate(login, password)
+ u = find_by_login(login) # need to get the salt
+ u && u.authenticated?(password) ? u : nil
+ end
+
+ # Encrypts some data with the salt.
+ def encrypt(password, salt)
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
+ end
+ end
+
+ # Encrypts the password with the user salt
+ def encrypt(password)
+ self.class.encrypt(password, salt)
+ end
+
+ def authenticated?(password)
+ crypted_password == encrypt(password)
+ end
+
+ def remember_token?
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
+ end
+
+ # These create and unset the fields required for remembering users between browser closes
+ def remember_me
+ remember_me_for 2.weeks
+ end
+
+ def remember_me_for(time)
+ remember_me_until time.from_now.utc
+ end
+
+ def remember_me_until(time)
+ self.remember_token_expires_at = time
+ self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
+ save(false)
+ end
+
+ def forget_me
+ self.remember_token_expires_at = nil
+ self.remember_token = nil
+ save(false)
+ end
+
+ protected
+ # before filter
+ def encrypt_password
+ return if password.blank?
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
+ self.crypted_password = encrypt(password)
+ end
+
+ def password_required?
+ crypted_password.blank? || !password.blank?
+ end
+end
View
@@ -0,0 +1,55 @@
+class Feed < ActiveRecord::Base
+ has_many :feed_statuses
+ has_many :feed_items
+
+ validates_uniqueness_of :url
+
+ def feed
+ self.body_from(self.url)
+ end
+
+ def parse_feed
+ rss = Hash.from_xml(self.feed)
+ unless self.source == "LastFM"
+ rss["rss"]["channel"]["item"].each do |feed_item|
+ item = Object.const_get(self.source).new(feed_item)
+ item.feed = self
+ item.data = feed_item
+ item.save if item.valid?
+ end
+ else
+ rss["recenttracks"]["track"].each do |feed_item|
+ item = Object.const_get(self.source).new(feed_item)
+ item.feed = self
+ item.data = feed_item
+ item.save_with_validation(false)
+ end
+ end
+ end
+
+ class << self
+ def sources
+ %w(Twitter LastFM Tumblr Flickr)
+ end
+
+ def formats
+ ["RSS 1.0", "RSS 2.0", "ATOM"]
+ end
+
+ def parse_all
+ Feed.find(:all).map(&:parse_feed)
+ end
+ end
+
+ protected
+
+ def body_from(u)
+ url = URI.parse(u)
+ req = Net::HTTP::Get.new("#{url.path}?#{url.query}")
+ res = Net::HTTP.start(url.host, url.port) {|http|
+ http.request(req)
+ }
+ FeedStatus.create(:response_code => res.code.to_i, :feed => self)
+ res.body
+ end
+end
View
@@ -0,0 +1,22 @@
+class FeedItem < ActiveRecord::Base
+ acts_as_archivable :on => :published_at
+
+ belongs_to :feed
+ validates_uniqueness_of :guid
+
+ validates_presence_of :description
+
+ def pubDate=(time)
+ self.published_at = Time.parse(time) rescue Time.now
+ end
+
+ def date=(time)
+ self.published_at = Time.parse(time) rescue Time.now
+ end
+
+ class << self
+ def types
+ Feed.sources
+ end
+ end
+end
@@ -0,0 +1,5 @@
+class FeedStatus < ActiveRecord::Base
+ acts_as_archivable
+
+ belongs_to :feed
+end
View
@@ -0,0 +1,2 @@
+class Flickr < FeedItem
+end
View
@@ -0,0 +1,17 @@
+class LastFM < FeedItem
+ attr_accessor :streamable, :mbid
+
+ def name=(n)
+ self.song = n
+ end
+
+ def url=(path)
+ self.link = path
+ end
+
+ before_save do |item|
+ item.guid = "#{item.artist}/#{item.album}/#{item.song}/#{item.published_at.to_s}"
+ item.description = item.link
+ item.valid?
+ end
+end
View
@@ -0,0 +1,2 @@
+class Tumblr < FeedItem
+end
View
@@ -0,0 +1,21 @@
+class Twitter < FeedItem
+ REPLY = /^\@\w/
+
+ def body
+ desc = self.description
+ parsed_text = if (users = self.description.scan(/\@[\w\_]+/)).any?
+ users.each do |user|
+ desc = desc.gsub(user, "<a href='http://twitter.com/#{user.gsub(/\@/, '')}'>#{user}</a>")
+ end
+ else
+ desc
+ end
+ parsed_text
+ end
+
+ before_save do |item|
+ item.description = item.description.gsub(/#{item.feed.username}\: +/, '')
+ item.description = nil if item.description =~ REPLY
+ item.valid?
+ end
+end
View
@@ -0,0 +1,8 @@
+require 'digest/sha1'
+class User < ActiveRecord::Base
+ include AuthenticatedBase
+
+ def full_name
+ (self.first_name.blank? || self.last_name.blank?) ? self.login : "#{self.first_name} #{self.last_name}"
+ end
+end
Oops, something went wrong.

0 comments on commit ccfd841

Please sign in to comment.