muck-activities 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +69 -0
- data/Rakefile +78 -0
- data/VERSION +1 -0
- data/app/controllers/muck/activities_controller.rb +152 -0
- data/app/helpers/muck_activity_helper.rb +67 -0
- data/app/models/activity.rb +62 -0
- data/app/models/activity_feed.rb +16 -0
- data/app/views/activities/_activity_feed.html.erb +18 -0
- data/app/views/activities/_cached_activities.html.erb +8 -0
- data/app/views/activities/_comment.html.erb +6 -0
- data/app/views/activities/_comments.html.erb +7 -0
- data/app/views/activities/_current_status.html.erb +14 -0
- data/app/views/activities/_current_status_wrapper.html.erb +4 -0
- data/app/views/activities/_delete.html.erb +11 -0
- data/app/views/activities/_status_update.html.erb +35 -0
- data/app/views/activities/_template_filter.html.erb +6 -0
- data/app/views/activity_templates/_status_update.html.erb +12 -0
- data/config/muck_activities_routes.rb +3 -0
- data/db/migrate/20090402033319_add_muck_activities.rb +36 -0
- data/install.rb +1 -0
- data/lib/muck_activities/initialize_routes.rb +8 -0
- data/lib/muck_activities/tasks.rb +28 -0
- data/lib/muck_activities.rb +76 -0
- data/locales/ar.yml +24 -0
- data/locales/bg.yml +24 -0
- data/locales/ca.yml +24 -0
- data/locales/cs.yml +24 -0
- data/locales/da.yml +24 -0
- data/locales/de.yml +24 -0
- data/locales/el.yml +24 -0
- data/locales/en.yml +25 -0
- data/locales/es.yml +24 -0
- data/locales/fr.yml +24 -0
- data/locales/it.yml +24 -0
- data/locales/iw.yml +24 -0
- data/locales/ja.yml +24 -0
- data/locales/ko.yml +24 -0
- data/locales/lt.yml +24 -0
- data/locales/lv.yml +24 -0
- data/locales/nl.yml +24 -0
- data/locales/no.yml +25 -0
- data/locales/pl.yml +24 -0
- data/locales/pt.yml +24 -0
- data/locales/ro.yml +24 -0
- data/locales/ru.yml +24 -0
- data/locales/sk.yml +24 -0
- data/locales/sl.yml +24 -0
- data/locales/sr.yml +24 -0
- data/locales/sv.yml +24 -0
- data/locales/tl.yml +24 -0
- data/locales/uk.yml +24 -0
- data/locales/vi.yml +24 -0
- data/locales/zh-CN.yml +24 -0
- data/locales/zh-TW.yml +24 -0
- data/locales/zh.yml +24 -0
- data/public/images/loading.gif +0 -0
- data/public/javascripts/muck_activities.js +65 -0
- data/rails/init.rb +4 -0
- data/rdoc/classes/ActionController/Routing/RouteSet.html +148 -0
- data/rdoc/classes/ActionController/Routing.html +107 -0
- data/rdoc/classes/ActionController.html +107 -0
- data/rdoc/classes/MuckActivity/ActMethods.html +189 -0
- data/rdoc/classes/MuckActivity/InstanceMethods.html +244 -0
- data/rdoc/classes/MuckActivity/Tasks.html +146 -0
- data/rdoc/created.rid +1 -0
- data/rdoc/files/README_rdoc.html +212 -0
- data/rdoc/files/lib/muck_activity/initialize_routes_rb.html +101 -0
- data/rdoc/files/lib/muck_activity/tasks_rb.html +110 -0
- data/rdoc/files/lib/muck_activity_rb.html +108 -0
- data/rdoc/fr_class_index.html +32 -0
- data/rdoc/fr_file_index.html +30 -0
- data/rdoc/fr_method_index.html +33 -0
- data/rdoc/index.html +24 -0
- data/rdoc/rdoc-style.css +208 -0
- data/tasks/muck_activity_tasks.rake +1 -0
- data/test/factories.rb +41 -0
- data/test/functional/activities_controller_test.rb +64 -0
- data/test/shoulda_macros/controller.rb +43 -0
- data/test/test_helper.rb +38 -0
- data/test/unit/activity_feed_test.rb +5 -0
- data/test/unit/activity_test.rb +57 -0
- data/uninstall.rb +1 -0
- metadata +169 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Justin Ball
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
= MuckActivity
|
2
|
+
|
3
|
+
== Installation
|
4
|
+
The muck activity engine is part of the muck framework and relies upon the muck_engine. The main engine can be found here:
|
5
|
+
http://github.com/jbasdf/muck_engine
|
6
|
+
|
7
|
+
The easiest way to get started with muck is to generate your application using a template:
|
8
|
+
$ rails <your-app> -m http://github.com/jbasdf/rails-templates/raw/master/muck.rb
|
9
|
+
|
10
|
+
Add optional functionality with the following command:
|
11
|
+
$ rake rails:template LOCATION=http://github.com/jbasdf/rails-templates/raw/master/mucktoo.rb
|
12
|
+
|
13
|
+
== Usage
|
14
|
+
This engine implements simple activity tracking.
|
15
|
+
|
16
|
+
Models that can have activity feed should call 'has_activities'
|
17
|
+
|
18
|
+
Example:
|
19
|
+
class User
|
20
|
+
has_activities
|
21
|
+
end
|
22
|
+
|
23
|
+
Adding an item to the activity feed requires 'acts_as_activity_source' which then profiles the method 'add_activity'
|
24
|
+
|
25
|
+
add_activity(feed_to, actor, item, template, check_method)
|
26
|
+
|
27
|
+
feed_to - contains an array of objects (typically users) that have use acts_as_activity_user
|
28
|
+
actor - the user performing the action
|
29
|
+
item - a reference to the object that
|
30
|
+
template - the template (partial) to use to render the activity
|
31
|
+
check_method - and optional method to call on each object in the feed_to array that determines whether or not to add the activity
|
32
|
+
|
33
|
+
Example:
|
34
|
+
add_activity(user.feed_to, user, comment, 'comment')
|
35
|
+
|
36
|
+
=== Authorization
|
37
|
+
By default 'has_activities' will add a simple 'can_view?' method:
|
38
|
+
|
39
|
+
def can_view?(check_object)
|
40
|
+
self == check_object
|
41
|
+
end
|
42
|
+
|
43
|
+
This method determines whether or not the check_object has access to the current object's activity feeds. For example, if the
|
44
|
+
intent is to recover the activities for a given user where the user is '@parent' thus:
|
45
|
+
|
46
|
+
@parent.can_view?(current_user)
|
47
|
+
|
48
|
+
The can view method will determine whether or not the current_user has the right to view the activity feed.
|
49
|
+
|
50
|
+
In most instances you will need to override this method to implement proper security. For example, for a group that has an activity feed
|
51
|
+
you might add a can_view? method like this:
|
52
|
+
|
53
|
+
def can_view?(check_object)
|
54
|
+
self.member?(check_object) || (check_object.is_a?(User) && check_object.admin?)
|
55
|
+
end
|
56
|
+
|
57
|
+
== Configuration
|
58
|
+
If you would like to enable comments for your project's activities feeds you can do so by adding an entry to GlobalConfig.yml (configuration file added by muck)
|
59
|
+
|
60
|
+
enable_activity_comments: true
|
61
|
+
|
62
|
+
If you would like to add live updates to the user's activity feed you can do so by adding an entry to GlobalConfig.yml (configuration file added by muck)
|
63
|
+
|
64
|
+
enable_live_activity_updates: true
|
65
|
+
live_activity_update_interval: 5
|
66
|
+
|
67
|
+
Note that this will poll the server every 5 seconds and so will increase server load and bandwidth usage.
|
68
|
+
|
69
|
+
Copyright (c) 2009 Justin Ball, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the muck_activities plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.libs << 'test'
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate documentation for the muck_activities plugin.'
|
17
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = 'MuckActivity'
|
20
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
21
|
+
rdoc.rdoc_files.include('README*')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
require 'jeweler'
|
27
|
+
Jeweler::Tasks.new do |gemspec|
|
28
|
+
gemspec.name = "muck-activities"
|
29
|
+
gemspec.summary = "Activity engine for the muck system"
|
30
|
+
gemspec.email = "justinball@gmail.com"
|
31
|
+
gemspec.homepage = "http://github.com/jbasdf/muck_activities"
|
32
|
+
gemspec.description = "Activity engine for the muck system."
|
33
|
+
gemspec.authors = ["Justin Ball"]
|
34
|
+
gemspec.rubyforge_project = 'muck-activities'
|
35
|
+
gemspec.add_dependency "muck-engine"
|
36
|
+
gemspec.add_dependency "muck-users"
|
37
|
+
gemspec.add_dependency "muck-comments"
|
38
|
+
gemspec.files.include %w(
|
39
|
+
tasks/*
|
40
|
+
db/migrate/*.rb
|
41
|
+
app/**/**/**/*
|
42
|
+
config/*
|
43
|
+
locales/*
|
44
|
+
rails/*
|
45
|
+
test/*
|
46
|
+
lib/**/*
|
47
|
+
public/**/* )
|
48
|
+
end
|
49
|
+
rescue LoadError
|
50
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
51
|
+
end
|
52
|
+
|
53
|
+
# rubyforge tasks
|
54
|
+
begin
|
55
|
+
require 'rake/contrib/sshpublisher'
|
56
|
+
namespace :rubyforge do
|
57
|
+
|
58
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
59
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
60
|
+
|
61
|
+
namespace :release do
|
62
|
+
desc "Publish RDoc to RubyForge."
|
63
|
+
task :docs => [:rdoc] do
|
64
|
+
config = YAML.load(
|
65
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
66
|
+
)
|
67
|
+
|
68
|
+
host = "#{config['username']}@rubyforge.org"
|
69
|
+
remote_dir = "/var/www/gforge-projects/muck-activities/"
|
70
|
+
local_dir = 'rdoc'
|
71
|
+
|
72
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
rescue LoadError
|
77
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
78
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.7
|
@@ -0,0 +1,152 @@
|
|
1
|
+
class Muck::ActivitiesController < ApplicationController
|
2
|
+
unloadable
|
3
|
+
|
4
|
+
include ApplicationHelper
|
5
|
+
include MuckActivityHelper
|
6
|
+
|
7
|
+
before_filter :login_required
|
8
|
+
before_filter :find_parent, :only => [:index, :create]
|
9
|
+
before_filter :get_activity, :only => [:destroy]
|
10
|
+
|
11
|
+
def index
|
12
|
+
if @parent.can_view?(current_user)
|
13
|
+
@activities = get_activities(@parent)
|
14
|
+
respond_to do |format|
|
15
|
+
format.js do
|
16
|
+
render :partial => "activities/cached_activities", :locals => {:activities => @activities}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def create
|
23
|
+
@activity = @parent.activities.build(params[:activity])
|
24
|
+
@activity.source = @parent
|
25
|
+
@activity.content.gsub!(@parent.display_name, '')
|
26
|
+
@parent.save!
|
27
|
+
|
28
|
+
@activity_html = get_activity_html(@activity)
|
29
|
+
if @activity.template == 'status_update'
|
30
|
+
@status_html = get_status_html(@parent)
|
31
|
+
else
|
32
|
+
@status_html = ''
|
33
|
+
end
|
34
|
+
respond_to do |format|
|
35
|
+
format.js do
|
36
|
+
render :update do |page|
|
37
|
+
page.insert_html :top, 'activity-feed-content', @activity_html
|
38
|
+
page.replace_html 'current-status', @status_html if @status_html
|
39
|
+
page.visual_effect :highlight, "#{@activity.dom_id}".to_sym
|
40
|
+
page << 'jQuery("#status_update").val(\'\');'
|
41
|
+
page << "jQuery('#status-update-field').removeClass('status-update-lit');"
|
42
|
+
page << "jQuery('#status-update-field').addClass('status-update-dim');"
|
43
|
+
page << 'jQuery("#status-update-field").show();'
|
44
|
+
page << 'jQuery("#submit_status").show();'
|
45
|
+
page << 'jQuery("#progress-bar").hide();'
|
46
|
+
page << 'setup_submit_delete();'
|
47
|
+
page << 'apply_comment_methods();'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
format.html do
|
51
|
+
redirect_back_or_default(@parent)
|
52
|
+
end
|
53
|
+
format.json do
|
54
|
+
render :json => { :success => true,
|
55
|
+
:is_status_update => @activity.is_status_update,
|
56
|
+
:html => @activity_html,
|
57
|
+
:status_html => @status_html,
|
58
|
+
:message => t("muck.activities.created") }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
rescue ActiveRecord::RecordInvalid => ex
|
63
|
+
|
64
|
+
if @activity
|
65
|
+
errors = @activity.errors.full_messages.to_sentence
|
66
|
+
else
|
67
|
+
errors = ex
|
68
|
+
end
|
69
|
+
message = t('muck.activities.update_error', :errors => errors)
|
70
|
+
respond_to do |format|
|
71
|
+
format.html do
|
72
|
+
flash[:error] = message
|
73
|
+
redirect_back_or_default(@parent)
|
74
|
+
end
|
75
|
+
format.js do
|
76
|
+
render :update do |page|
|
77
|
+
page_alert(page, message)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
def destroy
|
85
|
+
@activities_object = @activity.source
|
86
|
+
@activity.destroy
|
87
|
+
respond_to do |format|
|
88
|
+
format.html do
|
89
|
+
flash[:notice] = t("muck.activities.item_removed")
|
90
|
+
redirect_back_or_default(@activities_object)
|
91
|
+
end
|
92
|
+
format.js do
|
93
|
+
if @activity.is_status_update
|
94
|
+
@new_status = get_status_html(@activities_object)
|
95
|
+
render(:update) do |page|
|
96
|
+
page << "jQuery('##{@activity.dom_id}').fadeOut();"
|
97
|
+
page.replace_html 'current-status', @new_status if @new_status
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
format.json do
|
102
|
+
@new_status = get_status_html(@activities_object)
|
103
|
+
render :json => { :success => true,
|
104
|
+
:is_status_update => @activity.is_status_update,
|
105
|
+
:html => @new_status,
|
106
|
+
:message => t("muck.activities.item_removed") }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
rescue => ex
|
111
|
+
respond_to do |format|
|
112
|
+
format.html do
|
113
|
+
flash[:notice] = t("muck.activities.item_could_not_be_removed")
|
114
|
+
redirect_back_or_default(current_user)
|
115
|
+
end
|
116
|
+
format.js do
|
117
|
+
render(:update){ |page| page_alert(page, t("muck.activities.item_could_not_be_removed")) }
|
118
|
+
end
|
119
|
+
format.json { render :json => { :success => false, :message => t("muck.activities.item_could_not_be_removed") } }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
protected
|
124
|
+
|
125
|
+
def get_status_html(activity)
|
126
|
+
@template.template_format = :html
|
127
|
+
render_to_string(:partial => 'activities/current_status', :locals => {:activities_object => activity})
|
128
|
+
end
|
129
|
+
|
130
|
+
def get_activity_html(activity)
|
131
|
+
render_to_string(:partial => "activity_templates/#{activity.template}", :locals => { :activity => activity })
|
132
|
+
end
|
133
|
+
|
134
|
+
def find_parent
|
135
|
+
@klass = params[:parent_type].to_s.capitalize.constantize
|
136
|
+
@parent = @klass.find(params[:parent_id])
|
137
|
+
end
|
138
|
+
|
139
|
+
def get_activity
|
140
|
+
@activity = Activity.find(params[:id])
|
141
|
+
unless @activity.can_edit?(current_user)
|
142
|
+
respond_to do |format|
|
143
|
+
format.html do
|
144
|
+
flash[:notice] = t("muck.activities.permission_denied")
|
145
|
+
redirect_back_or_default(current_user)
|
146
|
+
end
|
147
|
+
format.js { render(:update){|page| page_alert(page, t("muck.activities.permission_denied"))}}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module MuckActivityHelper
|
2
|
+
|
3
|
+
def activity_comments(activity)
|
4
|
+
render :partial => 'activities/comments', :locals => { :activity => activity }
|
5
|
+
end
|
6
|
+
|
7
|
+
def activity_comment_link(activity, comment = nil)
|
8
|
+
if GlobalConfig.enable_activity_comments
|
9
|
+
comment_form(activity, comment = nil)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_comments_css(activity)
|
14
|
+
if activity.has_comments?
|
15
|
+
'activity-has-comments'
|
16
|
+
else
|
17
|
+
'activity-no-comments'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def activity_feed_for(activities_object)
|
22
|
+
activities = get_activities(activities_object)
|
23
|
+
render :partial => 'activities/activity_feed', :locals => { :activities_object => activities_object, :activities => activities }
|
24
|
+
end
|
25
|
+
|
26
|
+
def status_update(activities_object)
|
27
|
+
render :partial => 'activities/status_update', :locals => { :activities_object => activities_object }
|
28
|
+
end
|
29
|
+
|
30
|
+
def current_status(activities_object)
|
31
|
+
render :partial => 'activities/current_status_wrapper', :locals => { :activities_object => activities_object }
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete_activity(activity, button_type = :button, button_text = t("muck.activities.clear"))
|
35
|
+
render :partial => 'activities/delete', :locals => { :activity => activity, :button_type => button_type, :button_text => button_text }
|
36
|
+
end
|
37
|
+
|
38
|
+
def activity_filter(activities_object)
|
39
|
+
activity_types = activities_object.activities.all(:select => "DISTINCT activities.template")
|
40
|
+
render :partial => 'activities/template_filter', :locals => { :activity_types => activity_types }
|
41
|
+
end
|
42
|
+
|
43
|
+
def is_current_filter?(template)
|
44
|
+
if params[:activity_filter] == template
|
45
|
+
'current'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def no_filter?
|
50
|
+
if params[:activity_filter].blank?
|
51
|
+
'current'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def all_activities_url
|
56
|
+
request.url.gsub(request.query_string, '')
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_activities(activities_object)
|
60
|
+
if !params[:latest_activity_id].blank?
|
61
|
+
activities_object.activities.filter_by_template(params[:activity_filter]).after(params[:latest_activity_id]).find(:all, :include => ['comments']).paginate(:page => @page, :per_page => @per_page)
|
62
|
+
else
|
63
|
+
activities_object.activities.filter_by_template(params[:activity_filter]).find(:all, :include => ['comments']).paginate(:page => @page, :per_page => @per_page)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: activities
|
4
|
+
#
|
5
|
+
# id :integer(4) not null, primary key
|
6
|
+
# item_id :integer(4)
|
7
|
+
# item_type :string(255)
|
8
|
+
# template :string(255)
|
9
|
+
# source_id :integer(4)
|
10
|
+
# source_type :string(255)
|
11
|
+
# content :text
|
12
|
+
# title :string(255)
|
13
|
+
# status_update :boolean(1)
|
14
|
+
# created_at :datetime
|
15
|
+
# updated_at :datetime
|
16
|
+
#
|
17
|
+
|
18
|
+
class Activity < ActiveRecord::Base
|
19
|
+
unloadable
|
20
|
+
|
21
|
+
belongs_to :item, :polymorphic => true
|
22
|
+
belongs_to :source, :polymorphic => true
|
23
|
+
has_many :activity_feeds
|
24
|
+
|
25
|
+
acts_as_commentable
|
26
|
+
|
27
|
+
validates_presence_of :source
|
28
|
+
|
29
|
+
named_scope :since, lambda { |time| {:conditions => ["activities.created_at > ?", time] } }
|
30
|
+
named_scope :before, lambda {|time| {:conditions => ["activities.created_at < ?", time] } }
|
31
|
+
named_scope :recent, :order => "activities.created_at DESC"
|
32
|
+
named_scope :after, lambda {|id| {:conditions => ["activities.id > ?", id] } }
|
33
|
+
named_scope :only_public, :conditions => ["activities.is_public = true"]
|
34
|
+
named_scope :filter_by_template, lambda { |template| { :conditions => ["activities.template = ?", template] } unless template.blank? }
|
35
|
+
|
36
|
+
def validate
|
37
|
+
errors.add_to_base(I18n.t('muck.activities.template_or_item_required')) if template.blank? && item.blank?
|
38
|
+
end
|
39
|
+
|
40
|
+
# Provides a template that can be used to render a view of this activity.
|
41
|
+
# If 'template' is not specified when the object created then the item class
|
42
|
+
# name will be used to generated a template
|
43
|
+
def partial
|
44
|
+
template || item.class.name.underscore
|
45
|
+
end
|
46
|
+
|
47
|
+
def has_comments?
|
48
|
+
@has_comments ||= !self.comments.blank?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Checks to see if the specified object can edit this activity.
|
52
|
+
# Most likely check_object will be a user
|
53
|
+
def can_edit?(check_object)
|
54
|
+
if check_object.is_a?(User)
|
55
|
+
return true if check(check_object, :source_id)
|
56
|
+
else
|
57
|
+
source == check_object
|
58
|
+
end
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: activity_feeds
|
4
|
+
#
|
5
|
+
# id :integer(4) not null, primary key
|
6
|
+
# activity_id :integer(4)
|
7
|
+
# ownable_id :integer(4)
|
8
|
+
# ownable_type :string(255)
|
9
|
+
#
|
10
|
+
|
11
|
+
class ActivityFeed < ActiveRecord::Base
|
12
|
+
unloadable
|
13
|
+
|
14
|
+
belongs_to :activity
|
15
|
+
belongs_to :ownable, :polymorphic => true
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<div id="activity-feed">
|
2
|
+
<div id="activity-feed-content">
|
3
|
+
<%= render( :partial => "activities/cached_activities", :locals => {:activities => activities}) %>
|
4
|
+
</div>
|
5
|
+
</div>
|
6
|
+
<%= will_paginate activities, :previous_label => t('muck.activities.paging_newer'), :next_label => t('muck.activities.paging_older') -%>
|
7
|
+
<% content_for :head do -%>
|
8
|
+
<%= javascript_tag %[var COMMENT_PROMPT = '#{t('muck.activities.write_prompt')}';] %>
|
9
|
+
<% end -%>
|
10
|
+
<% content_for :javascript do -%>
|
11
|
+
jQuery(document).ready(function(){
|
12
|
+
setup_submit_delete();
|
13
|
+
<% if GlobalConfig.enable_live_activity_updates -%>
|
14
|
+
setInterval(function() {jQuery.ajax({success:function(request){update_feed(request);},url:'<%= activities_path(:parent_id => activities_object, :parent_type => activities_object.type, :activity_filter => params[:activity_filter], :format => 'js') %>&latest_activity_id=' + get_latest_activity_id()})}, <%=GlobalConfig.live_activity_update_interval%> * 1000)
|
15
|
+
<% end -%>
|
16
|
+
});
|
17
|
+
<% end -%>
|
18
|
+
<%= javascript_include_tag 'muck_activities' %>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<% if !activities.blank? -%>
|
2
|
+
<% activities.each do |activity| -%>
|
3
|
+
<% cache ("activities/#{I18n.locale}/#{activity.id}") do -%>
|
4
|
+
<%= render( :partial => "activity_templates/#{activity.partial}", :locals => {:activity => activity}) %>
|
5
|
+
<hr class="space">
|
6
|
+
<% end -%>
|
7
|
+
<% end -%>
|
8
|
+
<% end -%>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<div class="activity-comment">
|
2
|
+
<div class="actor-icon"><%= icon activity.source %></div>
|
3
|
+
<p><span class="actor"><%= link_to activity.source.display_name, activity.source %></span>
|
4
|
+
<span class="activity-time"><%= t("muck.activities.time_ago", :time_in_words => time_ago_in_words(activity.created_at)) %></span></p>
|
5
|
+
<p><%= h comment.body %></p>
|
6
|
+
</div>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<% if activity.has_comments? -%>
|
2
|
+
<%= render( :partial => "activities/comment", :collection => activity.comments, :locals => { :activity => activity } ) %>
|
3
|
+
<% end -%>
|
4
|
+
<div id="comment_<%=activity.dom_id%>" class="activity-comment <%= has_comments_css(activity) %>">
|
5
|
+
<div class="actor-icon"><%= icon current_user %></div>
|
6
|
+
<%= activity_comment_link(activity) %>
|
7
|
+
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<% if activities_object.status
|
2
|
+
status = h(activities_object.status.content)
|
3
|
+
time = activities_object.status.created_at
|
4
|
+
else
|
5
|
+
status = t('muck.activities.update_status_message')
|
6
|
+
time = false
|
7
|
+
end -%>
|
8
|
+
<p id="current-status-text"><span id="current-status-name"><%= activities_object.display_name %></span> <%= status %></p>
|
9
|
+
<span id="status-time" class="status-time"><%= t('muck.activities.time_ago', :time_in_words => time_ago_in_words(time)) if time %></span>
|
10
|
+
<span id="status-clear">
|
11
|
+
<% if activities_object.status -%>
|
12
|
+
- <%= delete_activity(activities_object.status, :text, t("muck.activities.clear")) %>
|
13
|
+
<% end -%>
|
14
|
+
</span>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% if button_type == :text -%>
|
2
|
+
<%= link_to_remote( button_text, :url => activity_path(activity, :format => 'js'), :method => :delete) %>
|
3
|
+
<% else -%>
|
4
|
+
<% remote_form_for(:activity, :url => activity_path(activity, :format => 'js'), :html => { :class => "delete-form activity-delete", :method => :delete } ) do |f| -%>
|
5
|
+
<% if button_type == :image -%>
|
6
|
+
<%= image_submit_tag '/images/icons/delete.png', {:title => button_text, :width => '15', :height => '15', :alt => button_text } %>
|
7
|
+
<% else -%>
|
8
|
+
<%= submit_tag button_text, { :title => button_text } %>
|
9
|
+
<% end -%>
|
10
|
+
<% end -%>
|
11
|
+
<% end -%>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<div id="status-update">
|
2
|
+
<%= output_errors(t('muck.users.problem_with_status'), {:class => 'help-box'}, @activity) %>
|
3
|
+
<% remote_form_for(:activity,
|
4
|
+
:url => activities_path(:parent_id => activities_object, :parent_type => activities_object.type, :format => 'js'),
|
5
|
+
:html => { :id => 'status_update_form', :class => 'clear',
|
6
|
+
:action => activities_path(:parent_id => activities_object, :parent_type => activities_object.type) } ) do |f| -%>
|
7
|
+
<div id="progress-bar" style="display:none;">
|
8
|
+
<h3><%= t('muck.activities.updating_status_message') %></h3>
|
9
|
+
<img src="/images/loading.gif" alt="progress bar">
|
10
|
+
</div>
|
11
|
+
<div id="status-update-field" class="status-update-dim">
|
12
|
+
<%= t("muck.activities.status_update_prompt") %>
|
13
|
+
<%= f.text_field :content, :id => 'status_update' %>
|
14
|
+
<span id="status-update-button">
|
15
|
+
<%= f.submit t('muck.activities.post'), :id => 'submit_status', :class => "button" %>
|
16
|
+
</span>
|
17
|
+
</div>
|
18
|
+
<%= f.hidden_field :template, :value => 'status_update' %>
|
19
|
+
<%= f.hidden_field :is_status_update, :value => true %>
|
20
|
+
<% end %>
|
21
|
+
</div>
|
22
|
+
<% content_for :javascript do -%>
|
23
|
+
jQuery(document).ready(function() {
|
24
|
+
jQuery('#status_update').click(function(){
|
25
|
+
jQuery('#status-update-field').removeClass('status-update-dim');
|
26
|
+
jQuery('#status-update-field').addClass('status-update-lit');
|
27
|
+
});
|
28
|
+
|
29
|
+
jQuery("#status_update_form").submit(function() {
|
30
|
+
jQuery("#status-update-field").hide();
|
31
|
+
jQuery("#submit_status").hide();
|
32
|
+
jQuery("#progress-bar").show();
|
33
|
+
});
|
34
|
+
});
|
35
|
+
<% end -%>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<ul id="activity-filter">
|
2
|
+
<li class="all-activities <%=no_filter?%>"><a href="<%=all_activities_url%>"><%=t('muck.activities.all_activities')%></a></li>
|
3
|
+
<% activity_types.each do |type| -%>
|
4
|
+
<li class="<%= type.template%> <%=is_current_filter?(type.template)%>"><a href="?activity_filter=<%= type.template%>"><%= type.template.titleize.pluralize %></a></li>
|
5
|
+
<% end -%>
|
6
|
+
</ul>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div class="activity activity-status-update delete-container" id="<%= activity.dom_id %>">
|
2
|
+
<div class="actor-icon"><%= icon activity.source %></div>
|
3
|
+
<div class="activity-content">
|
4
|
+
<p><span class="actor"><%= link_to activity.source.display_name, activity.source %></span> <%= activity.content %></p>
|
5
|
+
<span class="activity-time"><%= t("muck.activities.time_ago", :time_in_words => time_ago_in_words(activity.created_at)) %></span>
|
6
|
+
<% if !activity.has_comments? -%>
|
7
|
+
<span><a id="make_comment_<%=activity.dom_id%>" class="make-comment" href="#"><%=t('muck.activities.make_comment')%></a></span>
|
8
|
+
<% end -%>
|
9
|
+
<%= delete_activity(activity, :image) %>
|
10
|
+
</div>
|
11
|
+
<%= activity_comments(activity) %>
|
12
|
+
</div>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class AddMuckActivities < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def self.up
|
4
|
+
|
5
|
+
create_table :activities, :force => true do |t|
|
6
|
+
t.integer :item_id
|
7
|
+
t.string :item_type
|
8
|
+
t.string :template
|
9
|
+
t.integer :source_id
|
10
|
+
t.string :source_type
|
11
|
+
t.text :content
|
12
|
+
t.string :title
|
13
|
+
t.boolean :is_status_update, :default => false
|
14
|
+
t.boolean :is_public, :default => true
|
15
|
+
t.timestamps
|
16
|
+
end
|
17
|
+
|
18
|
+
add_index :activities, ["item_id", "item_type"]
|
19
|
+
|
20
|
+
create_table :activity_feeds, :force => true do |t|
|
21
|
+
t.integer :activity_id
|
22
|
+
t.integer :ownable_id
|
23
|
+
t.string :ownable_type
|
24
|
+
end
|
25
|
+
|
26
|
+
add_index :activity_feeds, ["activity_id"]
|
27
|
+
add_index :activity_feeds, ["ownable_id", "ownable_type"]
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.down
|
32
|
+
drop_table :activities
|
33
|
+
drop_table :activity_feeds
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class ActionController::Routing::RouteSet
|
2
|
+
def load_routes_with_muck_activities!
|
3
|
+
muck_activities_routes = File.join(File.dirname(__FILE__), *%w[.. .. config muck_activities_routes.rb])
|
4
|
+
add_configuration_file(muck_activities_routes) unless configuration_files.include? muck_activities_routes
|
5
|
+
load_routes_without_muck_activities!
|
6
|
+
end
|
7
|
+
alias_method_chain :load_routes!, :muck_activities
|
8
|
+
end
|