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
parent
6580414459
commit
0238d49415
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class PhotoFlag < ActiveRecord::Base
|
||||
belongs_to :photo, :counter_cache => true
|
||||
belongs_to :user
|
||||
validates_uniqueness_of :user_id
|
||||
end
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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] }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -1,6 +1,6 @@
|
|||
#main_photo_container
|
||||
= partial 'photos/photo'
|
||||
|
||||
|
||||
- v = Vote.voted_for? @photo, current_user
|
||||
- if v
|
||||
#mini_container.centered
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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|
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
require File.join( File.dirname(__FILE__), '..', "spec_helper" )
|
||||
|
||||
describe PhotoFlag do
|
||||
|
||||
it "should have specs"
|
||||
|
||||
end
|
Reference in New Issue