<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>app/models/activity_observer.rb</filename>
    </added>
    <added>
      <filename>app/views/home/_activity.html.haml</filename>
    </added>
    <added>
      <filename>test/unit/activity_observer_test.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -7,6 +7,7 @@ then you win. &#8211;- Mahatma Gandhi
 Wed, Apr 8, 2009
 ---------------------------------------------------------------------
 - Created Activity model, related table, and factory.
+- Basic initial implementation of activity observers and recent activity.
 
 Tue, Apr 7, 2009
 ---------------------------------------------------------------------</diff>
      <filename>CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -5,9 +5,10 @@ class HomeController &lt; ApplicationController
 
   #----------------------------------------------------------------------------
   def index
-    @hello = &quot;world&quot;
+    @hello = &quot;Hello world&quot; # The hook below can access controller's instance variables.
     hook(:home_controller, self, :params =&gt; &quot;it works!&quot;)
-    logger.p &quot;Hello, #{@hello}!&quot;
+
+    @activities = Activity.latest
   end
   
   # Save expand/collapse state in the session.                             AJAX</diff>
      <filename>app/controllers/home_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -21,12 +21,13 @@
 #
 
 class Account &lt; ActiveRecord::Base
-  belongs_to :user
-  has_many :account_contacts, :dependent =&gt; :destroy
-  has_many :contacts, :through =&gt; :account_contacts, :uniq =&gt; true
-  has_many :account_opportunities, :dependent =&gt; :destroy
-  has_many :opportunities, :through =&gt; :account_opportunities, :uniq =&gt; true, :order =&gt; &quot;id DESC&quot;
-  has_many :tasks, :as =&gt; :asset, :dependent =&gt; :destroy, :order =&gt; 'created_at DESC'
+  belongs_to  :user
+  has_many    :account_contacts, :dependent =&gt; :destroy
+  has_many    :contacts, :through =&gt; :account_contacts, :uniq =&gt; true
+  has_many    :account_opportunities, :dependent =&gt; :destroy
+  has_many    :opportunities, :through =&gt; :account_opportunities, :uniq =&gt; true, :order =&gt; &quot;id DESC&quot;
+  has_many    :tasks, :as =&gt; :asset, :dependent =&gt; :destroy, :order =&gt; 'created_at DESC'
+  has_many    :activities, :as =&gt; :subject, :order =&gt; 'created_at DESC'
 
   uses_mysql_uuid
   uses_user_permissions</diff>
      <filename>app/models/account.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,8 @@
 class Activity &lt; ActiveRecord::Base
 
-  belongs_to :user
-  belongs_to :asset, :polymorphic =&gt; true
+  belongs_to  :user
+  belongs_to  :subject, :polymorphic =&gt; true
+  named_scope :latest, lambda { { :conditions =&gt; [ &quot;activities.created_at &gt;= ?&quot;, Date.today - 1.week ], :include =&gt; :user, :order =&gt; &quot;activities.created_at DESC&quot; } }
 
-  validates_presence_of :user, :asset
+  validates_presence_of :user, :subject
 end</diff>
      <filename>app/models/activity.rb</filename>
    </modified>
    <modified>
      <diff>@@ -26,10 +26,11 @@
 #
 
 class Campaign &lt; ActiveRecord::Base
-  belongs_to :user
-  has_many :tasks, :as =&gt; :asset, :dependent =&gt; :destroy, :order =&gt; 'created_at DESC'
-  has_many :leads, :dependent =&gt; :destroy, :order =&gt; &quot;id DESC&quot;
-  has_many :opportunities, :dependent =&gt; :destroy, :order =&gt; &quot;id DESC&quot;
+  belongs_to  :user
+  has_many    :tasks, :as =&gt; :asset, :dependent =&gt; :destroy, :order =&gt; 'created_at DESC'
+  has_many    :leads, :dependent =&gt; :destroy, :order =&gt; &quot;id DESC&quot;
+  has_many    :opportunities, :dependent =&gt; :destroy, :order =&gt; &quot;id DESC&quot;
+  has_many    :activities, :as =&gt; :subject, :order =&gt; 'created_at DESC'
   named_scope :only, lambda { |filters| { :conditions =&gt; [ &quot;status IN (?)&quot; + (filters.delete(&quot;other&quot;) ? &quot; OR status IS NULL&quot; : &quot;&quot;), filters ] } }
 
   uses_mysql_uuid</diff>
      <filename>app/models/campaign.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,6 +17,7 @@
 class Comment &lt; ActiveRecord::Base
   belongs_to  :user
   belongs_to  :commentable, :polymorphic =&gt; true
+  has_many    :activities, :as =&gt; :subject, :order =&gt; 'created_at DESC'
 
   validates_presence_of :user_id, :commentable_id, :commentable_type, :comment
 end</diff>
      <filename>app/models/comment.rb</filename>
    </modified>
    <modified>
      <diff>@@ -33,14 +33,15 @@
 #
 
 class Contact &lt; ActiveRecord::Base
-  belongs_to :user
-  belongs_to :lead
-  belongs_to :assignee, :class_name =&gt; &quot;User&quot;, :foreign_key =&gt; :assigned_to
-  has_one :account_contact, :dependent =&gt; :destroy
-  has_one :account, :through =&gt; :account_contact
-  has_many :contact_opportunities, :dependent =&gt; :destroy
-  has_many :opportunities, :through =&gt; :contact_opportunities, :uniq =&gt; true, :order =&gt; &quot;id DESC&quot;
-  has_many :tasks, :as =&gt; :asset, :dependent =&gt; :destroy, :order =&gt; 'created_at DESC'
+  belongs_to  :user
+  belongs_to  :lead
+  belongs_to  :assignee, :class_name =&gt; &quot;User&quot;, :foreign_key =&gt; :assigned_to
+  has_one     :account_contact, :dependent =&gt; :destroy
+  has_one     :account, :through =&gt; :account_contact
+  has_many    :contact_opportunities, :dependent =&gt; :destroy
+  has_many    :opportunities, :through =&gt; :contact_opportunities, :uniq =&gt; true, :order =&gt; &quot;id DESC&quot;
+  has_many    :tasks, :as =&gt; :asset, :dependent =&gt; :destroy, :order =&gt; 'created_at DESC'
+  has_many    :activities, :as =&gt; :subject, :order =&gt; 'created_at DESC'
 
   uses_mysql_uuid
   uses_user_permissions</diff>
      <filename>app/models/contact.rb</filename>
    </modified>
    <modified>
      <diff>@@ -33,11 +33,12 @@
 #
 
 class Lead &lt; ActiveRecord::Base
-  belongs_to :user
-  belongs_to :campaign
-  belongs_to :assignee, :class_name =&gt; &quot;User&quot;, :foreign_key =&gt; :assigned_to
-  has_one :contact
-  has_many :tasks, :as =&gt; :asset, :dependent =&gt; :destroy, :order =&gt; 'created_at DESC'
+  belongs_to  :user
+  belongs_to  :campaign
+  belongs_to  :assignee, :class_name =&gt; &quot;User&quot;, :foreign_key =&gt; :assigned_to
+  has_one     :contact
+  has_many    :tasks, :as =&gt; :asset, :dependent =&gt; :destroy, :order =&gt; 'created_at DESC'
+  has_many    :activities, :as =&gt; :subject, :order =&gt; 'created_at DESC'
   named_scope :only, lambda { |filters| { :conditions =&gt; [ &quot;status IN (?)&quot; + (filters.delete(&quot;other&quot;) ? &quot; OR status IS NULL&quot; : &quot;&quot;), filters ] } }
   named_scope :converted, :conditions =&gt; &quot;status='converted'&quot;
   named_scope :for_campaign, lambda { |id| { :conditions =&gt; [ &quot;campaign_id=?&quot;, id ] } }</diff>
      <filename>app/models/lead.rb</filename>
    </modified>
    <modified>
      <diff>@@ -22,15 +22,16 @@
 #
 
 class Opportunity &lt; ActiveRecord::Base
-  belongs_to :user
-  belongs_to :account
-  belongs_to :campaign
-  belongs_to :assignee, :class_name =&gt; &quot;User&quot;, :foreign_key =&gt; :assigned_to
-  has_one :account_opportunity, :dependent =&gt; :destroy
-  has_one :account, :through =&gt; :account_opportunity
-  has_many :contact_opportunities, :dependent =&gt; :destroy
-  has_many :contacts, :through =&gt; :contact_opportunities, :uniq =&gt; true, :order =&gt; &quot;id DESC&quot;
-  has_many :tasks, :as =&gt; :asset, :dependent =&gt; :destroy, :order =&gt; 'created_at DESC'
+  belongs_to  :user
+  belongs_to  :account
+  belongs_to  :campaign
+  belongs_to  :assignee, :class_name =&gt; &quot;User&quot;, :foreign_key =&gt; :assigned_to
+  has_one     :account_opportunity, :dependent =&gt; :destroy
+  has_one     :account, :through =&gt; :account_opportunity
+  has_many    :contact_opportunities, :dependent =&gt; :destroy
+  has_many    :contacts, :through =&gt; :contact_opportunities, :uniq =&gt; true, :order =&gt; &quot;id DESC&quot;
+  has_many    :tasks, :as =&gt; :asset, :dependent =&gt; :destroy, :order =&gt; 'created_at DESC'
+  has_many    :activities, :as =&gt; :subject, :order =&gt; 'created_at DESC'
   named_scope :only, lambda { |filters| { :conditions =&gt; [ &quot;stage IN (?)&quot; + (filters.delete(&quot;other&quot;) ? &quot; OR stage IS NULL&quot; : &quot;&quot;), filters ] } }
 
   uses_mysql_uuid</diff>
      <filename>app/models/opportunity.rb</filename>
    </modified>
    <modified>
      <diff>@@ -23,9 +23,10 @@
 class Task &lt; ActiveRecord::Base
   attr_accessor :calendar
 
-  belongs_to :user
-  belongs_to :assignee, :class_name =&gt; &quot;User&quot;, :foreign_key =&gt; :assigned_to
-  belongs_to :asset, :polymorphic =&gt; true
+  belongs_to  :user
+  belongs_to  :assignee, :class_name =&gt; &quot;User&quot;, :foreign_key =&gt; :assigned_to
+  belongs_to  :asset, :polymorphic =&gt; true
+  has_many    :activities, :as =&gt; :subject, :order =&gt; 'created_at DESC'
 
   # Base scopes to be combined with the due date and completion time.
   named_scope :my,            lambda { |user| { :conditions =&gt; [ &quot;(user_id = ? AND assigned_to IS NULL) OR assigned_to = ?&quot;, user.id, user.id ], :include =&gt; :assignee } }</diff>
      <filename>app/models/task.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,11 @@
-.title_tools Not implemented yet.
-.title Dashboard
+.title Recent Activity
+.list#activities
+  - unless @activities.empty?
+    = render :partial =&gt; &quot;activity&quot;, :collection =&gt; @activities
+  - else
+    %div There are no recorded activities yet.
+
+
 /
   Check out HTML source to view the output of the hook if you have sample plugin installed
   http://github.com/michaeldv/crm_sample_plugin/tree/master</diff>
      <filename>app/views/home/index.html.haml</filename>
    </modified>
    <modified>
      <diff>@@ -73,7 +73,7 @@ Rails::Initializer.run do |config|
 
   # Activate observers that should always be running
   # Please note that observers generated using script/generate observer need to have an _observer suffix
-  # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
+  config.active_record.observers = :activity_observer
 
   # Have migrations with numeric prefix instead of UTC timestamp.
   config.active_record.timestamped_migrations = false</diff>
      <filename>config/environment.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,3 +13,5 @@ Sass::Plugin.options[:css_location] = File.join(RAILS_ROOT, &quot;public/stylesheets&quot;
 
 #---------------------------------------------------------------------
 Date::DATE_FORMATS[:mmddyyyy] = &quot;%m/%d/%Y&quot;
+Date::DATE_FORMATS[:mmdd] = &quot;%b %e&quot;
+Date::DATE_FORMATS[:mmddhhss] = &quot;%b %e at %I:%M%p&quot;</diff>
      <filename>config/initializers/fat_free_crm.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,11 +1,11 @@
 class CreateActivities &lt; ActiveRecord::Migration
   def self.up
     create_table :activities, :force =&gt; true do |t|
-      t.references	:user                                           # User who's activity gets recorded.
-      t.references	:asset,   :polymorphic =&gt; true                  # Associated asset, including tasks.
+      t.references  :user                                           # User who's activity gets recorded.
+      t.references  :subject, :polymorphic =&gt; true                  # Points to related asset (account, contact, etc.).
       t.string      :action,  :limit =&gt; 32, :default =&gt; &quot;created&quot;   # Action taken: created, updated, deleted.
-      t.string		  :info,    :default =&gt; &quot;&quot;                        # Extra information related to the asset and the action.
-      t.boolean		  :private, :default =&gt; false                     # True if the action shouldn't be shared with others.
+      t.string      :info,    :default =&gt; &quot;&quot;                        # Extra information related to the asset and the action.
+      t.boolean     :private, :default =&gt; false                     # True if the action shouldn't be shared with others.
       t.timestamps
     end
 </diff>
      <filename>db/migrate/017_create_activities.rb</filename>
    </modified>
    <modified>
      <diff>@@ -68,7 +68,7 @@ end
 #----------------------------------------------------------------------------
 Factory.define :activity do |a|
   a.user                { |a| a.association(:user) } 
-  a.asset               { raise &quot;Please specify :asset for the activity&quot; }
+  a.subject             { raise &quot;Please specify :subject for the activity&quot; }
   a.action              nil
   a.info                nil
   a.private             false</diff>
      <filename>spec/factories.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,8 +3,8 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
 describe Activity do
   before(:each) do
     @valid_attributes = {
-      :user  =&gt; Factory(:user),
-      :asset =&gt; Factory(:lead)
+      :user =&gt; Factory(:user),
+      :subject =&gt; Factory(:lead)
     }
   end
 </diff>
      <filename>spec/models/activity_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>815985fedd11486a0183cb931ae472c597f8fad1</id>
    </parent>
  </parents>
  <author>
    <name>Mike Dvorkin</name>
    <email>mike@dvorkin.net</email>
  </author>
  <url>http://github.com/mocra/fat_free_crm/commit/c3e97a3df26d72cb16ab6b9f30da37f6c60f73f0</url>
  <id>c3e97a3df26d72cb16ab6b9f30da37f6c60f73f0</id>
  <committed-date>2009-04-08T14:28:02-07:00</committed-date>
  <authored-date>2009-04-08T14:28:02-07:00</authored-date>
  <message>Basic initial implementation of activity observers and recent activity
[#17 state:open]</message>
  <tree>ecd2bf159ee390890f3833e09531bf70675528ed</tree>
  <committer>
    <name>Mike Dvorkin</name>
    <email>mike@dvorkin.net</email>
  </committer>
</commit>
