<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>app/models/gallery_keyword.rb</filename>
    </added>
    <added>
      <filename>db/migrate/20090131152801_add_keywords.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -124,10 +124,12 @@ If you want to generate that thumbnail just after the upload of the image, add t
     admin_thumb: 300x300   # do not remove
     admin_preview: 500x500 # do not remove
     small: 200x100 # this is the signature you use in the page.
+    square: c90x90 # specific sized images with a center crop
                 
 == Contributors
 
 * Version: 0.3.0  - Adam Salter - http://10outta10.com
+* Version: 0.7.*  - Tom Cowell - http://eightsquarestudio.com 
 
 == Credits
 </diff>
      <filename>README.rdoc</filename>
    </modified>
    <modified>
      <diff>@@ -2,6 +2,8 @@ require 'action_controller/test_process.rb'
 
 class GalleryImportingsController &lt; ApplicationController
   
+  protect_from_forgery :except =&gt; [ :import ]
+  
   helper 'galleries'
   layout 'gallery_popup'  
   </diff>
      <filename>app/controllers/gallery_importings_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -18,7 +18,9 @@ class Gallery &lt; ActiveRecord::Base
     
   belongs_to :created_by, :class_name =&gt; 'User', :foreign_key =&gt; 'created_by'
   belongs_to :update_by, :class_name =&gt; 'User', :foreign_key =&gt; 'update_by'
-      
+  has_and_belongs_to_many :gallery_keywords, :join_table =&gt; &quot;galleries_keywords&quot;, :foreign_key =&gt; &quot;gallery_id&quot;, :uniq =&gt; true,
+                            :class_name =&gt; &quot;GalleryKeyword&quot;, :association_foreign_key =&gt; &quot;keyword_id&quot;
+                               
   attr_protected :slug, :path    
   
   validates_presence_of :name
@@ -49,6 +51,23 @@ class Gallery &lt; ActiveRecord::Base
     File.join(self.absolute_path, 'thumbs')
   end
   
+  def keywords
+    str =''     
+    self.gallery_keywords.each do |key|
+      str += key.keyword
+      str += ','
+    end                                   
+    str.slice(0..-2)
+  end               
+  
+  def keywords=(keywords) 
+    self.gallery_keywords = []
+    keys = keywords.split(',')
+    keys.each do |word|
+      self.gallery_keywords &lt;&lt; GalleryKeyword.find_or_create_by_keyword(word.strip)
+    end
+  end
+  
   def url(root_id = nil)
     File.join((self.ancestors_from(root_id).reverse &lt;&lt; self).collect{|a| a.slug})
   end</diff>
      <filename>app/models/gallery.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,14 +10,14 @@ class GalleryItem &lt; ActiveRecord::Base
         @@extensions[extension.downcase] || 'Unknown'
       end
     end
-  end      
+  end
   
   attr_accessible :name, :description, :uploaded_data
   
   has_attachment :storage =&gt; :file_system,
     :path_prefix =&gt; Radiant::Config[&quot;gallery.path_prefix&quot;],
     :processor =&gt; Radiant::Config[&quot;gallery.processor&quot;],
-    :max_size =&gt; 3.megabytes     
+    :max_size =&gt; Radiant::Config[&quot;gallery.max_size&quot;].to_i.kilobytes     
   
   belongs_to :gallery
   
@@ -27,6 +27,9 @@ class GalleryItem &lt; ActiveRecord::Base
   belongs_to :parent, :class_name =&gt; 'GalleryItem', :foreign_key =&gt; 'parent_id'
   
   has_many :infos, :class_name =&gt; &quot;GalleryItemInfo&quot;, :dependent =&gt; :delete_all
+  
+  has_and_belongs_to_many :gallery_keywords, :join_table =&gt; &quot;gallery_items_keywords&quot;, :foreign_key =&gt; &quot;gallery_item_id&quot;, :uniq =&gt; true,
+                            :class_name =&gt; &quot;GalleryKeyword&quot;, :association_foreign_key =&gt; &quot;keyword_id&quot;
 
   before_create :set_filename_as_name
   before_create :set_position
@@ -37,7 +40,7 @@ class GalleryItem &lt; ActiveRecord::Base
   after_attachment_saved do |item|
     item.generate_default_thumbnails if item.parent.nil?
   end       
-  
+
   before_thumbnail_saved do |thumbnail|
     thumbnail.gallery_id = thumbnail.parent.gallery_id
   end                                                
@@ -68,6 +71,28 @@ class GalleryItem &lt; ActiveRecord::Base
     end
   end
   
+  def keywords                         
+    str = ''                 
+    if self.gallery_keywords.length &gt; 0 
+      self.gallery_keywords.uniq.each do |key|
+        str += key.keyword
+        str += ','
+      end                         
+      str = str.slice(0..-2)                        
+    else                
+      str += self.gallery.keywords          
+    end                           
+    return str
+  end           
+  
+  def keywords=(keywords) 
+    self.gallery_keywords = []
+    keys = keywords.split(',')
+    keys.each do |word|
+      self.gallery_keywords &lt;&lt; GalleryKeyword.find_or_create_by_keyword(word.strip)
+    end
+  end
+  
   def jpeg?
     not (self.content_type =~ /jpeg/).nil?
   end</diff>
      <filename>app/models/gallery_item.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,10 +15,14 @@
   &lt;/p&gt;
 	&lt;div&gt;
 		&lt;%= f.text_area &quot;description&quot;, :class =&gt; 'textbox', :maxlength =&gt; 255 %&gt;
+	&lt;/div&gt;                                                                
+	&lt;div&gt;     
+		&lt;label for=&quot;gallery_keywords&quot;&gt; Keywords for gallery. &lt;/label&gt; 
+		&lt;%= f.text_field &quot;keywords&quot;, :class =&gt; 'textbox', :maxlength =&gt; 255 %&gt;
 	&lt;/div&gt;
   &lt;p id=&quot;advanced_options&quot; style=&quot;display: none;&quot;&gt;
 		&lt;%= f.check_box :hidden, {:id =&gt; 'hide_gallery'} %&gt;
-		&lt;label for=&quot;hide_gallery&quot;&gt; Hide this gallery in galleries list. &lt;/label&gt;
+		&lt;label for=&quot;hide_gallery&quot;&gt; Hide this gallery in galleries list. &lt;/label&gt; 
 	&lt;/p&gt;
 	&lt;p&gt;
 		&lt;small id=&quot;close_advanced_options&quot; style=&quot;display: none;&quot;&gt;&lt;a href=&quot;#&quot; onclick=&quot;toggle_advanced(); return false;&quot;&gt;Close advanced options&lt;/a&gt;&lt;/small&gt;</diff>
      <filename>app/views/galleries/_form.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -19,10 +19,13 @@ spinner = image('spinner.gif', :class =&gt; 'busy', :id =&gt; &quot;gallery-busy-#{gallery.
 			&lt;a title=&quot;&lt;%= gallery.name %&gt;&quot; href=&quot;&lt;%= admin_gallery_path(gallery) %&gt;&quot;&gt;
 				&lt;%= icon %&gt;
 				&lt;span class=&quot;name&quot;&gt;&lt;%= gallery.name %&gt;&lt;/span&gt;
-			&lt;/a&gt; 
+			&lt;/a&gt;                                           
 			&lt;small class=&quot;info&quot;&gt;(&lt;%= gallery.items.size %&gt; files)&lt;/small&gt;
 			&lt;%= spinner %&gt;
 		&lt;/span&gt;
+	&lt;/td&gt;
+	&lt;td class='keywords'&gt;                                                                                                               
+		&lt;span style='color:#666; font-size:0.75em;padding-left:0.5em;'&gt;&lt;%= gallery.keywords if gallery.items.size &gt; 0 &amp;&amp; gallery.gallery_keywords.length &gt; 0 %&gt;&lt;/span&gt;
 	&lt;/td&gt;	
 	&lt;td class=&quot;status&quot;&gt;&lt;%= gallery.id %&gt;&lt;/td&gt;
 	&lt;td class=&quot;status published-status&quot;&gt;&lt;%= gallery.hidden ? 'hidden' : 'visible' %&gt;&lt;/td&gt;</diff>
      <filename>app/views/galleries/_gallery.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,8 @@
 &lt;table id=&quot;gallery_tree&quot; class=&quot;index&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; border=&quot;0&quot;&gt;
   &lt;thead&gt;
     &lt;tr&gt;      
-			&lt;th class=&quot;gallery&quot;&gt;Name&lt;/th&gt;
+			&lt;th class=&quot;gallery&quot;&gt;Name&lt;/th&gt; 
+			&lt;th&gt;Keywords &lt;!-- link_to &quot;(Manage Keywords)&quot;, --&gt;&lt;/th&gt;     
       &lt;th&gt;ID&lt;/th&gt;
 			&lt;th class=&quot;status&quot;&gt;Status&lt;/th&gt;
       &lt;th class=&quot;modify&quot; colspan=&quot;3&quot;&gt;Actions&lt;/th&gt;</diff>
      <filename>app/views/galleries/index.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -49,6 +49,10 @@
       &lt;div&gt;      
         &lt;label for=&quot;edit-item-description&quot;&gt;Description&lt;/label&gt;
         &lt;textarea id=&quot;edit-item-description&quot; cols=&quot;40&quot; rows=&quot;12&quot;&gt;&lt;/textarea&gt;
+      &lt;/div&gt;  
+      &lt;div&gt;      
+        &lt;label for=&quot;edit-item-keywords&quot;&gt;Keywords&lt;/label&gt;
+        &lt;input type=&quot;text&quot; id=&quot;edit-item-keywords&quot; size=&quot;30&quot;&gt;
       &lt;/div&gt;
       &lt;p&gt;
         &lt;img id=&quot;edit-spinner&quot; src=&quot;/images/admin/spinner.gif&quot; style=&quot;display: none;&quot;/&gt;</diff>
      <filename>app/views/galleries/show.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -3,4 +3,5 @@
   &lt;%= item_preview(item) %&gt;
   &lt;%= item_buttons(item) %&gt;
   &lt;div class=&quot;description&quot; id=&quot;item_&lt;%= item.id %&gt;_description&quot; style=&quot;display:none;&quot;&gt;&lt;%= &quot;#{item.description}&quot; %&gt;&lt;/div&gt;
+	&lt;div class=&quot;keywords&quot; id=&quot;item_&lt;%= item.id %&gt;_keywords&quot; style=&quot;display:none;&quot;&gt;&lt;%= &quot;#{item.keywords}&quot; %&gt;&lt;/div&gt;
 &lt;/div&gt;
\ No newline at end of file</diff>
      <filename>app/views/gallery_items/_item.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -9,7 +9,9 @@
     	&lt;label for=&quot;update-item-popup-name-field&quot;&gt;Name:&lt;/label&gt; &lt;br /&gt;
       &lt;%= f.text_field :name, :maxlength =&gt; 255 %&gt; &lt;br /&gt;
     	&lt;label for=&quot;update-item-popup-description-field&quot;&gt;Description:&lt;/label&gt; &lt;br /&gt;
-    	&lt;%= f.text_area :description %&gt;&lt;br /&gt;
+    	&lt;%= f.text_area :description %&gt;&lt;br /&gt;       
+			&lt;label for='update-item-popup-keywords-field'&gt;Keywords:&lt;/label&gt;&lt;br/&gt;
+			&lt;%= f.text_field :keywords, :maxlength =&gt; 255 %&gt;&lt;br/&gt;
       &lt;%= submit_tag &quot;Save&quot;, :id =&gt; 'popup_save_button' %&gt; 
     &lt;/div&gt;
     &lt;% end %&gt;</diff>
      <filename>app/views/gallery_items/edit.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,4 @@
 page[&quot;item_#{@item.id}&quot;].down('.label a').update(item_label_text(@item))
-page[&quot;item_#{@item.id}&quot;].down('.description').update(@item.description)
+page[&quot;item_#{@item.id}&quot;].down('.description').update(@item.description)    
+page[&quot;item_#{@item.id}&quot;].down('.keywords').update(@item.keywords) 
 page.call 'Gallery.EditForm.close'
\ No newline at end of file</diff>
      <filename>app/views/gallery_items/update.js.rjs</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 path_prefix: public/galleries
 processor: rmagick  
-# max_size: 3.megabytes
+max_size: 3000 #file max size in kilobytes
 gallery_based: no
 default_thumbnails:
   admin_thumb: 300x300   # do not remove</diff>
      <filename>config/gallery.yml.default</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 require 'tempfile'
-require_dependency 'application'
+require_dependency 'application_controller'
 require_dependency 'open-uri'
 require_dependency 'exifr/jpeg'
 require_dependency 'exifr/tiff'
@@ -36,8 +36,10 @@ class GalleryExtension &lt; Radiant::Extension
   
   def activate
     init
-    tab_options = {:visibility =&gt; [:all]}
-    Radiant::Config[&quot;gallery.gallery_based&quot;] == 'true' ? tab_options[:before] = &quot;Pages&quot; : tab_options[:after] = &quot;Layouts&quot;
+    tab_options = {:visibility =&gt; [:all]}  
+    if Radiant::Config.table_exists? 
+      Radiant::Config[&quot;gallery.gallery_based&quot;] == 'true' ? tab_options[:before] = &quot;Pages&quot; : tab_options[:after] = &quot;Layouts&quot;
+    end
     admin.tabs.add(&quot;Galleries&quot;, &quot;/admin/galleries&quot;, tab_options)
     admin.page.edit.add :layout_row, 'base_gallery' if admin.respond_to?(:page)
   end</diff>
      <filename>gallery_extension.rb</filename>
    </modified>
    <modified>
      <diff>@@ -11,12 +11,10 @@ module GalleryItemTags
   
   desc %{    
     Usage:
-    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:items:each [order='order' by='by' limit='limit' offset='offset'
-scope='all|gallery']&gt;
-...
-&lt;/r:gallery:items:each&gt;&lt;/code&gt;&lt;/pre&gt;
-    Iterates over all items in current gallery.
-    Valid scopes are 'all' (find all Gallery Items) and 'gallery' (find Items that belong to the current Gallery) }
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:items:each [order='order' by='by' limit='limit' offset='offset' scope='all|gallery'
+    keywords='key1,key2,key3' current_keywords='is|is_not']&gt;&lt;/r:gallery:items:each&gt;&lt;/code&gt;&lt;/pre&gt;
+    Valid scopes are 'all' (find all Gallery Items) and 'gallery' (find Items that belong to the current Gallery)
+    Iterates through gallery items keywords=(manual entered keywords) and/or current_keywords=(is|is_not) } 
   tag &quot;gallery:items:each&quot; do |tag|
     content = &quot;&quot;
     gallery = find_gallery(tag)
@@ -38,6 +36,18 @@ scope='all|gallery']&gt;
     options[:offset] = tag.attr['offset'] ? tag.attr['offset'].to_i  : 0
     options[:conditions] = {:parent_id =&gt; nil}
     
+    if !tag.attr['keywords'].nil? || !tag.attr['current_keywords'].nil?                                                                                                                                  
+      keywords = !tag.attr['keywords'].nil? ? tag.attr['keywords'].split(',') : []
+      if (tag.attr['current_keywords'] == 'is' || tag.attr['current_keywords'] == 'is_not') &amp;&amp; !tag.globals.page.request.parameters['keywords'].nil?
+        @current_keywords = tag.globals.page.request.parameters['keywords'].split(',') if !tag.globals.page.request.parameters['keywords'].nil?
+        if !@current_keywords.nil? &amp;&amp; @current_keywords.length &gt; 0
+          keywords.concat(@current_keywords)
+        end
+      end
+      options[:joins] = :gallery_keywords
+      options[:conditions].merge!({&quot;gallery_keywords.keyword&quot; =&gt; keywords}) if keywords.length &gt; 0              
+    end
+    
     @page_number = tag.globals.page.request.params[&quot;page&quot;] &amp;&amp; tag.globals.page.request.params[&quot;page&quot;].first.to_i &gt; 1 ? tag.globals.page.request.params[&quot;page&quot;].first.to_i : 1
     if !tag.attr['limit'].nil? &amp;&amp; tag.attr['offset'].nil?
       options[:offset] = tag.attr['limit'].to_i * (@page_number - 1)      
@@ -45,7 +55,7 @@ scope='all|gallery']&gt;
     end
                   
     scope = tag.attr['scope'] ? tag.attr['scope'] : 'gallery'
-    raise GalleryTagError.new('Invalid value for attribute scope. Valid values are: gallery, all') unless %[gallery all].include?(scope)
+    raise GalleryTagError.new('Invalid value for attribute scope. Valid values are: gallery, all') unless %[gallery all].include?(scope)  
     items = case scope
       when 'gallery'
         gallery ? gallery.items.find(:all, options) : []
@@ -86,12 +96,34 @@ scope='all|gallery']&gt;
 
   desc %{
     Usage:
-    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:item:name /&gt;&lt;/code&gt;&lt;/pre&gt;
-    Provides name for current gallery item }
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:item:name [safe='true']/&gt;&lt;/code&gt;&lt;/pre&gt;
+    Provides name for current gallery item, safe is to make safe for web }
   tag &quot;gallery:item:name&quot; do |tag|      
-    item = find_item(tag)
-    item.name
-  end  
+    item = find_item(tag)  
+    if tag.attr['safe'] == 'true'                        
+      @safe = item.name.gsub(/[\s]+/, '_').downcase
+    else 
+      @normal = item.name
+    end
+    name = tag.attr['safe'] ? @safe : @normal
+  end 
+  
+  desc %{
+    Usage:
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:item:keywords [separator=',' safe='true']/&gt;&lt;/code&gt;&lt;/pre&gt;
+    Provides keywords for current gallery item, use 
+    separator=&quot;separator_string&quot; to specify the character between keywords}
+  tag &quot;gallery:item:keywords&quot; do |tag|      
+    item = find_item(tag)    
+    joiner = tag.attr['separator'] ? tag.attr['separator'] : ' '  
+    if tag.attr['safe'] == 'true'   
+      @safe = item.keywords.gsub(/[\s]+/, '_').downcase
+    else 
+      @normal = item.keywords
+    end
+    keys = tag.attr['safe'] ? @safe : @normal
+    keys.gsub(/\,/, joiner);
+  end 
   
   desc %{
     Usage:
@@ -131,7 +163,7 @@ scope='all|gallery']&gt;
   
   desc %{
     Usage:
-    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:item:page_url /&gt;&lt;/code&gt;&lt;/pre&gt;
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:item:page_url/&gt;&lt;/code&gt;&lt;/pre&gt;
     Provides page url for current gallery item }
   tag &quot;gallery:item:page_url&quot; do |tag|
     item = find_item(tag)</diff>
      <filename>lib/gallery_item_tags.rb</filename>
    </modified>
    <modified>
      <diff>@@ -25,7 +25,8 @@ module GalleryPageExtensions
   desc %{    
     Usage:
     &lt;pre&gt;&lt;code&gt;&lt;r:gallery:link /&gt;&lt;/code&gt;&lt;/pre&gt;
-    Provides link for current gallery }
+    Provides link for current gallery options are rendered 
+    inline as key:value pairs i.e. class='value' id='value', etc.}
   tag &quot;gallery:link&quot; do |tag|
     gallery = find_gallery(tag)
     options = tag.attr.dup
@@ -46,6 +47,34 @@ module GalleryPageExtensions
     File.join(tag.render('url'), gallery.url(self.base_gallery_id))
   end    
   
+  desc %{
+    Usage:
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:if_current_keywords&gt;....&lt;/r:gallery:if_current_keywords&gt;&lt;/code&gt;&lt;/pre&gt;
+    Check to see if keywords are available in the parameters of the request URI
+  }
+  tag 'gallery:if_current_keywords' do |tag|    
+    tag.expand if tag.globals.page.request.parameters['keywords']
+  end  
+  
+  desc %{
+    Usage:
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:unless_current_keywords&gt;....&lt;/r:gallery:unless_current_keywords&gt;&lt;/code&gt;&lt;/pre&gt;
+    Check to for no keywords being available in the parameters of the request URI
+  }
+  tag 'gallery:unless_current_keywords' do |tag|    
+    tag.expand unless tag.globals.page.request.parameters['keywords']
+  end
+  
+  desc %{
+    Usage:
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:current_keywords [separator=',']/&gt;&lt;/code&gt;&lt;/pre&gt;
+    Outputs the current keywords in the parameters of the request URI
+  }
+  tag &quot;gallery:current_keywords&quot; do |tag|          
+    joiner = tag.attr['separator'] ? tag.attr['separator'] : ','
+    tag.globals.page.request.parameters['keywords'].gsub(/\,/, joiner)
+  end
+  
   def current_gallery
     @current_gallery
   end
@@ -56,7 +85,7 @@ module GalleryPageExtensions
       path, item, action = $1, nil, nil
       if path =~ /^(.*)\/(\d+\.\w+)\/(show|download)\/?$/
         path, item, action = $1, $2, $3
-      end            
+      end                                
       @current_gallery = find_gallery_by_path(path)      
       if @current_gallery
         if !item.nil? &amp;&amp; !action.nil?</diff>
      <filename>lib/gallery_page_extensions.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,8 +10,9 @@ module GalleryTags
   
   desc %{    
     Usage:
-    &lt;pre&gt;&lt;code&gt;&lt;r:galleries:each&gt;...&lt;/r:galleries:each&gt;&lt;/code&gt;&lt;/pre&gt;
-    Iterates through all galleries }
+    &lt;pre&gt;&lt;code&gt;&lt;r:galleries:each [order='order' by='by' limit='limit' offset='offset' level='top|current|bottom|all' 
+      keywords='key1,key2,key3' current_keywords='is|is_not']&gt;...&lt;/r:galleries:each&gt;&lt;/code&gt;&lt;/pre&gt;
+      Iterates through all gallery items keywords=(manual entered keywords) and/or current_keywords=(is|is_not) }
   tag &quot;galleries:each&quot; do |tag|
     content = ''
     options = {}
@@ -32,27 +33,44 @@ module GalleryTags
       options[:conditions][:parent_id] = nil
     when 'bottom'
       options[:conditions][:children_count] = 0
-    end
+    end  
 
     by = tag.attr['by'] ? tag.attr['by'] : &quot;position&quot;
     unless Gallery.columns.find{|c| c.name == by }
       raise GalleryTagError.new(&quot;`by' attribute of `each' tag must be set to a valid field name&quot;)
+    end  
+    
+    if !tag.attr['keywords'].nil? || !tag.attr['current_keywords'].nil?                                                                                                                                  
+      keywords = !tag.attr['keywords'].nil? ? tag.attr['keywords'].split(',') : []
+      if (tag.attr['current_keywords'] == 'is' || tag.attr['current_keywords'] == 'is_not') &amp;&amp; !tag.globals.page.request.parameters['keywords'].nil?
+        @current_keywords = tag.globals.page.request.parameters['keywords'].split(',') if !tag.globals.page.request.parameters['keywords'].nil?
+        if !@current_keywords.nil? &amp;&amp; @current_keywords.length &gt; 0
+          keywords.concat(@current_keywords)
+        end
+      end
+      options[:joins] = :gallery_keywords
+      options[:conditions].merge!({&quot;gallery_keywords.keyword&quot; =&gt; keywords}) if keywords.length &gt; 0              
     end
+    
     options[:limit] = tag.attr['limit'] ? tag.attr['limit'].to_i : 9999
     options[:offset] = tag.attr['offset'] ? tag.attr['offset'].to_i  : 0
     order = (%w[ASC DESC].include?(tag.attr['order'].to_s.upcase)) ? tag.attr['order'] : &quot;ASC&quot;
-    options[:order] = &quot;#{by} #{order}&quot;
-    galleries = Gallery.find(:all, options)
+    options[:order] = &quot;#{by} #{order}&quot;  
+    galleries = Gallery.find(:all, options).uniq unless @current_keywords.nil? &amp;&amp; tag.attr['current_keywords'] == 'is'
+    if !@current_keywords.nil? &amp;&amp; tag.attr['current_keywords'] == 'is_not' &amp;&amp; galleries.length &gt; 0                                                   
+      options.merge!(:conditions =&gt; ['galleries.id NOT IN (?) AND hidden =? AND external =?', galleries, false, false])   
+      galleries = Gallery.find(:all, options).uniq
+    end
     galleries.each do |gallery|
       tag.locals.gallery = gallery
       content &lt;&lt; tag.expand
-    end
+    end unless @current_keywords.nil? &amp;&amp; tag.attr['current_keywords'] == 'is' 
     content
   end
   
   desc %{    
     Usage:
-    &lt;pre&gt;&lt;code&gt;&lt;r:gallery [id='id'] [name='name']&gt;...&lt;/r:gallery&gt;&lt;/code&gt;&lt;/pre&gt;
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery [id='id'] [name='name'] [keywords='key1, key2, key3']&gt;...&lt;/r:gallery&gt;&lt;/code&gt;&lt;/pre&gt;
     Selects current gallery }
   tag &quot;gallery&quot; do |tag|
     tag.locals.gallery = find_gallery(tag)
@@ -61,7 +79,7 @@ module GalleryTags
   
   tag 'gallery:if_current' do |tag|    
     tag.expand if @current_gallery
-  end
+  end  
   
   tag 'gallery:unless_current' do |tag|    
     tag.expand unless @current_gallery
@@ -70,15 +88,82 @@ module GalleryTags
   tag 'gallery:current' do |tag|    
     tag.locals.item = @current_gallery
     tag.expand
-  end  
+  end
   
   desc %{    
     Usage:
-    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:name /&gt;&lt;/code&gt;&lt;/pre&gt;
-    Provides name for current gallery }
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:name [safe='true']/&gt;&lt;/code&gt;&lt;/pre&gt;
+    Provides name for current gallery, safe is to make safe for web }
   tag &quot;gallery:name&quot; do |tag|
+    gallery = tag.locals.gallery
+    if tag.attr['safe'] == 'true'  
+      @safe = gallery.name.gsub(/[\s]+/, '_').downcase
+    else 
+      @normal = gallery.name
+    end
+    name = tag.attr['safe'] ? @safe : @normal
+  end
+  
+  desc %{                 
+    Usage:
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:keywords [separator=',' safe='true']/&gt;&lt;/code&gt;&lt;/pre&gt;
+    Provides keywords for current and children galleries, use
+    separator=&quot;separator_string&quot; to specify the character between keywords }
+  tag &quot;gallery:keywords&quot; do |tag|
     gallery = tag.locals.gallery    
-    gallery.name
+    joiner = tag.attr['separator'] ? tag.attr['separator'] : ' ' 
+    if tag.attr['safe'] == 'true'  
+      @safe = gallery.keywords.gsub(/[\s]+/, '_').downcase
+    else 
+      @normal = gallery.keywords
+    end
+    keys = tag.attr['safe'] ? @safe : @normal
+    keys.gsub(/\,/, joiner);
+    tag.expand
+  end                            
+
+  desc %{
+    Usage:
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:keywords:each /&gt;&lt;/code&gt;&lt;/pre&gt;
+    Loops over each keywords for current and children galleries }
+  tag &quot;gallery:keywords:each&quot; do |tag|
+    content =''
+    gallery = tag.locals.gallery
+    gallery.gallery_keywords.uniq.each do |key|
+      tag.locals.uniq_keywords = key
+      content &lt;&lt; tag.expand
+    end
+    content
+  end 
+  
+  desc %{
+    Usage:
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:keywords:keyword [safe='true']/&gt;&lt;/code&gt;&lt;/pre&gt;
+    Get the keyword of the current gallery:keywords loop } 
+  tag 'gallery:keywords:keyword' do |tag|
+    gallery_keyword = tag.locals.uniq_keywords
+    if tag.attr['safe'] == 'true'  
+      @safe = gallery_keyword.keywords.gsub(/[\s]+/, '_').downcase
+    else 
+      @normal = gallery_keyword.keywords
+    end
+    keys = tag.attr['safe'] ? @safe : @normal
+    keys
+  end
+       
+  desc %{
+    Usage:
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:keywords:link [*options]/&gt;&lt;/code&gt;&lt;/pre&gt;
+    Get the keyword and creates a link for the current gallery:keywords loop 
+    options are rendered inline as key:value pairs i.e. class='' id='', etc.}    
+  tag 'gallery:keywords:link' do |tag|
+    keyword = tag.locals.uniq_keywords ? tag.locals.uniq_keywords.keyword : tag.locals.gallery.keywords
+    options = tag.attr.dup
+    attributes = options.inject('') { |s, (k, v)| s &lt;&lt; %{#{k.downcase}=&quot;#{v}&quot; } }.strip
+    attributes = &quot; #{attributes}&quot; unless attributes.empty?
+    text = tag.double? ? tag.expand : tag.render('name')  
+    gallery_url = File.join(tag.render('url'))
+    %{&lt;a href=&quot;#{gallery_url[0..-2]}?keywords=#{keyword.gsub(/[\s]+/, '_')}&quot;#{attributes}&gt;#{keyword}&lt;/a&gt;}
   end
   
   tag 'gallery:breadcrumbs' do |tag|
@@ -107,8 +192,9 @@ module GalleryTags
   
   desc %{    
     Usage:
-    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:children:each&gt;...&lt;/r:gallery:children:each&gt;&lt;/code&gt;&lt;/pre&gt;
-    Iterates over all children in current gallery }
+    &lt;pre&gt;&lt;code&gt;&lt;r:gallery:children:each [order='order' by='by' limit='limit' offset='offset'
+    keywords='key1,key2,key3' current_keywords='is|is_not']&gt;...&lt;/r:gallery:children:each&gt;&lt;/code&gt;&lt;/pre&gt;
+    Iterates through all gallery items keywords=(manual entered keywords) and/or current_keywords=(is|is_not) }
   tag &quot;gallery:children:each&quot; do |tag|
     content = &quot;&quot;
     gallery = find_gallery(tag)
@@ -117,15 +203,33 @@ module GalleryTags
     by = tag.attr['by'] ? tag.attr['by'] : &quot;position&quot;
     unless Gallery.columns.find{|c| c.name == by }
       raise GalleryTagError.new(&quot;`by' attribute of `each' tag must be set to a valid field name&quot;)
+    end   
+               
+    if !tag.attr['keywords'].nil? || !tag.attr['current_keywords'].nil?                                                                                                                                  
+      keywords = !tag.attr['keywords'].nil? ? tag.attr['keywords'].split(',') : []
+      if (tag.attr['current_keywords'] == 'is' || tag.attr['current_keywords'] == 'is_not') &amp;&amp; !tag.globals.page.request.parameters['keywords'].nil?
+        @current_keywords = tag.globals.page.request.parameters['keywords'].split(',') if !tag.globals.page.request.parameters['keywords'].nil?
+        if !@current_keywords.nil? &amp;&amp; @current_keywords.length &gt; 0
+          keywords.concat(@current_keywords)
+        end
+      end
+      options[:joins] = :gallery_keywords
+      options[:conditions].merge!({&quot;gallery_keywords.keyword&quot; =&gt; keywords}) if keywords.length &gt; 0              
     end
+    
     options[:limit] = tag.attr['limit'] ? tag.attr['limit'].to_i : 9999
     options[:offset] = tag.attr['offset'] ? tag.attr['offset'].to_i  : 0
     order = (%w[ASC DESC].include?(tag.attr['order'].to_s.upcase)) ? tag.attr['order'] : &quot;ASC&quot;
-    options[:order] = &quot;#{by} #{order}&quot;        
-    gallery.children.find(:all, options).each do |sub_gallery| 
+    options[:order] = &quot;#{by} #{order}&quot;   
+    galleries = gallery.children.find(:all, options).uniq unless @current_keywords.nil? &amp;&amp; tag.attr['current_keywords'] == 'is'
+    if !@current_keywords.nil? &amp;&amp; tag.attr['current_keywords'] == 'is_not' &amp;&amp; galleries.length &gt; 0                                                   
+      options.merge!(:conditions =&gt; ['galleries.id NOT IN (?) AND hidden =? AND external =?', galleries, false, false])   
+      galleries = gallery.children.find(:all, options).uniq
+    end                                    
+    galleries.each do |sub_gallery| 
       tag.locals.gallery = sub_gallery
       content &lt;&lt; tag.expand      
-    end
+    end unless @current_keywords.nil? &amp;&amp; tag.attr['current_keywords'] == 'is'
     content
   end
   
@@ -210,11 +314,13 @@ module GalleryTags
 
 protected
   
-  def find_gallery(tag)
+  def find_gallery(tag)  
     if tag.locals.gallery
-      tag.locals.gallery
-    elsif tag.attr[&quot;id&quot;]
-      Gallery.find_by_id tag.attr[&quot;id&quot;]
+      tag.locals.gallery 
+    elsif tag.attr[&quot;name&quot;]
+      Gallery.find_by_name tag.attr[&quot;name&quot;]
+    elsif tag.attr[&quot;id&quot;] 
+      Gallery.find_by_id tag.attr[&quot;id&quot;] 
     elsif @current_gallery
       @current_gallery
     elsif tag.locals.page.base_gallery</diff>
      <filename>lib/gallery_tags.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,61 +1,68 @@
 if (!Gallery) var Gallery = {};
 
-Gallery.EditForm = {   
-  
-  init: function(popup, link) {
-    if (!this.submitHandler) this.initializeHandler();
-    var item = link.up('.item');
-    popup.down('form').setAttribute('action', link.getAttribute('href'));
-    var name = item.down('.label a').innerHTML;
-    if (name == '&amp;nbsp;') name = '';
-    $('edit-item-name').value = name;
-    $('edit-item-description').value = item.down('.description').innerHTML;
-  },
-  
-  initializeHandler: function() {    
-    this.submitHandler = $('edit-gallery-item-popup').down('form').observe('submit', this.handleFormSubmit);
-  },
-  
-  open: function(link) {    
-    var popup = $('edit-gallery-item-popup');
-    this.init(popup, link);
-    this.show(popup);
-  },    
-  
-  show: function(popup) {
-    center(popup.show());
-    popup.setStyle({top: '100px'});
-  },
-  
-  close: function() {    
-    var popup = $('edit-gallery-item-popup');
-    popup.hide();
-    this.reset(popup);    
-  },
-  
-  reset: function(popup) {
-    popup.down('form').setAttribute('action', '');
-    $('edit-item-name').value = '';
-    $('edit-item-description').value = '';
-    $('edit-spinner').hide(); 
-    $('edit-submit').show();
-  },
-  
-  handleFormSubmit: function(event) {
-    event.stop();
-    var url = event.target.getAttribute('action');
-    var name = $('edit-item-name').value;
-    var description = $('edit-item-description').value;
-    $('edit-spinner').show();
-    $('edit-submit').hide();
-    new Ajax.Request(url, {
-      method: 'put',
-      parameters: {
-        'gallery_item[name]': name, 'gallery_item[description]': description,
-        authenticity_token: encodeURIComponent($('authenticity_token').value)
-      }
-    });
-    
-  }
-    
+Gallery.EditForm = {
+
+    init: function(popup, link) {
+        if (!this.submitHandler) this.initializeHandler();
+        var item = link.up('.item');
+        popup.down('form').setAttribute('action', link.getAttribute('href'));
+        var name = item.down('.label a').innerHTML;
+        if (name == '&amp;nbsp;') name = '';
+        $('edit-item-name').value = name;
+        $('edit-item-description').value = item.down('.description').innerHTML;
+        $('edit-item-keywords').value = item.down('.keywords').innerHTML;
+    },
+
+    initializeHandler: function() {
+        this.submitHandler = $('edit-gallery-item-popup').down('form').observe('submit', this.handleFormSubmit);
+    },
+
+    open: function(link) {
+        var popup = $('edit-gallery-item-popup');
+        this.init(popup, link);
+        this.show(popup);
+    },
+
+    show: function(popup) {
+        center(popup.show());
+        popup.setStyle({
+            top: '100px'
+        });
+    },
+
+    close: function() {
+        var popup = $('edit-gallery-item-popup');
+        popup.hide();
+        this.reset(popup);
+    },
+
+    reset: function(popup) {
+        popup.down('form').setAttribute('action', '');
+        $('edit-item-name').value = '';
+        $('edit-item-description').value = '';
+        $('edit-item-keywords').value = '';
+        $('edit-spinner').hide();
+        $('edit-submit').show();
+    },
+
+    handleFormSubmit: function(event) {
+        event.stop();
+        var url = event.target.getAttribute('action');
+        var name = $('edit-item-name').value;
+        var description = $('edit-item-description').value;
+        var keywords = $('edit-item-keywords').value;
+        $('edit-spinner').show();
+        $('edit-submit').hide();
+        new Ajax.Request(url, {
+            method: 'put',
+            parameters: {
+                'gallery_item[name]': name,
+                'gallery_item[description]': description,
+                'gallery_item[keywords]': keywords,
+                authenticity_token: encodeURIComponent($('authenticity_token').value)
+            }
+        });
+
+    }
+
 }
\ No newline at end of file</diff>
      <filename>public/javascripts/extensions/gallery/gallery/edit_form.js</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>ce5dedcfdf4a59c95b8d34777bd4f1cb3fad3eb5</id>
    </parent>
  </parents>
  <author>
    <name>Tom Cowell</name>
    <email>tc@eightsquarestudio.com</email>
  </author>
  <url>http://github.com/pilu/radiant-gallery/commit/4a15698bd45ab9c29faf9983b607ed5da090d397</url>
  <id>4a15698bd45ab9c29faf9983b607ed5da090d397</id>
  <committed-date>2009-06-20T09:07:25-07:00</committed-date>
  <authored-date>2009-06-20T09:07:25-07:00</authored-date>
  <message>merging with hairballopolis/radiant-gallery</message>
  <tree>2dd6f4392812af336bf47a45986ffed52f4664c1</tree>
  <committer>
    <name>Tom Cowell</name>
    <email>tc@eightsquarestudio.com</email>
  </committer>
</commit>
