adding users back into photos for better tracking, adding approve, flag and moderate photos, adding moderate link for administrators in application layout, improving photo controls, improving photo routing, adding a few indexes for photos table

master
Coleman 2008-10-15 01:31:50 -05:00
parent 6580414459
commit 0238d49415
15 changed files with 184 additions and 39 deletions

View File

@ -1,8 +1,8 @@
class Photos < Application
before :logged_in?, :only => [ :new, :create, :delete ]
before :administrator?, :only => [ :delete ]
before :logged_in?, :only => [ :new, :create, :destroy ]
before :administrator?, :only => [ :destroy, :approve, :moderate ]
before :make_photo, :only => [ :new, :create ]
before :fetch_photo, :only => [ :show, :delete, :thumbnail ]
before :fetch_photo, :only => [ :show, :destroy, :thumbnail, :approve, :flag ]
def index
@page = params[:page].to_i
@ -25,6 +25,7 @@ class Photos < Application
end
def create
@photo.user = current_user
if @photo.save
flash[:notice] = 'Great success'
redirect url(:photo, @photo)
@ -33,12 +34,12 @@ class Photos < Application
end
end
def delete
def destroy
raise NotAcceptable unless request.xhr?
if current_user and current_user.administrator?
render
if @photo.destroy
render '', :status => 200
else
redirect '/'
render '', :status => 401
end
end
@ -55,6 +56,31 @@ class Photos < Application
end
end
def approve
raise NotAllowed unless request.xhr?
@photo.approved = true
if @photo.save
render '', :status => 200
else
render '', :status => 401
end
end
def flag
raise NotAllowed unless request.xhr?
pf = PhotoFlag.new :photo_id => @photo.id, :user_id => current_user.id
if pf.save
render '', :status => 200
else
render '', :status => 401
end
end
def moderate
@photos = Photo.find :all, :order => "photo_flags_count DESC, id ASC", :conditions => [ 'approved = ?', false ]
render
end
protected
def make_photo

View File

@ -3,8 +3,12 @@ class Photo < ActiveRecord::Base
attr_accessor :file
attr_protected :email_hash
validates_presence_of :user_id
belongs_to :user
has_many :votes, :dependent => :destroy
has_many :photo_favorites, :dependent => :destroy
has_many :photo_flags, :dependent => :destroy
before_create :validate_image_sanity
before_create :hashify_email
@ -41,6 +45,10 @@ class Photo < ActiveRecord::Base
"#{self.relative_directory}/#{self.filename}"
end
##
# Finds a Photo that the requested user can vote on by checking it against
# all of the photos previously voted.
#
def self.next_available_votable_photo(user)
pids = Vote.voted_photo_ids(user)
c = if pids.empty?
@ -51,6 +59,18 @@ class Photo < ActiveRecord::Base
self.find :first, :conditions => c, :order => 'id ASC'
end
##
# Checks to see if a User or anonymous user has flagged a Photo as unsuitable.
#
def flagged?(user)
f = if user.respond_to?('id')
self.photo_flags.find :first, :conditions => "photo_flags.user_id = #{user.id}"
else
self.photo_flags.find :first, :conditions => "photo_flags.session_id = #{user}"
end
!f.nil?
end
protected
##
@ -132,6 +152,10 @@ class Photo < ActiveRecord::Base
# Regenerates the calculated value of oneness.
#
def set_oneness
self.oneness = (self.one_votes.to_f / self.votes_count.to_f * 100.0)
if self.votes_count == 0
self.oneness = 0
else
self.oneness = (self.one_votes.to_f / self.votes_count.to_f * 100.0)
end
end
end

5
app/models/photo_flag.rb Normal file
View File

@ -0,0 +1,5 @@
class PhotoFlag < ActiveRecord::Base
belongs_to :photo, :counter_cache => true
belongs_to :user
validates_uniqueness_of :user_id
end

View File

@ -9,10 +9,12 @@ class User < ActiveRecord::Base
validates_uniqueness_of :user_name
validates_format_of :user_name, :with => /[\w_-]+/
has_many :photos, :dependent => :destroy
has_many :votes, :dependent => :destroy, :order => 'votes.photo_id ASC'
has_many :photos, :dependent => :destroy, :through => :votes
has_many :voted_photos, :through => :votes, :class_name => 'Photo', :source => :photo
has_many :photo_favorites, :dependent => :destroy
has_many :favorite_photos, :through => :photo_favorites, :class_name => 'Photo', :source => :photo
has_many :photo_flags, :dependent => :destroy
before_validation :saltify_password

View File

@ -21,12 +21,15 @@
- if logged_in?
#header_user
%p
Welcome,
Welcome,
== <strong>#{current_user.user_name}</strong>
%br
%a{ :href => url(:delete_session, :id => session[:user_id]), :title => 'Log out of B.A.' }
Log out
%img{ :src => '/images/system-log-out.png' }
- if administrator?
%br
%a{ :href => url(:moderate_photos) } Moderate
#tool_bar
- menu_items.each do |menu_item|
%a{ :href => menu_item[:href], :title => menu_item[:title] }

View File

@ -1,22 +1,14 @@
:javascript
function add_favorite(pid)
{
new Ajax.Request('#{url(:favorites)}?photo_id=' + pid, { onSuccess: function(){ $('favorite_add').hide(); $('favorite_remove').show(); new Effect.Highlight($('favorites')); } });
}
function remove_favorite(del_url)
{
new Ajax.Request(del_url, { onSuccess: function(){ $('favorite_remove').hide(); $('favorite_add').show(); new Effect.Highlight($('favorites')); } });
}
function transition_out_controls()
function transition_voting_controls()
{
if($('to_be_unvoted').style.display == 'none')
return false;
new Effect.DropOut($('to_be_unvoted'));
setTimeout('transition_in_controls()', 1000);
setTimeout("new Effect.Appear($('to_be_voted'));", 1000);
}
function transition_in_controls()
function notify_controls()
{
new Effect.Appear($('to_be_voted'));
new Effect.Highlight($('photo_controls'));
}
%style{ :type => "text/css" }
@ -42,18 +34,29 @@
%div.centered{ :style => "width: #{@photo.width rescue 50}px" }
- if @photo and @photo.exist?
#photo_controls
- unless Vote.voted_for?(@photo, current_user) or params[:controller] =~ /vote/i
%span#to_be_unvoted
%a{ :href => '#', :onclick => "transition_out_controls(); return false;" } Vote!
- if logged_in?
#photo_controls
- unless Vote.voted_for?(@photo, current_user) or params[:controller] =~ /vote/i
%span#to_be_unvoted
%a{ :href => '#', :onclick => "transition_voting_controls(); return false;" } Vote!
|
- if !@photo.approved? and !@photo.flagged?(current_user)
%span#to_be_flagged
%a{ :href => '#', :onclick => "new Ajax.Request('#{url(:flag_photo, @photo)}', { method: 'get', onSuccess: function() { $('to_be_flagged').hide(); notify_controls(); } }); return false;" } Flag inappropriate
|
- if !@photo.approved? and administrator?
%span#to_be_approved
%a{ :href => '#', :onclick => "new Ajax.Request('#{url(:approve_photo, @photo)}', { method: 'get', onSuccess: function() { $('to_be_approved').hide(); if($('to_be_flagged')){$('to_be_flagged').hide();}; notify_controls(); } }); return false;" } Approve
|
- if administrator?
%a{ :href => '#', :onclick => "if(confirm('Are you sure you want to destroy this photo?')){ new Ajax.Request('#{url(:photo, @photo)}', { method: 'delete', onSuccess: function() { window.location.href = '#{url(:photos)}'; } }); return false; }" } Destroy
|
- if logged_in?
%span#favorites
%span#favorite_add{ :style => (current_user.photo_favorites.detect { |f| f.photo_id == @photo.id }.nil? ? '' : "display: none;") }
%a{ :href => '#', :onclick => "add_favorite(#{@photo.id}); return false;" }
%a{ :href => '#', :onclick => "new Ajax.Request('#{url(:favorites, :photo_id => @photo.id)}', { onSuccess: function(){ $('favorite_add').hide(); new Effect.Appear($('favorite_remove')); notify_controls(); } }); return false;" }
Add as favorite
%span#favorite_remove{ :style => (current_user.photo_favorites.detect { |f| f.photo_id == @photo.id }.nil? ? "display: none;" : '') }
%a{ :href => '#', :onclick => "remove_favorite('#{url(:favorite, :id => @photo.id)}?_method=delete'); return false;" }
%a{ :href => '#', :onclick => "new Ajax.Request('#{url(:favorite, :id => @photo.id)}', { method: 'delete', onSuccess: function(){ $('favorite_remove').hide(); new Effect.Appear($('favorite_add')); notify_controls(); } }); return false;" }
Remove favorite
%img{ :src => @photo.pathname, :alt => @photo.filename, :width => @photo.width, :height => @photo.height }
- else

View File

@ -0,0 +1,22 @@
%style{ :type => 'text/css' }
:sass
.moderate_box
:width 310px
:height 320px
:margin 10px auto
:text-align center
img
:display block
:margin-left auto
:margin-right auto
%h1 Photo moderation panel
- @photos.each do |photo|
%div.moderate_box{ :id => "photo_#{photo.id}" }
%img{ :src => photo_url(photo, 300, 300) }
%a{ :href => '#', :onclick => "new Ajax.Request('#{url(:approve_photo, photo)}', { method: 'get', onSuccess: function() { new Effect.DropOut($('photo_#{photo.id}')); } }); return false;" } Approve
|
== #{photo.photo_flags_count} Flags
|
%a{ :href => '#', :onclick => "if(confirm('Are you sure you want to destroy this photo?')){ new Ajax.Request('#{url(:photo, photo)}', { method: 'delete', onSuccess: function() { new Effect.DropOut($('photo_#{photo.id}')); } }); return false; }" } Destroy

View File

@ -1,6 +1,6 @@
#main_photo_container
= partial 'photos/photo'
- v = Vote.voted_for? @photo, current_user
- if v
#mini_container.centered

View File

@ -11,5 +11,11 @@ Merb::Router.prepare do |r|
r.resources :users
r.resources :votes
r.resources :favorites
r.resources :photos, :member => { :thumbnail => :get }
r.resources :photos, :member => {
:thumbnail => :get,
:flag => :get,
:approve => :get
}, :collection => {
:moderate => :get
}
end

View File

@ -85,6 +85,10 @@ h1 img {
top: 10px;
}
#header_user a:hover {
font-weight: bold;
}
#tool_bar {
margin-top: 7px;
margin-bottom: 10x;
@ -118,7 +122,7 @@ h1 img {
#footer {
font-size: 12px;
height: 25px;
width: 90%;
width: 700px;
border-top: 2px solid #e8e8e8;
text-align: right;
padding: 5px 0 10px 0;

View File

@ -2,11 +2,12 @@ class CreatePhotosMigration < ActiveRecord::Migration
def self.up
create_table :photos do |t|
t.string :filename, :content_type, :email_hash
t.integer :width, :height
t.integer :width, :height, :user_id
t.datetime :created_at
t.boolean :approved
t.boolean :approved, :default => false
end
add_index :photos, :email_hash
add_index :photos, :user_id
end
def self.down

View File

@ -0,0 +1,16 @@
class PhotoFlagMigration < ActiveRecord::Migration
def self.up
create_table :photo_flags do |t|
t.integer :user_id, :photo_id
t.string :session_id
end
add_index :photo_flags, :user_id
add_index :photo_flags, :photo_id
add_column :photos, :photo_flags_count, :integer, :default => 0
end
def self.down
drop_table :photo_flags
remove_column :photos, :photo_flags_count
end
end

View File

@ -0,0 +1,13 @@
class PhotoIndexesMigration < ActiveRecord::Migration
def self.up
add_index :photos, :votes_count
add_index :photos, :oneness
add_index :photos, :approved
end
def self.down
remove_index :photos, :votes_count
remove_index :photos, :oneness
remove_index :photos, :approved
end
end

View File

@ -9,7 +9,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 6) do
ActiveRecord::Schema.define(:version => 8) do
create_table "photo_favorites", :force => true do |t|
t.integer "photo_id"
@ -19,6 +19,15 @@ ActiveRecord::Schema.define(:version => 6) do
add_index "photo_favorites", ["user_id"], :name => "index_photo_favorites_on_user_id"
add_index "photo_favorites", ["photo_id"], :name => "index_photo_favorites_on_photo_id"
create_table "photo_flags", :force => true do |t|
t.integer "user_id"
t.integer "photo_id"
t.string "session_id"
end
add_index "photo_flags", ["photo_id"], :name => "index_photo_flags_on_photo_id"
add_index "photo_flags", ["user_id"], :name => "index_photo_flags_on_user_id"
create_table "photos", :force => true do |t|
t.string "filename"
t.string "content_type"
@ -26,13 +35,17 @@ ActiveRecord::Schema.define(:version => 6) do
t.integer "width"
t.integer "height"
t.datetime "created_at"
t.boolean "approved"
t.integer "votes_count", :default => 0
t.integer "one_votes", :default => 0
t.integer "zero_votes", :default => 0
t.boolean "approved", :default => false
t.integer "votes_count", :default => 0
t.integer "one_votes", :default => 0
t.integer "zero_votes", :default => 0
t.float "oneness"
t.integer "photo_flags_count", :default => 0
end
add_index "photos", ["approved"], :name => "index_photos_on_approved"
add_index "photos", ["oneness"], :name => "index_photos_on_oneness"
add_index "photos", ["votes_count"], :name => "index_photos_on_votes_count"
add_index "photos", ["email_hash"], :name => "index_photos_on_email_hash"
create_table "sessions", :force => true do |t|

View File

@ -0,0 +1,7 @@
require File.join( File.dirname(__FILE__), '..', "spec_helper" )
describe PhotoFlag do
it "should have specs"
end