diff --git a/Gemfile b/Gemfile index 261b71b7d..f4decb75c 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,7 @@ gem "coderay", "~> 1.0.6" gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby] gem "builder", "3.0.0" gem 'acts-as-taggable-on' +gem 'seems_rateable' # Optional gem for LDAP authentication group :ldap do gem "net-ldap", "~> 0.3.1" diff --git a/Gemfile.lock b/Gemfile.lock index 99f29e915..aa29558c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -98,6 +98,9 @@ GEM rmagick (2.13.2) ruby-openid (2.1.8) rubyzip (0.9.9) + seems_rateable (1.0.9) + jquery-rails + rails selenium-webdriver (2.33.0) childprocess (>= 0.2.5) multi_json (~> 1.0) @@ -150,6 +153,7 @@ DEPENDENCIES rdoc (>= 2.4.2) rmagick (>= 2.0.0) ruby-openid (~> 2.1.4) + seems_rateable shoulda (~> 3.3.2) sqlite3 yard diff --git a/app/assets/javascripts/praise_tread.js b/app/assets/javascripts/praise_tread.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/praise_tread.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/rateable/jRating.js.erb b/app/assets/javascripts/rateable/jRating.js.erb new file mode 100644 index 000000000..4f43a4f97 --- /dev/null +++ b/app/assets/javascripts/rateable/jRating.js.erb @@ -0,0 +1,225 @@ +/************************************************************************ +************************************************************************* +@Name : jRating - jQuery Plugin +@Revison : 3.0 +@Date : 28/01/2013 +@Author: ALPIXEL - (www.myjqueryplugins.com - www.alpixel.fr) +@License : Open Source - MIT License : http://www.opensource.org/licenses/mit-license.php + +************************************************************************** +*************************************************************************/ +(function($) { + $.fn.jRating = function(op) { + var defaults = { + /** String vars **/ + bigStarsPath : '<%= image_path "seems_rateable/stars.png" %>', // path of the icon stars.png + smallStarsPath : '<%= image_path "seems_rateable/small.png" %>', // path of the icon small.png + path : '<%= SeemsRateable::Engine.routes.url_helpers.ratings_path %>', + type : 'big', // can be set to 'small' or 'big' + + /** Boolean vars **/ + step:false, // if true, mouseover binded star by star, + isDisabled:false, + showRateInfo: false, + canRateAgain : false, + + /** Integer vars **/ + length:5, // number of star to display + decimalLength : 0, // number of decimals.. Max 3, but you can complete the function 'getNote' + rateMax : 20, // maximal rate - integer from 0 to 9999 (or more) + rateInfosX : -45, // relative position in X axis of the info box when mouseover + rateInfosY : 5, // relative position in Y axis of the info box when mouseover + nbRates : 1, + + /** Functions **/ + onSuccess : null, + onError : null + }; + + if(this.length>0) + return this.each(function() { + /*vars*/ + var opts = $.extend(defaults, op), + newWidth = 0, + starWidth = 0, + starHeight = 0, + bgPath = '', + hasRated = false, + globalWidth = 0, + nbOfRates = opts.nbRates; + + if($(this).hasClass('jDisabled') || opts.isDisabled) + var jDisabled = true; + else + var jDisabled = false; + + getStarWidth(); + $(this).height(starHeight); + + + + var average = parseFloat($(this).attr('data-average')), // get the average of all rates + idBox = parseInt($(this).attr('data-id')), // get the id of the box + kls = $(this).attr('data-kls'), + dimension = $(this).attr('data-dimension'), + widthRatingContainer = starWidth*opts.length, // Width of the Container + widthColor = average/opts.rateMax*widthRatingContainer, // Width of the color Container + quotient = + $('
', + { + 'class' : 'jRatingColor', + css:{ + width:widthColor + } + }).appendTo($(this)), + + average = + $('
', + { + 'class' : 'jRatingAverage', + css:{ + width:0, + top:- starHeight + } + }).appendTo($(this)), + + jstar = + $('
', + { + 'class' : 'jStar', + css:{ + width:widthRatingContainer, + height:starHeight, + top:- (starHeight*2), + background: 'url('+bgPath+') repeat-x' + } + }).appendTo($(this)); + + $(this).css({width: widthRatingContainer,overflow:'hidden',zIndex:1,position:'relative'}); + + if(!jDisabled) + $(this).unbind().bind({ + mouseenter : function(e){ + var realOffsetLeft = findRealLeft(this); + var relativeX = e.pageX - realOffsetLeft; + if (opts.showRateInfo) + var tooltip = + $('

',{ + 'class' : 'jRatingInfos', + html : getNote(relativeX)+' / '+opts.rateMax+'', + css : { + top: (e.pageY + opts.rateInfosY), + left: (e.pageX + opts.rateInfosX) + } + }).appendTo('body').show(); + }, + mouseover : function(e){ + $(this).css('cursor','pointer'); + }, + mouseout : function(){ + $(this).css('cursor','default'); + if(hasRated) average.width(globalWidth); + else average.width(0); + }, + mousemove : function(e){ + var realOffsetLeft = findRealLeft(this); + var relativeX = e.pageX - realOffsetLeft; + if(opts.step) newWidth = Math.floor(relativeX/starWidth)*starWidth + starWidth; + else newWidth = relativeX; + average.width(newWidth); + if (opts.showRateInfo) + $("p.jRatingInfos") + .css({ + left: (e.pageX + opts.rateInfosX) + }) + .html(getNote(newWidth) +' / '+opts.rateMax+''); + }, + mouseleave : function(){ + $("p.jRatingInfos").remove(); + }, + click : function(e){ + var element = this; + + /*set vars*/ + hasRated = true; + globalWidth = newWidth; + nbOfRates--; + + if(!opts.canRateAgain || parseInt(nbOfRates) <= 0) $(this).unbind().css('cursor','default').addClass('jDisabled'); + + if (opts.showRateInfo) $("p.jRatingInfos").fadeOut('fast',function(){$(this).remove();}); + e.preventDefault(); + var rate = getNote(newWidth); + average.width(newWidth); + + + $.post(defaults.path, + { + idBox : idBox, + rate : rate, + kls : kls, + dimension : dimension + /** action : 'rating' **/ + }, + function(data) { + if(!data.error) + { + /** Here you can display an alert box, + or use the jNotify Plugin :) http://www.myqjqueryplugins.com/jNotify + exemple : */ + if(opts.onSuccess) opts.onSuccess( element, rate ); + } + else + { + + /** Here you can display an alert box, + or use the jNotify Plugin :) http://www.myqjqueryplugins.com/jNotify + exemple : */ + if(opts.onError) opts.onError( element, rate ); + } + }, + 'json' + ); + } + }); + + function getNote(relativeX) { + var noteBrut = parseFloat((relativeX*100/widthRatingContainer)*opts.rateMax/100); + switch(opts.decimalLength) { + case 1 : + var note = Math.round(noteBrut*10)/10; + break; + case 2 : + var note = Math.round(noteBrut*100)/100; + break; + case 3 : + var note = Math.round(noteBrut*1000)/1000; + break; + default : + var note = Math.round(noteBrut*1)/1; + } + return note; + }; + + function getStarWidth(){ + switch(opts.type) { + case 'small' : + starWidth = 12; // width of the picture small.png + starHeight = 10; // height of the picture small.png + bgPath = opts.smallStarsPath; + break; + default : + starWidth = 23; // width of the picture stars.png + starHeight = 20; // height of the picture stars.png + bgPath = opts.bigStarsPath; + } + }; + + function findRealLeft(obj) { + if( !obj ) return 0; + return obj.offsetLeft + findRealLeft( obj.offsetParent ); + }; + }); + + } +})(jQuery); diff --git a/app/assets/javascripts/rateable/rateable.js.erb b/app/assets/javascripts/rateable/rateable.js.erb new file mode 100644 index 000000000..da6cc3097 --- /dev/null +++ b/app/assets/javascripts/rateable/rateable.js.erb @@ -0,0 +1,25 @@ +$(document).ready(function(){ + $(".rateable").jRating({ + //default options displayed below -> + + rateMax: 5, //Maximal rate + length : 5, //Number of stars + //decimalLength : 0, //Number of decimals in the rate + //type : 'big', //Big or small + //step : true, //If set to true, filling of the stars is done star by star (step by step). + //isDisabled: false, //Set true to display static rating + //showRateInfo:false, //Rate info panel, set true to display + //rateInfosX : 45, //In pixel - Absolute left position of the information box during mousemove. + //rateInfosY : 5, //In pixel - Absolute top position of the information box during mousemove. + path : '<%= SeemsRateable::Engine.routes.url_helpers.ratings_path %>', + onSuccess : function(element, rate){ + //something like -> + //alert('success'); + $('Thanks for rating!').insertAfter(element) + }, + onError : function(element, rate) { + $('You have already rated!').insertAfter(element) + } + }); + +}); diff --git a/app/assets/stylesheets/praise_tread.css b/app/assets/stylesheets/praise_tread.css new file mode 100644 index 000000000..afad32db0 --- /dev/null +++ b/app/assets/stylesheets/praise_tread.css @@ -0,0 +1,4 @@ +/* + Place all the styles related to the matching controller here. + They will automatically be included in application.css. +*/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2a4b5fb53..f57983e30 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -95,6 +95,9 @@ class ApplicationController < ActionController::Base # Returns the current user or nil if no user is logged in # and starts a session if needed + def current_user + find_current_user + end def find_current_user user = nil unless api_request? diff --git a/app/controllers/praise_tread_controller.rb b/app/controllers/praise_tread_controller.rb new file mode 100644 index 000000000..528011102 --- /dev/null +++ b/app/controllers/praise_tread_controller.rb @@ -0,0 +1,68 @@ +class PraiseTreadController < ApplicationController + + def praise_plus + @obj = nil + if request.get? + @obj = params[:obj] # 传的是对象,最后变成id了 + + #首先创建或更新praise_tread 表 + @pt = PraiseTread.find_by_user_id_and_praise_tread_object_id(User.current.id,@obj) + @pt = @pt.nil? ? PraiseTread.new : @pt + + @pt.user_id = User.current.id + @pt.praise_tread_object_id = @obj.to_i + @pt.praise_tread_object_type = User.find_by_id(@obj).class.name.underscore + @pt.praise_or_tread = 1 + @pt.save + + #再创建或更新praise_tread_cache表 + @ptc = PraiseTreadCache.find_by_object_id(@obj) + @ptc = @ptc.nil? ? PraiseTreadCache.new : @ptc + @ptc.object_id = @obj.to_i + @ptc.object_type = User.find_by_id(@obj).class.name.underscore + @ptc.plus(1) + @ptc.save + end + @obj = User.find_by_id(@obj) + respond_to do |format| + format.html + format.js + end + end + + def praise_minus + @obj = nil + if request.get? + @obj = params[:obj] # 传的是对象,最后变成id了 + + #首先更新praise_tread 表 删除关注记录 + @pt = PraiseTread.find_by_user_id_and_praise_tread_object_id_and_praise_tread_object_type(User.current.id,@obj,"user") + @pt.delete + + #再更新praise_tread_cache表 使相应的记录减1 当为0时删除 + @ptc = PraiseTreadCache.find_by_object_id(@obj) + @ptc.minus(1) + if @ptc.praise_num == 0 + @ptc.delete + end + + end + @obj = User.find_by_id(@obj) + respond_to do |format| + format.html + format.js + end + end + + def tread_plus + + end + + def tread_minus + respond_to do |format| + format.html + format.js + end + end + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8d14e63f7..9935b577d 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -26,12 +26,8 @@ module ApplicationHelper include GravatarHelper::PublicMethods include Redmine::Pagination::Helper include AvatarHelper - - ### added by william - include ActsAsTaggableOn::TagsHelper - # include WatchersHelper - - + include PraiseTreadHelper + extend Forwardable def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter diff --git a/app/helpers/praise_tread_helper.rb b/app/helpers/praise_tread_helper.rb new file mode 100644 index 000000000..9b2eba969 --- /dev/null +++ b/app/helpers/praise_tread_helper.rb @@ -0,0 +1,23 @@ +module PraiseTreadHelper + #added by william + def is_praise_or_tread(object,user_id) + @obj_type = object.class.name.underscore + @obj_id = object.id + @is_praise = PraiseTread.find_by_sql("select * from praise_treads where user_id=#{user_id} and " + + "praise_tread_object_type='#{@obj_type}' and praise_tread_object_id=#{@obj_id} ") + return @is_praise + end + #end + + def get_praise_num(object) + @obj_type = object.class.name.underscore + @obj_id = object.id + @record = PraiseTreadCache.find_by_object_id_and_object_type(@obj_id,@obj_type) + if @record + return @record.praise_num + else + return 0 + end + end + +end diff --git a/app/models/praise_tread.rb b/app/models/praise_tread.rb new file mode 100644 index 000000000..901a31660 --- /dev/null +++ b/app/models/praise_tread.rb @@ -0,0 +1,4 @@ +class PraiseTread < ActiveRecord::Base + attr_accessible :user_id,:praise_tread_object_id,:praise_tread_object_type,:praise_or_tread + +end diff --git a/app/models/praise_tread_cache.rb b/app/models/praise_tread_cache.rb new file mode 100644 index 000000000..330e197cf --- /dev/null +++ b/app/models/praise_tread_cache.rb @@ -0,0 +1,11 @@ +class PraiseTreadCache < ActiveRecord::Base + attr_accessible :object_id,:object_type,:praise_num,:tread_num + + def plus(num) + self.update_attribute(:praise_num, self.praise_num.to_i + num) + end + + def minus(num) + self.update_attribute(:praise_num, self.praise_num.to_i - num) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 6e720c06d..8050082a8 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -103,7 +103,7 @@ class User < Principal acts_as_customizable ############################added by william acts_as_taggable - + seems_rateable ############################# added by liuping 关注 acts_as_watchable diff --git a/app/views/layouts/base_users.html.erb b/app/views/layouts/base_users.html.erb index 985fdc909..f6abf5e6b 100644 --- a/app/views/layouts/base_users.html.erb +++ b/app/views/layouts/base_users.html.erb @@ -9,7 +9,11 @@ <%= favicon %> <%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', 'application', :media => 'all' %> <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %> + + <%= seems_rateable_stylesheet %> + <%= javascript_heads %> + <%= heads_for_theme %> <%= call_hook :view_layouts_base_html_head %> @@ -44,6 +48,11 @@ + +

+ <%= render :partial => "/praise_tread/praise_tread",:locals => {:obj => @user,:show_flag => false,:user_id => User.current.id}%> +
+
<%= l(:label_user_watcher) %> (<%= User.watched_by(@user.id).count %>)   <%= l(:label_user_fans) %> (<%= @user.watcher_users.count %>) diff --git a/app/views/praise_tread/_praise.html.erb b/app/views/praise_tread/_praise.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/praise_tread/_praise_tread.html.erb b/app/views/praise_tread/_praise_tread.html.erb new file mode 100644 index 000000000..c579df7fc --- /dev/null +++ b/app/views/praise_tread/_praise_tread.html.erb @@ -0,0 +1,17 @@ +
+ <% if is_praise_or_tread(obj,user_id).size > 0 %> + <%= image_tag("/images/praise.png") %> + <%= link_to "取消贊",:controller=>"praise_tread",:action=>"praise_minus",:remote=>true,:obj => obj %> + (<%= get_praise_num(obj)%>) + <% else %> + <%= image_tag("/images/tread.png") %> + <%= link_to "贊",:controller=>"praise_tread",:action=>"praise_plus",:remote=>true,:obj => obj %> + (<%= get_praise_num(obj)%>) + <% end %> +
+<% if show_flag %> +
+ <%= link_to image_tag("/images/tread.png"),:controller=>"praise_tread", + :action=>"tread_minus",:remote=>true,:obj => obj %>踩 +
+<% end %> diff --git a/app/views/praise_tread/_tread.html.erb b/app/views/praise_tread/_tread.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/praise_tread/praise_minus.js.erb b/app/views/praise_tread/praise_minus.js.erb new file mode 100644 index 000000000..74f7d6bfe --- /dev/null +++ b/app/views/praise_tread/praise_minus.js.erb @@ -0,0 +1,3 @@ +$('#praise_tread').html('<%= j( +render :partial => "/praise_tread/praise_tread",:locals => {:obj => @obj,:show_flag => false,:user_id => User.current.id} +)%>'); diff --git a/app/views/praise_tread/praise_plus.js.erb b/app/views/praise_tread/praise_plus.js.erb new file mode 100644 index 000000000..74f7d6bfe --- /dev/null +++ b/app/views/praise_tread/praise_plus.js.erb @@ -0,0 +1,3 @@ +$('#praise_tread').html('<%= j( +render :partial => "/praise_tread/praise_tread",:locals => {:obj => @obj,:show_flag => false,:user_id => User.current.id} +)%>'); diff --git a/app/views/praise_tread/tread_minus.js.erb b/app/views/praise_tread/tread_minus.js.erb new file mode 100644 index 000000000..e69de29bb diff --git a/config/initializers/seems_rateable.rb b/config/initializers/seems_rateable.rb new file mode 100644 index 000000000..2391948fa --- /dev/null +++ b/config/initializers/seems_rateable.rb @@ -0,0 +1,4 @@ +#SeemsRateable engine Initializer + +#Configure owner class of the given ratings +SeemsRateable::Engine.config.owner_class = "User" diff --git a/config/routes.rb b/config/routes.rb index 59189de89..d43a594fa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -17,11 +17,13 @@ RedmineApp::Application.routes.draw do resources :shares - - + get "tags/index" get "tags/show" + get "praise_tread/praise_plus" + get "praise_tread/praise_minus" + get "praise_tread/tread_minus" root :to => 'welcome#index', :as => 'home' @@ -95,7 +97,7 @@ RedmineApp::Application.routes.draw do match 'users/:id/memberships/:membership_id', :to => 'users#edit_membership', :via => :put, :as => 'user_membership' match 'users/:id/memberships/:membership_id', :to => 'users#destroy_membership', :via => :delete match 'users/:id/memberships', :to => 'users#edit_membership', :via => :post, :as => 'user_memberships' - ################# added by william + ################# added by william match 'users/tag_save', :to => 'users#tag_save', :via => :post, :as => 'tag' post 'watchers/watch', :to => 'watchers#watch', :as => 'watch' @@ -422,8 +424,12 @@ RedmineApp::Application.routes.draw do match 'bids/:id', :controller => 'bids', :action => 'show', :as => 'respond' + + ########### added by liuping match 'tags/add_tag',:to => 'tags#add_tag',:as=>"add_tag" match 'tags/delete_tag',:to => 'tags#delete_tag',:as=>"add_tag" + match 'parise_tread/praise_plus',:to => 'parise_tread#praise_plus',:as=>"praise" + match 'parise_tread/tread_minus',:to => 'parise_tread#tread_minus',:as=>"tread" end diff --git a/db/migrate/20130806083151_create_seems_rateable_rates.rb b/db/migrate/20130806083151_create_seems_rateable_rates.rb new file mode 100644 index 000000000..40303f11c --- /dev/null +++ b/db/migrate/20130806083151_create_seems_rateable_rates.rb @@ -0,0 +1,18 @@ +class CreateSeemsRateableRates < ActiveRecord::Migration + def self.up + create_table :seems_rateable_rates do |t| + t.belongs_to :rater + t.belongs_to :rateable, :polymorphic => true + t.float :stars, :null => false + t.integer :rater_id, :limit => 8 + t.integer :rateable_id + t.string :rateable_type + t.string :dimension + t.timestamps + end + end + + def self.down + drop_table :rates + end +end diff --git a/db/migrate/20130806083152_create_seems_rateable_cached_ratings.rb b/db/migrate/20130806083152_create_seems_rateable_cached_ratings.rb new file mode 100644 index 000000000..1b91bdadc --- /dev/null +++ b/db/migrate/20130806083152_create_seems_rateable_cached_ratings.rb @@ -0,0 +1,17 @@ +class CreateSeemsRateableCachedRatings < ActiveRecord::Migration + def self.up + create_table :seems_rateable_cached_ratings do |t| + t.belongs_to :cacheable, :polymorphic => true + t.float :avg, :null => false + t.integer :cnt, :null => false + t.string :dimension + t.integer :cacheable_id, :limit => 8 + t.string :cacheable_type + t.timestamps + end + end + + def self.down + drop_table :cached_ratings + end +end diff --git a/db/migrate/20130807021235_create_praise_treads.rb b/db/migrate/20130807021235_create_praise_treads.rb new file mode 100644 index 000000000..a30c11520 --- /dev/null +++ b/db/migrate/20130807021235_create_praise_treads.rb @@ -0,0 +1,15 @@ +class CreatePraiseTreads < ActiveRecord::Migration + def self.up + create_table :praise_treads do |t| + t.column :user_id,:integer,:null => false + t.column :praise_tread_object_id,:integer + t.column :praise_tread_object_type,:string + t.column :praise_or_tread,:integer + t.timestamps + end + end + + def self.down + drop_table :praise_treads + end +end diff --git a/db/migrate/20130807021309_create_praise_tread_caches.rb b/db/migrate/20130807021309_create_praise_tread_caches.rb new file mode 100644 index 000000000..423286f45 --- /dev/null +++ b/db/migrate/20130807021309_create_praise_tread_caches.rb @@ -0,0 +1,16 @@ +class CreatePraiseTreadCaches < ActiveRecord::Migration + def self.up + create_table :praise_tread_caches do |t| + t.column :object_id,:integer,:null => false + t.column :object_type,:string + t.column :praise_num,:integer + t.column :tread_num,:integer + + t.timestamps + end + end + + def self.down + drop_table :praise_tread_caches + end +end diff --git a/db/schema.rb b/db/schema.rb index 34ef17bd4..b0cf92655 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130805131602) do +ActiveRecord::Schema.define(:version => 20130807021309) do create_table "a_user_watchers", :force => true do |t| t.string "name" @@ -432,6 +432,24 @@ ActiveRecord::Schema.define(:version => 20130805131602) do t.string "salt", :null => false end + create_table "praise_tread_caches", :force => true do |t| + t.integer "object_id", :null => false + t.string "object_type" + t.integer "praise_num" + t.integer "tread_num" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "praise_treads", :force => true do |t| + t.integer "user_id", :null => false + t.integer "praise_tread_object_id" + t.string "praise_tread_object_type" + t.integer "praise_or_tread" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "project_tags", :force => true do |t| t.integer "project_id" t.integer "tag_id" @@ -507,6 +525,26 @@ ActiveRecord::Schema.define(:version => 20130805131602) do t.string "issues_visibility", :limit => 30, :default => "default", :null => false end + create_table "seems_rateable_cached_ratings", :force => true do |t| + t.integer "cacheable_id", :limit => 8 + t.string "cacheable_type" + t.float "avg", :null => false + t.integer "cnt", :null => false + t.string "dimension" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "seems_rateable_rates", :force => true do |t| + t.integer "rater_id", :limit => 8 + t.integer "rateable_id" + t.string "rateable_type" + t.float "stars", :null => false + t.string "dimension" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "settings", :force => true do |t| t.string "name", :default => "", :null => false t.text "value" @@ -522,6 +560,7 @@ ActiveRecord::Schema.define(:version => 20130805131602) do t.string "url" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false + t.integer "project_id" end create_table "students", :force => true do |t| diff --git a/lib/plugins/seems_rateable-master/.gitignore b/lib/plugins/seems_rateable-master/.gitignore new file mode 100644 index 000000000..f372f8d4f --- /dev/null +++ b/lib/plugins/seems_rateable-master/.gitignore @@ -0,0 +1,21 @@ +*.gem +*.rbc +.bundle +.config +.yardoc +Gemfile.lock +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp +.project +.rvmrc +spec +test diff --git a/lib/plugins/seems_rateable-master/Gemfile b/lib/plugins/seems_rateable-master/Gemfile new file mode 100644 index 000000000..a1025271a --- /dev/null +++ b/lib/plugins/seems_rateable-master/Gemfile @@ -0,0 +1,23 @@ +source "https://rubygems.org" + +# Declare your gem's dependencies in seems_rateable.gemspec. +# Bundler will treat runtime dependencies like base dependencies, and +# development dependencies will be added by default to the :development group. +gemspec + +# Declare any dependencies that are still in development here instead of in +# your gemspec. These might include edge Rails or gems from your path or +# Git. Remember to move these dependencies to your gemspec before releasing +# your gem to rubygems.org. + +# To use debugger +# gem 'debugger' + + +group :development do + gem 'sqlite3' + gem 'jquery-rails' + gem 'twitter-bootstrap-rails' + gem 'sorcery' +end + diff --git a/lib/plugins/seems_rateable-master/MIT-LICENSE b/lib/plugins/seems_rateable-master/MIT-LICENSE new file mode 100644 index 000000000..5146945d9 --- /dev/null +++ b/lib/plugins/seems_rateable-master/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright 2013 YOURNAME + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/plugins/seems_rateable-master/README.md b/lib/plugins/seems_rateable-master/README.md new file mode 100644 index 000000000..3e78d0250 --- /dev/null +++ b/lib/plugins/seems_rateable-master/README.md @@ -0,0 +1,111 @@ +# SeemsRateable + +Star rating gem for Rails application using jQuery plugin jRating + +## Demo + +Demo application, requires to sign up before rating + +## Instructions + +### Installation + +Add this line to your application's Gemfile: + + gem 'seems_rateable' + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install seems_rateable + +### Generation + + $ rails generate seems_rateable:install + +Generator creates migration files, javascript files and initializer + +### Prepare + +Require javascript files by adding this line to application.js + + #application.js + //= require_directory ./rateable + +Add seems_rateable to routes.rb file + +Include stylesheet adding <%= seems_rateable_stylesheet %> to your layout header + +Also make sure you have an existing current_user helper method + +Don't forget to run + + $ rake db:migrate + +To prepare model add seems_rateable to your rateable model file. You can also pass a hash of options to +customize the functionality + +
    +
  • :dimensionsArray of dimensions e.g :dimensions => [:quality, :quantity]
  • +
  • :allow_updateAllowing user to re-rate his own ratings, default set to false e.g :allow_update=> true
  • +
+ + class Post < ActiveRecord::Base + seems_rateable :allow_update => true, :dimensions => [:quality, :length] + end + +To access object's rates use rates method, to get dimension rates pass an argument eg : + + @object.rates + @object.rates(:quality) + @object.rates(:quantity) + +This also applies to cached average rating e.g + + @object.average + @object.average(:quality) + @object.average(:quantity) + +And to object's raters e.g + + @object.raters + @object.raters(:quality) + @object.raters(:quantity) + +To track user's given ratings add seems_rateable_rater to your rater model. +If your rater class is not "User"(e.g "Client" or "Customer") change configuration in initializer generated by this engine. +Now you can access user's ratings by @user.ratings_given + +### Usage + +To display star rating use helper method rating_for in your view + + #index.html.erb + + rating_for @post + + rating_for @post, :dimension => :quality, :class => 'post', :id => 'list' + + rating_for @post, :static => true + +You can specify these options : +
    +
  • :dimensionThe dimension of the object
  • +
  • :staticSet to true to display static star rating, default false
  • +
  • :classClass of the div, default set to 'rateable'
  • +
  • :idID of the div e.g :id => "info", default nil
  • +
+ +To edit the javascript options locate rateable.js file in /app/assets/javascripts/rateable/. +The javascript options are explained directly in the file + +## Contributing + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request diff --git a/lib/plugins/seems_rateable-master/Rakefile b/lib/plugins/seems_rateable-master/Rakefile new file mode 100644 index 000000000..c2ef03737 --- /dev/null +++ b/lib/plugins/seems_rateable-master/Rakefile @@ -0,0 +1,32 @@ +begin + require 'bundler/setup' +rescue LoadError + puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end + +require 'rdoc/task' + +RDoc::Task.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'SeemsRateable' + rdoc.options << '--line-numbers' + rdoc.rdoc_files.include('README.rdoc') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__) +load 'rails/tasks/engine.rake' +Bundler::GemHelper.install_tasks + +APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__) +load 'rails/tasks/engine.rake' + +Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f } +require 'rspec/core' +require 'rspec/core/rake_task' + +task :default => :spec + + + + diff --git a/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/bg_jRatingInfos.png b/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/bg_jRatingInfos.png new file mode 100644 index 000000000..af5bf556e Binary files /dev/null and b/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/bg_jRatingInfos.png differ diff --git a/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/small.png b/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/small.png new file mode 100644 index 000000000..de1826f59 Binary files /dev/null and b/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/small.png differ diff --git a/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/stars.png b/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/stars.png new file mode 100644 index 000000000..2df62b026 Binary files /dev/null and b/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/stars.png differ diff --git a/lib/plugins/seems_rateable-master/app/assets/javascripts/seems_rateable/application.js b/lib/plugins/seems_rateable-master/app/assets/javascripts/seems_rateable/application.js new file mode 100644 index 000000000..806c8cc06 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/assets/javascripts/seems_rateable/application.js @@ -0,0 +1,15 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. +// +// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details +// about supported directives. + +//= require_tree . + + diff --git a/lib/plugins/seems_rateable-master/app/assets/stylesheets/seems_rateable/application.css b/lib/plugins/seems_rateable-master/app/assets/stylesheets/seems_rateable/application.css new file mode 100644 index 000000000..64099e546 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/assets/stylesheets/seems_rateable/application.css @@ -0,0 +1,62 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the top of the + * compiled file, but it's generally better to create a new file per style scope. + * + *= require_self + *= require_tree . + */ + +/*********************/ +/** jRating CSS **/ +/*********************/ + +/**Div containing the color of the stars */ + + +.jRatingAverage { + background-color:#f62929; + position:relative; + top:0; + left:0; + z-index:2; + height:100%; +} +.jRatingColor { + background-color:#FFD400; /* bgcolor of the stars*/ + position:relative; + top:0; + left:0; + z-index:2; + height:100%; +} + +/** Div containing the stars **/ +.jStar { + position:relative; + left:0; + z-index:3; +} + +/** P containing the rate informations **/ +p.jRatingInfos { + position: absolute; + z-index:9999; + background: transparent url('bg_jRatingInfos.png') no-repeat; + color: #CACACA; + display: none; + width: 91px; + height: 29px; + font-size:16px; + text-align:center; + padding-top:5px; +} +p.jRatingInfos span.maxRate { + color:#c9c9c9; + font-size:14px; +} diff --git a/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/application_controller.rb b/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/application_controller.rb new file mode 100644 index 000000000..cd94242dd --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/application_controller.rb @@ -0,0 +1,4 @@ +module SeemsRateable + class ApplicationController < ActionController::Base + end +end diff --git a/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/ratings_controller.rb b/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/ratings_controller.rb new file mode 100644 index 000000000..afc47d5ef --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/ratings_controller.rb @@ -0,0 +1,17 @@ +require_dependency "seems_rateable/application_controller" + +module SeemsRateable + class RatingsController < ::ApplicationController + def create + raise NoCurrentUserInstanceError unless current_user + + obj = params[:kls].classify.constantize.find(params[:idBox]) + begin + obj.rate(params[:rate].to_i, current_user.id, params[:dimension]) + render :json => true + rescue Errors::AlreadyRatedError + render :json => {:error => true} + end + end + end +end diff --git a/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/application_helper.rb b/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/application_helper.rb new file mode 100644 index 000000000..596eeb4c9 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/application_helper.rb @@ -0,0 +1,4 @@ +module SeemsRateable + module ApplicationHelper + end +end diff --git a/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/ratings_helper.rb b/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/ratings_helper.rb new file mode 100644 index 000000000..91c7411c4 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/ratings_helper.rb @@ -0,0 +1,4 @@ +module SeemsRateable + module RatingsHelper + end +end diff --git a/lib/plugins/seems_rateable-master/app/models/seems_rateable/cached_rating.rb b/lib/plugins/seems_rateable-master/app/models/seems_rateable/cached_rating.rb new file mode 100644 index 000000000..fa56e7d44 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/models/seems_rateable/cached_rating.rb @@ -0,0 +1,5 @@ +module SeemsRateable + class CachedRating < ActiveRecord::Base + belongs_to :cacheable, :polymorphic => true + end +end diff --git a/lib/plugins/seems_rateable-master/app/models/seems_rateable/rate.rb b/lib/plugins/seems_rateable-master/app/models/seems_rateable/rate.rb new file mode 100644 index 000000000..f9db806a3 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/models/seems_rateable/rate.rb @@ -0,0 +1,6 @@ +module SeemsRateable + class Rate < ActiveRecord::Base + belongs_to :rater, :class_name => SeemsRateable::Engine.config.owner_class + belongs_to :rateable, :polymorphic => true + end +end diff --git a/lib/plugins/seems_rateable-master/app/views/layouts/seems_rateable/application.html.erb b/lib/plugins/seems_rateable-master/app/views/layouts/seems_rateable/application.html.erb new file mode 100644 index 000000000..3c63ba433 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/views/layouts/seems_rateable/application.html.erb @@ -0,0 +1,14 @@ + + + + SeemsRateable + <%= stylesheet_link_tag "seems_rateable/application", media: "all" %> + <%= javascript_include_tag "seems_rateable/application" %> + <%= csrf_meta_tags %> + + + +<%= yield %> + + + diff --git a/lib/plugins/seems_rateable-master/bin/rails b/lib/plugins/seems_rateable-master/bin/rails new file mode 100644 index 000000000..773450b9c --- /dev/null +++ b/lib/plugins/seems_rateable-master/bin/rails @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby1.9.1 +# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application. + +ENGINE_ROOT = File.expand_path('../..', __FILE__) +ENGINE_PATH = File.expand_path('../../lib/seems_rateable/engine', __FILE__) + +require 'rails/all' +require 'rails/engine/commands' diff --git a/lib/plugins/seems_rateable-master/config/routes.rb b/lib/plugins/seems_rateable-master/config/routes.rb new file mode 100644 index 000000000..4191f5460 --- /dev/null +++ b/lib/plugins/seems_rateable-master/config/routes.rb @@ -0,0 +1,3 @@ +SeemsRateable::Engine.routes.draw do + resources :ratings, :only => :create +end diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/install_generator.rb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/install_generator.rb new file mode 100644 index 000000000..9a3aac0a5 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/install_generator.rb @@ -0,0 +1,39 @@ +require 'rails/generators/migration' +require 'fileutils' + +module SeemsRateable + module Generators + class InstallGenerator < ::Rails::Generators::Base + include Rails::Generators::Migration + source_root File.expand_path('../templates', __FILE__) + + def self.next_migration_number(path) + unless @prev_migration_nr + @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i + else + @prev_migration_nr += 1 + end + @prev_migration_nr.to_s + end + + desc "generating migration files" + def copy_migrations + migration_template "rates_migration.rb", "db/migrate/create_seems_rateable_rates.rb" + migration_template "cached_ratings_migration.rb", "db/migrate/create_seems_rateable_cached_ratings.rb" + end + + desc "generating initializer" + def copy_initializer + template "initializer.rb", "config/initializers/seems_rateable.rb" + end + + desc "generating javascript files" + def copy_javascript_asset + Dir.mkdir "app/assets/javascripts/rateable" unless File.directory?("app/assets/javascripts/rateable") + copy_file "rateable.js.erb", "app/assets/javascripts/rateable/rateable.js.erb" unless File.exists?("app/assets/javascripts/rateable/rateable.js.erb") + copy_file "jRating.js.erb", "app/assets/javascripts/rateable/jRating.js.erb" unless File.exists?("app/assets/javascripts/rateable/jRating.js.erb") + end + + end + end +end diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/cached_ratings_migration.rb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/cached_ratings_migration.rb new file mode 100644 index 000000000..1b91bdadc --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/cached_ratings_migration.rb @@ -0,0 +1,17 @@ +class CreateSeemsRateableCachedRatings < ActiveRecord::Migration + def self.up + create_table :seems_rateable_cached_ratings do |t| + t.belongs_to :cacheable, :polymorphic => true + t.float :avg, :null => false + t.integer :cnt, :null => false + t.string :dimension + t.integer :cacheable_id, :limit => 8 + t.string :cacheable_type + t.timestamps + end + end + + def self.down + drop_table :cached_ratings + end +end diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/initializer.rb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/initializer.rb new file mode 100644 index 000000000..2391948fa --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/initializer.rb @@ -0,0 +1,4 @@ +#SeemsRateable engine Initializer + +#Configure owner class of the given ratings +SeemsRateable::Engine.config.owner_class = "User" diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/jRating.js.erb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/jRating.js.erb new file mode 100644 index 000000000..4f43a4f97 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/jRating.js.erb @@ -0,0 +1,225 @@ +/************************************************************************ +************************************************************************* +@Name : jRating - jQuery Plugin +@Revison : 3.0 +@Date : 28/01/2013 +@Author: ALPIXEL - (www.myjqueryplugins.com - www.alpixel.fr) +@License : Open Source - MIT License : http://www.opensource.org/licenses/mit-license.php + +************************************************************************** +*************************************************************************/ +(function($) { + $.fn.jRating = function(op) { + var defaults = { + /** String vars **/ + bigStarsPath : '<%= image_path "seems_rateable/stars.png" %>', // path of the icon stars.png + smallStarsPath : '<%= image_path "seems_rateable/small.png" %>', // path of the icon small.png + path : '<%= SeemsRateable::Engine.routes.url_helpers.ratings_path %>', + type : 'big', // can be set to 'small' or 'big' + + /** Boolean vars **/ + step:false, // if true, mouseover binded star by star, + isDisabled:false, + showRateInfo: false, + canRateAgain : false, + + /** Integer vars **/ + length:5, // number of star to display + decimalLength : 0, // number of decimals.. Max 3, but you can complete the function 'getNote' + rateMax : 20, // maximal rate - integer from 0 to 9999 (or more) + rateInfosX : -45, // relative position in X axis of the info box when mouseover + rateInfosY : 5, // relative position in Y axis of the info box when mouseover + nbRates : 1, + + /** Functions **/ + onSuccess : null, + onError : null + }; + + if(this.length>0) + return this.each(function() { + /*vars*/ + var opts = $.extend(defaults, op), + newWidth = 0, + starWidth = 0, + starHeight = 0, + bgPath = '', + hasRated = false, + globalWidth = 0, + nbOfRates = opts.nbRates; + + if($(this).hasClass('jDisabled') || opts.isDisabled) + var jDisabled = true; + else + var jDisabled = false; + + getStarWidth(); + $(this).height(starHeight); + + + + var average = parseFloat($(this).attr('data-average')), // get the average of all rates + idBox = parseInt($(this).attr('data-id')), // get the id of the box + kls = $(this).attr('data-kls'), + dimension = $(this).attr('data-dimension'), + widthRatingContainer = starWidth*opts.length, // Width of the Container + widthColor = average/opts.rateMax*widthRatingContainer, // Width of the color Container + quotient = + $('
', + { + 'class' : 'jRatingColor', + css:{ + width:widthColor + } + }).appendTo($(this)), + + average = + $('
', + { + 'class' : 'jRatingAverage', + css:{ + width:0, + top:- starHeight + } + }).appendTo($(this)), + + jstar = + $('
', + { + 'class' : 'jStar', + css:{ + width:widthRatingContainer, + height:starHeight, + top:- (starHeight*2), + background: 'url('+bgPath+') repeat-x' + } + }).appendTo($(this)); + + $(this).css({width: widthRatingContainer,overflow:'hidden',zIndex:1,position:'relative'}); + + if(!jDisabled) + $(this).unbind().bind({ + mouseenter : function(e){ + var realOffsetLeft = findRealLeft(this); + var relativeX = e.pageX - realOffsetLeft; + if (opts.showRateInfo) + var tooltip = + $('

',{ + 'class' : 'jRatingInfos', + html : getNote(relativeX)+' / '+opts.rateMax+'', + css : { + top: (e.pageY + opts.rateInfosY), + left: (e.pageX + opts.rateInfosX) + } + }).appendTo('body').show(); + }, + mouseover : function(e){ + $(this).css('cursor','pointer'); + }, + mouseout : function(){ + $(this).css('cursor','default'); + if(hasRated) average.width(globalWidth); + else average.width(0); + }, + mousemove : function(e){ + var realOffsetLeft = findRealLeft(this); + var relativeX = e.pageX - realOffsetLeft; + if(opts.step) newWidth = Math.floor(relativeX/starWidth)*starWidth + starWidth; + else newWidth = relativeX; + average.width(newWidth); + if (opts.showRateInfo) + $("p.jRatingInfos") + .css({ + left: (e.pageX + opts.rateInfosX) + }) + .html(getNote(newWidth) +' / '+opts.rateMax+''); + }, + mouseleave : function(){ + $("p.jRatingInfos").remove(); + }, + click : function(e){ + var element = this; + + /*set vars*/ + hasRated = true; + globalWidth = newWidth; + nbOfRates--; + + if(!opts.canRateAgain || parseInt(nbOfRates) <= 0) $(this).unbind().css('cursor','default').addClass('jDisabled'); + + if (opts.showRateInfo) $("p.jRatingInfos").fadeOut('fast',function(){$(this).remove();}); + e.preventDefault(); + var rate = getNote(newWidth); + average.width(newWidth); + + + $.post(defaults.path, + { + idBox : idBox, + rate : rate, + kls : kls, + dimension : dimension + /** action : 'rating' **/ + }, + function(data) { + if(!data.error) + { + /** Here you can display an alert box, + or use the jNotify Plugin :) http://www.myqjqueryplugins.com/jNotify + exemple : */ + if(opts.onSuccess) opts.onSuccess( element, rate ); + } + else + { + + /** Here you can display an alert box, + or use the jNotify Plugin :) http://www.myqjqueryplugins.com/jNotify + exemple : */ + if(opts.onError) opts.onError( element, rate ); + } + }, + 'json' + ); + } + }); + + function getNote(relativeX) { + var noteBrut = parseFloat((relativeX*100/widthRatingContainer)*opts.rateMax/100); + switch(opts.decimalLength) { + case 1 : + var note = Math.round(noteBrut*10)/10; + break; + case 2 : + var note = Math.round(noteBrut*100)/100; + break; + case 3 : + var note = Math.round(noteBrut*1000)/1000; + break; + default : + var note = Math.round(noteBrut*1)/1; + } + return note; + }; + + function getStarWidth(){ + switch(opts.type) { + case 'small' : + starWidth = 12; // width of the picture small.png + starHeight = 10; // height of the picture small.png + bgPath = opts.smallStarsPath; + break; + default : + starWidth = 23; // width of the picture stars.png + starHeight = 20; // height of the picture stars.png + bgPath = opts.bigStarsPath; + } + }; + + function findRealLeft(obj) { + if( !obj ) return 0; + return obj.offsetLeft + findRealLeft( obj.offsetParent ); + }; + }); + + } +})(jQuery); diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rateable.js.erb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rateable.js.erb new file mode 100644 index 000000000..da6cc3097 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rateable.js.erb @@ -0,0 +1,25 @@ +$(document).ready(function(){ + $(".rateable").jRating({ + //default options displayed below -> + + rateMax: 5, //Maximal rate + length : 5, //Number of stars + //decimalLength : 0, //Number of decimals in the rate + //type : 'big', //Big or small + //step : true, //If set to true, filling of the stars is done star by star (step by step). + //isDisabled: false, //Set true to display static rating + //showRateInfo:false, //Rate info panel, set true to display + //rateInfosX : 45, //In pixel - Absolute left position of the information box during mousemove. + //rateInfosY : 5, //In pixel - Absolute top position of the information box during mousemove. + path : '<%= SeemsRateable::Engine.routes.url_helpers.ratings_path %>', + onSuccess : function(element, rate){ + //something like -> + //alert('success'); + $('Thanks for rating!').insertAfter(element) + }, + onError : function(element, rate) { + $('You have already rated!').insertAfter(element) + } + }); + +}); diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rates_migration.rb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rates_migration.rb new file mode 100644 index 000000000..40303f11c --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rates_migration.rb @@ -0,0 +1,18 @@ +class CreateSeemsRateableRates < ActiveRecord::Migration + def self.up + create_table :seems_rateable_rates do |t| + t.belongs_to :rater + t.belongs_to :rateable, :polymorphic => true + t.float :stars, :null => false + t.integer :rater_id, :limit => 8 + t.integer :rateable_id + t.string :rateable_type + t.string :dimension + t.timestamps + end + end + + def self.down + drop_table :rates + end +end diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable.rb new file mode 100644 index 000000000..5f84a8b8c --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable.rb @@ -0,0 +1,14 @@ +begin + require 'rails' +rescue LoadError +end + +require "seems_rateable/engine" +require "seems_rateable/errors" +require "seems_rateable/helpers" +require "seems_rateable/model" +require "seems_rateable/routes" +require "seems_rateable/version" + +module SeemsRateable +end diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/engine.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/engine.rb new file mode 100644 index 000000000..185d6f9d9 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/engine.rb @@ -0,0 +1,17 @@ +module SeemsRateable + class Engine < ::Rails::Engine + isolate_namespace SeemsRateable + + config.generators do |g| + g.test_framework :rspec, :fixture => false + g.fixture_replacement :factory_girl, :dir => 'spec/factories' + end + + initializer :seems_rateable do + ActiveRecord::Base.send :include, SeemsRateable::Model + ActionView::Base.send :include, SeemsRateable::Helpers + ActionDispatch::Routing::Mapper.send :include, SeemsRateable::Routes + end + + end +end diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/errors.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/errors.rb new file mode 100644 index 000000000..8a83059ba --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/errors.rb @@ -0,0 +1,21 @@ +module SeemsRateable + module Errors + class InvalidRateableObjectError < StandardError + def to_s + "Stated object is not rateable. Add 'seems_rateable' to your object's class model." + end + end + + class NoCurrentUserInstanceError < StandardError + def to_s + "User instance current_user is not available." + end + end + + class AlreadyRatedError < StandardError + def to_s + "User has already rated an object." + end + end + end +end diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/helpers.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/helpers.rb new file mode 100644 index 000000000..399f06fd8 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/helpers.rb @@ -0,0 +1,27 @@ +module SeemsRateable + module Helpers + def rating_for(obj, opts={}) + raise Errors::InvalidRateableObjectError unless obj.class.respond_to?(:rateable?) + + options = { + :dimension => nil, + :static => false, + :class => 'rateable', + :id => nil + }.update(opts) + + content_tag :div, "", "data-average" => obj.average(options[:dimension]) ? obj.average(options[:dimension]).avg : 0, :id => options[:id], + :class => "#{options[:class]}#{jdisabled?(options[:static])}", + "data-id" => obj.id, "data-kls" => obj.class.name, "data-dimension" => options[:dimension] + end + + def seems_rateable_stylesheet + stylesheet_link_tag "seems_rateable/application", media: "all", "data-turbolinks-track" => true + end + + private + def jdisabled?(option) + " jDisabled" if option || !current_user + end + end +end diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/model.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/model.rb new file mode 100644 index 000000000..fa1b7af11 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/model.rb @@ -0,0 +1,111 @@ +require 'active_support/concern' +module SeemsRateable + module Model + extend ActiveSupport::Concern + + def rate(stars, user_id, dimension=nil) + if !has_rated?(user_id, dimension) + self.rates.create do |r| + r.stars = stars + r.rater_id = user_id + end + update_overall_average_rating(stars, dimension) + elsif has_rated?(user_id, dimension) && can_update? + update_users_rating(stars, user_id, dimension) + else + raise Errors::AlreadyRatedError + end + end + + def update_overall_average_rating(stars, dimension=nil) + if average(dimension).nil? + CachedRating.create do |r| + r.avg = stars + r.dimension = dimension + r.cacheable_id = self.id + r.cacheable_type = self.class.name + r.cnt = 1 + end + else + r = average(dimension) + r.avg = (r.avg * r.cnt + stars) / (r.cnt+1) + r.cnt += 1 + r.save! + end + end + + def update_users_rating(stars, user_id, dimension=nil) + obj = rates(dimension).where(:rater_id => user_id).first + current_record = average(dimension) + current_record.avg = (current_record.avg*current_record.cnt - obj.stars + stars) / (current_record.cnt) + current_record.save! + obj.stars = stars + obj.save! + end + + + def average(dimension=nil) + if dimension.nil? + self.send "rate_average_without_dimension" + else + self.send "#{dimension}_average" + end + end + + def rates(dimension=nil) + if dimension.nil? + self.send "rates_without_dimension" + else + self.send "#{dimension}_rates" + end + end + + def raters(dimension=nil) + if dimension.nil? + self.send "raters_without_dimension" + else + self.send "#{dimension}_raters" + end + end + + def has_rated?(user_id, dimension=nil) + record = self.rates(dimension).where(:rater_id => user_id) + record.empty? ? false : true + end + + def can_update? + self.class.can_update? + end + + module ClassMethods + def seems_rateable(opts={}) + #has_many :rates_without_dimension, -> { where(dimension: nil) }, :as => :rateable, :class_name => SeemsRateable::Rate, :dependent => :destroy + has_many :rates_without_dimension, :conditions => { dimension: nil }, :as => :rateable, :class_name => SeemsRateable::Rate, :dependent => :destroy + has_many :raters_without_dimension, :through => :rates_without_dimension, :source => :rater + has_one :rate_average_without_dimension, :conditions => { dimension: nil }, :as => :cacheable, :class_name => SeemsRateable::CachedRating, :dependent => :destroy + + @permission = opts[:allow_update] ? true : false + + def self.can_update? + @permission + end + + def self.rateable? + true + end + + if opts[:dimensions].is_a?(Array) + opts[:dimensions].each do |dimension| + has_many :"#{dimension}_rates", :conditions => { dimension: dimension.to_s }, :dependent => :destroy, :class_name => SeemsRateable::Rate, :as => :rateable + has_many :"#{dimension}_raters", :through => :"#{dimension}_rates", :source => :rater + has_one :"#{dimension}_average", :conditions => { dimension: dimension.to_s }, :as => :cacheable, :class_name => SeemsRateable::CachedRating, :dependent => :destroy + end + end + end + + def seems_rateable_rater + has_many :ratings_given, :class_name => SeemsRateable::Rate, :foreign_key => :rater_id + end + end + end +end diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/routes.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/routes.rb new file mode 100644 index 000000000..81a243339 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/routes.rb @@ -0,0 +1,7 @@ +module SeemsRateable + module Routes + def seems_rateable + mount SeemsRateable::Engine => '/rateable', :as => :rateable + end + end +end diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/version.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/version.rb new file mode 100644 index 000000000..180e47824 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/version.rb @@ -0,0 +1,3 @@ +module SeemsRateable + VERSION = "1.0.9" +end diff --git a/lib/plugins/seems_rateable-master/lib/tasks/seems_rateable_tasks.rake b/lib/plugins/seems_rateable-master/lib/tasks/seems_rateable_tasks.rake new file mode 100644 index 000000000..98c5403ac --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/tasks/seems_rateable_tasks.rake @@ -0,0 +1,4 @@ +# desc "Explaining what the task does" +# task :seems_rateable do +# # Task goes here +# end diff --git a/lib/plugins/seems_rateable-master/seems_rateable.gemspec b/lib/plugins/seems_rateable-master/seems_rateable.gemspec new file mode 100644 index 000000000..b4f32a9fc --- /dev/null +++ b/lib/plugins/seems_rateable-master/seems_rateable.gemspec @@ -0,0 +1,25 @@ +$:.push File.expand_path("../lib", __FILE__) + +# Maintain your gem's version: +require "seems_rateable/version" + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "seems_rateable" + s.version = SeemsRateable::VERSION + s.authors = ["Peter Toth"] + s.email = ["proximin@gmail.com"] + s.homepage = "http://rateable.herokuapp.com" + s.summary = "Star Rating Engine" + s.description = "Star rating engine using jQuery plugin jRating for Rails applications" + + s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] + + s.add_dependency "rails" + s.add_dependency "jquery-rails" + + s.add_development_dependency "sqlite3" + s.add_development_dependency 'rspec-rails' + s.add_development_dependency 'capybara' + s.add_development_dependency 'factory_girl_rails' +end diff --git a/public/images/praise.png b/public/images/praise.png new file mode 100644 index 000000000..808d18134 Binary files /dev/null and b/public/images/praise.png differ diff --git a/public/images/tread.png b/public/images/tread.png new file mode 100644 index 000000000..a843cc3dc Binary files /dev/null and b/public/images/tread.png differ