Skip to content

Commit

Permalink
topic podcasts
Browse files Browse the repository at this point in the history
Each topic can now have its own podcast stream.  Teachers
have to manually turn on the podcast for the topic, and
can specify whether student comments show up in the stream
or not.

fixes #3538

Change-Id: I19b1b44fc2eec864cfeb298163ef34a0b0181067
Reviewed-on: https://gerrit.instructure.com/2369
Tested-by: Hudson <[email protected]>
Reviewed-by: Brian Palmer <[email protected]>
  • Loading branch information
whitmer committed Mar 28, 2011
1 parent 471267d commit 76bfff3
Show file tree
Hide file tree
Showing 15 changed files with 270 additions and 66 deletions.
62 changes: 48 additions & 14 deletions app/controllers/discussion_entries_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,21 +122,55 @@ def destroy

def public_feed
return unless get_feed_context
topic = @context.discussion_topics.active.find(params[:discussion_topic_id])
feed = Atom::Feed.new do |f|
f.title = "#{topic.title}: #{@context.name} Discussion Feed"
f.links << Atom::Link.new(:href => named_context_url(@context, :context_discussion_topic_url, topic.id))
f.updated = Time.now
f.id = named_context_url(@context, :context_discussion_topic_url, topic.id)
@topic = @context.discussion_topics.active.find(params[:discussion_topic_id])
if !@topic.podcast_enabled && request.format == :rss
@problem = "Podcasts have not been enabled for this topic."
@template_format = 'html'
@template.template_format = 'html'
render :text => @template.render(:file => "shared/unauthorized_feed", :layout => "layouts/application"), :status => :bad_request # :template => "shared/unauthorized_feed", :status => :bad_request
return
end
@entries = []
@entries.concat topic.discussion_entries
@entries = @entries.sort_by{|e| e.updated_at}
@entries.each do |entry|
feed.entries << entry.to_atom
end
respond_to do |format|
format.atom { render :text => feed.to_xml }
if authorized_action(@topic, @current_user, :read)
@all_discussion_entries = @topic.discussion_entries.active
@discussion_entries = @all_discussion_entries
if request.format == :rss && !@topic.podcast_has_student_posts
@admins = @context.admins
@discussion_entries = @discussion_entries.find_all_by_user_id(@admins.map(&:id))
end
if @topic.locked_for?(@current_user) && !@topic.grants_right?(@current_user, nil, :update)
@discussion_entries = []
end
respond_to do |format|
format.atom {
feed = Atom::Feed.new do |f|
f.title = "#{@topic.title} Posts Feed"
f.links << Atom::Link.new(:href => named_context_url(@context, :context_discussion_topic_url, @topic.id))
f.updated = Time.now
f.id = named_context_url(@context, :context_discussion_topic_url, @topic.id)
end
feed.entries << @topic.to_atom
@discussion_entries.sort_by{|e| e.updated_at}.each do |e|
feed.entries << e.to_atom
end
render :text => feed.to_xml
}
format.rss {
@entries = [@topic] + @discussion_entries
require 'rss/2.0'
rss = RSS::Rss.new("2.0")
channel = RSS::Rss::Channel.new
channel.title = "#{@topic.title} Posts Podcast Feed"
channel.description = "Any media files linked from or embedded within entries in the topic \"#{@topic.title}\" will appear in this feed."
channel.link = named_context_url(@context, :context_discussion_topic_url, @topic.id)
channel.pubDate = Time.now.strftime("%a, %d %b %Y %H:%M:%S %z")
elements = Announcement.podcast_elements(@entries, @context)
elements.each do |item|
channel.items << item
end
rss.channel = channel
render :text => rss.to_s
}
end
end
end
end
11 changes: 11 additions & 0 deletions app/controllers/discussion_topics_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ def create
assignment = params[:discussion_topic].delete(:assignment)
generate_assignment(assignment) if assignment && assignment[:set_assignment]

unless @context.grants_right?(@current_user, session, :moderate_forum)
params[:discussion_topic].delete :podcast_enabled
params[:discussion_topic].delete :podcast_has_student_posts
end
if params[:discussion_topic].delete(:is_announcement) == "1" && @context.announcements.new.grants_right?(@current_user, session, :create)
@topic = @context.announcements.build(params[:discussion_topic])
else
Expand Down Expand Up @@ -204,6 +208,10 @@ def update
@topic.workflow_state = (params[:discussion_topic][:lock] == '1') ? 'locked' : 'active'
params[:discussion_topic].delete :lock
end
unless @context.grants_right?(@current_user, session, :moderate_forum)
params[:discussion_topic].delete :podcast_enabled
params[:discussion_topic].delete :podcast_has_student_posts
end
delay_posting = params[:discussion_topic].delete :delay_posting
delayed_post_at = params[:discussion_topic].delete :delayed_post_at
if @topic.post_delayed?
Expand Down Expand Up @@ -270,4 +278,7 @@ def public_feed
format.atom { render :text => feed.to_xml }
end
end

def public_topic_feed
end
end
2 changes: 1 addition & 1 deletion app/models/attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class Attachment < ActiveRecord::Base
after_save :touch_context
after_create :build_media_object

attr_accessor :podcast_associated_announcement
attr_accessor :podcast_associated_asset

# this is a magic method that gets run by attachment-fu after it is done sending to s3,
# that is the moment that we also want to submit it to scribd.
Expand Down
5 changes: 4 additions & 1 deletion app/models/discussion_entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,13 @@ def update_topic
named_scope :after, lambda{|date|
{:conditions => ['created_at > ?', date] }
}
named_scope :include_subentries, lambda{
{:include => discussion_subentries}
}

def to_atom(opts={})
Atom::Entry.new do |entry|
entry.title = "Entry#{", " + self.discussion_topic.context.name if opts[:include_context]}: #{self.discussion_topic.title}"
entry.title = "#{"Re: " if parent_id != 0}#{self.discussion_topic.title}#{", " + self.discussion_topic.context.name if opts[:include_context]}"
entry.updated = self.updated_at
entry.published = self.created_at
entry.id = "tag:#{HostUrl.default_host},#{self.created_at.strftime("%Y-%m-%d")}:/discussion_entries/#{self.feed_code}"
Expand Down
34 changes: 20 additions & 14 deletions app/models/discussion_topic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class DiscussionTopic < ActiveRecord::Base
include HasContentTags
include CopyAuthorizedLinks

attr_accessible :title, :message, :user, :delayed_post_at, :assignment, :plaintext_message
attr_accessible :title, :message, :user, :delayed_post_at, :assignment, :plaintext_message, :podcast_enabled, :podcast_has_student_posts

attr_readonly :context_id, :context_type, :user_id
adheres_to_policy
Expand Down Expand Up @@ -518,34 +518,34 @@ def self.import_from_migration(hash, context, item=nil)
item
end

def self.podcast_elements(announcements, context)
def self.podcast_elements(messages, context)
attachment_ids = []
media_object_ids = []
announcements_hash = {}
announcements.each do |announcement|
txt = (announcement.message || "")
messages_hash = {}
messages.each do |message|
txt = (message.message || "")
attachment_matches = txt.scan(/\/#{context.class.to_s.pluralize.underscore}\/#{context.id}\/files\/(\d+)\/download/)
attachment_ids += (attachment_matches || []).map{|m| m[0] }
media_object_matches = txt.scan(/media_comment_([0-9a-z_]+)/)
media_object_ids += (media_object_matches || []).map{|m| m[0] }
(attachment_ids + media_object_ids).each do |id|
announcements_hash[id] ||= announcement
messages_hash[id] ||= message
end
end
media_object_ids = media_object_ids.uniq.compact
attachment_ids = attachment_ids.uniq.compact
attachments = context.attachments.active.find_all_by_id(attachment_ids).compact
attachments = attachments.select{|a| a.content_type && a.content_type.match(/(video|audio)/) }
attachments.each do |attachment|
attachment.podcast_associated_announcement = announcements_hash[attachment.id]
attachment.podcast_associated_asset = messages_hash[attachment.id]
end
media_objects = MediaObject.find_all_by_media_id(media_object_ids)
media_objects += media_object_ids.map{|id| MediaObject.new(:media_id => id) }
media_objects = media_objects.once_per(&:media_id)
media_objects = media_objects.map do |media_object|
if media_object.new_record?
media_object.context = context
media_object.user_id = announcements_hash[media_object.media_id].user_id rescue nil
media_object.user_id = messages_hash[media_object.media_id].user_id rescue nil
media_object.root_account_id = context.root_account_id rescue nil
media_object.save
elsif media_object.deleted? || media_object.context != context
Expand All @@ -554,7 +554,7 @@ def self.podcast_elements(announcements, context)
if !media_object.podcast_format_details
media_object = nil
end
media_object.podcast_associated_announcement = announcements_hash[media_object.media_id]
media_object.podcast_associated_asset = messages_hash[media_object.media_id] if media_object
media_object
end
to_podcast(attachments + media_objects.compact)
Expand All @@ -563,15 +563,21 @@ def self.podcast_elements(announcements, context)
def self.to_podcast(elements, opts={})
require 'rss/2.0'
elements.map do |elem|
announcement = elem.podcast_associated_announcement rescue nil
next unless announcement
asset = elem.podcast_associated_asset
next unless asset
item = RSS::Rss::Channel::Item.new
item.title = (announcement.title rescue "") + ": " + elem.name
link = "http://#{HostUrl.context_host(announcement.context)}/#{announcement.context_url_prefix}/announcements/#{announcement.id}"
item.title = (asset.title rescue "") + ": " + elem.name
link = nil
if asset.is_a?(DiscussionTopic)
link = "http://#{HostUrl.context_host(asset.context)}/#{asset.context_url_prefix}/discussion_topics/#{asset.id}"
elsif asset.is_a?(DiscussionEntry)
link = "http://#{HostUrl.context_host(asset.context)}/#{asset.context_url_prefix}/discussion_topics/#{asset.discussion_topic_id}"
end

item.link = link
item.guid = RSS::Rss::Channel::Item::Guid.new
item.pubDate = elem.updated_at.utc
item.description = announcement ? announcement.message : elem.name
item.description = asset ? asset.message : elem.name
item.enclosure
if elem.is_a?(Attachment)
item.guid.content = link + "/#{elem.uuid}"
Expand Down
2 changes: 2 additions & 0 deletions app/models/media_object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class MediaObject < ActiveRecord::Base
after_save :update_title_on_kaltura_later
serialize :data

attr_accessor :podcast_associated_asset

def infer_defaults
self.user_type = "admin" if self.user && self.cached_context_grants_right?(self.user, nil, :manage_content)
self.user_type ||= "student"
Expand Down
8 changes: 8 additions & 0 deletions app/stylesheets/g_instructure.sass
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,8 @@ ul.group_list
:font-size 0.8em
:padding-right 5px
:padding-left 0
.podcast
display: none

.user_name
:white-space nowrap
Expand Down Expand Up @@ -857,6 +859,12 @@ ul.group_list
.user_content
p:last-child
margin-bottom: 0
&.has_podcast
.header
.podcast
display: block
float: right
+opacity(.7)
.communication_message_hover
div.header
.link_box
Expand Down
3 changes: 3 additions & 0 deletions app/stylesheets/g_util_fancy_links.sass
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ a.email
a.expand
+icon_link
:background-image url(/images/expand.png)
a.feed
+icon_link
:background-image url(/images/atom.png)
a.file-multiple
+icon_link
:background-image url(/images/file_multiple.png)
Expand Down
6 changes: 3 additions & 3 deletions app/views/announcements/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
<% content_for :auto_discovery do %>
<% if @context_enrollment %>
<%= auto_discovery_link_tag(:atom, feeds_announcements_format_path(@context_enrollment.feed_code, :atom), {:title => "#{@context.class.to_s} Announcements Atom Feed"}) %>
<%= auto_discovery_link_tag(:atom, feeds_announcements_format_path(@context_enrollment.feed_code, :rss), {:title => "#{@context.class.to_s} Announcements Podcast Feed"}) %>
<%= auto_discovery_link_tag(:rss, feeds_announcements_format_path(@context_enrollment.feed_code, :rss), {:title => "#{@context.class.to_s} Announcements Podcast Feed"}) %>
<% elsif can_do(@context, @current_user, :manage) %>
<%= auto_discovery_link_tag(:atom, feeds_announcements_format_path(@context.feed_code, :atom), {:title => "#{@context.class.to_s} Announcements Atom Feed"}) %>
<%= auto_discovery_link_tag(:atom, feeds_announcements_format_path(@context.feed_code, :rss), {:title => "#{@context.class.to_s} Announcements Podcast Feed"}) %>
<%= auto_discovery_link_tag(:rss, feeds_announcements_format_path(@context.feed_code, :rss), {:title => "#{@context.class.to_s} Announcements Podcast Feed"}) %>
<% elsif @context.available? && @context.respond_to?(:is_public) && @context.is_public %>
<%= auto_discovery_link_tag(:atom, feeds_announcements_format_path(@context.asset_string, :atom), {:title => "#{@context.class.to_s} Announcements Atom Feed"}) %>
<%= auto_discovery_link_tag(:atom, feeds_announcements_format_path(@context.asset_string, :rss), {:title => "#{@context.class.to_s} Announcements Podcast Feed"}) %>
<%= auto_discovery_link_tag(:rss, feeds_announcements_format_path(@context.asset_string, :rss), {:title => "#{@context.class.to_s} Announcements Podcast Feed"}) %>
<% end %>
<% end %>

Expand Down
79 changes: 48 additions & 31 deletions app/views/discussion_topics/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
<% content_for :auto_discovery do %>
<% if @context_enrollment %>
<%= auto_discovery_link_tag(:atom, feeds_topic_format_path(@topic.id, @context_enrollment.feed_code, :atom), {:title => "Discussion Atom Feed"}) %>
<% if @topic.podcast_enabled %>
<%= auto_discovery_link_tag(:rss, feeds_topic_format_path(@topic.id, @context_enrollment.feed_code, :rss), {:title => "Discussion Podcast Feed"}) %>
<% end %>
<% elsif @context.available? %>
<%= auto_discovery_link_tag(:atom, feeds_topic_format_path(@topic.id, @context.feed_code, :atom), {:title => "Discussion Atom Feed"}) %>
<% if @topic.podcast_enabled %>
<%= auto_discovery_link_tag(:rss, feeds_topic_format_path(@topic.id, @context.feed_code, :rss), {:title => "Discussion Podcast Feed"}) %>
<% end %>
<% end %>
<% end %>

Expand All @@ -30,38 +36,49 @@
<% else %>
<div class="rs-margin-all">
<div id="sidebar_content">
<p>
<b><span class="message_count"><%= @entries.length %></span> <span class="message_count_text"><%= @entries.length == 1 ? 'post' : 'posts' %></span></b>
<% if @entries.length > 0 && !@topic_agglomerated %>
<span style="font-size: 0.8em; padding-left: 10px;">( <span class="total_message_count"><%= @topic.discussion_entries.active.length %></span> including subtopics )</span>
<% end %>
</p>
<p>
<% if @topic_agglomerated %>
This view shows all the messages from all this topic's group topics. If you want to
comment or edit posts, you'll have to visit each topic individually.
<ul class="unstyled_list" style="line-height: 1.8em; margin: 5px 20px 10px;">
<% @groups.select{|g| can_do(g, @current_user, :read) }.each do |group| %>
<li class="unstyled_list">
<% cnt = (@topics || []).find{|t| t.context == group}.discussion_entries.count rescue 0 %>
<b><a href="<%= context_url(group, :context_discussion_topics_url, :root_discussion_topic_id => @topic.id) %>"><%= group.name %></a></b> - <%= pluralize(cnt, 'Post') %>
</li>
<p>
<b><span class="message_count"><%= @entries.length %></span> <span class="message_count_text"><%= @entries.length == 1 ? 'post' : 'posts' %></span></b>
<% if @entries.length > 0 && !@topic_agglomerated %>
<span style="font-size: 0.8em; padding-left: 10px;">( <span class="total_message_count"><%= @topic.discussion_entries.active.length %></span> including subtopics )</span>
<% end %>
</ul>
<% else %>
<% end %>
</p>
<p>
<% if can_do(@topic, @current_user, :update) %>
<a href="#" class="edit_topic_link button button-sidebar-wide"><%= image_tag "edit.png", :alt => "" %> Edit Topic</a>
<% end %>
<% if can_do(@topic, @current_user, :reply) && !params[:combined] %>
<a href="#" class="add_entry_link button button-sidebar-wide"><%= image_tag "add.png", :alt => "" %> Add New Entry</a>
<% end %>
<% if can_do(@topic, @current_user, :delete) && !params[:combined] %>
<a href="#" class="delete_topic_link button button-sidebar-wide"><%= image_tag "delete.png", :alt => "" %> Delete Topic</a>
<% end %>
</p>
</p>
<p>
<% if @topic_agglomerated %>
This view shows all the messages from all this topic's group topics. If you want to
comment or edit posts, you'll have to visit each topic individually.
<ul class="unstyled_list" style="line-height: 1.8em; margin: 5px 20px 10px;">
<% @groups.select{|g| can_do(g, @current_user, :read) }.each do |group| %>
<li class="unstyled_list">
<% cnt = (@topics || []).find{|t| t.context == group}.discussion_entries.count rescue 0 %>
<b><a href="<%= context_url(group, :context_discussion_topics_url, :root_discussion_topic_id => @topic.id) %>"><%= group.name %></a></b> - <%= pluralize(cnt, 'Post') %>
</li>
<% end %>
</ul>
<% else %>
<% end %>
</p>
<p>
<% if can_do(@topic, @current_user, :update) %>
<a href="#" class="edit_topic_link button button-sidebar-wide"><%= image_tag "edit.png", :alt => "" %> Edit Topic</a>
<% end %>
<% if can_do(@topic, @current_user, :reply) && !params[:combined] %>
<a href="#" class="add_entry_link button button-sidebar-wide"><%= image_tag "add.png", :alt => "" %> Add New Entry</a>
<% end %>
<% if can_do(@topic, @current_user, :delete) && !params[:combined] %>
<a href="#" class="delete_topic_link button button-sidebar-wide"><%= image_tag "delete.png", :alt => "" %> Delete Topic</a>
<% end %>
</p>
<div id="podcast_link_holder" style="<%= hidden unless @topic.podcast_enabled %>">
<% if @context_enrollment %>
<p>
<a class="feed" href="<%= feeds_topic_format_path(@topic.id, @context_enrollment.feed_code, :rss) %>">Topic Podcast Feed</a>
</p>
<% elsif @context.available? %>
<p>
<a class="feed" href="<%= feeds_topic_format_path(@topic.id, @context.feed_code, :rss) %>">Topic Podcast Feed</a>
</p>
<% end %>
</div>
</div>
</div>
<%= render :partial => "shared/wiki_sidebar" %>
Expand Down
Loading

0 comments on commit 76bfff3

Please sign in to comment.