Diff
Modified: trunk/Gemfile (3528 => 3529)
--- trunk/Gemfile 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/Gemfile 2013-05-07 14:06:29 UTC (rev 3529)
@@ -27,4 +27,5 @@
gem "sunspot_solr", "~> 2.0.0"
gem "will_paginate", "~> 2.3.16"
gem "open_id_authentication", "~> 1.1.0"
+gem "simple-rss", "~> 1.2.3"
Modified: trunk/Rakefile (3528 => 3529)
--- trunk/Rakefile 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/Rakefile 2013-05-07 14:06:29 UTC (rev 3529)
@@ -199,42 +199,20 @@
activities = []
User.find(:all, :conditions => "activated_at IS NOT NULL", :include => :profile).map do |object|
-
- activities << Activity.new(
- :subject => object,
- :subject_label => object.name,
- :action ="" 'register',
- :created_at => object.created_at)
-
+ activities += Activity.new_activities(:subject => object, :action ="" 'create', :object => object, :timestamp => object.created_at)
if object.profile.updated_at && object.profile.updated_at != object.profile.created_at
-
-
- activities << Activity.new(
- :subject => object,
- :subject_label => object.name,
- :action ="" 'edit',
- :created_at => object.profile.updated_at)
-
+ activities += Activity.new_activities(:subject => object, :action ="" 'edit', :object => object, :timestamp => object.profile.updated_at)
end
end
(Workflow.all + Blob.all + Pack.all).map do |object|
-
- activities << Activity.new(
- :subject => object.contributor,
- :action ="" 'create',
- :objekt => object,
- :auth => object,
- :created_at => object.created_at)
-
+ activities += Activity.new_activities(:subject => object.contributor, :action ="" 'create', :object => object, :timestamp => object.created_at)
if object.updated_at && object.updated_at != object.created_at
+ activities += Activity.new_activities(:subject => object.contributor, :action ="" 'edit', :object => object, :timestamp => object.updated_at)
+ end
- activities << Activity.new(
- :subject => object.contributor,
- :action ="" 'edit',
- :objekt => object,
- :auth => object,
- :created_at => object.updated_at)
+ object.contribution.policy.permissions.each do |permission|
+ activities += Activity.new_activities(:subject => object.contributor, :action ="" 'create', :object => permission, :timestamp => permission.created_at, :contributable => object)
end
end
@@ -243,182 +221,80 @@
end
workflow_versions.map do |object|
-
- activities << Activity.new(
- :subject => object.contributor,
- :action ="" 'create',
- :objekt => object,
- :extra => object.version,
- :auth => object.versioned_resource,
- :created_at => object.created_at)
-
+ activities += Activity.new_activities(:subject => object.contributor, :action ="" 'create', :object => object, :timestamp => object.created_at)
if object.updated_at && object.updated_at != object.created_at
-
- activities << Activity.new(
- :subject => object.contributor,
- :action ="" 'edit',
- :objekt => object,
- :extra => object.version,
- :auth => object.versioned_resource,
- :created_at => object.updated_at)
+ activities += Activity.new_activities(:subject => object.contributor, :action ="" 'edit', :object => object, :timestamp => object.updated_at)
end
end
(BlobVersion.find(:all, :conditions => "version > 1")).map do |object|
-
- activities << Activity.new(
- :subject => object.blob.contributor,
- :action ="" 'create',
- :objekt => object,
- :extra => object.version,
- :auth => object.versioned_resource,
- :created_at => object.created_at)
-
+ activities += Activity.new_activities(:subject => object.blob.contributor, :action ="" 'create', :object => object, :timestamp => object.created_at)
if object.updated_at && object.updated_at != object.created_at
-
- activities << Activity.new(
- :subject => object.blob.contributor,
- :action ="" 'edit',
- :objekt => object,
- :extra => object.version,
- :auth => object.versioned_resource,
- :created_at => object.updated_at)
+ activities += Activity.new_activities(:subject => object.blob.contributor, :action ="" 'edit', :object => object, :timestamp => object.updated_at)
end
end
- activities += Comment.all.map do |comment|
-
- Activity.new(
- :subject => comment.user,
- :action ="" 'create',
- :objekt => comment,
- :auth => comment.commentable,
- :created_at => comment.created_at)
+ Comment.all.each do |comment|
+ activities += Activity.new_activities(:subject => comment.user, :action ="" 'create', :object => comment, :timestamp => comment.created_at)
end
- activities += Bookmark.all.map do |bookmark|
-
- Activity.new(
- :subject => bookmark.user,
- :action ="" 'create',
- :objekt => bookmark,
- :auth => bookmark.bookmarkable,
- :created_at => bookmark.created_at)
+ Bookmark.all.each do |bookmark|
+ activities += Activity.new_activities(:subject => bookmark.user, :action ="" 'create', :object => bookmark, :timestamp => bookmark.created_at)
end
- Announcement.all.each do |object|
-
- activities << Activity.new(
- :subject => object.user,
- :action ="" 'create',
- :objekt => object,
- :created_at => object.created_at)
-
- if object.updated_at && object.updated_at != object.created_at
-
-
- activities << Activity.new(
- :subject => object.user,
- :action ="" 'edit',
- :objekt => object,
- :created_at => object.updated_at)
+ Announcement.all.each do |announcement|
+ activities += Activity.new_activities(:subject => announcement.user, :action ="" 'create', :object => announcement, :timestamp => announcement.created_at)
+ if announcement.updated_at && announcement.updated_at != announcement.created_at
+ activities += Activity.new_activities(:subject => announcement.user, :action ="" 'edit', :object => announcement, :timestamp => announcement.updated_at)
end
end
- Citation.all.each do |object|
-
- activities << Activity.new(
- :subject => object.user,
- :action ="" 'create',
- :objekt => object,
- :auth => object.workflow,
- :created_at => object.created_at)
-
- if object.updated_at && object.updated_at != object.created_at
-
- activities << Activity.new(
- :subject => object.user,
- :action ="" 'edit',
- :objekt => object,
- :auth => object.workflow,
- :created_at => object.updated_at)
+ Citation.all.each do |citation|
+ activities += Activity.new_activities(:subject => citation.user, :action ="" 'create', :object => citation, :timestamp => citation.created_at)
+ if citation.updated_at && citation.updated_at != citation.created_at
+ activities += Activity.new_activities(:subject => citation.user, :action ="" 'edit', :object => citation, :timestamp => citation.updated_at)
end
end
- Rating.all.each do |object|
-
- activities << Activity.new(
- :subject => object.user,
- :action ="" 'create',
- :objekt => object,
- :auth => object.rateable,
- :extra => object.rating,
- :created_at => object.created_at)
+ Rating.all.each do |rating|
+ activities += Activity.new_activities(:subject => rating.user, :action ="" 'create', :object => rating, :timestamp => rating.created_at)
end
- Review.all.each do |object|
-
- activities << Activity.new(
- :subject => object.user,
- :action ="" 'create',
- :objekt => object,
- :auth => object.reviewable,
- :created_at => object.created_at)
-
- if object.updated_at && object.updated_at != object.created_at
-
- activities << Activity.new(
- :subject => object.user,
- :action ="" 'edit',
- :objekt => object,
- :auth => object.reviewable,
- :created_at => object.updated_at)
+ Review.all.each do |review|
+ activities += Activity.new_activities(:subject => review.user, :action ="" 'create', :object => review, :timestamp => review.created_at)
+ if review.updated_at && review.updated_at != review.created_at
+ activities += Activity.new_activities(:subject => review.user, :action ="" 'edit', :object => review, :timestamp => review.updated_at)
end
end
- Tagging.all.each do |object|
-
- activities << Activity.new(
- :subject => object.user,
- :action ="" 'create',
- :objekt => object,
- :auth => object.taggable,
- :extra => object.tag.name,
- :created_at => object.created_at)
+ Tagging.all.each do |tagging|
+ activities += Activity.new_activities(:subject => tagging.user, :action ="" 'create', :object => tagging, :timestamp => tagging.created_at)
end
- Network.all.each do |object|
-
- activities << Activity.new(
- :subject => object.owner,
- :action ="" 'create',
- :objekt => object,
- :created_at => object.created_at)
-
- if object.updated_at && object.updated_at != object.created_at
-
- activities << Activity.new(
- :subject => object.owner,
- :action ="" 'edit',
- :objekt => object,
- :created_at => object.updated_at)
+ Network.all.each do |network|
+ activities += Activity.new_activities(:subject => network.owner, :action ="" 'create', :object => network, :timestamp => network.created_at)
+ if network.updated_at && network.updated_at != network.created_at
+ activities += Activity.new_activities(:subject => network.owner, :action ="" 'edit', :object => network, :timestamp => network.updated_at)
end
end
Membership.all.each do |membership|
-
- next unless membership.accepted?
-
- activities << Activity.new(
- :subject => membership.user,
- :action ="" 'join',
- :objekt => membership.network,
- :created_at => membership.accepted_at)
+ if membership.accepted_at
+ activities += Activity.new_activities(:subject => membership.user, :action ="" 'create', :object => membership, :timestamp => membership.accepted_at)
+ end
end
+ GroupAnnouncement.all.each do |group_announcement|
+ activities += Activity.new_activities(:subject => group_announcement.user, :action ="" 'create', :object => group_announcement, :timestamp => group_announcement.created_at)
+ end
+
+ Creditation.all.each do |credit|
+ activities += Activity.new_activities(:subject => credit.creditable.contributor, :action ="" 'create', :object => credit, :timestamp => credit.created_at)
+ end
+
activities.sort! do |a, b|
- if a.created_at && b.created_at
- a.created_at <=> b.created_at
+ if a.timestamp && b.timestamp
+ a.timestamp <=> b.timestamp
else
a.object_id <=> b.object_id
end
@@ -430,6 +306,18 @@
end
+desc 'Synchronize all Atom feeds'
+task "myexp:feed:sync:all" do
+ require File.dirname(__FILE__) + '/config/environment'
+
+ Feed.all.each do |feed|
+ begin
+ feed.synchronize!
+ rescue
+ end
+ end
+end
+
desc 'Perform spam analysis on user profiles'
task "myexp:spam:run" do
require File.dirname(__FILE__) + '/config/environment'
Added: trunk/app/controllers/activities_controller.rb (0 => 3529)
--- trunk/app/controllers/activities_controller.rb (rev 0)
+++ trunk/app/controllers/activities_controller.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,29 @@
+# myExperiment: app/controllers/activities_controller.rb
+#
+# Copyright (c) 2007-2013 The University of Manchester, the University of
+# Oxford, and the University of Southampton. See license.txt for details.
+
+class ActivitiesController < ApplicationController
+
+ def feature
+
+ context = extract_resource_context(params)
+ render_404("Activity context not found.") if context.nil?
+ render_401("Not authorized.") unless Authorization.check('edit', context, current_user)
+
+ activity = context.activities.find(params[:id].to_i)
+ render_404("Activity not found.") if activity.nil?
+
+ case request.method
+ when :put
+ activity.update_attribute(:featured, true)
+
+ when :delete
+ activity.update_attribute(:featured, false)
+ end
+
+ redirect_to context
+ end
+
+end
+
Modified: trunk/app/controllers/application_controller.rb (3528 => 3529)
--- trunk/app/controllers/application_controller.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/controllers/application_controller.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -184,106 +184,128 @@
list = s.split(',')
end
- def update_policy(contributable, params)
+ def update_policy(contributable, params, user)
- # this method will return an error message is something goes wrong (empty string in case of success)
- error_msg = ""
+ def aux(contributable, params)
- # BEGIN validation and initialisation
+ # this method will return an error message is something goes wrong (empty string in case of success)
+ error_msg = ""
- # If a group policy was selected, use that, and delete the old custom one (if there was one).
- if params[:policy_type] == "group"
- if contributable.contribution.policy && !contributable.contribution.policy.group_policy?
- contributable.contribution.policy.destroy
+ # BEGIN validation and initialisation
+
+ # If a group policy was selected, use that, and delete the old custom one (if there was one).
+ if params[:policy_type] == "group"
+ if contributable.contribution.policy && !contributable.contribution.policy.group_policy?
+ contributable.contribution.policy.destroy
+ end
+ contributable.contribution.policy_id = params[:group_policy]
+ contributable.contribution.save
+ return
end
- contributable.contribution.policy_id = params[:group_policy]
- contributable.contribution.save
- return
- end
- # This variable will hold current settings of the policy in case something
- # goes wrong and a revert would be needed at some point
- last_saved_policy = nil
-
- return if params[:sharing].nil? or params[:sharing][:class_id].blank?
+ # This variable will hold current settings of the policy in case something
+ # goes wrong and a revert would be needed at some point
+ last_saved_policy = nil
+
+ return if params[:sharing].nil? or params[:sharing][:class_id].blank?
- sharing_class = params[:sharing][:class_id]
- updating_class = (params[:updating] and !params[:updating][:class_id].blank?) ? params[:updating][:class_id] : "6"
+ sharing_class = params[:sharing][:class_id]
+ updating_class = (params[:updating] and !params[:updating][:class_id].blank?) ? params[:updating][:class_id] : "6"
- # Check allowed sharing_class values
- return unless [ "0", "1", "2", "3", "4", "7" ].include? sharing_class
-
- # Check allowed updating_class values
- return unless [ "0", "1", "5", "6" ].include? updating_class
-
- view_protected = 0
- view_public = 0
- download_protected = 0
- download_public = 0
- edit_protected = 0
- edit_public = 0
-
- # BEGIN initialisation and validation
+ # Check allowed sharing_class values
+ return unless [ "0", "1", "2", "3", "4", "7" ].include? sharing_class
+
+ # Check allowed updating_class values
+ return unless [ "0", "1", "5", "6" ].include? updating_class
+
+ view_protected = 0
+ view_public = 0
+ download_protected = 0
+ download_public = 0
+ edit_protected = 0
+ edit_public = 0
+
+ # BEGIN initialisation and validation
- if contributable.contribution.policy.nil? || contributable.contribution.policy.group_policy?
- last_saved_policy = Policy._default(current_user, nil) # second parameter ensures that this policy is not applied anywhere
+ if contributable.contribution.policy.nil? || contributable.contribution.policy.group_policy?
+ last_saved_policy = Policy._default(current_user, nil) # second parameter ensures that this policy is not applied anywhere
- policy = Policy.new(:name => 'auto',
- :contributor_type => 'User', :contributor_id => current_user.id,
- :share_mode => sharing_class,
- :update_mode => updating_class)
- contributable.contribution.policy = policy # by doing this the new policy object is saved implicitly too
- contributable.contribution.save
- else
- policy = contributable.contribution.policy
- last_saved_policy = policy.clone # clone required, not 'dup' (which still works through reference, so the values in both get changed anyway - which is not what's needed here)
-
- policy.share_mode = sharing_class
- policy.update_mode = updating_class
- policy.save
- end
+ policy = Policy.new(:name => 'auto',
+ :contributor_type => 'User', :contributor_id => current_user.id,
+ :share_mode => sharing_class,
+ :update_mode => updating_class)
+ contributable.contribution.policy = policy # by doing this the new policy object is saved implicitly too
+ contributable.contribution.save
+ else
+ policy = contributable.contribution.policy
+ last_saved_policy = policy.clone # clone required, not 'dup' (which still works through reference, so the values in both get changed anyway - which is not what's needed here)
+
+ policy.share_mode = sharing_class
+ policy.update_mode = updating_class
+ policy.save
+ end
- # Process 'update' permissions for "Some of my Friends"
+ # Process 'update' permissions for "Some of my Friends"
- if updating_class == "5"
- if params[:updating_somefriends]
- # Delete old User permissions
+ if updating_class == "5"
+ if params[:updating_somefriends]
+ # Delete old User permissions
+ policy.delete_all_user_permissions
+
+ # Now create new User permissions, if required
+ params[:updating_somefriends].each do |f|
+ Permission.new(:policy => policy,
+ :contributor => (User.find f[1].to_i),
+ :view => 1, :download => 1, :edit => 1).save
+ end
+ else # none of the 'some of my friends' were selected, error
+ # revert changes made to policy (however any permissions updated will preserve the state)
+ policy.copy_values_from( last_saved_policy )
+ policy.save
+ error_msg += "You have selected to set 'update' permissions for 'Some of your Friends', but didn't select any from the list.</br>Previous (if any) or default sharing permissions have been set."
+ return error_msg
+ end
+ else
+ # Delete all User permissions - as this isn't mode 5 (i.e. the mode has changed),
+ # where some explicit permissions to friends are set
policy.delete_all_user_permissions
-
- # Now create new User permissions, if required
- params[:updating_somefriends].each do |f|
- Permission.new(:policy => policy,
- :contributor => (User.find f[1].to_i),
- :view => 1, :download => 1, :edit => 1).save
- end
- else # none of the 'some of my friends' were selected, error
- # revert changes made to policy (however any permissions updated will preserve the state)
- policy.copy_values_from( last_saved_policy )
- policy.save
- error_msg += "You have selected to set 'update' permissions for 'Some of your Friends', but didn't select any from the list.</br>Previous (if any) or default sharing permissions have been set."
- return error_msg
end
- else
- # Delete all User permissions - as this isn't mode 5 (i.e. the mode has changed),
- # where some explicit permissions to friends are set
- policy.delete_all_user_permissions
+
+
+ # Process explicit Group permissions now
+ process_permissions(policy, params)
+
+ logger.debug("------ Workflow create summary ------------------------------------")
+ logger.debug("current_user = #{current_user.id}")
+ logger.debug("updating_class = #{updating_class}")
+ logger.debug("sharing_class = #{sharing_class}")
+ logger.debug("policy = #{policy}")
+ logger.debug("group_sharing = #{params[:group_sharing]}")
+ logger.debug("-------------------------------------------------------------------")
+
+ # returns some message in case of errors (or empty string in case of success)
+ return error_msg
end
-
- # Process explicit Group permissions now
- process_permissions(policy, params)
+ # Remember which groups have view access to the contributable before
+ # changes are made.
- logger.debug("------ Workflow create summary ------------------------------------")
- logger.debug("current_user = #{current_user.id}")
- logger.debug("updating_class = #{updating_class}")
- logger.debug("sharing_class = #{sharing_class}")
- logger.debug("policy = #{policy}")
- logger.debug("group_sharing = #{params[:group_sharing]}")
- logger.debug("-------------------------------------------------------------------")
+ conditions = { :contributor_type => 'Network', :view => true }
- # returns some message in case of errors (or empty string in case of success)
- return error_msg
+ old_groups = contributable.contribution.policy.permissions.find(:all, :conditions => conditions).map { |p| p.contributor }
+
+ result = aux(contributable, params)
+
+ # Work out which groups have view access after the changes were made and
+ # generate activities for them.
+
+ contributable.contribution.policy.permissions.find(:all, :conditions => conditions).each do |permission|
+ next if old_groups.include?(permission.contributor)
+ Activity.create_activities(:subject => user, :action ="" 'create', :object => permission, :contributable => contributable)
+ end
+
+ result
end
def process_permissions(policy, params)
@@ -393,6 +415,33 @@
end
+ def update_feed_definition(resource, params)
+
+ attributes = { :uri => params[:feed_uri], :username => params[:feed_user] }
+
+ # Only set the password if one was provided
+
+ attributes[:password] = params[:feed_pass] unless params[:feed_pass].empty?
+
+ # Create the feed if necessary
+
+ if resource.feed.nil? && !params[:feed_uri].empty?
+ resource.create_feed(attributes)
+ end
+
+ # Delete the feed if necessary
+
+ if resource.feed && params[:feed_uri].empty?
+ resource.feed.destroy
+ end
+
+ # Update the feed if necessary
+
+ if resource.feed && !params[:feed_uri].empty?
+ resource.feed.update_attributes(attributes)
+ end
+ end
+
# helper function to determine the context of a polymorphic nested resource
def extract_resource_context(params)
Modified: trunk/app/controllers/blobs_controller.rb (3528 => 3529)
--- trunk/app/controllers/blobs_controller.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/controllers/blobs_controller.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -177,7 +177,7 @@
# update policy
@blob.contribution.update_attributes(params[:contribution])
- policy_err_msg = update_policy(@blob, params)
+ policy_err_msg = update_policy(@blob, params, current_user)
update_credits(@blob, params)
update_attributions(@blob, params)
@@ -246,7 +246,7 @@
@blob.refresh_tags(convert_tags_to_gem_format(params[:blob][:tag_list]), current_user) if params[:blob][:tag_list]
- policy_err_msg = update_policy(@blob, params)
+ policy_err_msg = update_policy(@blob, params, current_user)
update_credits(@blob, params)
update_attributions(@blob, params)
Modified: trunk/app/controllers/comments_controller.rb (3528 => 3529)
--- trunk/app/controllers/comments_controller.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/controllers/comments_controller.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -5,6 +5,8 @@
class CommentsController < ApplicationController
+ include ActivitiesHelper
+
before_filter :find_context, : [ :create, :index, :timeline ]
before_filter :find_comment, : [ :destroy ]
@@ -38,13 +40,17 @@
success = comment.save
if success
- Activity.create(:subject => current_user, :action ="" 'create', :objekt => comment, :auth => @context)
+ Activity.create_activities(:subject => current_user, :action ="" 'create', :object => comment, :auth => @context)
@context.solr_index if @context.respond_to?(:solr_index)
end
end
respond_to do |format|
- if ajaxy
+ if params[:activity_feed] || @context.kind_of?(Activity)
+ @context = @context.context if @context.kind_of?(Activity)
+ activities = activities_for_feed(:context => @context, :user => current_user)
+ format.html { render :partial => "activities/list", :locals => { :context => @context, :activities => activities, :user => current_user } }
+ elsif ajaxy
format.html { render :partial => "comments/comments", :locals => { :commentable => @context } }
else
format.html { redirect_to rest_resource_uri(@context) }
@@ -83,7 +89,11 @@
end
def find_context
- @context = extract_resource_context(params)
+ if request.path_parameters.include?("activity_id")
+ @context = Activity.find(request.path_parameters["activity_id"])
+ else
+ @context = extract_resource_context(params)
+ end
if @context.nil?
render_404("Comment context not found.")
Modified: trunk/app/controllers/group_announcements_controller.rb (3528 => 3529)
--- trunk/app/controllers/group_announcements_controller.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/controllers/group_announcements_controller.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -65,6 +65,9 @@
respond_to do |format|
if @announcement.save
+
+ Activity.create_activities(:subject => @announcement.user, :action ="" 'create', :object => @announcement)
+
flash[:notice] = 'Group announcement was successfully created.'
format.html { redirect_to group_announcements_url(@group) }
else
@@ -77,6 +80,9 @@
def update
respond_to do |format|
if @announcement.update_attributes(params[:announcement])
+
+ Activity.create_activities(:subject => @announcement.user, :action ="" 'edit', :object => @announcement)
+
flash[:notice] = 'GroupAnnouncement was successfully updated'
format.html { redirect_to group_announcement_url(@group, @announcement) }
else
Modified: trunk/app/controllers/memberships_controller.rb (3528 => 3529)
--- trunk/app/controllers/memberships_controller.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/controllers/memberships_controller.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -82,6 +82,9 @@
respond_to do |format|
if @membership.accept!
+
+ Activity.create_activities(:subject => @membership.user, :action ="" 'create', :object => @membership)
+
flash[:notice] = 'Membership was successfully accepted.'
format.html { redirect_to network_url(@membership.network_id) }
else
Modified: trunk/app/controllers/networks_controller.rb (3528 => 3529)
--- trunk/app/controllers/networks_controller.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/controllers/networks_controller.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -8,14 +8,15 @@
class NetworksController < ApplicationController
include ApplicationHelper
+ include ActivitiesHelper
before_filter :login_required, :except => [:index, :show, :content, :search, :all]
before_filter :find_networks, : [:all]
before_filter :find_network, : [:membership_request, :show, :tag, :content,
:edit, :update, :destroy, :invite, :membership_invite,
- :membership_invite_external]
- before_filter :find_network_auth_admin, : [:invite, :membership_invite, :membership_invite_external]
+ :membership_invite_external, :sync_feed, :subscription]
+ before_filter :find_network_auth_admin, : [:invite, :membership_invite, :membership_invite_external, :sync_feed]
before_filter :find_network_auth_owner, : [:edit, :update, :destroy]
# declare sweepers and which actions should invoke them
@@ -275,6 +276,15 @@
render :inline => `#{Conf.rdfgen_tool} groups address@hidden
}
end
+
+ format.atom {
+ @title = @network.title
+ @id = @resource = network_url(@network)
+ @updated = @network.updated_at.to_datetime.rfc3339
+ @entries = activities_for_feed(:context => @network, :user => current_user, :no_combine => true)
+
+ render "activities/feed.atom"
+ }
end
end
@@ -322,7 +332,10 @@
respond_to do |format|
if @network.save
- Activity.create(:subject => current_user, :action ="" 'create', :objekt => @network)
+ Activity.create_activities(:subject => current_user, :action ="" 'create', :object => @network)
+
+ update_feed_definition(@network, params)
+
if params[:network][:tag_list]
@network.tags_user_id = current_user
@network.tag_list = convert_tags_to_gem_format params[:network][:tag_list]
@@ -343,7 +356,8 @@
respond_to do |format|
if @network.update_attributes(params[:network])
- Activity.create(:subject => current_user, :action ="" 'edit', :objekt => @network)
+ Activity.create_activities(:subject => current_user, :action ="" 'edit', :object => @network)
+ update_feed_definition(@network, params)
@network.refresh_tags(convert_tags_to_gem_format(params[:network][:tag_list]), current_user) if params[:network][:tag_list]
flash[:notice] = 'Group was successfully updated.'
format.html { redirect_to network_url(@network) }
@@ -377,6 +391,7 @@
page.replace_html "mini_nav_tag_link", "(#{unique_tag_count})"
page.replace_html "tags_box_header_tag_count_span", "(#{unique_tag_count})"
page.replace_html "tags_inner_box", :partial => "tags/tags_box_inner", :locals => { :taggable => @network, :owner_id => @network.user_id }
+ page.replace_html "activities", :partial => "activities/list", :locals => { :context => @network, :activities => activities_for_feed(:context => @network, :user => current_user), :user => current_user }
end
}
end
@@ -394,7 +409,30 @@
render :partial => 'networks/autocomplete_list', :locals => { :networks => groups }
end
+ def sync_feed
+ @network.feed.synchronize! if @network.feed
+ redirect_to network_path(@network)
+ end
+ # PUT/DELETE /groups/1/subscription
+ def subscription
+
+ object = @network
+
+ existing_subscription = current_user.subscriptions.find(:first,
+ :conditions => { :objekt_type => object.class.name, :objekt_id => object.id } )
+
+ case request.method
+ when :put
+ current_user.subscriptions.create(:objekt => object) unless existing_subscription
+
+ when :delete
+ current_user.subscriptions.delete(existing_subscription) if existing_subscription
+ end
+
+ redirect_to object
+ end
+
protected
def find_networks
Modified: trunk/app/controllers/packs_controller.rb (3528 => 3529)
--- trunk/app/controllers/packs_controller.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/controllers/packs_controller.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -143,7 +143,7 @@
end
# update policy
- policy_err_msg = update_policy(@pack, params)
+ policy_err_msg = update_policy(@pack, params, current_user)
if policy_err_msg.blank?
update_layout(@pack, params[:layout]) unless params[:policy_type] == "group"
flash[:notice] = 'Pack was successfully created.'
@@ -171,7 +171,7 @@
respond_to do |format|
if @pack.update_attributes(params[:pack])
@pack.refresh_tags(convert_tags_to_gem_format(params[:pack][:tag_list]), current_user) if params[:pack][:tag_list]
- policy_err_msg = update_policy(@pack, params)
+ policy_err_msg = update_policy(@pack, params, current_user)
if policy_err_msg.blank?
update_layout(@pack, params[:layout]) unless params[:policy_type] == "group"
flash[:notice] = 'Pack was successfully updated.'
Modified: trunk/app/controllers/workflows_controller.rb (3528 => 3529)
--- trunk/app/controllers/workflows_controller.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/controllers/workflows_controller.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -368,7 +368,7 @@
rescue
end
- policy_err_msg = update_policy(@workflow, params)
+ policy_err_msg = update_policy(@workflow, params, current_user)
# Credits and Attributions:
update_credits(@workflow, params)
@@ -552,7 +552,7 @@
@workflow.solr_index if Conf.solr_enable
end
- policy_err_msg = update_policy(@workflow, params)
+ policy_err_msg = update_policy(@workflow, params, current_user)
update_credits(@workflow, params)
update_attributions(@workflow, params)
Added: trunk/app/helpers/activities_helper.rb (0 => 3529)
--- trunk/app/helpers/activities_helper.rb (rev 0)
+++ trunk/app/helpers/activities_helper.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,280 @@
+# myExperiment: app/helpers/activities_helper.rb
+#
+# Copyright (c) 2007-2013 The University of Manchester, the University of
+# Oxford, and the University of Southampton. See license.txt for details.
+
+module ActivitiesHelper
+
+ def activity_link(activity, source)
+
+ case source
+ when :subject
+ thing = activity.subject
+ label = activity.subject_label
+ when :object
+ thing = activity.objekt
+ label = activity.objekt_label
+ when :context
+ thing = activity.context
+ label = activity.context.label
+ when :auth
+ thing = activity.auth
+ label = activity.auth.label
+ end
+
+ thing = thing.versioned_resource if thing.respond_to?(:versioned_resource)
+
+ if thing
+ path = case thing.class.name
+ when "Bookmark"
+ polymorphic_path(thing.bookmarkable)
+ when "Comment"
+ polymorphic_path(thing.commentable)
+ when "Citation"
+ polymorphic_path(thing.workflow)
+ when "GroupAnnouncement"
+ group_announcement_path(thing.network, thing)
+ else
+ polymorphic_path(thing)
+ end
+
+ link_to(h(label), path)
+ else
+ h(label)
+ end
+ end
+
+ def combine_activities?(a, b)
+
+ return false if a.action != b.action
+ return false if a.objekt_type != b.objekt_type
+
+ return true if a.action == 'create' && a.objekt_type == 'Membership'
+ return true if a.action == 'create' && a.objekt_type == 'Tagging' && a.subject == b.subject
+
+ false
+ end
+
+ def activities_for_feed(opts)
+
+ page = opts.delete(:page) || 1
+ per_page = opts.delete(:per_page) || 12
+
+ first = (page - 1) * per_page
+ last = first + (per_page - 1)
+
+ # Create the conditions based on the contexts (resources to which news
+ # items belong) and also the types of new items to show.
+
+ context_bits = []
+ context_vars = []
+
+ if opts[:context]
+ context_bits << "(activities.context_type = ? AND activities.context_id = ?)"
+ context_vars << opts[:context].class.name
+ context_vars << opts[:context].id
+ else
+
+ end
+
+ type_bits = []
+ type_vars = []
+
+ # Create the conditions
+
+ conditions_bits = []
+ conditions_vars = []
+
+ unless context_bits.empty?
+ conditions_bits << "(" + context_bits.join(" OR ") + ")"
+ conditions_vars += context_vars
+ end
+
+ unless type_bits.empty?
+ conditions_bits << "(" + type_bits.join(" OR ") + ")"
+ conditions_vars += type_vars
+ end
+
+ if context_bits.length > 0
+ conditions = [context_bits.join(" AND "), *context_vars]
+ end
+
+ order = 'featured DESC, timestamp DESC, priority ASC'
+
+ activities = Authorization.scoped(Activity,
+ :auth_type => 'activities.auth_type',
+ :auth_id => 'activities.auth_id',
+ :group => 'activities.id',
+ :authorised_user => opts[:user])
+
+ results = []
+ pos = 0
+
+ while results.length <= last
+ incoming = activities.all(:conditions => conditions, :order => order, :limit => "#{pos}, #{per_page}")
+
+ break if incoming.length == 0
+
+ incoming.each do |activity|
+ if results.length > 0 && !opts[:no_combine] && combine_activities?(activity, results.last.first)
+ results.last << activity
+ else
+ results << [activity]
+ end
+ end
+
+ pos = pos + per_page
+ end
+
+ results[first..last]
+ end
+
+ def sentence(bits)
+ result = ""
+
+ bits.each_index do |i|
+ result << bits[i]
+ result << ", " unless i == bits.length - 1
+ result << "and " if i == bits.length - 2
+ end
+
+ result
+ end
+
+ def activity_text_summary(text, opts = {})
+
+ tokens = HTML::Tokenizer.new(CGI::unescapeHTML(text))
+
+ result = ""
+
+ while (token = tokens.next) do
+ node = HTML::Node.parse(nil, 0, 0, token, false)
+ result << node.to_s if node.kind_of?(HTML::Text) || node.kind_of?(String)
+ result << " "
+ end
+
+ result = result.gsub(/\s+/, ' ').strip
+
+ min_chars = opts[:min_chars] || 200
+
+ if (result.length > min_chars)
+ result = h("#{result[0..min_chars]}") + " …"
+ end
+
+ "<div class='summary'>#{result}</div>"
+ end
+
+ def news_item_avatar(news_items)
+
+ news_item = news_items.first
+
+ # Only show an image if all the subjects match
+
+ if news_items.length > 1
+ news_items.each do |news_item|
+ return if news_item.subject != news_item.subject
+ end
+ end
+
+ if news_item.subject_type == 'User'
+ avatar(news_item.subject_id, 48)
+ elsif news_item.subject_type == 'Network'
+ avatar(news_item.subject.owner.id, 48)
+ end
+ end
+
+ def activity_title(activity_set)
+
+ activity = activity_set.first
+
+ case activity.objekt ? "#{activity.objekt_type} #{activity.action}" : activity.action
+ when "Announcement create"
+ "#{activity_link(activity, :subject)} announced #{activity_link(activity, :object)}"
+ when "Announcement edit"
+ "#{activity_link(activity, :subject)} edited #{activity_link(activity, :object)}"
+ when "Blob create"
+ "#{activity_link(activity, :subject)} uploaded #{activity_link(activity, :object)}"
+ when "Blob edit"
+ "#{activity_link(activity, :subject)} edited #{activity_link(activity, :object)}"
+ when "BlobVersion create"
+ "#{activity_link(activity, :subject)} uploaded version #{activity.extra} of #{activity_link(activity, :object)}"
+ when "BlobVersion edit"
+ "#{activity_link(activity, :subject)} edited version #{activity.extra} of #{activity_link(activity, :object)}"
+ when "Bookmark create"
+ "#{activity_link(activity, :subject)} favourited #{activity_link(activity, :object)}"
+ when "Citation create"
+ "#{activity_link(activity, :subject)} added the citation #{activity_link(activity, :object)} to #{activity_link(activity, :auth)}"
+ when "Citation edit"
+ "#{activity_link(activity, :subject)} edited the citation #{activity_link(activity, :object)} on #{activity_link(activity, :auth)}"
+ when "Comment create"
+ "#{activity_link(activity, :subject)} commented:"
+ when "Friendship create"
+ user1 = activity.subject = activity.context ? activity.objekt.user : activity.objekt.friend
+ user2 = activity.subject = activity.context ? activity.objekt.friend : activity.objekt.user
+ "#{link_to(h(user1.name), polymorphic_path(user1))} is friends with #{link_to(h(user2.name), polymorphic_path(user2))}"
+ when "Network create"
+ "#{activity_link(activity, :subject)} created the #{activity_link(activity, :object)} group"
+ when "Network edit"
+ "#{activity_link(activity, :subject)} edited the #{activity_link(activity, :object)} group"
+ when "Pack create"
+ "#{activity_link(activity, :subject)} created #{activity_link(activity, :object)}"
+ when "Pack edit"
+ "#{activity_link(activity, :subject)} edited #{activity_link(activity, :object)}"
+ when "Rating create"
+ "#{activity_link(activity, :subject)} rated #{activity_link(activity, :auth)} with #{activity.extra}"
+ when "Review create"
+ "#{activity_link(activity, :subject)} reviewed #{activity_link(activity, :auth)}"
+ when "Review edit"
+ "#{activity_link(activity, :subject)} edited a review on #{activity_link(activity, :auth)}"
+ when "Tagging create"
+ "#{activity_link(activity, :subject)} tagged #{activity_link(activity, :auth)} with #{sentence(activity_set.map { |a| link_to(h(a.objekt.tag.name), tag_path(a.objekt.tag)) })}"
+ when "Workflow create"
+ "#{activity_link(activity, :subject)} uploaded #{activity_link(activity, :object)}"
+ when "Workflow edit"
+ "#{activity_link(activity, :subject)} edited #{activity_link(activity, :object)}"
+ when "WorkflowVersion create"
+ "#{activity_link(activity, :subject)} uploaded version #{activity.extra} of #{activity_link(activity, :object)}"
+ when "WorkflowVersion edit"
+ "#{activity_link(activity, :subject)} edited version #{activity.extra} of #{activity_link(activity, :object)}"
+ when "edit"
+ "#{activity_link(activity, :subject)} edited their profile"
+ when "register"
+ "#{activity_link(activity, :subject)} joined #{Conf.sitename}"
+ when "Membership create"
+ "#{sentence(activity_set.map { |a| activity_link(a, :subject) })} joined the #{activity_link(activity, :context)} group"
+ when "Permission create"
+ "#{activity_link(activity, :subject)} shared #{activity_link(activity, :auth)}"
+ when "FeedItem create"
+ link_to(h(activity.objekt.title), activity.objekt.link, :rel => "nofollow")
+ when "GroupAnnouncement create"
+ activity_link(activity, :object)
+ end
+ end
+
+ def activity_description(activity_set, opts = {})
+
+ min_chars = opts[:min_chars] || 300
+
+ activity = activity_set.first
+
+ case activity.objekt ? "#{activity.objekt_type} #{activity.action}" : activity.action
+ when "BlobVersion create"
+ activity.objekt.body_html
+ when "Comment create"
+ "<a name='comment_#{activity.objekt.id}'></a>#{activity_text_summary(activity.objekt.comment, :min_chars => min_chars)}"
+ when "Workflow create"
+ "<div style='float: left; width: 64px'>#{link_to(image_tag(workflow_version_preview_path(activity.objekt, 1, 'thumb'), :width => 64, :height => 64), workflow_version_path(activity.objekt 1))}</div><div class='activity-text'>#{activity.objekt.body_html}</div>"
+ when "WorkflowVersion create"
+ "<div style='float: left; width: 64px'>#{link_to(image_tag(workflow_version_preview_path(activity.objekt.workflow, activity.objekt.version, 'thumb'), :width => 64, :height => 64), workflow_version_path(activity.objekt.workflow, activity.objekt.version))}</div><div class='activity-text'>#{white_list(activity.objekt.revision_comments)}</div>"
+ when "Permission create"
+ if activity.auth.class == Workflow
+ "<div><div style='float: left; margin: 6px'>#{link_to(image_tag(workflow_preview_path(activity.auth, 'thumb'), :width => 64, :height => 64), workflow_path(activity.auth))}</div>#{activity_text_summary(activity.auth.body_html, :min_chars => min_chars)}<div style='clear: both'></div></div>"
+ end
+ when "FeedItem create"
+ "<div class='summary'>#{activity_text_summary(activity.objekt.content, :min_chars => min_chars)}</div>"
+ when "GroupAnnouncement create"
+ activity_text_summary(activity.objekt.body_html, :min_chars => min_chars)
+ end
+ end
+end
+
Modified: trunk/app/models/activity.rb (3528 => 3529)
--- trunk/app/models/activity.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/models/activity.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -3,12 +3,17 @@
# Copyright (c) 2012 University of Manchester and the University of Southampton.
# See license.txt for details.
+require 'securerandom'
+
class Activity < ActiveRecord::Base
belongs_to :subject, :polymorphic => true
belongs_to :objekt, :polymorphic => true
+ belongs_to :context, :polymorphic => true
belongs_to :auth, :polymorphic => true
+ has_many :comments, :as => :commentable, :dependent => :destroy
+
validates_presence_of :subject
validates_presence_of :action
validates_presence_of :subject_label
@@ -33,5 +38,129 @@
e.objekt_label = e.auth.name if e.auth.respond_to?(:name)
end
end
+
+ def self.new_activities(opts)
+
+ subject = opts[:subject]
+ action = ""
+ object = opts[:object]
+ timestamp = opts[:timestamp]
+ auth = nil
+ extra = nil
+ contexts = [subject]
+ priority = 0
+
+ return [] if opts[:auth].kind_of?(Activity)
+
+ # Set the timestamp to the current time if no timestamp was provided.
+
+ timestamp = Time.now if timestamp.nil?
+
+ case object.class.name
+
+ when "Workflow", "Blob", "Pack"
+
+ contexts << object
+ auth = object
+
+ priority = 100 if action == 'create'
+
+ when "WorkflowVersion", "BlobVersion"
+
+ contexts << object.versioned_resource
+ extra = object.version
+ auth = object.versioned_resource
+
+ priority = 50 if action == 'create'
+
+ when "Comment"
+
+ contexts << object.commentable
+ auth = object.commentable
+
+ when "Bookmark"
+
+ contexts << object.bookmarkable
+ auth = object.bookmarkable
+
+ when "Citation"
+
+ contexts << object.workflow
+ auth = object.workflow
+
+ when "Rating"
+
+ contexts << object.rateable
+ auth = object.rateable
+ extra = object.rating
+
+ when "Review"
+
+ contexts << object.reviewable
+ auth = object.reviewable
+
+ when "Tagging"
+
+ contexts << object.taggable
+ auth = object.taggable
+ extra = object.tag.name
+
+ when "Network"
+
+ contexts << object
+
+ priority = 100 if action == 'create'
+
+ when "Membership"
+
+ contexts << object.network
+
+ when "Permission"
+
+ contexts << opts[:contributable]
+ contexts << object.contributor
+ auth = opts[:contributable]
+
+ when "GroupAnnouncement"
+
+ contexts << object.network
+ extra = object.public
+
+ when "Creditation"
+
+ contexts << object.creditable
+ contexts << object.creditor if object.creditor != subject
+ auth = object.creditable
+
+ when "FeedItem"
+
+ auth = subject.feed.context
+ end
+
+ uuid = SecureRandom.uuid
+
+ contexts.map do |context|
+ Activity.new(
+ :subject => subject,
+ :action ="" action,
+ :objekt => object,
+ :extra => extra,
+ :auth => auth,
+ :uuid => uuid,
+ :timestamp => timestamp,
+ :priority => priority,
+ :context => context)
+ end
+ end
+
+
+ def self.create_activities(opts)
+ activities = self.new_activities(opts)
+
+ activities.each do |activity|
+ activity.save
+ end
+ end
+
end
Added: trunk/app/models/feed.rb (0 => 3529)
--- trunk/app/models/feed.rb (rev 0)
+++ trunk/app/models/feed.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,72 @@
+# myExperiment: app/models/feeds.rb
+#
+# Copyright (c) 2007-2013 The University of Manchester, the University of
+# Oxford, and the University of Southampton. See license.txt for details.
+
+require 'simple-rss'
+require 'open-uri'
+require 'encrypted_attributes'
+require 'curb'
+
+class Feed < ActiveRecord::Base
+
+ encrypts :password, :mode => :symmetric, :password => Conf.sym_encryption_key
+
+ belongs_to :context, :polymorphic => true
+
+ has_many :feed_items, :dependent => :destroy
+
+ def synchronize!
+
+ if uri
+
+ begin
+
+ c = Curl::Easy.new(uri)
+ c.http_auth_types = :basic
+
+ if username && password
+ c.username = username
+ c.password = password.decrypt
+ end
+
+ c.perform
+
+ result = SimpleRSS.parse(c.body_str)
+
+ result.feed.items.each do |item|
+
+ # Obtain a unique identifier for use within the context of this feed.
+ identifier = item[:id]
+
+ # Try to find an existing item in this feed using the identifier.
+ object = feed_items.find_by_identifier(item[:id])
+
+ if object.nil?
+ # Create a new object if an existing object wasn't found.
+ object = feed_items.new if object.nil?
+
+ notify = true
+ end
+
+ object.identifier = item[:id]
+ object.title = item[:title]
+ object.content = item[:content]
+ object.author = item[:author]
+ object.link = item[:link]
+ object.item_published_at = item[:published]
+
+ success = object.save
+
+ if (success && notify)
+ Activity.create_activities(:subject => context, :action ="" 'create', :object => object, :timestamp => object.item_published_at)
+ end
+ end
+
+ rescue
+ return false
+ end
+ end
+ end
+end
+
Added: trunk/app/models/feed_item.rb (0 => 3529)
--- trunk/app/models/feed_item.rb (rev 0)
+++ trunk/app/models/feed_item.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,10 @@
+# myExperiment: app/models/feed_item.rb
+#
+# Copyright (c) 2007-2013 The University of Manchester, the University of
+# Oxford, and the University of Southampton. See license.txt for details.
+
+class FeedItem < ActiveRecord::Base
+ belongs_to :feed
+ has_many :activities, :as => :objekt
+end
+
Modified: trunk/app/models/membership.rb (3528 => 3529)
--- trunk/app/models/membership.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/models/membership.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -42,6 +42,9 @@
if self.network_established_at.nil?
self.network_establish!
end
+
+ Activity.create_activities(:subject => user, :action ="" 'create', :object => self)
+
return true
else
return false
Modified: trunk/app/models/network.rb (3528 => 3529)
--- trunk/app/models/network.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/models/network.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -20,6 +20,8 @@
has_many :blobs, :as => :contributor
has_many :workflows, :as => :contributor
has_many :policies, :as => :contributor
+ has_one :feed, :as => :context
+ has_many :activities, :as => :context
if Conf.solr_enable
searchable do
Added: trunk/app/models/subscription.rb (0 => 3529)
--- trunk/app/models/subscription.rb (rev 0)
+++ trunk/app/models/subscription.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,12 @@
+# myExperiment: app/models/subscription.rb
+#
+# Copyright (c) 2007-2013 The University of Manchester, the University of
+# Oxford, and the University of Southampton. See license.txt for details.
+
+class Subscription < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :objekt, :polymorphic => true
+
+ validates_uniqueness_of :objekt_id, :scope => [:user_id, :objekt_type]
+end
+
Modified: trunk/app/models/user.rb (3528 => 3529)
--- trunk/app/models/user.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/models/user.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -26,6 +26,8 @@
has_many :curation_events, :dependent => :destroy
+ has_many :subscriptions, :dependent => :destroy
+
def self.most_recent(limit=5)
self.find(:all,
:order => "users.created_at DESC",
Added: trunk/app/views/activities/_activities.rhtml (0 => 3529)
--- trunk/app/views/activities/_activities.rhtml (rev 0)
+++ trunk/app/views/activities/_activities.rhtml 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,7 @@
+<ol class="activity-feed">
+ <% activities.each do |activity_set| %>
+ <li<%= " class='featured'" if activity_set.length == 1 && activity_set.first.featured -%><%= " id='activity-#{activity_set.first.id}'" if activity_set.length == 1 -%>>
+ <%= render(:partial => "activities/activity", :locals => { :activity_set => activity_set, :user => user, :enable_feature => enable_feature } ) -%>
+ </li>
+ <% end %>
+</ol>
Added: trunk/app/views/activities/_activity.rhtml (0 => 3529)
--- trunk/app/views/activities/_activity.rhtml (rev 0)
+++ trunk/app/views/activities/_activity.rhtml 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,80 @@
+<% activity = (activity_set.class == Activity ? activity_set : activity_set.first) %>
+<div class="activity">
+ <div>
+ <div class="avatar-column">
+ <%= news_item_avatar(activity_set) -%>
+ </div>
+ <div class="activity-column">
+ <%= activity_title(activity_set) -%>
+ <%= activity_description(activity_set) -%>
+ <% if activity_set.length == 1 %>
+ <div class="actions">
+ <% if enable_feature %>
+ <% if Authorization.check('edit', activity.context, user) %>
+ <% if activity.featured %>
+ <span><%= button_to("Unfeature", polymorphic_path([activity.context, activity], :action ="" :feature), :method => :delete) -%></span>
+ <% else %>
+ <span><%= button_to("Feature", polymorphic_path([activity.context, activity], :action ="" :feature), :method => :put) -%></span>
+ <% end %>
+ <% else %>
+ <% if activity.featured %>
+ <span>Featured</span>
+ <% end %>
+ <% end %>
+ <% end %>
+ <% if (activity.comments.length == 0) && (activity_set.length == 1) %>
+ <span><a href="" activity.id -%>').style.display = 'block'; return false;">Comment</a></span>
+ <% else %>
+ <span>Comment</span>
+ <% end %>
+ <span class="date"><%= datetime(activity.timestamp) -%></span>
+ </div>
+ <div class="commentSection"<%= " id='comment-section-#{activity_set.first.id}'" if activity_set.length == 1 -%>>
+ <% activity.comments.each do |comment| %>
+ <div class="activityCommentBox">
+ <div style="float: left">
+ <%= avatar(comment.user_id, 24) -%>
+ </div>
+
+ <div class="rhs">
+ <div class="username"><%= link_to(h(comment.user.name), user_path(comment.user)) -%></div>
+ <div class="comment-body"><%= simple_format(comment.comment) -%></div>
+ <div class="comment-timestamp"><%= datetime(comment.created_at) -%></div>
+ </div>
+ <div style="clear: left"></div>
+ </div>
+ <% end %>
+ <% if Authorization.check('create', Comment, user, activity.context) %>
+
+ <div class="activityCommentBox">
+ <div style="float: left">
+ <%= avatar(current_user.id, 24) -%>
+ </div>
+
+ <div class="rhs">
+ <% form_remote_tag(
+ :url ="" polymorphic_path([activity.context, activity, :comments]),
+ :update => 'activities',
+ :loading => "Element.show('addcomment_indicator')",
+ :complete => "Element.hide('addcomment_indicator'); $('comment').value = '';") do %>
+ <%= text_area_tag("comment[comment]") -%>
+ <br/>
+ <%= hidden_field_tag :activity_feed -%>
+ <%= submit_tag "Comment" %>
+ <%= image_tag "/images/spinner.gif", :id => "addcomment_indicator", :style => "margin-left: 1em; display: none;" %>
+ <% end %>
+ </div>
+ <div style="clear: left"></div>
+ </div>
+ <% end %>
+ </div>
+ <% if (activity.comments.length == 0) && (activity_set.length == 1) %>
+ <script>
+ document.getElementById("comment-section-<%= activity.id -%>").style.display = "none";
+ </script>
+ <% end %>
+ <% end %>
+ </div>
+ <div style="clear: both"/>
+ </div>
+</div>
Added: trunk/app/views/activities/_list.rhtml (0 => 3529)
--- trunk/app/views/activities/_list.rhtml (rev 0)
+++ trunk/app/views/activities/_list.rhtml 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,19 @@
+<% enable_feature = true unless defined?(enable_feature) %>
+<%= render(:partial => "activities/activities", :locals => { :activities => activities, :user => user, :enable_feature => enable_feature } ) -%>
+
+<% if defined?(context) && Authorization.check('create', Comment, user, context) %>
+ <div class="addCommentBox">
+ <% form_remote_tag(
+ :url ="" polymorphic_path([context, :comments]),
+ :update => 'activities',
+ :loading => "Element.show('addcomment_indicator')",
+ :complete => "Element.hide('addcomment_indicator'); $('comment').value = '';") do %>
+ <%= text_area_tag("comment[comment]") -%>
+ <br/>
+ <%= hidden_field_tag :activity_feed -%>
+ <%= submit_tag "Post news" %>
+ <%= image_tag "/images/spinner.gif", :id => "addcomment_indicator", :style => "margin-left: 1em; display: none;" %>
+ <% end %>
+ </div>
+<% end %>
+
Added: trunk/app/views/activities/feed.atom.erb (0 => 3529)
--- trunk/app/views/activities/feed.atom.erb (rev 0)
+++ trunk/app/views/activities/feed.atom.erb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<feed xml:lang="en" xmlns="http://www.w3.org/2005/Atom">
+ <title><%= CGI.escapeHTML(@title) -%></title>
+ <updated><%= @updated -%></updated>
+ <id><%= @id -%></id>
+ <link type="text/html" rel="alternate" href="" @resource -%>"/>
+<% @entries.each do |entry| %>
+ <entry>
+ <published><%= entry[0].timestamp.to_datetime.rfc3339 -%></published>
+ <title><%= strip_tags(activity_title(entry)) -%></title>
+ <content type="html"><%= strip_tags(activity_description(entry, :min_chars => 800)) -%></content>
+ <link type="text/html" rel="alternate" href="" @resource -%>"/>
+ <id>urn:uuid:<%= entry[0].uuid -%></id>
+ <author>
+ <name><%= h(entry[0].subject.label) -%></name>
+ </author>
+ </entry>
+<% end %>
+</feed>
Added: trunk/app/views/contributions/_subscription_box.html.erb (0 => 3529)
--- trunk/app/views/contributions/_subscription_box.html.erb (rev 0)
+++ trunk/app/views/contributions/_subscription_box.html.erb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,19 @@
+<% if logged_in? %>
+ <div class="contribution_section_box">
+ <p class="heading">
+ <%= info_icon_with_tooltip("By subscribing you can view the news items of this resource on your home page, personal atom feed or email notifications.") %>
+ Subscription
+ <% if current_user.subscriptions.find(:first, :conditions => { :objekt_type => object.class.name, :objekt_id => object.id } ) %>
+ <p>You have subscribed to this resource.</p>
+ <% form_tag polymorphic_path([:subscription, @network]), :method => :delete do %>
+ <%= submit_tag "Unsubscribe" -%>
+ <% end %>
+ <% else %>
+ <p>Subscribe to this resource to get notifications on your home page.</p>
+ <% form_tag polymorphic_path([:subscription, @network]), :method => :put do %>
+ <%= submit_tag "Subscribe" -%>
+ <% end %>
+ <% end %>
+ </p>
+ </div>
+<% end %>
Modified: trunk/app/views/networks/_form.rhtml (3528 => 3529)
--- trunk/app/views/networks/_form.rhtml 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/views/networks/_form.rhtml 2013-05-07 14:06:29 UTC (rev 3529)
@@ -37,9 +37,26 @@
<%= form.select :new_member_policy, Network::NEW_MEMBER_POLICY_OPTIONS.map {|o| [o[1],o[0]]} %>
</p>
</fieldset>
-
- <br/>
+ <fieldset class="atom-feed">
+ <legend>Atom feed</legend>
+ <p>
+ You can specify an Atom feed which will be used to insert items into this group's activity stream.
+ </p>
+ <p>
+ <b>Atom feed URL</b><br />
+ <%= text_field_tag :feed_uri, (@network.feed ? @network.feed.uri : nil) -%>
+ </p>
+ <p>
+ <b>Atom feed username (optional)</b><br />
+ <%= text_field_tag :feed_user, (@network.feed ? @network.feed.username : nil) -%>
+ </p>
+ <p>
+ <b>Atom feed password (optional)</b><br />
+ <%= password_field_tag :feed_pass -%>
+ </p>
+ </fieldset>
+
<p>
<strong>Description: </strong>
</p>
Modified: trunk/app/views/networks/show.rhtml (3528 => 3529)
--- trunk/app/views/networks/show.rhtml 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/app/views/networks/show.rhtml 2013-05-07 14:06:29 UTC (rev 3529)
@@ -38,6 +38,12 @@
<li><%= icon('content', content_network_path(@network), 'View Group Content', nil, 'View Group Content') %></li>
</ul>
+<% if @network.feed && Authorization.check("edit", @network, current_user) %>
+ <% form_tag sync_feed_network_path(@network), :method => :post do %>
+ <%= submit_tag "Synchronize feed" -%>
+ <% end %>
+<% end %>
+
<h1>
Group: <%=h @network.title %>
</h1>
@@ -99,13 +105,26 @@
<% end %>
<a name="news"></a>
- <h3>News</h3>
- <%= render :partial => "layouts/news", :locals => { :collection => news(@network, true) } %>
+
+ <div>
+ <div style="float: right"><%= link_to(image_tag("feed-icon.png", :alt_text => "Atom feed for the news items of this group"), network_url(@network, :format => :atom)) -%></div>
+ <h3>News</h3>
+ <div style="clear: both"></div>
+ </div>
+ <% activities = activities_for_feed(:context => @network, :user => current_user) %>
+
+ <div id="activities">
+ <%= render(:partial => "activities/list", :locals => { :context => @network, :activities => activities, :user => current_user, :enable_feature => true } ) -%>
+ </div>
+
</div>
<div class="contribution_right_box">
<%= render :partial => "owner_box", :locals => { :network => @network } %>
+ <% if false %>
+ <%= render :partial => "contributions/subscription_box", :locals => { :object => @network } -%>
+ <% end %>
<%= render :partial => "statistics_box", :locals => { :network => @network, :items => @shared_items } %>
<div class="contribution_section_box"> <!-- style="width: 130px; padding: 0.4em 0.8em; font-size: 93%;" -->
@@ -244,9 +263,5 @@
<br/>
-<div id="commentsBox">
- <%= render :partial => "comments/comments", :locals => { :commentable => @network } %>
-</div>
-
<%= render :partial => "contributions/alternative_formats" %>
Modified: trunk/config/routes.rb (3528 => 3529)
--- trunk/config/routes.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/config/routes.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -223,10 +223,15 @@
:membership_invite_external => :post,
:membership_request => :get,
:rate => :post,
+ :sync_feed => :post,
+ :subscription => [:put, :delete],
:tag => :post } do |network|
network.resources :group_announcements, :as => :announcements, :name_prefix => nil
network.resources :comments, :collection => { :timeline => :get }
network.resources :policies, :controller => 'group_policies'
+ network.resources :activities, :member => { :feature => [:put, :delete] } do |activity|
+ activity.resources :comments
+ end
# resources shared with network
network.resources :workflows, : :index
Modified: trunk/db/migrate/099_add_activities.rb (3528 => 3529)
--- trunk/db/migrate/099_add_activities.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/db/migrate/099_add_activities.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -7,26 +7,41 @@
def self.up
create_table :activities do |t|
- t.string :subject_type
- t.integer :subject_id
- t.string :subject_label
+ t.string :subject_type
+ t.integer :subject_id
+ t.string :subject_label
- t.string :action
+ t.string :action
- t.string :objekt_type
- t.integer :objekt_id
- t.string :objekt_label
+ t.string :objekt_type
+ t.integer :objekt_id
+ t.string :objekt_label
- t.string :auth_type
- t.integer :auth_id
+ t.string :context_type
+ t.integer :context_id
- t.string :extra
+ t.string :auth_type
+ t.integer :auth_id
- t.datetime :created_at
+ t.string :extra
+ t.string :uuid
+ t.integer :priority, :default => 0
+
+ t.boolean :featured, :default => false
+ t.boolean :hidden, :default => false
+
+ t.datetime :timestamp
end
+
+ create_table :subscriptions do |t|
+ t.integer :user_id
+ t.string :objekt_type
+ t.integer :objekt_id
+ end
end
def self.down
+ drop_table :subscriptions
drop_table :activities
end
end
Added: trunk/db/migrate/20130423091433_create_atom_feed_tables.rb (0 => 3529)
--- trunk/db/migrate/20130423091433_create_atom_feed_tables.rb (rev 0)
+++ trunk/db/migrate/20130423091433_create_atom_feed_tables.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -0,0 +1,41 @@
+# myExperiment: db/migrate/20130423091433_create_atom_feed_tables.rb
+#
+# Copyright (c) 2007-2013 The University of Manchester, the University of
+# Oxford, and the University of Southampton. See license.txt for details.
+
+class CreateAtomFeedTables < ActiveRecord::Migration
+ def self.up
+
+ create_table :feeds do |t|
+
+ t.string :title
+ t.text :uri
+ t.string :context_type
+ t.integer :context_id
+ t.string :username
+ t.string :password
+
+ t.timestamps
+ end
+
+ create_table :feed_items do |t|
+
+ t.integer :feed_id
+ t.string :identifier
+ t.string :title
+ t.text :content
+ t.string :author
+ t.string :link
+ t.datetime :item_published_at
+ t.datetime :item_updated_at
+ t.text :data
+
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :feed_items
+ drop_table :feeds
+ end
+end
Modified: trunk/db/schema.rb (3528 => 3529)
--- trunk/db/schema.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/db/schema.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -9,7 +9,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20130308085716) do
+ActiveRecord::Schema.define(:version => 20130423091433) do
create_table "activities", :force => true do |t|
t.string "subject_type"
@@ -19,10 +19,16 @@
t.string "objekt_type"
t.integer "objekt_id"
t.string "objekt_label"
+ t.string "context_type"
+ t.integer "context_id"
t.string "auth_type"
t.integer "auth_id"
t.string "extra"
- t.datetime "created_at"
+ t.string "uuid"
+ t.integer "priority", :default => 0
+ t.boolean "featured", :default => false
+ t.boolean "hidden", :default => false
+ t.datetime "timestamp"
end
create_table "activity_limits", :force => true do |t|
@@ -54,11 +60,6 @@
t.datetime "updated_at"
end
- create_table "auto_tables", :force => true do |t|
- t.string "name"
- t.text "schema"
- end
-
create_table "blob_versions", :force => true do |t|
t.integer "blob_id"
t.integer "version"
@@ -98,27 +99,6 @@
add_index "bookmarks", ["user_id"], :name => "index_bookmarks_on_user_id"
- create_table "checksums", :id => false, :force => true do |t|
- t.integer "id"
- t.string "sha1"
- end
-
- add_index "checksums", ["id"], :name => "i1", :unique => true
-
- create_table "checksums_new", :id => false, :force => true do |t|
- t.integer "id"
- t.string "sha1"
- end
-
- add_index "checksums_new", ["id"], :name => "i1", :unique => true
-
- create_table "checksums_new_new", :id => false, :force => true do |t|
- t.integer "id"
- t.string "sha1"
- end
-
- add_index "checksums_new_new", ["id"], :name => "ii", :unique => true
-
create_table "citations", :force => true do |t|
t.integer "user_id"
t.integer "workflow_id"
@@ -267,6 +247,31 @@
t.string "name"
end
+ create_table "feed_items", :force => true do |t|
+ t.integer "feed_id"
+ t.string "identifier"
+ t.string "title"
+ t.text "content"
+ t.string "author"
+ t.string "link"
+ t.datetime "item_published_at"
+ t.datetime "item_updated_at"
+ t.text "data"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ create_table "feeds", :force => true do |t|
+ t.string "title"
+ t.text "uri"
+ t.string "context_type"
+ t.integer "context_id"
+ t.string "username"
+ t.string "password"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
create_table "friendships", :force => true do |t|
t.integer "user_id"
t.integer "friend_id"
@@ -511,11 +516,6 @@
t.integer "user_id"
end
- create_table "plugin_schema_info", :id => false, :force => true do |t|
- t.string "plugin_name"
- t.integer "version"
- end
-
create_table "policies", :force => true do |t|
t.integer "contributor_id"
t.string "contributor_type"
@@ -704,6 +704,12 @@
add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
+ create_table "subscriptions", :force => true do |t|
+ t.integer "user_id"
+ t.string "objekt_type"
+ t.integer "objekt_id"
+ end
+
create_table "taggings", :force => true do |t|
t.integer "tag_id"
t.integer "taggable_id"
@@ -852,6 +858,8 @@
t.text "body_html"
t.datetime "created_at"
t.datetime "updated_at"
+ t.string "license"
+ t.integer "preview_id"
t.string "image"
t.string "svg"
t.text "revision_comments"
@@ -859,8 +867,6 @@
t.string "file_ext"
t.string "last_edited_by"
t.integer "content_type_id"
- t.string "license"
- t.integer "preview_id"
end
add_index "workflow_versions", ["workflow_id"], :name => "index_workflow_versions_on_workflow_id"
@@ -874,15 +880,15 @@
t.string "unique_name"
t.text "body"
t.text "body_html"
+ t.integer "current_version"
+ t.integer "preview_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "current_version"
t.integer "content_blob_id"
t.string "file_ext"
t.string "last_edited_by"
t.integer "content_type_id"
t.integer "license_id"
- t.integer "preview_id"
end
create_table "wsdl_deprecations", :force => true do |t|
Modified: trunk/public/stylesheets/styles.css (3528 => 3529)
--- trunk/public/stylesheets/styles.css 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/public/stylesheets/styles.css 2013-05-07 14:06:29 UTC (rev 3529)
@@ -1274,14 +1274,61 @@
margin-bottom: 1em;
}
+.commentSection {
+ margin-top: 10px;
+}
+
.addCommentBox {
- margin-top: 3em;
- padding: 1em;
- width: 500px;
- background-color: #EEEEEE;
- border: 1px dotted #999999;
+ background-color: #E8E8E8;
}
+.addCommentBox TEXTAREA {
+ padding: 4px;
+ width: 500px;
+}
+
+.addCommentBox INPUT {
+ margin-top: 4px;
+}
+
+.activityCommentBox {
+ padding: 4px;
+ background-color: #E8E8E8;
+ width: 400px;
+}
+
+.activityCommentBox + .activityCommentBox {
+ border-top: 1px solid #F4F4F4;
+}
+
+.activityCommentBox .avatar {
+}
+
+.activityCommentBox .username {
+/* font-weight: bold; */
+}
+
+.activityCommentBox .username:after {
+/* content: ": "; */
+}
+
+.activityCommentBox .comment-body {
+ font-size: 11px;
+}
+
+.activityCommentBox .rhs {
+ padding-left: 32px;
+}
+
+.activityCommentBox TEXTAREA {
+ padding: 4px;
+ width: 334px;
+}
+
+.activityCommentBox INPUT {
+ margin-top: 4px;
+}
+
/* End Comments styles */
/* Begin Reviews styles */
@@ -2422,6 +2469,126 @@
margin-top: 0;
}
+OL.activity-feed {
+ margin: 0;
+}
+
+.activity-feed LI {
+ list-style: none;
+}
+
+.activity-feed LI + LI {
+ border-top: 1px SOLID #E0E0E0;
+}
+
+.activity-feed LI + LI.featured,
+.activity-feed LI.featured + LI {
+ border-top: none;
+}
+
+DIV.activity IMG.framed {
+ border: 0;
+ padding: 0;
+}
+
+.activity {
+ padding-top: 12px;
+ padding-bottom: 12px;
+}
+
+.activity-feed LI.featured {
+ padding: 8px;
+ background: #ffffcc;
+ border: 1px dotted #999999;
+}
+
+.activity .avatar-column {
+ width: 64px;
+ float: left;
+}
+
+.activity .activity-column {
+ margin-left: 72px;
+}
+
+.activity .actions {
+ font-size: 8pt;
+ color: gray;
+}
+
+.activity .actions DIV,
+.activity .actions FORM {
+ display: inline;
+}
+
+.activity .date {
+}
+
+.activity SPAN + SPAN:before {
+ content: " \0000a0\0000a0 ";
+ color: gray;
+}
+
+.activity .summary {
+ padding-top: 4px;
+ padding-bottom: 4px;
+ font-size: 8pt;
+}
+
+.activity .actions INPUT {
+ display: inline;
+ border: none;
+ background: transparent;
+ padding: 0;
+ margin: 0;
+ color: #009;
+}
+
+.activity .actions INPUT:hover {
+ color: red;
+ text-decoration: underline;
+}
+
+.activity .comment-timestamp {
+ font-size: 8pt;
+ color: gray;
+}
+
+.activity .actions FORM.button-to,
+.activity .actions FORM.button-to DIV {
+ display: inline;
+}
+
+.inset-preview {
+ float: left;
+ margin-right: 16px;
+ margin-bottom: 16px;
+}
+
+.inset-preview IMG {
+ max-width: 200px;
+}
+
+.workflow-type {
+}
+
+.metadata-datetime {
+ font-size: 8pt;
+}
+
+FIELDSET.atom-feed INPUT {
+ width: 700px;
+}
+
+.new-session-sign-in {
+ width: 190px;
+ border: 1px solid #CCCCCC;
+ border-radius: 6px 6px 6px 6px;
+ -moz-border-radius: 6px 6px 6px 6px;
+ -webkit-border-bottom-left-radius: 6px;
+ -webkit-border-bottom-right-radius: 6px;
+}
+
#user_menu {
position: absolute;
background-color: white;
Modified: trunk/test/functional/networks_controller_test.rb (3528 => 3529)
--- trunk/test/functional/networks_controller_test.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/test/functional/networks_controller_test.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -26,7 +26,7 @@
old_count = Network.count
login_as(:john)
- post :create, :network => { :title => 'test network', :unique_name => 'test_network', :new_member_policy => 'open', :description => "..." }
+ post :create, :network => { :title => 'test network', :unique_name => 'test_network', :new_member_policy => 'open', :description => "..." }, :feed_uri => "", :feed_user => "", :feed_pass => ""
assert_equal old_count+1, Network.count
assert_redirected_to network_path(assigns(:network))
@@ -47,7 +47,7 @@
def test_should_update_network
login_as(:john)
put :update, :id => 1,
- :network => { :title => 'test network', :unique_name => 'update_network', :new_member_policy => 'open', :description => ".?."}
+ :network => { :title => 'test network', :unique_name => 'update_network', :new_member_policy => 'open', :description => ".?."}, :feed_uri => "", :feed_user => "", :feed_pass => ""
assert_redirected_to network_path(assigns(:network))
end
Modified: trunk/vendor/plugins/acts_as_taggable_redux/lib/tag.rb (3528 => 3529)
--- trunk/vendor/plugins/acts_as_taggable_redux/lib/tag.rb 2013-05-07 09:02:34 UTC (rev 3528)
+++ trunk/vendor/plugins/acts_as_taggable_redux/lib/tag.rb 2013-05-07 14:06:29 UTC (rev 3529)
@@ -32,8 +32,11 @@
# Tag a taggable with this tag, optionally add user to add owner to tagging
def tag(taggable, user_id = nil)
- taggings.create :taggable => taggable, :user_id => user_id
+ t = taggings.create :taggable => taggable, :user_id => user_id
taggings.reset
+
+ Activity.create_activities(:subject => User.find(user_id), :action ="" 'create', :object => t, :timestamp => t.created_at)
+
@tagged = nil
end