adding taggable images
git-svn-id: http://svn.barleysodas.com/barleysodas/trunk@103 0f7b21a7-9e3a-4941-bbeb-ce5c7c368fa7master
parent
543d65ebb6
commit
044824947b
|
@ -0,0 +1,78 @@
|
|||
class GalleriesController < ApplicationController
|
||||
append_before_filter :fetch_image, :only => [ :show, :destroy,
|
||||
:download_original ]
|
||||
|
||||
# GET /images
|
||||
# GET /images.xml
|
||||
def index
|
||||
@content_title = 'Image Gallery'
|
||||
cond_ary = []
|
||||
cond_var = {
|
||||
:people_id => params[:id]
|
||||
}
|
||||
@secondary_title = "Everybody's Images"
|
||||
if params[:id]
|
||||
cond_ary << 'images.people_id = :people_id'
|
||||
@people = People.find(params[:id])
|
||||
@secondary_title = "Images from #{@people.title}"
|
||||
end
|
||||
cond_ary << '1 = 1' if cond_ary.empty?
|
||||
@pages, @images = paginate :images, :per_page => per_page,
|
||||
:order => 'images.created_at DESC', :include => [ 'people' ],
|
||||
:conditions => [ cond_ary.join(' AND '), cond_var ]
|
||||
flash.now[:notice] = 'There are no images yet.' if @images.empty?
|
||||
respond_to do |format|
|
||||
format.html # index.rhtml
|
||||
format.xml { render :xml => @images.to_xml }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /galleries/1
|
||||
# GET /galleries/1.xml
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html # show.rhtml
|
||||
format.xml { render :xml => @image.to_xml }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /galleries/new
|
||||
def new
|
||||
@image = Image.new
|
||||
end
|
||||
|
||||
# POST /images
|
||||
# POST /images.xml
|
||||
def create
|
||||
@image = Image.new(params[:image])
|
||||
if @image.save
|
||||
flash[:notice] = 'Great success!'
|
||||
redirect_to gallery_url(@image)
|
||||
else
|
||||
render :action => :new
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /galleries/1
|
||||
# DELETE /galleries/1.xml
|
||||
def destroy
|
||||
@image.destroy
|
||||
flash[:notice] = 'Destroyed the image.'
|
||||
redirect_to galleries_url(:id => @image.people_id)
|
||||
end
|
||||
|
||||
##
|
||||
# Sends a copy of the original Image to the People.
|
||||
#
|
||||
def download_original
|
||||
send_file("#{RAILS_ROOT}/public/images/" +
|
||||
@image.filename_for_version(:original),
|
||||
:disposition => 'inline', :type => @image.content_type)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def fetch_image
|
||||
@image = Image.find(params[:id])
|
||||
end
|
||||
end
|
|
@ -0,0 +1,88 @@
|
|||
class TagImagesController < ApplicationController
|
||||
# GET /tag_images
|
||||
# GET /tag_images.xml
|
||||
def index
|
||||
redirect_to images_url
|
||||
end
|
||||
|
||||
# GET /tag_images/1
|
||||
# GET /tag_images/1.xml
|
||||
def show
|
||||
@content_title = 'Tag your friends and beers!'
|
||||
@image = Image.find(params[:id], :include => [ :tag_images ])
|
||||
@tag_images = @image.tag_images
|
||||
respond_to do |format|
|
||||
format.html # show.rhtml
|
||||
format.xml { render :xml => @tag_images.to_xml }
|
||||
end
|
||||
end
|
||||
|
||||
# POST /tag_images
|
||||
# POST /tag_images.xml
|
||||
def create
|
||||
@tag_image = TagImage.new(params[:tag_image])
|
||||
@image = @tag_image.image
|
||||
if @tag_image.save
|
||||
@tag_images = @image.tag_images
|
||||
render :partial => 'tag_images'
|
||||
else
|
||||
render :partial => 'tag_image_errors', :status => 500
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /tag_images/1
|
||||
# DELETE /tag_images/1.xml
|
||||
def destroy
|
||||
@tag_image = TagImage.find params[:id], :include => [ :image ]
|
||||
@image = @tag_image.image
|
||||
@tag_image.destroy
|
||||
@image.tag_images.reload
|
||||
@tag_images = @image.tag_images
|
||||
render :partial => 'tag_images'
|
||||
end
|
||||
|
||||
##
|
||||
# Searches for all known models that support image tagging. Sticks all of
|
||||
# the matching results into a hash that is indexed by the type.
|
||||
#
|
||||
def taggable_search
|
||||
@results = {}
|
||||
cond_ary = [ 'title ILIKE :title' ]
|
||||
cond_var = { :title => "%#{params[:name]}%" }
|
||||
TagImage.types_for_select.flatten.each do |ctype|
|
||||
klass = Class.class_eval(ctype)
|
||||
@results[ctype] = klass.find :all, :order => 'title ASC',
|
||||
:conditions => [ cond_ary.join(' AND '), cond_var ]
|
||||
end
|
||||
render :partial => 'taggable_results'
|
||||
end
|
||||
|
||||
##
|
||||
# Renders an Ajax browser of all tagged Image models for any +:taggable_type+
|
||||
#
|
||||
def tagged_images
|
||||
images_per_page = 4
|
||||
@page_count, @current_page, @tagged_type, @tagged_images = nil, nil, nil, nil
|
||||
@tagged_type = params[:tagged_type]
|
||||
if TagImage.types_for_select.flatten.include?(@tagged_type)
|
||||
cond_ary = [
|
||||
'tagged_type = :tt',
|
||||
'tagged_id = :tid'
|
||||
]
|
||||
cond_var = { :tt => @tagged_type, :tid => params[:id] }
|
||||
conditions = [ cond_ary.join(' AND '), cond_var ]
|
||||
@current_page = params[:page].to_i
|
||||
@current_page = 1 if @current_page == 0
|
||||
image_count = TagImage.count(conditions)
|
||||
@page_count = (image_count.to_f / per_page.to_f + 0.5).to_i
|
||||
@page_count = 1 if @page_count == 0 and image_count >= 0
|
||||
@tagged_images = TagImage.find :all, :limit => images_per_page,
|
||||
:conditions => conditions, :order => 'created_at ASC',
|
||||
:offset => ((@current_page - 1) * images_per_page),
|
||||
:include => [ 'image' ]
|
||||
render :partial => 'tag_images/tagged_images'
|
||||
else
|
||||
render :nothing => true, :status => 500
|
||||
end
|
||||
end
|
||||
end
|
|
@ -91,4 +91,48 @@ module ApplicationHelper
|
|||
res
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Captures a block output and renders it in a partial as <tt>body</tt>
|
||||
#
|
||||
def block_to_partial(partial_name, options = {}, &block)
|
||||
options.merge!(:body => capture(&block))
|
||||
concat(render(:partial => partial_name, :locals => options), block.binding)
|
||||
end
|
||||
|
||||
##
|
||||
# Helper to build a prototype dialog.
|
||||
#
|
||||
def lightbox(options = {}, &block)
|
||||
options = {
|
||||
:title => 'DialogTitle',
|
||||
:window_id => 'DialogId',
|
||||
:modal => false
|
||||
}.merge(options)
|
||||
block_to_partial('shared/lightbox', options, &block)
|
||||
end
|
||||
|
||||
##
|
||||
# Pagination link image browser thingey for the tagged image lightbox.
|
||||
#
|
||||
def image_browser_navigation_link(image_name, page_number, total_pages,
|
||||
tagged_class, tagged_id)
|
||||
if page_number == 0 or
|
||||
(page_number == 1 and total_pages == 1) or
|
||||
(page_number > total_pages)
|
||||
image_tag(image_name)
|
||||
else
|
||||
link_to_remote image_tag(image_name), :update => 'browser_box',
|
||||
:url => { :controller => 'tag_images', :action => 'tagged_images',
|
||||
:id => :tagged_id, :tagged_class => tagged_class }
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Link to open the dialog box for the tagged image browser.
|
||||
#
|
||||
def tagged_image_browser_link
|
||||
link_to_function 'Tagged Images',
|
||||
"lightboxes['tagged_image_browser'].open()"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
module GalleriesHelper
|
||||
def new_image_link
|
||||
link_to 'Upload Image', new_gallery_url
|
||||
end
|
||||
end
|
|
@ -0,0 +1,2 @@
|
|||
module TagImagesHelper
|
||||
end
|
|
@ -6,6 +6,7 @@ class Beer < ActiveRecord::Base
|
|||
has_one_tuxwiki_page :owner_class => 'Beer'
|
||||
belongs_to :style
|
||||
validates_presence_of :style_id
|
||||
has_many_tagged_images
|
||||
|
||||
##
|
||||
# Returns a list of attributes for the Page partial.
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
require 'mini_magick'
|
||||
|
||||
class Image < ActiveRecord::Base
|
||||
attr_accessor :file
|
||||
belongs_to :people
|
||||
validates_presence_of :people_id
|
||||
before_validation_on_create :set_people_id
|
||||
before_create :validate_image_sanity
|
||||
after_create :setup_directories
|
||||
before_destroy :destroy_directories
|
||||
has_many :tag_images, :dependent => :destroy
|
||||
has_many :tagged_items, :through => :tag_images
|
||||
|
||||
##
|
||||
# Builds the filename for this model for a particular version of the file.
|
||||
#
|
||||
def filename_for_version(ver = :screen)
|
||||
if respond_to?(ver)
|
||||
"community/#{id}/#{self.send(ver)}"
|
||||
else
|
||||
"/images/image-missing.png"
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
##
|
||||
# Determines the base directory for all files in this model.
|
||||
#
|
||||
def base_directory
|
||||
"#{RAILS_ROOT}/public/images/community/#{id}"
|
||||
end
|
||||
|
||||
##
|
||||
# Sets the People marker for ownership on creation.
|
||||
#
|
||||
def set_people_id
|
||||
self[:people_id] = ApplicationController.current_people_id rescue nil
|
||||
self[:people_id] ||= People.penguincoder.id rescue nil
|
||||
end
|
||||
|
||||
##
|
||||
# Checks to make sure that the file exists and is an image.
|
||||
#
|
||||
def validate_image_sanity
|
||||
if @file.nil? or @file.to_s.empty?
|
||||
errors.add(:file, 'is not a file')
|
||||
return false
|
||||
end
|
||||
errors.add(:file, 'is too big (3MB max)') if @file.size > 3 * 1048576
|
||||
begin
|
||||
@magick_image = MiniMagick::Image.from_blob(@file.read,
|
||||
File.extname(@file.original_filename))
|
||||
rescue
|
||||
logger.debug("Caught an exception saving an image:")
|
||||
logger.debug("* #{$!}")
|
||||
errors.add(:file, 'is not an image')
|
||||
end
|
||||
return false if self.errors.size > 0
|
||||
self.content_type = @file.content_type.chomp
|
||||
true
|
||||
end
|
||||
|
||||
##
|
||||
# Makes the directories and writes the different versions for the uploaded
|
||||
# files if applicable.
|
||||
#
|
||||
def setup_directories
|
||||
Dir.mkdir(base_directory) unless File.exist?(base_directory)
|
||||
self.original = File.basename(@file.original_filename).gsub(/[^\w._-]/, '')
|
||||
@magick_image.write("#{base_directory}/#{self.original}")
|
||||
@magick_image.thumbnail("600x600>")
|
||||
self.screen = "screen_#{self.original}"
|
||||
@magick_image.write("#{base_directory}/#{self.screen}")
|
||||
if @magick_image.output =~ / (\d+)x(\d+) /
|
||||
self.screen_width = $1
|
||||
self.screen_height = $2
|
||||
end
|
||||
@magick_image.thumbnail("50x50>")
|
||||
self.thumbnail = "thumbnail_#{self.original}"
|
||||
@magick_image.write("#{base_directory}/#{self.thumbnail}")
|
||||
self.save
|
||||
end
|
||||
|
||||
##
|
||||
# Removes the directories and files associated with this model on destroy.
|
||||
#
|
||||
def destroy_directories
|
||||
return unless File.exists?(base_directory)
|
||||
Dir.foreach(base_directory) do |file|
|
||||
next if file =~ /^\.\.?$/
|
||||
File.delete(base_directory + '/' + file)
|
||||
end
|
||||
Dir.delete(base_directory)
|
||||
end
|
||||
end
|
|
@ -7,6 +7,8 @@ class People < ActiveRecord::Base
|
|||
attr_protected :role_id
|
||||
has_many :created_pages, :class_name => 'Page', :foreign_key => 'created_by'
|
||||
has_many :updated_pages, :class_name => 'Page', :foreign_key => 'updated_by'
|
||||
has_many :images, :dependent => :destroy
|
||||
has_many_tagged_images
|
||||
validates_uniqueness_of :title
|
||||
|
||||
make_authenticatable
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
class TagImage < ActiveRecord::Base
|
||||
belongs_to :image
|
||||
belongs_to :tagged, :polymorphic => true
|
||||
validates_presence_of :image_id, :tagged_id, :tagged_type
|
||||
validates_uniqueness_of :tagged_id, :scope => :tagged_type
|
||||
|
||||
def self.types_for_select
|
||||
[ 'Beer', 'People', 'Brewery' ].collect { |x| [x] }
|
||||
end
|
||||
end
|
|
@ -1,7 +1,10 @@
|
|||
<%= render :partial => 'shared/tagged_image_browser', :locals => { :obj => @beer } %>
|
||||
|
||||
<%= render :partial => 'pages/page' %>
|
||||
|
||||
<% content_for :sidebar do -%>
|
||||
<%= new_beer_link -%><br />
|
||||
<%= edit_beer_link(@beer) -%><br />
|
||||
<%= link_to 'Destroy', beer_path(@beer.page.title_for_url), :confirm => 'Are you sure?', :method => :delete %><br />
|
||||
<% unless @beer.tagged_images.empty? -%><%= tagged_image_browser_link -%><br /><% end -%>
|
||||
<% end -%>
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
<%= new_brewery_link -%><br />
|
||||
<%= edit_brewery_link(@brewery) -%><br />
|
||||
<%= link_to 'Destroy', brewery_path(@brewery.page.title_for_url), :confirm => 'Are you sure?', :method => :delete %><br />
|
||||
<%= tagged_image_browser_link -%><br />
|
||||
<% end -%>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<br />
|
||||
<% version ||= :screen -%>
|
||||
<div class="people_image" id="image_<%= image.id -%>">
|
||||
<%= link_to_unless_current(image_tag(image.filename_for_version(version), :alt => image.original), gallery_url(image)) %>
|
||||
<p class="author">Uploaded by <%= link_to(image.people.title, galleries_url(:id => image.people_id)) -%></p>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
<fieldset>
|
||||
<h2>Upload an image</h2>
|
||||
<p>
|
||||
<label>
|
||||
File:
|
||||
<%= file_field 'image', 'file' %>
|
||||
</label>
|
||||
</p>
|
||||
</fieldset>
|
|
@ -0,0 +1,12 @@
|
|||
<h1>Editing images</h1>
|
||||
|
||||
<%= error_messages_for :images %>
|
||||
|
||||
<% form_for(:images, :url => images_path(@images), :html => { :method => :put }) do |f| %>
|
||||
<p>
|
||||
<%= submit_tag "Update" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%= link_to 'Show', images_path(@images) %> |
|
||||
<%= link_to 'Back', images_path %>
|
|
@ -0,0 +1,7 @@
|
|||
<%= render :partial => 'image', :collection => @images, :locals => { :version => :thumbnail } %>
|
||||
|
||||
<%= render :partial => 'shared/pagination_links' %>
|
||||
|
||||
<% content_for :sidebar do -%>
|
||||
<%= new_image_link -%><br />
|
||||
<% end -%>
|
|
@ -0,0 +1,20 @@
|
|||
<%= error_messages_for :image %>
|
||||
|
||||
<% form_for(:images, :url => galleries_path, :html => { :multipart => true, :onsubmit => "$('spinner').style.display = 'inline';" }) do |f| %>
|
||||
<fieldset>
|
||||
<h2>Upload an image</h2>
|
||||
<p>
|
||||
<label>
|
||||
File:
|
||||
<%= file_field 'image', 'file' %>
|
||||
</label>
|
||||
</p>
|
||||
</fieldset>
|
||||
<p>
|
||||
<%= submit_tag "Create" %> <%= image_tag '/images/spinner.gif', :id => 'spinner', :style => 'display:none' %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<% content_for :sidebar do -%>
|
||||
<%= new_image_link -%><br />
|
||||
<% end -%>
|
|
@ -0,0 +1,9 @@
|
|||
<%= render :partial => 'image', :locals => { :image => @image } %>
|
||||
<br />
|
||||
|
||||
<% content_for :sidebar do -%>
|
||||
<%= link_to "#{@image.people.title}'s images (#{@image.people.images.size})", galleries_path(:id => @image.people_id) -%><br />
|
||||
<%= link_to "Download original", :action => 'download_original', :id => @image.id -%><br />
|
||||
<%= link_to 'Destroy', gallery_path(@image), :confirm => 'Are you sure?', :method => :delete %><br />
|
||||
<%= link_to 'Tag Image', :controller => :tag_images, :action => :show, :id => @image.id -%><br />
|
||||
<% end -%>
|
|
@ -5,6 +5,7 @@
|
|||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<%= stylesheet_link_tag 'application', :media => 'all' %>
|
||||
<%= javascript_include_tag :defaults %>
|
||||
<%= javascript_include_tag 'control.modal.js' %>
|
||||
<script type="text/javascript">
|
||||
<%= yield :script %>
|
||||
</script>
|
||||
|
@ -53,4 +54,5 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<div id="<%= window_id -%>_dialog">
|
||||
<div class="dialogBox">
|
||||
<div class="dialogHeader"><%= title -%></div>
|
||||
<br />
|
||||
<div class="dialogContent"><%= body -%></div>
|
||||
</div>
|
||||
</div>
|
||||
<%= link_to(title, "##{window_id}_dialog", { :class => "#{modal ? '' : 'non'}modal_controls", :onclick => "return false;", :id => "#{window_id}_id_key", :style => 'display: none;' }) %>
|
||||
<% content_for :script do -%>
|
||||
addLoadEvent(function(){
|
||||
if(!window.lightboxes)
|
||||
lightboxes = {};
|
||||
if(!window.after_opens)
|
||||
after_opens = {};
|
||||
if(!window.before_closes)
|
||||
before_closes = {}
|
||||
var link = $("<%= window_id -%>_id_key");
|
||||
var key = '<%= window_id -%>';
|
||||
var ao = after_opens[key];
|
||||
var bc = before_closes[key];
|
||||
if(ao == undefined)
|
||||
ao = function(){};
|
||||
if(bc == undefined)
|
||||
bc = function(){};
|
||||
lightboxes[key] = new Control.Modal(link, {
|
||||
afterOpen: ao, beforeClose: bc,
|
||||
overlayCloseOnClick: <%= modal ? 'false' : 'true' -%>
|
||||
});
|
||||
});
|
||||
<% end -%>
|
|
@ -0,0 +1,46 @@
|
|||
<% content_for :stylesheet do -%>
|
||||
#browser_box {
|
||||
}
|
||||
|
||||
#browser_box img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#browser_controls {
|
||||
padding: 3px 10px 3px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
<% end -%>
|
||||
|
||||
<% content_for :script do -%>
|
||||
if(!window.after_opens)
|
||||
after_opens = {};
|
||||
if(!window.before_closes)
|
||||
before_closes = {}
|
||||
|
||||
after_opens['tagged_image_browser'] = function(){
|
||||
$('browser_box').hide();
|
||||
new Ajax.Updater('browser_box',
|
||||
'<%= url_for(:controller => :tag_images, :action => :tagged_images, :id => ((obj ||= nil).nil? ? nil : obj.id), :tagged_type => obj.class) -%>',
|
||||
{
|
||||
onFailure: function() {
|
||||
lightboxes['tagged_image_browser'].close();
|
||||
},
|
||||
onSuccess: function() {
|
||||
$('browser_spinner').hide();
|
||||
new Effect.Appear('browser_box', { duration: 1.5 });
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
before_closes['tagged_image_browser'] = function(){
|
||||
$('browser_box').hide();
|
||||
$('browser_spinner').show();
|
||||
}
|
||||
<% end -%>
|
||||
|
||||
<% lightbox :title => 'Tagged Images', :window_id => 'tagged_image_browser' do -%>
|
||||
<div id="browser_spinner"><div class="centered"><%= image_tag('spinner.gif') -%></div></div>
|
||||
<div id="browser_box"></div>
|
||||
<% end -%>
|
|
@ -0,0 +1 @@
|
|||
<%= error_messages_for 'tag_image' %>
|
|
@ -0,0 +1,8 @@
|
|||
<p>
|
||||
<strong>Tagged items:</strong>
|
||||
<% if @tag_images.empty? -%>
|
||||
None.
|
||||
<% else -%>
|
||||
<%= @tag_images.collect { |t| "<span onmouseover=\"show_tag_at(#{t.x}, #{t.y})\" onmouseout=\"hide_tag_box()\">#{t.tagged.title} (<em>#{t.tagged_type}</em> | #{link_to_remote('Remove', :url => tag_image_path(t), :update => 'tag_images', :method => :delete)})</span>" }.join(', ') -%>
|
||||
<% end -%>
|
||||
</p>
|
|
@ -0,0 +1,10 @@
|
|||
<% @results.keys.each do |ctype| -%>
|
||||
<% next if @results[ctype].empty? -%>
|
||||
<h3><%= ctype.pluralize -%></h3>
|
||||
<p>
|
||||
<%= @results[ctype].collect { |r| link_to_function(r.title, "set_taggable_item(#{r.id}, '#{r.title}', '#{ctype}');") }.join(', ') %>
|
||||
</p>
|
||||
<% end -%>
|
||||
<% unless @results.detect { |key, val| !val.empty? } -%>
|
||||
<small>No results found...</small>
|
||||
<% end -%>
|
|
@ -0,0 +1,11 @@
|
|||
<div class="centered">
|
||||
<% @tagged_images.each do |tag_image| -%>
|
||||
<%= link_to(image_tag(tag_image.image.filename_for_version(:thumbnail)), gallery_path(tag_image.image), :popup => true) %>
|
||||
<% end -%>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div id="browser_controls" class="centered">
|
||||
<%= image_browser_navigation_link('go-first.png', 1, @page_count, @tagged_type, params[:id]) -%> <%= image_browser_navigation_link('go-previous.png', @current_page - 1, @page_count, @tagged_type, params[:id]) -%> <%= @current_page -%> / <%= @page_count -%> <%= image_browser_navigation_link('go-next.png', @current_page + 1, @page_count, @tagged_type, params[:id]) -%> <%= image_browser_navigation_link('go-last.png', @page_count, @page_count, @tagged_type, params[:id]) -%>
|
||||
</div>
|
|
@ -0,0 +1,110 @@
|
|||
<% content_for :stylesheet do -%>
|
||||
#image_block {
|
||||
z-index: 0;
|
||||
border: 1px solid black;
|
||||
padding: 0px;
|
||||
width: <%= @image.screen_width -%>px;
|
||||
height: <%= @image.screen_height -%>px;
|
||||
background-image: url('/images/<%= @image.filename_for_version -%>');
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
#image_block_container {
|
||||
margin: 10px <%= (605 - @image.screen_width) / 2 -%>px 20px <%= (605 - @image.screen_width) / 2 -%>px;
|
||||
}
|
||||
#image_tag_box {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 5px solid #db3333;
|
||||
left: 0;
|
||||
top: 0;
|
||||
display: none;
|
||||
}
|
||||
<% end -%>
|
||||
|
||||
<% content_for :script do -%>
|
||||
function show_tag_at(xcoord, ycoord)
|
||||
{
|
||||
$('image_tag_box').style.top = (ycoord - 50) + 'px';
|
||||
$('image_tag_box').style.left = (xcoord - 50) + 'px';
|
||||
$('image_tag_box').style.display = 'block';
|
||||
}
|
||||
|
||||
function hide_tag_box()
|
||||
{
|
||||
$('image_tag_box').style.display = 'none';
|
||||
}
|
||||
|
||||
function set_coordinates(event)
|
||||
{
|
||||
xcoord = (event.offsetX ? event.offsetX : (event.pageX - $('image_block').offsetLeft));
|
||||
ycoord = (event.offsetY ? event.offsetY : (event.pageY - $('image_block').offsetTop));
|
||||
show_tag_at(xcoord, ycoord);
|
||||
lightboxes['taggedContentDialog'].open();
|
||||
}
|
||||
|
||||
function set_taggable_item(id, title, type)
|
||||
{
|
||||
$('tag_image_tagged_id').value = id;
|
||||
$('tag_image_title').innerHTML = title;
|
||||
$('tag_image_tagged_type').value = type;
|
||||
}
|
||||
|
||||
if(!window.after_opens)
|
||||
after_opens = {};
|
||||
if(!window.before_closes)
|
||||
before_closes = {};
|
||||
after_opens['taggedContentDialog'] = function(){
|
||||
$('tag_image_x').value = xcoord;
|
||||
$('tag_image_y').value = ycoord;
|
||||
$('tag_image_image_id').value = <%= params[:id] -%>;
|
||||
$('search').focus();
|
||||
}
|
||||
before_closes['taggedContentDialog'] = function(){
|
||||
hide_tag_box();
|
||||
}
|
||||
<% end -%>
|
||||
|
||||
<div id="image_block_container" class="centered">
|
||||
<div id="image_block" onclick='set_coordinates(event);'>
|
||||
<div id="image_tag_box" style="display: none;"></div>
|
||||
</div>
|
||||
<br />
|
||||
<div id="tag_images">
|
||||
<%= render :partial => 'tag_images' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% lightbox :title => 'Search for a taggable item', :window_id => 'taggedContentDialog' do -%>
|
||||
<div id="tag_image_errors" class="errorExplanation"></div>
|
||||
<form id="tag_image_fields">
|
||||
<%= hidden_field 'tag_image', 'x' %>
|
||||
<%= hidden_field 'tag_image', 'y' %>
|
||||
<%= hidden_field 'tag_image', 'image_id' %>
|
||||
<%= hidden_field 'tag_image', 'tagged_id' %>
|
||||
<%= hidden_field 'tag_image', 'tagged_type' %>
|
||||
</form>
|
||||
|
||||
<div class="centered">
|
||||
<%= link_to_function(image_tag('edit-clear.png'), "$('search').value = '';") -%> <%= text_field_tag 'search', '', :size => 30 -%><br />
|
||||
Selected: <span id="tag_image_title">None</span>
|
||||
</div>
|
||||
|
||||
<div id="taggable_results" class="dialogSearchResults"></div>
|
||||
|
||||
<%= observe_field 'search',
|
||||
:url => { :action => 'taggable_search' },
|
||||
:frequency => 2,
|
||||
:update => 'taggable_results',
|
||||
:with => "'name='+escape(value)" %>
|
||||
|
||||
<div class="dialogControls">
|
||||
<%= link_to_remote('Save', { :url => tag_images_path, :with => "Form.serialize($('tag_image_fields'))", :success => 'Control.Modal.close()', :update => { :success => 'tag_images', :failure => 'tag_image_errors' } }, { :method => :post }) -%>
|
||||
<%= link_to_function 'Cancel', "Control.Modal.close()" -%>
|
||||
</div>
|
||||
<% end -%>
|
||||
|
||||
<% content_for :sidebar do -%>
|
||||
<%= link_to 'Image Details', gallery_path(@image) -%><br />
|
||||
<% end -%>
|
|
@ -9,3 +9,4 @@ end
|
|||
|
||||
require 'redcloth'
|
||||
require 'has_one_page'
|
||||
require 'has_many_tagged_images'
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
ActionController::Routing::Routes.draw do |map|
|
||||
map.resources :tag_images
|
||||
|
||||
map.resources :beers, :breweries, :pages, :discussions, :peoples, :roles,
|
||||
:sessions, :styles, :galleries
|
||||
:sessions, :styles, :galleries, :tag_images
|
||||
|
||||
map.connect ':controller/:action/:id.:format'
|
||||
map.connect ':controller/:action/:id'
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
class CreateImages < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :images do |t|
|
||||
t.column :people_id, :integer
|
||||
t.column :created_at, :datetime
|
||||
t.column :original, :string
|
||||
t.column :thumbnail, :string
|
||||
t.column :screen, :string
|
||||
t.column :screen_width, :integer
|
||||
t.column :screen_height, :integer
|
||||
t.column :content_type, :string
|
||||
end
|
||||
add_index :images, :people_id
|
||||
create_table :images_pages, :id => false do |t|
|
||||
t.column :image_id, :integer
|
||||
t.column :page_id, :integer
|
||||
end
|
||||
add_index :images_pages, :image_id
|
||||
add_index :images_pages, :page_id
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :images
|
||||
drop_table :images_pages
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
class CreateTagImages < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :tag_images do |t|
|
||||
t.column :image_id, :integer
|
||||
t.column :tagged_id, :integer
|
||||
t.column :tagged_type, :string, :limit => 32
|
||||
t.column :primary, :boolean
|
||||
t.column :x, :integer
|
||||
t.column :y, :integer
|
||||
end
|
||||
add_index :tag_images, :image_id
|
||||
add_index :tag_images, :tagged_id
|
||||
add_index :tag_images, :tagged_type
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :tag_images
|
||||
end
|
||||
end
|
|
@ -5,7 +5,7 @@ base_actions = ApplicationController.action_methods
|
|||
# rather than defining them here.
|
||||
controllers = [ PagesController, DiscussionsController, StylesController,
|
||||
PeoplesController, BeersController, BreweriesController, RolesController,
|
||||
GalleriesController ]
|
||||
GalleriesController, TagImagesController ]
|
||||
controllers.each do |c|
|
||||
actions = c.action_methods - base_actions
|
||||
cname = c.controller_name
|
||||
|
@ -28,6 +28,11 @@ Permission.find(:all,
|
|||
next if [ 'new', 'create', 'edit', 'update', 'destroy' ].include?(p.action)
|
||||
r.permissions << p
|
||||
end
|
||||
Permission.find(:all,
|
||||
:conditions => [ 'controller = ?', 'tag_images' ]).each do |p|
|
||||
next if [ 'show', 'create', 'destroy', 'taggable_search' ].include?(p.action)
|
||||
r.permissions << p
|
||||
end
|
||||
|
||||
r2 = Role.admin_role
|
||||
Permission.find(:all).each do |p|
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
module ActiveRecord # :nodoc:
|
||||
class Base # :nodoc:
|
||||
class << self
|
||||
##
|
||||
# This method will add a has_one :page association and a few useful
|
||||
# callbacks to the requested model. It expects to have a
|
||||
# :owner_class parameter given so that it knows what the owner class
|
||||
# name should be. The associated model will automatically be deleted
|
||||
# when this model is deleted.
|
||||
#
|
||||
# The Page will automatically have the title updated from the owner's
|
||||
# title field and be saved after a successful save. When a Page errors
|
||||
# on validation, the errors are automatically copied into the owner so
|
||||
# that the user doesn't even have to know what is going on.
|
||||
#
|
||||
def has_many_tagged_images(options = {})
|
||||
class_eval do
|
||||
has_many :tagged_images, :source_type => self.base_class.to_s,
|
||||
:source => :tagged, :through => :tag_images
|
||||
has_many :tag_images, :dependent => :destroy, :as => :tagged
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Binary file not shown.
After Width: | Height: | Size: 773 B |
Binary file not shown.
After Width: | Height: | Size: 666 B |
Binary file not shown.
After Width: | Height: | Size: 685 B |
Binary file not shown.
After Width: | Height: | Size: 676 B |
Binary file not shown.
After Width: | Height: | Size: 655 B |
|
@ -1,3 +1,15 @@
|
|||
function addLoadEvent(func) {
|
||||
var oldonload = window.onload;
|
||||
if (typeof window.onload != 'function') {
|
||||
window.onload = func;
|
||||
} else {
|
||||
window.onload = function() {
|
||||
oldonload();
|
||||
func();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function set_all_checkboxes(form_name, field_name, check_value)
|
||||
{
|
||||
if(!document.forms[form_name])
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
@import 'layout.css';
|
||||
@import 'content.css';
|
||||
@import 'syntax.css';
|
||||
@import 'lightboxes.css';
|
||||
|
||||
@media print { #sidebar { display: none; }
|
||||
#content { float: none; width: 90%; }
|
||||
|
@ -22,10 +23,10 @@ textarea { font: normal 12px "bitstream vera sans", verdana, sans-serif; }
|
|||
|
||||
abbr { border: none; }
|
||||
cite { font-style: normal; }
|
||||
a img { border: none; padding: 0; margin: 0; }
|
||||
a img { border: none; padding: 0; margin: 0; vertical-align: middle; text-decoration: none; }
|
||||
|
||||
a:link, a:visited { color: #000; }
|
||||
a:hover, a:active { color: #fff; background: #000; }
|
||||
a:link, a:visited { color: #000; text-decoration: none; }
|
||||
a:hover, a:active { text-decoration: underline; }
|
||||
|
||||
/* http://longren.org/2006/09/27/wrapping-text-inside-pre-tags */
|
||||
pre {
|
||||
|
@ -35,3 +36,5 @@ pre {
|
|||
white-space: -o-pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.centered { text-align: center; }
|
||||
|
|
|
@ -263,15 +263,17 @@
|
|||
}
|
||||
|
||||
#content .people_image .author {
|
||||
margin: 0 0 0.5em 0.5em;
|
||||
width: 60px; height: 60px;
|
||||
float: right;
|
||||
margin: 0 2.5em 0.5em 0.5em;
|
||||
}
|
||||
|
||||
#content .people_image .meta {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.people_image img {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
Sidebar
|
||||
--------------------------------------------------------------*/
|
||||
|
@ -280,6 +282,12 @@
|
|||
font-size: 11px;
|
||||
}
|
||||
|
||||
#sidebar a:hover, #sidebar a:active {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#sidebar h3 {
|
||||
font: bold 14px "lucidamac bold", "lucida grande", verdana, arial, helvetica, sans-serif;
|
||||
margin: 0 0 0.5em;
|
||||
|
@ -351,30 +359,6 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
/* Flickr sidebar-node */
|
||||
#sidebar #flickr {
|
||||
margin: 0 0 2em;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#sidebar #flickr div {
|
||||
}
|
||||
|
||||
#sidebar #flickr img {
|
||||
margin: 0 0 5px;
|
||||
padding: 5px;
|
||||
border: 1px solid #ddd;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#sidebar #flickr img:hover {
|
||||
background: #ffc;
|
||||
}
|
||||
|
||||
#sidebar #flickr a {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
Footer
|
||||
--------------------------------------------------------------*/
|
||||
|
@ -393,6 +377,7 @@
|
|||
#footer a:active {
|
||||
color: #fff;
|
||||
background: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#footer hr {
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
.dialogBox {
|
||||
background-color: #eee;
|
||||
border: 1px solid #444;
|
||||
-moz-border-radius: 5px;
|
||||
padding: 4px;
|
||||
height: auto;
|
||||
min-height: 75px;
|
||||
max-height: 450px;
|
||||
width: auto;
|
||||
min-width: 275px;
|
||||
max-width: 550px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dialogHeader {
|
||||
background: #999;
|
||||
padding: 2px 5px 2px 5px;
|
||||
text-align: center;
|
||||
-moz-border-radius: 5px;
|
||||
border: 1px solid #444;
|
||||
color: #FFF;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.dialogControls {
|
||||
height: auto;
|
||||
text-align: center;
|
||||
width: auto;
|
||||
font-size: 1.1em;
|
||||
margin: 10px;
|
||||
}
|
||||
.dialogControls a {
|
||||
background-color: #CCC;
|
||||
border: 1px solid black;
|
||||
padding: 0.25em 1.0em 0.25em 1.0em;
|
||||
text-decoration: none;
|
||||
-moz-border-radius: 3px;
|
||||
}
|
||||
.dialogControls a:hover, .dialogControls a.over {
|
||||
text-decoration: none;
|
||||
border: 1px solid black;
|
||||
background-color: #999;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dialogContent {
|
||||
padding: 0px 10px 0px 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dialogSearchResults {
|
||||
padding: 0.25em;
|
||||
margin-bottom: 5px;
|
||||
font-size: 1.1em;
|
||||
min-height: 50px;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
.dialogSearchResults li {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
#modal_overlay {
|
||||
/* konqueror doesn't like these styles. omit for now.
|
||||
background-color: #727272;
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
|
||||
one:
|
||||
id: 1
|
||||
two:
|
||||
id: 2
|
|
@ -0,0 +1,5 @@
|
|||
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
|
||||
one:
|
||||
id: 1
|
||||
two:
|
||||
id: 2
|
|
@ -0,0 +1,57 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'images_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class GalleriesController; def rescue_action(e) raise e end; end
|
||||
|
||||
class GalleriesControllerTest < Test::Unit::TestCase
|
||||
fixtures :images
|
||||
|
||||
def setup
|
||||
@controller = GalleriesController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_should_get_index
|
||||
get :index
|
||||
assert_response :success
|
||||
assert assigns(:images)
|
||||
end
|
||||
|
||||
def test_should_get_new
|
||||
get :new
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_create_images
|
||||
old_count = Images.count
|
||||
post :create, :images => { }
|
||||
assert_equal old_count+1, Images.count
|
||||
|
||||
assert_redirected_to images_path(assigns(:images))
|
||||
end
|
||||
|
||||
def test_should_show_images
|
||||
get :show, :id => 1
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_get_edit
|
||||
get :edit, :id => 1
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_update_images
|
||||
put :update, :id => 1, :images => { }
|
||||
assert_redirected_to images_path(assigns(:images))
|
||||
end
|
||||
|
||||
def test_should_destroy_images
|
||||
old_count = Images.count
|
||||
delete :destroy, :id => 1
|
||||
assert_equal old_count-1, Images.count
|
||||
|
||||
assert_redirected_to images_path
|
||||
end
|
||||
end
|
|
@ -0,0 +1,57 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'tag_images_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class TagImagesController; def rescue_action(e) raise e end; end
|
||||
|
||||
class TagImagesControllerTest < Test::Unit::TestCase
|
||||
fixtures :tag_images
|
||||
|
||||
def setup
|
||||
@controller = TagImagesController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_should_get_index
|
||||
get :index
|
||||
assert_response :success
|
||||
assert assigns(:tag_images)
|
||||
end
|
||||
|
||||
def test_should_get_new
|
||||
get :new
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_create_tag_image
|
||||
old_count = TagImage.count
|
||||
post :create, :tag_image => { }
|
||||
assert_equal old_count+1, TagImage.count
|
||||
|
||||
assert_redirected_to tag_image_path(assigns(:tag_image))
|
||||
end
|
||||
|
||||
def test_should_show_tag_image
|
||||
get :show, :id => 1
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_get_edit
|
||||
get :edit, :id => 1
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_update_tag_image
|
||||
put :update, :id => 1, :tag_image => { }
|
||||
assert_redirected_to tag_image_path(assigns(:tag_image))
|
||||
end
|
||||
|
||||
def test_should_destroy_tag_image
|
||||
old_count = TagImage.count
|
||||
delete :destroy, :id => 1
|
||||
assert_equal old_count-1, TagImage.count
|
||||
|
||||
assert_redirected_to tag_images_path
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class ImageTest < Test::Unit::TestCase
|
||||
fixtures :images
|
||||
|
||||
# Replace this with your real tests.
|
||||
def test_truth
|
||||
assert true
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class TagImageTest < Test::Unit::TestCase
|
||||
fixtures :tag_images
|
||||
|
||||
# Replace this with your real tests.
|
||||
def test_truth
|
||||
assert true
|
||||
end
|
||||
end
|
Reference in New Issue