spree_reviews 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +43 -0
- data/Rakefile +120 -0
- data/TODO +8 -0
- data/app/controllers/admin/feedback_reviews_controller.rb +16 -0
- data/app/controllers/admin/review_settings_controller.rb +13 -0
- data/app/controllers/admin/reviews_controller.rb +36 -0
- data/app/controllers/feedback_reviews_controller.rb +17 -0
- data/app/controllers/reviews_controller.rb +30 -0
- data/app/helpers/admin/feedback_reviews_helper.rb +2 -0
- data/app/helpers/feedback_reviews_helper.rb +2 -0
- data/app/helpers/reviews_helper.rb +17 -0
- data/app/models/feedback_review.rb +10 -0
- data/app/models/product_decorator.rb +14 -0
- data/app/models/rating.rb +20 -0
- data/app/models/review.rb +20 -0
- data/app/models/reviews_configuration.rb +7 -0
- data/app/views/admin/feedback_reviews/index.html.erb +21 -0
- data/app/views/admin/review_settings/edit.html.erb +18 -0
- data/app/views/admin/review_settings/show.html.erb +18 -0
- data/app/views/admin/reviews/_form.html.erb +10 -0
- data/app/views/admin/reviews/edit.html.erb +17 -0
- data/app/views/admin/reviews/index.html.erb +45 -0
- data/app/views/feedback_reviews/_form.html.erb +14 -0
- data/app/views/feedback_reviews/_summary.html.erb +6 -0
- data/app/views/feedback_reviews/create.js.erb +7 -0
- data/app/views/reviews/_fine_print.html.erb +7 -0
- data/app/views/reviews/_stars.html.erb +21 -0
- data/app/views/reviews/_stars0.html.erb +1 -0
- data/app/views/reviews/_stars1.html.erb +1 -0
- data/app/views/reviews/_stars2.html.erb +1 -0
- data/app/views/reviews/_stars3.html.erb +1 -0
- data/app/views/reviews/_stars4.html.erb +1 -0
- data/app/views/reviews/_stars5.html.erb +1 -0
- data/app/views/reviews/index.html.erb +2 -0
- data/app/views/reviews/new.html.erb +47 -0
- data/app/views/reviews/submissionguidelines.html.erb +6 -0
- data/app/views/reviews/terms.html.erb +6 -0
- data/app/views/shared/_rating.html.erb +16 -0
- data/app/views/shared/_review.html.erb +14 -0
- data/app/views/shared/_review_summary.html.erb +13 -0
- data/app/views/shared/_reviews.html.erb +9 -0
- data/app/views/shared/_shortrating.html.erb +11 -0
- data/config/locales/de.yml +40 -0
- data/config/locales/en-GB.yml +23 -0
- data/config/locales/en.yml +53 -0
- data/config/locales/fr-FR.yml +19 -0
- data/config/locales/ru.yml +47 -0
- data/config/routes.rb +22 -0
- data/db/migrate/20081020220700_create_ratings.rb +14 -0
- data/db/migrate/20081020220724_create_reviews.rb +18 -0
- data/db/migrate/20101222083309_create_feedback_reviews.rb +17 -0
- data/db/sample/ratings.yml +16 -0
- data/db/sample/reviews.yml +59 -0
- data/lib/spree/reviews/config.rb +11 -0
- data/lib/spree_reviews.rb +17 -0
- data/lib/spree_reviews_hook.rb +17 -0
- data/lib/tasks/reviews_extension_tasks.rake +25 -0
- data/public/images/delete.gif +0 -0
- data/public/images/star.gif +0 -0
- data/public/javascripts/jquery.rating.js +11 -0
- data/public/stylesheets/reviews.css +44 -0
- data/spec/controllers/reviews_controller_spec.rb +15 -0
- data/spec/helpers/reviews_helper_spec.rb +11 -0
- data/spec/models/ratings_spec.rb +11 -0
- data/spec/models/reviews_spec.rb +11 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/views/reviews/submit_view_spec.rb +12 -0
- data/spree_reviews.gemspec +22 -0
- metadata +145 -0
data/README.markdown
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
Reviews
|
2
|
+
=======
|
3
|
+
|
4
|
+
Straightforward review/rating facility
|
5
|
+
|
6
|
+
Please see TODO file
|
7
|
+
|
8
|
+
|
9
|
+
Usage
|
10
|
+
-----
|
11
|
+
|
12
|
+
Action "submit" in "reviews" controller - goes to review entry form
|
13
|
+
|
14
|
+
Users must be logged in to submit a review
|
15
|
+
|
16
|
+
Three partials:
|
17
|
+
- ./app/views/products/_rating.html.erb -- display number of stars
|
18
|
+
- ./app/views/products/_shortrating.html.erb -- shorter version of above
|
19
|
+
- ./app/views/products/_review.html.erb -- display a single review
|
20
|
+
|
21
|
+
Administrator can edit and/or approve and/or delete reviews.
|
22
|
+
|
23
|
+
|
24
|
+
Implementation
|
25
|
+
--------------
|
26
|
+
|
27
|
+
reviews table is quite obvious - and note the "approved" flag which is for the
|
28
|
+
administrator to update
|
29
|
+
|
30
|
+
ratings table holds current fractional value - avoids frequent recalc...
|
31
|
+
|
32
|
+
|
33
|
+
Discussion
|
34
|
+
----------
|
35
|
+
|
36
|
+
Some points which might need modification in future:
|
37
|
+
- I don't track the actual user on a review (just their "screen name" at the
|
38
|
+
time), but we may want to use this information to avoid duplicate reviews
|
39
|
+
etc.
|
40
|
+
|
41
|
+
- Rating votes are tied to a review, to avoid spam. However: ratings are
|
42
|
+
accepted whether or not the review is accepted. Perhaps they should only
|
43
|
+
be counted when the review is approved.
|
data/Rakefile
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# I think this is the one that should be moved to the extension Rakefile template
|
2
|
+
|
3
|
+
# In rails 1.2, plugins aren't available in the path until they're loaded.
|
4
|
+
# Check to see if the rspec plugin is installed first and require
|
5
|
+
# it if it is. If not, use the gem version.
|
6
|
+
|
7
|
+
# Determine where the RSpec plugin is by loading the boot
|
8
|
+
unless defined? SPREE_ROOT
|
9
|
+
ENV["RAILS_ENV"] = "test"
|
10
|
+
case
|
11
|
+
when ENV["SPREE_ENV_FILE"]
|
12
|
+
require File.dirname(ENV["SPREE_ENV_FILE"]) + "/boot"
|
13
|
+
when File.dirname(__FILE__) =~ %r{vendor/SPREE/vendor/extensions}
|
14
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
|
15
|
+
else
|
16
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake'
|
21
|
+
require 'rake/rdoctask'
|
22
|
+
require 'rake/testtask'
|
23
|
+
|
24
|
+
rspec_base = File.expand_path(SPREE_ROOT + '/vendor/plugins/rspec/lib')
|
25
|
+
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
26
|
+
require 'spec/rake/spectask'
|
27
|
+
# require 'spec/translator'
|
28
|
+
|
29
|
+
# Cleanup the SPREE_ROOT constant so specs will load the environment
|
30
|
+
Object.send(:remove_const, :SPREE_ROOT)
|
31
|
+
|
32
|
+
extension_root = File.expand_path(File.dirname(__FILE__))
|
33
|
+
|
34
|
+
task :default => :spec
|
35
|
+
task :stats => "spec:statsetup"
|
36
|
+
|
37
|
+
desc "Run all specs in spec directory"
|
38
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
39
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
40
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
41
|
+
end
|
42
|
+
|
43
|
+
namespace :spec do
|
44
|
+
desc "Run all specs in spec directory with RCov"
|
45
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
46
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
47
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
48
|
+
t.rcov = true
|
49
|
+
t.rcov_opts = ['--exclude', 'spec', '--rails']
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Print Specdoc for all specs"
|
53
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
54
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
55
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
56
|
+
end
|
57
|
+
|
58
|
+
[:models, :controllers, :views, :helpers].each do |sub|
|
59
|
+
desc "Run the specs under spec/#{sub}"
|
60
|
+
Spec::Rake::SpecTask.new(sub) do |t|
|
61
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
62
|
+
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Hopefully no one has written their extensions in pre-0.9 style
|
67
|
+
# desc "Translate specs from pre-0.9 to 0.9 style"
|
68
|
+
# task :translate do
|
69
|
+
# translator = ::Spec::Translator.new
|
70
|
+
# dir = RAILS_ROOT + '/spec'
|
71
|
+
# translator.translate(dir, dir)
|
72
|
+
# end
|
73
|
+
|
74
|
+
# Setup specs for stats
|
75
|
+
task :statsetup do
|
76
|
+
require 'code_statistics'
|
77
|
+
::STATS_DIRECTORIES << %w(Model\ specs spec/models)
|
78
|
+
::STATS_DIRECTORIES << %w(View\ specs spec/views)
|
79
|
+
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
|
80
|
+
::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
|
81
|
+
::CodeStatistics::TEST_TYPES << "Model specs"
|
82
|
+
::CodeStatistics::TEST_TYPES << "View specs"
|
83
|
+
::CodeStatistics::TEST_TYPES << "Controller specs"
|
84
|
+
::CodeStatistics::TEST_TYPES << "Helper specs"
|
85
|
+
::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
|
86
|
+
end
|
87
|
+
|
88
|
+
namespace :db do
|
89
|
+
namespace :fixtures do
|
90
|
+
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
|
91
|
+
task :load => :environment do
|
92
|
+
require 'active_record/fixtures'
|
93
|
+
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
94
|
+
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
|
95
|
+
Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
desc 'Generate documentation for the reviews extension.'
|
103
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
104
|
+
rdoc.rdoc_dir = 'rdoc'
|
105
|
+
rdoc.title = 'ReviewsExtension'
|
106
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
107
|
+
rdoc.rdoc_files.include('README')
|
108
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
109
|
+
end
|
110
|
+
|
111
|
+
# For extensions that are in transition
|
112
|
+
desc 'Test the reviews extension.'
|
113
|
+
Rake::TestTask.new(:test) do |t|
|
114
|
+
t.libs << 'lib'
|
115
|
+
t.pattern = 'test/**/*_test.rb'
|
116
|
+
t.verbose = true
|
117
|
+
end
|
118
|
+
|
119
|
+
# Load any custom rakefiles for extension
|
120
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
data/TODO
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class Admin::FeedbackReviewsController < Admin::BaseController
|
2
|
+
resource_controller
|
3
|
+
belongs_to :review
|
4
|
+
|
5
|
+
create.response do |wants|
|
6
|
+
wants.html { redirect_to collection_path }
|
7
|
+
end
|
8
|
+
|
9
|
+
update.response do |wants|
|
10
|
+
wants.html { redirect_to collection_path }
|
11
|
+
end
|
12
|
+
|
13
|
+
destroy.response do |wants|
|
14
|
+
wants.html { redirect_to collection_path }
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Admin::ReviewSettingsController < Admin::BaseController
|
2
|
+
def update
|
3
|
+
# workaround for unset checkbox behaviour
|
4
|
+
params[:preferences][:include_unapproved_reviews] = false if params[:preferences][:include_unapproved_reviews].blank?
|
5
|
+
Spree::Reviews::Config.set(params[:preferences])
|
6
|
+
|
7
|
+
respond_to do |format|
|
8
|
+
format.html {
|
9
|
+
redirect_to admin_review_settings_path
|
10
|
+
}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Admin::ReviewsController < Admin::BaseController
|
2
|
+
|
3
|
+
layout 'admin'
|
4
|
+
|
5
|
+
resource_controller
|
6
|
+
|
7
|
+
index.before do
|
8
|
+
@unapproved_reviews = Review.not_approved.find(:all, :order => "created_at DESC")
|
9
|
+
@approved_reviews = Review.approved.find(:all, :order => "created_at DESC")
|
10
|
+
end
|
11
|
+
|
12
|
+
create.response do |wants|
|
13
|
+
wants.html { redirect_to admin_reviews_path }
|
14
|
+
end
|
15
|
+
|
16
|
+
update.response do |wants|
|
17
|
+
wants.html { redirect_to admin_reviews_path }
|
18
|
+
end
|
19
|
+
|
20
|
+
def approve
|
21
|
+
r = Review.find(params[:id])
|
22
|
+
|
23
|
+
r.approved = true
|
24
|
+
if r.product.rating.nil?
|
25
|
+
r.product.rating = Rating.create :value => 0, :count => 0
|
26
|
+
end
|
27
|
+
r.product.rating.add_rating(r.rating)
|
28
|
+
|
29
|
+
if r.save
|
30
|
+
flash[:notice] = t("info_approve_review")
|
31
|
+
else
|
32
|
+
flash[:error] = t("error_approve_review")
|
33
|
+
end
|
34
|
+
redirect_to admin_reviews_path
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class FeedbackReviewsController < Spree::BaseController
|
2
|
+
helper Spree::BaseHelper
|
3
|
+
def create
|
4
|
+
params[:feedback_review][:rating].sub!(/\s*stars/,'') unless params[:feedback_review][:rating].blank?
|
5
|
+
|
6
|
+
@review = Review.find_by_id(params[:review_id])
|
7
|
+
@review &&
|
8
|
+
(@feedback_review = @review.feedback_reviews.new(params[:feedback_review])) && @feedback_review.save
|
9
|
+
|
10
|
+
respond_to do |format|
|
11
|
+
format.html { redirect_to :back }
|
12
|
+
format.js { render :action => :create }
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class ReviewsController < Spree::BaseController
|
2
|
+
helper Spree::BaseHelper
|
3
|
+
|
4
|
+
def index
|
5
|
+
@product = Product.find_by_permalink params[:product_id]
|
6
|
+
@approved_reviews = Review.approved.find_all_by_product_id(@product.id)
|
7
|
+
end
|
8
|
+
|
9
|
+
def new
|
10
|
+
@product = Product.find_by_permalink params[:product_id]
|
11
|
+
@review = Review.new :product => @product
|
12
|
+
end
|
13
|
+
|
14
|
+
# save if all ok
|
15
|
+
def create
|
16
|
+
@product = Product.find_by_permalink params[:product_id]
|
17
|
+
params[:review][:rating].sub!(/\s*stars/,'') unless params[:review][:rating].blank?
|
18
|
+
|
19
|
+
@review = Review.new :product => @product
|
20
|
+
if @review.update_attributes(params[:review])
|
21
|
+
flash[:notice] = t('review_successfully_submitted')
|
22
|
+
redirect_to (product_path(@product))
|
23
|
+
else
|
24
|
+
# flash[:notice] = 'There was a problem in the submitted review'
|
25
|
+
render :action => "new"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
def terms
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ReviewsHelper
|
2
|
+
|
3
|
+
def star(the_class)
|
4
|
+
"<span class=\"#{the_class}\"> ✮ </span>"
|
5
|
+
end
|
6
|
+
|
7
|
+
def mk_stars(m)
|
8
|
+
(1..5).collect {|n| n <= m ? star("lit") : star("unlit") }.join
|
9
|
+
end
|
10
|
+
|
11
|
+
def txt_stars(n, show_out_of = true)
|
12
|
+
res = I18n.t('star', :count => n)
|
13
|
+
res += ' ' + t('out_of_5') if show_out_of
|
14
|
+
res
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class FeedbackReview < ActiveRecord::Base
|
2
|
+
belongs_to :review
|
3
|
+
belongs_to :user
|
4
|
+
|
5
|
+
validates_presence_of :review_id
|
6
|
+
validates_numericality_of :rating, :only_integer => true, :greater_than_or_equal_to => 1, :less_than_or_equal_to => 5, :message => I18n.t('you_must_enter_value_for_rating')
|
7
|
+
|
8
|
+
default_scope order("feedback_reviews.created_at DESC")
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Rating < ActiveRecord::Base
|
2
|
+
belongs_to :product
|
3
|
+
|
4
|
+
NB_STARS=5
|
5
|
+
def self.get_nb_stars
|
6
|
+
NB_STARS
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_rating(n)
|
10
|
+
self.value = value * count + n
|
11
|
+
self.count = count + 1
|
12
|
+
self.value = value / count
|
13
|
+
save
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_stars
|
17
|
+
(self.value + 0.5).floor
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Review < ActiveRecord::Base
|
2
|
+
belongs_to :product
|
3
|
+
has_many :feedback_reviews
|
4
|
+
|
5
|
+
validates_presence_of :title, :review
|
6
|
+
validates_numericality_of :rating, :only_integer => true
|
7
|
+
default_scope order("reviews.created_at DESC")
|
8
|
+
scope :approved, where("approved = ?", true)
|
9
|
+
scope :not_approved, where("approved = ?", false)
|
10
|
+
|
11
|
+
scope :approval_filter, lambda {|*args| {:conditions => ["(? = ?) or (approved = ?)", Spree::Reviews::Config[:include_unapproved_reviews], true, true ]}}
|
12
|
+
|
13
|
+
scope :oldest_first, :order => "created_at asc"
|
14
|
+
scope :preview, :limit => Spree::Reviews::Config[:preview_size], :order=>"created_at desc"
|
15
|
+
|
16
|
+
def feedback_stars
|
17
|
+
return 0 if feedback_reviews.count <= 0
|
18
|
+
((feedback_reviews.sum(:rating)/feedback_reviews.count) + 0.5).floor
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class ReviewsConfiguration < Configuration
|
2
|
+
# include non-approved reviews in (public) listings
|
3
|
+
preference :include_unapproved_reviews, :boolean, :default => 'false'
|
4
|
+
|
5
|
+
# control how many reviews are shown in summaries etc.
|
6
|
+
preference :preview_size, :integer, :default => 3
|
7
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<h1><%= link_to t('feedback_review_for', :review => @review.title ), admin_reviews_path %></h1>
|
2
|
+
|
3
|
+
|
4
|
+
<table class="index">
|
5
|
+
<tr>
|
6
|
+
<th><%= t('date_') %></th>
|
7
|
+
<th><%= t('user') %></th>
|
8
|
+
<th><%= t('stars') %></th>
|
9
|
+
<th></th>
|
10
|
+
</tr>
|
11
|
+
<% collection.each do |feedback| %>
|
12
|
+
<tr id="<%= dom_id(feedback) %>">
|
13
|
+
<td><%= l feedback.created_at %></td>
|
14
|
+
<td><%= feedback.user.try(:login) || "anonimus" %></td>
|
15
|
+
<td><%= feedback.rating %></td>
|
16
|
+
<td class="actions">
|
17
|
+
<%= link_to_delete feedback %>
|
18
|
+
</td>
|
19
|
+
</tr>
|
20
|
+
<% end %>
|
21
|
+
</table>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%= render :partial => 'admin/shared/configuration_menu' %>
|
2
|
+
|
3
|
+
<h1><%= t("reviews.review_settings") %></h1>
|
4
|
+
|
5
|
+
<% form_tag(admin_review_settings_path, :method => :put) do -%>
|
6
|
+
<p>
|
7
|
+
<label><%= t("reviews.include_unapproved") %>:</label>
|
8
|
+
<%= check_box_tag('preferences[include_unapproved_reviews]', "1", Spree::Reviews::Config[:include_unapproved_reviews]) %>
|
9
|
+
</p>
|
10
|
+
<p>
|
11
|
+
<label><%= t('reviews.preview_size') %>:</label>
|
12
|
+
<%= text_field_tag('preferences[preview_size]', Spree::Reviews::Config[:preview_size], :size => 3) %>
|
13
|
+
</p>
|
14
|
+
<p class="form-buttons">
|
15
|
+
<%= button t('update') %>
|
16
|
+
<%= t("or") %> <%= link_to t("cancel"), admin_review_settings_url %>
|
17
|
+
</p>
|
18
|
+
<% end -%>
|