<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>app/models/assigned_asset.rb</filename>
    </added>
    <added>
      <filename>app/views/admin/articles/attach.rjs</filename>
    </added>
    <added>
      <filename>app/views/admin/articles/detach.rjs</filename>
    </added>
    <added>
      <filename>app/views/admin/articles/label.rjs</filename>
    </added>
    <added>
      <filename>db/migrate/070_create_assigned_assets.rb</filename>
    </added>
    <added>
      <filename>test/fixtures/assigned_assets.yml</filename>
    </added>
    <added>
      <filename>test/unit/assigned_asset_test.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,5 +1,7 @@
 * SVN *
 
+* Initial article/asset assignment support.  Perfect for podcasting.  
+
 * Raise MissingThemesError if Site#theme is nil
 
 * RIP: Site#search_layout</diff>
      <filename>CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -9,7 +9,7 @@ class Admin::ArticlesController &lt; Admin::BaseController
   before_filter :convert_times_to_utc, :only =&gt; [:create, :update, :upload]
   before_filter :check_for_new_draft,  :only =&gt; [:create, :update, :upload]
   
-  before_filter :find_site_article, :only =&gt; [:edit, :update, :comments, :approve, :unapprove, :destroy]
+  before_filter :find_site_article, :only =&gt; [:edit, :update, :comments, :approve, :unapprove, :destroy, :attach, :detach]
   before_filter :login_required, :except =&gt; :upload
   before_filter :load_sections, :only =&gt; [:new, :edit]
 
@@ -117,6 +117,20 @@ class Admin::ArticlesController &lt; Admin::BaseController
     end
   end
 
+  def attach
+    @asset = site.assets.find(params[:version])
+    @article.assets.add @asset
+  end
+
+  def detach
+    @asset = site.assets.find(params[:version])
+    @article.assets.remove @asset
+  end
+
+  def label
+    AssignedAsset.update_all ['label = ?', params[:label]], ['article_id = ? and asset_id = ?', params[:id], params[:version]]
+  end
+
   protected
     def load_sections
       @assets = site.assets.find(:all, :order =&gt; 'created_at desc', :limit =&gt; 6)</diff>
      <filename>app/controllers/admin/articles_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -16,7 +16,8 @@ class ApplicationController &lt; ActionController::Base
   
     # so not the best place for this...
     def asset_image_args_for(asset, thumbnail = :tiny, options = {})
-      options = options.reverse_merge(:title =&gt; &quot;#{asset.title} \n #{asset.tags.join(', ')}&quot;)
+      thumb_size = Array.new(2).fill(Asset.attachment_options[:thumbnails][thumbnail].to_i).join('x')
+      options    = options.reverse_merge(:title =&gt; &quot;#{asset.title} \n #{asset.tags.join(', ')}&quot;, :size =&gt; thumb_size)
       if asset.movie?
         ['/images/mephisto/icons/video.png', options]
       elsif asset.audio?
@@ -26,9 +27,9 @@ class ApplicationController &lt; ActionController::Base
       elsif asset.other?
         ['/images/mephisto/icons/doc.png', options]
       elsif asset.thumbnails_count.zero?
-        [asset.public_filename, options.update(:size =&gt; Array.new(2).fill(Asset.attachment_options[:thumbnails][thumbnail].to_i).join('x'))]
+        [asset.public_filename, options]
       else
-        [asset.public_filename(thumbnail), options]
+        [asset.public_filename(thumbnail), options.merge(:size =&gt; asset.image_size)]
       end
     end
     helper_method :asset_image_args_for</diff>
      <filename>app/controllers/application.rb</filename>
    </modified>
    <modified>
      <diff>@@ -53,6 +53,10 @@ class ArticleDrop &lt; BaseDrop
   def changes_feed_url
     @changes_feed_url ||= url + '/changes.xml'
   end
+  
+  def assets
+    @assets ||= liquify(*@source.assets)
+  end
 
   protected
     def body_for_mode(mode)</diff>
      <filename>app/drops/article_drop.rb</filename>
    </modified>
    <modified>
      <diff>@@ -32,6 +32,10 @@ module DropFilters
   def latest_comments(site, limit = nil)
     site.latest_comments(limit || site['articles_per_page'])
   end
+  
+  def find_asset(article, label)
+    article.assets.detect { |a| a.source.label == label }
+  end
 
   def monthly_articles(section, date = nil)
     date = parse_date(date)</diff>
      <filename>app/filters/drop_filters.rb</filename>
    </modified>
    <modified>
      <diff>@@ -52,6 +52,21 @@ class Article &lt; Content
     end
     comment.has_many :all_comments
   end
+  
+  has_many :assigned_assets, :order =&gt; 'position', :dependent =&gt; :destroy
+  has_many :assets, :through =&gt; :assigned_assets, :conditions =&gt; ['assigned_assets.active = ?', true], :select =&gt; 'assets.*, assigned_assets.label' do
+    def add(asset, label = nil)
+      returning AssignedAsset.find_or_create_by_article_id_and_asset_id(proxy_owner.id, asset.id) do |aa|
+        aa.label  = label
+        aa.active = true
+        aa.save!
+      end
+    end
+    
+    def remove(asset)
+      AssignedAsset.update_all ['active = ?', false], ['article_id = ? AND asset_id = ?', proxy_owner.id, asset.id]
+    end
+  end
 
   class &lt;&lt; self
     def with_published(&amp;block)
@@ -139,6 +154,10 @@ class Article &lt; Content
     set_default_filter_from user
   end
 
+  def add_xml(builder)
+    add_podcast_xml(builder)
+  end
+
   protected
     def convert_to_utc
       self.published_at = published_at.utc if published_at
@@ -161,4 +180,10 @@ class Article &lt; Content
     def reset_comment_attributes
       Content.update_all ['title = ?, published_at = ?, permalink = ?', title, published_at, permalink], ['article_id = ?', id]
     end
+    
+    def add_podcast_xml(builder)
+      if asset = assets.find(:first, :conditions =&gt; ['label = ?', 'podcast'], :select =&gt; 'assets.*, assigned_assets.label')
+        builder.link :rel =&gt; :enclosure, :type =&gt; asset.content_type, :length =&gt; asset.size, :href =&gt; asset.public_filename
+      end
+    end
 end
\ No newline at end of file</diff>
      <filename>app/models/article.rb</filename>
    </modified>
    <modified>
      <diff>@@ -45,6 +45,7 @@ class Asset &lt; ActiveRecord::Base
   include Mephisto::TaggableMethods
 
   belongs_to :site
+  has_many :assigned_assets, :order =&gt; 'position', :dependent =&gt; :destroy
   has_attachment :storage =&gt; :file_system, :thumbnails =&gt; { :thumb =&gt; '120&gt;', :tiny =&gt; '50&gt;' }, :max_size =&gt; 30.megabytes, 
     :processor =&gt; (Object.const_defined?(:ASSET_IMAGE_PROCESSOR) ? ASSET_IMAGE_PROCESSOR : nil)
   before_validation_on_create :set_site_from_parent</diff>
      <filename>app/models/asset.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,14 +1,14 @@
 class AssignedSection &lt; ActiveRecord::Base
   belongs_to :article
   belongs_to :section, :counter_cache =&gt; 'articles_count'
-  acts_as_list :scope =&gt; 'section_id = #{section_id}'
+  acts_as_list :scope =&gt; :section_id
   validates_presence_of :article_id, :section_id
   validate_on_create    :check_for_dupe_article_and_section
 
   protected
     def check_for_dupe_article_and_section
       unless self.class.count(:all, :conditions =&gt; ['article_id = ? and section_id = ?', article_id, section_id]).zero?
-        errors.add_to_base(&quot;Cannot have a duplicate categorization for this article and section&quot;)
+        errors.add_to_base(&quot;Cannot have a duplicate assignment for this article and section&quot;)
       end
     end
 end</diff>
      <filename>app/models/assigned_section.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,6 +14,7 @@
 &lt;div class=&quot;sgroup&quot; id=&quot;filetabs&quot;&gt;
   &lt;ul class=&quot;stabs&quot;&gt;
     &lt;li id=&quot;tab-latest&quot;&gt;&lt;a href=&quot;#latest-files&quot; class=&quot;selected&quot; title=&quot;Latest files uploaded&quot;&gt;Latest&lt;/a&gt;&lt;/li&gt;
+    &lt;li id=&quot;tab-attached&quot;&gt;&lt;a href=&quot;#attached-files&quot; title=&quot;Attached Assets&quot;&gt;Attached&lt;/a&gt;&lt;/li&gt;
     &lt;li id=&quot;tab-search&quot;&gt;&lt;a href=&quot;#search-files&quot; title=&quot;Search for files&quot;&gt;Search&lt;/a&gt;&lt;/li&gt;
     &lt;li id=&quot;tab-files&quot;&gt;&lt;a href=&quot;#upload-files&quot; title=&quot;Upload new files&quot;&gt;Upload&lt;/a&gt;&lt;/li&gt;
     &lt;li id=&quot;tab-bucket&quot;&gt;&lt;a href=&quot;#bucket&quot; title=&quot;Upload new files&quot;&gt;Bucket&lt;/a&gt;&lt;/li&gt;
@@ -21,8 +22,13 @@
   
   &lt;div id=&quot;tabpanels&quot;&gt;
     &lt;div class=&quot;tabpanel&quot; id=&quot;latest-files&quot;&gt;
-      &lt;ul id=&quot;latest-assets&quot;&gt;
-        &lt;%= render :partial =&gt; &quot;admin/assets/widget&quot;, :collection =&gt; @assets %&gt;
+      &lt;ul id=&quot;latest-assets&quot; class=&quot;asset-list&quot;&gt;
+        &lt;%= render :partial =&gt; &quot;admin/assets/widget&quot;, :collection =&gt; @assets, :locals =&gt; { :prefix =&gt; 'latest' } %&gt;
+      &lt;/ul&gt;
+    &lt;/div&gt;
+    &lt;div class=&quot;tabpanel&quot; id=&quot;attached-files&quot; style=&quot;display:none;&quot;&gt;
+      &lt;ul id=&quot;attached-assets&quot; class=&quot;asset-list&quot;&gt;
+        &lt;%= render :partial =&gt; &quot;admin/assets/widget&quot;, :collection =&gt; @article.assets, :locals =&gt; { :prefix =&gt; 'attached' } %&gt;
       &lt;/ul&gt;
     &lt;/div&gt;
     &lt;div class=&quot;tabpanel&quot; id=&quot;search-files&quot; style=&quot;display:none;&quot;&gt;
@@ -32,7 +38,7 @@
       &lt;/label&gt;
       &lt;input class=&quot;searchbox&quot; type=&quot;text&quot; size=&quot;30&quot; name=&quot;q&quot; id=&quot;search-files-q&quot; /&gt;
       &lt;input type=&quot;button&quot; value=&quot;Search&quot; onclick=&quot;TinyTab.callbacks['search-files']($F('search-files-q'))&quot; /&gt;
-      &lt;ul id=&quot;search-assets&quot;&gt;&lt;/ul&gt;
+      &lt;ul id=&quot;search-assets&quot; class=&quot;asset-list&quot;&gt;&lt;/ul&gt;
     &lt;/div&gt;
     &lt;div class=&quot;tabpanel&quot; id=&quot;upload-files&quot; style=&quot;display:none;&quot;&gt;
       &lt;p&gt;
@@ -42,7 +48,7 @@
       &lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; &lt;input type=&quot;button&quot; value=&quot;Upload&quot; onclick=&quot;Asset.upload('article-form');&quot; /&gt;&lt;/p&gt;
     &lt;/div&gt;
     &lt;div class=&quot;tabpanel&quot; id=&quot;bucket&quot; style=&quot;display:none;&quot;&gt;
-      &lt;ul id=&quot;bucket-assets&quot;&gt;
+      &lt;ul id=&quot;bucket-assets&quot; class=&quot;asset-list&quot;&gt;
         &lt;% session[:bucket].each do |filename, values| -%&gt;
         &lt;li&gt;
           &lt;%= link_to(image_tag(*values), filename, :target =&gt; '_blank') %&gt;</diff>
      <filename>app/views/admin/articles/_shared_options.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,17 @@
-&lt;li id=&quot;&lt;%= widget.dom_id(:widget) %&gt;&quot;&gt;
+&lt;li id=&quot;&lt;%= widget.dom_id(&quot;#{prefix}-widget&quot;) %&gt;&quot; class=&quot;widget&lt;%= ' selected-widget' if @article &amp;&amp; @article.assets.include?(widget) %&gt;&quot;&gt;
+&lt;% if prefix == 'attached' -%&gt;
+  &lt;div&gt;
+    &lt;%= link_to asset_image_for(widget), widget.public_filename %&gt;
+    &lt;span&gt;
+      &lt;%= text_field_tag :version, (widget.respond_to?(:label) ? widget.label : nil), :id =&gt; widget.dom_id(&quot;#{prefix}-widget-version&quot;), :class =&gt; 'txt' %&gt;
+      &lt;a href=&quot;#&quot; class=&quot;label-widget&quot; id=&quot;&lt;%= widget.dom_id(&quot;label-#{prefix}-widget&quot;) %&gt;&quot;&gt;Save&lt;/a&gt;
+    &lt;/span&gt;
+  &lt;/div&gt;
+&lt;% else -%&gt;
   &lt;%= link_to asset_image_for(widget), widget.public_filename %&gt;
+&lt;% end -%&gt;
+  &lt;a href=&quot;#&quot;&gt;&lt;%= image_tag('mephisto/icons/8-em-cross.png', :size =&gt; '16x16', :id =&gt; widget.dom_id(&quot;detach-#{prefix}-widget&quot;), 
+        :style =&gt; 'display:none', :class =&gt; 'detach-widget') %&gt;&lt;/a&gt;
+  &lt;a href=&quot;#&quot;&gt;&lt;%= image_tag('mephisto/icons/8-em-plus.png', :size =&gt; '16x16', :id =&gt; widget.dom_id(&quot;attach-#{prefix}-widget&quot;), 
+        :style =&gt; 'display:none', :class =&gt; 'attach-widget') %&gt;&lt;/a&gt;
 &lt;/li&gt;
\ No newline at end of file</diff>
      <filename>app/views/admin/assets/_widget.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -22,4 +22,5 @@ xm.entry 'xml:base' =&gt; home_url do
             #{sanitize_feed_content [article.excerpt_html, article.body_html].compact * &quot;\n&quot;}
           &lt;/content&gt;}
   end
+  article.add_xml(xm)
 end
\ No newline at end of file</diff>
      <filename>app/views/feed/_article.rxml</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@
 # migrations feature of ActiveRecord to incrementally modify your database, and
 # then regenerate this schema definition.
 
-ActiveRecord::Schema.define(:version =&gt; 69) do
+ActiveRecord::Schema.define(:version =&gt; 70) do
 
   create_table &quot;assets&quot;, :force =&gt; true do |t|
     t.column &quot;content_type&quot;,     :string
@@ -19,6 +19,15 @@ ActiveRecord::Schema.define(:version =&gt; 69) do
     t.column &quot;user_id&quot;,          :integer
   end
 
+  create_table &quot;assigned_assets&quot;, :force =&gt; true do |t|
+    t.column &quot;article_id&quot;, :integer
+    t.column &quot;asset_id&quot;,   :integer
+    t.column &quot;position&quot;,   :integer
+    t.column &quot;label&quot;,      :string
+    t.column &quot;created_at&quot;, :datetime
+    t.column &quot;active&quot;,     :boolean
+  end
+
   create_table &quot;assigned_sections&quot;, :force =&gt; true do |t|
     t.column &quot;article_id&quot;, :integer
     t.column &quot;section_id&quot;, :integer
@@ -60,6 +69,7 @@ ActiveRecord::Schema.define(:version =&gt; 69) do
     t.column &quot;filter&quot;,         :string
     t.column &quot;user_agent&quot;,     :string
     t.column &quot;referrer&quot;,       :string
+    t.column &quot;assets_count&quot;,   :integer,                 :default =&gt; 0
   end
 
   create_table &quot;contents&quot;, :force =&gt; true do |t|
@@ -88,6 +98,7 @@ ActiveRecord::Schema.define(:version =&gt; 69) do
     t.column &quot;filter&quot;,         :string
     t.column &quot;user_agent&quot;,     :string
     t.column &quot;referrer&quot;,       :string
+    t.column &quot;assets_count&quot;,   :integer,                 :default =&gt; 0
   end
 
   create_table &quot;events&quot;, :force =&gt; true do |t|</diff>
      <filename>db/schema.rb</filename>
    </modified>
    <modified>
      <diff>@@ -206,9 +206,9 @@ TinyTab.prototype = {
 Asset = {
   upload: function(form) {
     form = $(form);
-    article_id   = location.href.match(/\/edit\/([0-9]+)/);
+    article_id   = location.href.match(/\/(edit|upload)\/([0-9]+)/);
     form.action  = Mephisto.root + &quot;/admin/articles/upload&quot;
-    if(article_id) form.action += &quot;/&quot; + article_id[1]
+    if(article_id) form.action += &quot;/&quot; + article_id[2]
     form.submit();
   },
   
@@ -295,7 +295,32 @@ var ArticleForm = {
   getAvailableComments: function() {
     return $$('ul.commentlist li').reject(function(div) { return !(div.visible() &amp;&amp; !div.hasClassName('disabled') &amp;&amp; div.id.match(/^comment-/)); }).collect(function(div) { return div.id.match(/comment-(\d+)/)[1] });
   },
-  
+
+  attachAsset: function(assetId) {
+    var articleId = location.href.match(/\/(edit|upload)\/([0-9]+)/)[2];
+    var attached  = $('attached-widget-' + assetId);
+    if(attached) return;
+    new Ajax.Request('/admin/articles/attach/' + articleId + '/' + assetId);
+    $$('.widget').each(function(asset) { if(assetId == asset.getAttribute('id').match(/-(\d+)$/)[1]) asset.addClassName('selected-widget'); });
+  },
+
+  labelAsset: function(assetId) {
+    var articleId = location.href.match(/\/(edit|upload)\/([0-9]+)/)[2];
+    var attached  = $('attached-widget-' + assetId);
+    var label     = $('attached-widget-version-' + assetId);
+    new Ajax.Request('/admin/articles/label/' + articleId + '/' + assetId + '?label=' + escape(label.value));
+    if(attached) return;
+  },
+
+  detachAsset: function(assetId) {
+    var articleId = location.href.match(/\/(edit|upload)\/([0-9]+)/)[2];
+    var attached  = $('attached-widget-' + assetId);
+    if(!attached) return;
+    new Ajax.Request('/admin/articles/detach/' + articleId + '/' + assetId);
+    new Effect.DropOut(attached, {afterFinish: function() { attached.remove(); }});
+    $$('.widget').each(function(asset) { if(assetId == asset.getAttribute('id').match(/-(\d+)$/)[1]) asset.removeClassName('selected-widget'); });
+  },
+
   getRevision: function() {
     var rev = $F(this)
     var url = Mephisto.root + '/admin/articles/edit/' + location.href.match(/\/edit\/([0-9]+)/)[1];
@@ -548,7 +573,37 @@ Event.addBehavior({
     $('published').value = '0';
     $('article-search').submit();
   },
+
+  'li.widget:mouseover': function() {
+    var attach = $('attach-' + this.getAttribute('id'));
+    var detach = $('detach-' + this.getAttribute('id'));
+    if(attach) attach.show();
+    if(detach) detach.show();
+  },
   
+  'li.widget:mouseout': function() {
+    var attach = $('attach-' + this.getAttribute('id'));
+    var detach = $('detach-' + this.getAttribute('id'));
+    if(attach) attach.hide();
+    if(detach) detach.hide();
+  },
+  
+  '.attach-widget:click': function() {
+    ArticleForm.attachAsset(this.getAttribute('id').match(/-(\d+)$/)[1]);
+    return false;
+  },
+  
+  '.label-widget:click': function() {
+    ArticleForm.labelAsset(this.getAttribute('id').match(/-(\d+)$/)[1]);
+    this.innerHTML = 'Saving...'
+    return false;
+  },
+  
+  '.detach-widget:click': function() {
+    ArticleForm.detachAsset(this.getAttribute('id').match(/-(\d+)$/)[1]);
+    return false;
+  },
+
   'a.theme_dialog:click': function() {
     var img = this.down('img');
     var pieces = img.src.split('/');</diff>
      <filename>public/javascripts/mephisto/application.js</filename>
    </modified>
    <modified>
      <diff>@@ -1305,29 +1305,75 @@ li.event-article  { background-image: url(/images/mephisto/icons/articleb.gif);
  *=ASSET MANAGEMENT
  */
 
-#assets, #files, #latest-assets, #bucket-assets, #search-assets {
+#assets, #files, ul.asset-list {
   list-style: none;
   margin-top: 5px;
 }
 
-#assets li, #files li, #latest-assets li, #bucket-assets li, #search-assets li {
+#assets li, #files li, ul.asset-list li {
   display: inline;
+  white-space: no-wrap;
   margin-left: 5px;
   margin-bottom: 5px;
 }
 
-#assets li a img,
+#attached-assets li {
+  display:block;
+}
+
+#attached-assets li div {
+  position:relative;
+}
+
+#attached-assets li div a {
+  display:inline;
+}
+
+#attached-assets div input.txt {
+  width:125px;
+}
+
+#attached-assets div span {
+  position:absolute;
+  top:20px;  right:8px;
+}
+
+ul.asset-list .widget {
+  position:relative;
+}
+
+.widget .attach-widget {
+  z-index:500;
+  position:absolute;
+  top:-13px; left:-23px;
+}
+
+.widget .detach-widget {
+  z-index:500;
+  position:absolute;
+  top:-36px; left:-23px;
+}
+
+#attached-assets .widget .attach-widget {
+  position:absolute;
+  top:11px; left:-23px;
+}
+
+#attached-assets .widget .detach-widget {
+  position:absolute;
+  top:34px; left:-23px;
+}
+
+#assets        li a img,
 #latest-assets li a img,
 #bucket-assets li a img,
-#search-assets li a img {
+#search-assets li a img,
+ul.asset-list  li a img {
   padding: 4px;
   background: #fff;
 }
 
-#assets img,
-#latest-assets img,
-#bucket-assets img,
-#search-assets img {
+ul.asset-list img {
   max-height: 50px;
   max-width: 50px;
   padding: 3px;
@@ -1335,6 +1381,10 @@ li.event-article  { background-image: url(/images/mephisto/icons/articleb.gif);
   border: 1px solid #8BCACD;
 }
 
+ul.asset-list .selected-widget img {
+  border-color:#000;
+}
+
 #assets span,
 #latest-assets span,
 #bucket-assets span,</diff>
      <filename>public/stylesheets/mephisto/mephisto.css</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,7 @@ require 'admin/articles_controller'
 class Admin::ArticlesController; def rescue_action(e) raise e end; end
 
 class Admin::ArticlesControllerAssetsTest &lt; Test::Unit::TestCase
-  fixtures :contents, :content_versions, :sections, :assigned_sections, :users, :sites, :tags, :taggings, :memberships
+  fixtures :contents, :content_versions, :sections, :assigned_sections, :users, :sites, :tags, :taggings, :memberships, :assigned_assets, :assets
 
   def setup
     @controller = Admin::ArticlesController.new
@@ -15,7 +15,7 @@ class Admin::ArticlesControllerAssetsTest &lt; Test::Unit::TestCase
     FileUtils.mkdir_p ASSET_PATH
   end
 
-  def test_should_upload_asset
+  specify &quot;should upload asset&quot; do
     asset_count = has_image_processor? ? 3 : 1 # asset + 2 thumbnails
     
     assert_difference Asset, :count, asset_count do
@@ -25,7 +25,7 @@ class Admin::ArticlesControllerAssetsTest &lt; Test::Unit::TestCase
     end
   end
 
-  def test_should_upload_asset_and_redirect_to_article
+  specify &quot;should upload asset and redirect to article&quot; do
     asset_count = has_image_processor? ? 3 : 1 # asset + 2 thumbnails
     
     assert_difference Asset, :count, asset_count do
@@ -37,7 +37,7 @@ class Admin::ArticlesControllerAssetsTest &lt; Test::Unit::TestCase
     end
   end
 
-  def test_should_upload_asset_as_member
+  specify &quot;should upload asset as member&quot; do
     asset_count = has_image_processor? ? 3 : 1 # asset + 2 thumbnails
     
     login_as :ben
@@ -48,7 +48,7 @@ class Admin::ArticlesControllerAssetsTest &lt; Test::Unit::TestCase
     end
   end
 
-  def test_should_upload_asset_and_redirect_to_article_as_member
+  specify &quot;should upload asset and redirect to article as member&quot; do
     asset_count = has_image_processor? ? 3 : 1 # asset + 2 thumbnails
     
     login_as :ben
@@ -61,7 +61,7 @@ class Admin::ArticlesControllerAssetsTest &lt; Test::Unit::TestCase
     end
   end
 
-  def test_should_not_error_on_new_article_asset_upload
+  specify &quot;should not error on new article asset upload&quot; do
     assert_no_difference Asset, :count do
       post :upload
       assert_response :success
@@ -69,7 +69,7 @@ class Admin::ArticlesControllerAssetsTest &lt; Test::Unit::TestCase
     end
   end
 
-  def test_should_not_error_on_article_asset_upload
+  specify &quot;should not error on article asset upload&quot; do
     assert_no_difference Asset, :count do
       post :upload, :id =&gt; contents(:welcome).id
       assert_response :success
@@ -78,7 +78,7 @@ class Admin::ArticlesControllerAssetsTest &lt; Test::Unit::TestCase
     end
   end
 
-  def test_should_not_create_article_when_uploading_asset
+  specify &quot;should not create article when uploading asset&quot; do
     Time.mock! Time.local(2005, 1, 1, 12, 0, 0) do
       assert_no_difference Article, :count do
         post :upload, :asset =&gt; { :uploaded_data =&gt; fixture_file_upload('assets/logo.png', 'image/png') }, 
@@ -94,6 +94,29 @@ class Admin::ArticlesControllerAssetsTest &lt; Test::Unit::TestCase
     end
   end
 
+  specify &quot;should add asset to article&quot; do
+    assert_difference AssignedAsset, :count do
+      post :attach, :id =&gt; contents(:welcome).id, :version =&gt; assets(:mov).id, :label =&gt; 'avatar'
+    end
+    assert_models_equal [assets(:gif), assets(:mp3), assets(:mov)], contents(:welcome).assets(true)
+    assert_equal 'avatar', contents(:welcome).assets[2].label
+  end
+  
+  specify &quot;should add inactive asset to article&quot; do
+    assert_no_difference AssignedAsset, :count do
+      post :attach, :id =&gt; contents(:welcome).id, :version =&gt; assets(:png).id, :label =&gt; 'avatar'
+    end
+    assert_models_equal [assets(:gif), assets(:mp3), assets(:png)], contents(:welcome).assets(true)
+    assert_equal 'avatar', contents(:welcome).assets[2].label
+  end
+
+  specify &quot;should find deactivate article assets&quot; do
+    assert_no_difference AssignedAsset, :count do
+      post :detach, :id =&gt; contents(:welcome).id, :version =&gt; assets(:mp3).id
+    end
+    assert_models_equal [assets(:gif)], contents(:welcome).assets
+  end
+
   def teardown
     FileUtils.rm_rf ASSET_PATH
   end</diff>
      <filename>test/functional/admin/articles_controller_assets_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -78,10 +78,11 @@ context &quot;About Section Feed&quot; do
   
   specify &quot;should show correct links&quot; do
     assert_select 'feed&gt;link[href=?][type=?]', 'http://test.host/about', 'text/html'
-    assert_select 'feed&gt;entry&gt;link[href]', 3 do |hrefs|
+    assert_select 'feed&gt;entry&gt;link[href]', 4 do |hrefs|
       assert_equal &quot;http://test.host/about&quot;,                 hrefs[0]['href']
-      assert_equal &quot;http://test.host/about/about-this-page&quot;, hrefs[1]['href']
-      assert_equal &quot;http://test.host/about/the-site-map&quot;,    hrefs[2]['href']
+      assert_match /asset\.mp3$/,                            hrefs[1]['href']
+      assert_equal &quot;http://test.host/about/about-this-page&quot;, hrefs[2]['href']
+      assert_equal &quot;http://test.host/about/the-site-map&quot;,    hrefs[3]['href']
     end
   end
 end
@@ -105,14 +106,18 @@ context &quot;Home Section Feed&quot; do
   end
   
   specify &quot;should show correct links&quot; do
-    
     assert_select 'feed&gt;link[href=?][type=?]', 'http://test.host/', 'text/html'
-    assert_select 'feed&gt;entry&gt;link[href]', 2 do |hrefs|
+    assert_select 'feed&gt;entry&gt;link[href]', 3 do |hrefs|
       assert_match /\/welcome-to-mephisto$/,         hrefs[0]['href']
-      assert_match /\/another-welcome-to-mephisto$/, hrefs[1]['href']
+      assert_match /asset\.mp3$/,                    hrefs[1]['href']
+      assert_match /\/another-welcome-to-mephisto$/, hrefs[2]['href']
     end
   end
 
+  specify &quot;should show podcast&quot; do
+    assert_select 'feed&gt;entry&gt;link[rel=?][length=?][type=?]', 'enclosure', '252366', 'audio/mpeg'
+  end
+
   specify &quot;show absolute urls with custom relative url root&quot; do
     begin
       old_root = ActionController::AbstractRequest.relative_url_root</diff>
      <filename>test/functional/feed_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,10 @@
 require File.dirname(__FILE__) + '/../test_helper'
 
 class ArticleDropTest &lt; Test::Unit::TestCase
-  fixtures :sites, :sections, :contents, :assigned_sections, :users, :tags, :taggings
+  fixtures :sites, :sections, :contents, :assigned_sections, :users, :tags, :taggings, :assigned_assets, :assets
   
   def setup
-    @article = contents(:welcome).to_liquid :mode =&gt; :single
+    @article = contents(:welcome).to_liquid(:mode =&gt; :single)
     @article.context = mock_context('site' =&gt; sites(:first).to_liquid)
   end
 
@@ -70,17 +70,17 @@ class ArticleDropTest &lt; Test::Unit::TestCase
     assert_equal '&lt;p&gt;body&lt;/p&gt;', a.send(:body_for_mode, :list)
   end
   
-  def test_article_url
+  specify &quot;should show article url&quot; do
     t = Time.now.utc - 3.days
     assert_equal &quot;/#{t.year}/#{t.month}/#{t.day}/welcome-to-mephisto&quot;, @article.url
   end
   
-  def test_comments_feed_url
+  specify &quot;should show comments feed url&quot; do
     t = Time.now.utc - 3.days
     assert_equal &quot;/#{t.year}/#{t.month}/#{t.day}/welcome-to-mephisto/comments.xml&quot;, @article.comments_feed_url
   end
   
-  def test_changes_feed_url
+  specify &quot;should change feed url&quot; do
     t = Time.now.utc - 3.days
     assert_equal &quot;/#{t.year}/#{t.month}/#{t.day}/welcome-to-mephisto/changes.xml&quot;, @article.changes_feed_url
   end
@@ -88,4 +88,8 @@ class ArticleDropTest &lt; Test::Unit::TestCase
   specify &quot;should show taggable tags&quot; do
     assert_equal %w(rails), contents(:another).to_liquid.tags
   end
+
+  specify &quot;should find article assets&quot; do
+    assert_models_equal [assets(:gif), assets(:mp3)], @article.assets.collect(&amp;:source)
+  end
 end</diff>
      <filename>test/unit/article_drop_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 require File.dirname(__FILE__) + '/../test_helper'
 context &quot;Drop Filters&quot; do
-  fixtures :sites, :sections, :contents, :assigned_sections, :assets
+  fixtures :sites, :sections, :contents, :assigned_sections, :assigned_assets, :assets
   include DropFilters, CoreFilters
 
   def setup
@@ -56,6 +56,12 @@ context &quot;Drop Filters&quot; do
     assert_models_equal sections(:home).articles.find_all_in_month(Time.now.year, Time.now.month), monthly_articles(liquify(sections(:home)).first).collect(&amp;:source)
   end
 
+  specify &quot;should find article assets&quot; do
+    article = contents(:welcome).to_liquid(:mode =&gt; :single)
+    article.context = @context
+    assert_equal assets(:mp3), find_asset(article, 'podcast').source
+  end
+
   specify &quot;should find movies&quot; do
     assert_models_equal [assets(:swf), assets(:mov)], assets_by_type('movie').collect(&amp;:source)
   end</diff>
      <filename>test/unit/drop_filters_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,7 +17,7 @@ module Technoweenie # :nodoc:
 
         protected
           def process_attachment_with_processing
-            return unless process_attachment_without_processing || !image?
+            return unless process_attachment_without_processing &amp;&amp; image?
             with_image { |img| resize_image_or_thumbnail! img }
           end
 </diff>
      <filename>vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/processors/image_science.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>45893b3ad2bc5f28efcfe4777d80a53a6ecf4ed5</id>
    </parent>
  </parents>
  <author>
    <name>technoweenie</name>
    <email>technoweenie@567b1171-46fb-0310-a4c9-b4bef9110e78</email>
  </author>
  <url>http://github.com/francois/mephisto/commit/ecc989e1d5b94895baed0772aa011657f8b02298</url>
  <id>ecc989e1d5b94895baed0772aa011657f8b02298</id>
  <committed-date>2007-01-14T23:34:08-08:00</committed-date>
  <authored-date>2007-01-14T23:34:08-08:00</authored-date>
  <message>Initial article/asset assignment support.  Perfect for podcasting.

git-svn-id: http://svn.techno-weenie.net/projects/mephisto/trunk@2684 567b1171-46fb-0310-a4c9-b4bef9110e78</message>
  <tree>c27e09260d2ad28f788752f1594febfc8d8aa795</tree>
  <committer>
    <name>technoweenie</name>
    <email>technoweenie@567b1171-46fb-0310-a4c9-b4bef9110e78</email>
  </committer>
</commit>
