workarea-reviews 3.0.8

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.
Files changed (166) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.eslintignore +2 -0
  4. data/.eslintrc +24 -0
  5. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  6. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  7. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  8. data/.gitignore +16 -0
  9. data/.scss-lint.yml +188 -0
  10. data/.yardopts +1 -0
  11. data/CHANGELOG.md +756 -0
  12. data/CODE_OF_CONDUCT.md +3 -0
  13. data/CONTRIBUTING.md +3 -0
  14. data/Gemfile +6 -0
  15. data/LICENSE +52 -0
  16. data/README.md +107 -0
  17. data/Rakefile +54 -0
  18. data/app/assets/images/workarea/admin/icons/star.svg +1 -0
  19. data/app/assets/images/workarea/storefront/icons/empty_star.svg +1 -0
  20. data/app/assets/images/workarea/storefront/icons/half_star.svg +1 -0
  21. data/app/assets/images/workarea/storefront/icons/star.svg +1 -0
  22. data/app/assets/javascripts/workarea/storefront/reviews/modules/product_review_ajax_submit.js +39 -0
  23. data/app/assets/javascripts/workarea/storefront/reviews/modules/product_reviews_sort_menus.js +82 -0
  24. data/app/assets/javascripts/workarea/storefront/reviews/modules/rating_buttons.js +103 -0
  25. data/app/assets/javascripts/workarea/storefront/reviews/templates/sort_by_property.jst.ejs +11 -0
  26. data/app/assets/stylesheets/workarea/storefront/reviews/components/_rating.scss +28 -0
  27. data/app/assets/stylesheets/workarea/storefront/reviews/components/_reviews.scss +59 -0
  28. data/app/assets/stylesheets/workarea/storefront/reviews/components/_reviews_aggregate.scss +38 -0
  29. data/app/assets/stylesheets/workarea/storefront/reviews/components/_write_review.scss +63 -0
  30. data/app/controllers/workarea/admin/catalog_products_controller.decorator +9 -0
  31. data/app/controllers/workarea/admin/import_reviews_controller.rb +35 -0
  32. data/app/controllers/workarea/admin/reviews_controller.rb +51 -0
  33. data/app/controllers/workarea/storefront/review_requests_controller.rb +50 -0
  34. data/app/controllers/workarea/storefront/reviews_controller.rb +53 -0
  35. data/app/helpers/workarea/admin/reviews_helper.rb +12 -0
  36. data/app/helpers/workarea/storefront/reviews_helper.rb +24 -0
  37. data/app/mailers/workarea/admin/status_report_mailer.decorator +8 -0
  38. data/app/mailers/workarea/storefront/review_mailer.rb +23 -0
  39. data/app/models/workarea/catalog/product.decorator +15 -0
  40. data/app/models/workarea/fulfillment.decorator +11 -0
  41. data/app/models/workarea/review/request.rb +62 -0
  42. data/app/models/workarea/review.rb +135 -0
  43. data/app/models/workarea/search/admin/review.rb +72 -0
  44. data/app/models/workarea/search/storefront/product.decorator +9 -0
  45. data/app/models/workarea/sort.decorator +17 -0
  46. data/app/queries/workarea/review_request_params.rb +47 -0
  47. data/app/queries/workarea/review_summary.rb +11 -0
  48. data/app/queries/workarea/search/admin_reviews.rb +40 -0
  49. data/app/queries/workarea/search/category_browse.decorator +9 -0
  50. data/app/queries/workarea/search/product_search.decorator +9 -0
  51. data/app/seeds/reviews.rb +38 -0
  52. data/app/services/workarea/create_review.rb +99 -0
  53. data/app/view_models/workarea/admin/product_view_model.decorator +7 -0
  54. data/app/view_models/workarea/admin/review_view_model.rb +23 -0
  55. data/app/view_models/workarea/admin/reviews_search_view_model.rb +46 -0
  56. data/app/view_models/workarea/storefront/product_view_model.decorator +13 -0
  57. data/app/view_models/workarea/storefront/review_view_model.rb +14 -0
  58. data/app/views/workarea/admin/activities/_review_create.html.haml +12 -0
  59. data/app/views/workarea/admin/activities/_review_destroy.html.haml +10 -0
  60. data/app/views/workarea/admin/activities/_review_update.html.haml +10 -0
  61. data/app/views/workarea/admin/reviews/_aux_navigation.html.haml +5 -0
  62. data/app/views/workarea/admin/reviews/_catalog_products_aux_link.html.haml +5 -0
  63. data/app/views/workarea/admin/reviews/_dashboard_navigation.html.haml +1 -0
  64. data/app/views/workarea/admin/reviews/_menu.html.haml +3 -0
  65. data/app/views/workarea/admin/reviews/_summary.html.haml +12 -0
  66. data/app/views/workarea/admin/reviews/edit.html.haml +77 -0
  67. data/app/views/workarea/admin/reviews/index.html.haml +74 -0
  68. data/app/views/workarea/admin/status_report_mailer/_reviews.html.haml +5 -0
  69. data/app/views/workarea/storefront/products/_rating.html.haml +10 -0
  70. data/app/views/workarea/storefront/products/_reviews.html.haml +23 -0
  71. data/app/views/workarea/storefront/products/_reviews_aggregate.html.haml +22 -0
  72. data/app/views/workarea/storefront/products/_reviews_summary.html.haml +2 -0
  73. data/app/views/workarea/storefront/review_mailer/review_request.html.haml +22 -0
  74. data/app/views/workarea/storefront/review_requests/show.html.haml +44 -0
  75. data/app/views/workarea/storefront/reviews/new.html.haml +68 -0
  76. data/app/views/workarea/storefront/style_guides/_reviews_product_summary_docs.html.haml +13 -0
  77. data/app/views/workarea/storefront/style_guides/components/_reviews.html.haml +17 -0
  78. data/app/workers/workarea/create_review_requests.rb +33 -0
  79. data/app/workers/workarea/schedule_review_requests.rb +44 -0
  80. data/app/workers/workarea/send_review_requests.rb +12 -0
  81. data/app/workers/workarea/update_product_review_data.rb +26 -0
  82. data/bin/rails +18 -0
  83. data/config/initializers/append_points.rb +70 -0
  84. data/config/initializers/assets.rb +1 -0
  85. data/config/initializers/configuration.rb +29 -0
  86. data/config/initializers/rack_attack.rb +11 -0
  87. data/config/initializers/recaptcha.rb +19 -0
  88. data/config/initializers/scheduled_jobs.rb +6 -0
  89. data/config/locales/en.yml +104 -0
  90. data/config/routes.rb +21 -0
  91. data/lib/tasks/verify.rake +16 -0
  92. data/lib/workarea/mailer_previews/storefront/review_mailer_preview.rb +10 -0
  93. data/lib/workarea/reviews/engine.rb +12 -0
  94. data/lib/workarea/reviews/version.rb +5 -0
  95. data/lib/workarea/reviews.rb +13 -0
  96. data/test/dummy/Rakefile +6 -0
  97. data/test/dummy/app/assets/config/manifest.js +4 -0
  98. data/test/dummy/app/assets/images/.keep +0 -0
  99. data/test/dummy/app/assets/javascripts/application.js +13 -0
  100. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  101. data/test/dummy/app/controllers/application_controller.rb +3 -0
  102. data/test/dummy/app/controllers/concerns/.keep +0 -0
  103. data/test/dummy/app/helpers/application_helper.rb +2 -0
  104. data/test/dummy/app/jobs/application_job.rb +2 -0
  105. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  106. data/test/dummy/app/models/concerns/.keep +0 -0
  107. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  108. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  109. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  110. data/test/dummy/bin/bundle +3 -0
  111. data/test/dummy/bin/rails +4 -0
  112. data/test/dummy/bin/rake +4 -0
  113. data/test/dummy/bin/setup +34 -0
  114. data/test/dummy/bin/update +29 -0
  115. data/test/dummy/config/application.rb +25 -0
  116. data/test/dummy/config/boot.rb +5 -0
  117. data/test/dummy/config/cable.yml +9 -0
  118. data/test/dummy/config/environment.rb +5 -0
  119. data/test/dummy/config/environments/development.rb +55 -0
  120. data/test/dummy/config/environments/production.rb +86 -0
  121. data/test/dummy/config/environments/test.rb +43 -0
  122. data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
  123. data/test/dummy/config/initializers/assets.rb +11 -0
  124. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  125. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  126. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  127. data/test/dummy/config/initializers/inflections.rb +16 -0
  128. data/test/dummy/config/initializers/mime_types.rb +4 -0
  129. data/test/dummy/config/initializers/new_framework_defaults.rb +21 -0
  130. data/test/dummy/config/initializers/session_store.rb +3 -0
  131. data/test/dummy/config/initializers/workarea.rb +5 -0
  132. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  133. data/test/dummy/config/locales/en.yml +23 -0
  134. data/test/dummy/config/puma.rb +47 -0
  135. data/test/dummy/config/routes.rb +5 -0
  136. data/test/dummy/config/secrets.yml +22 -0
  137. data/test/dummy/config/spring.rb +6 -0
  138. data/test/dummy/config.ru +5 -0
  139. data/test/dummy/db/seeds.rb +2 -0
  140. data/test/dummy/lib/assets/.keep +0 -0
  141. data/test/dummy/log/.keep +0 -0
  142. data/test/factories/reviews.rb +32 -0
  143. data/test/fixtures/reviews.csv +5 -0
  144. data/test/helpers/workarea/admin/reviews_helper_test.rb +17 -0
  145. data/test/helpers/workarea/storefront/reviews_helper_test.rb +19 -0
  146. data/test/integration/workarea/admin/reviews_integration_test.rb +33 -0
  147. data/test/integration/workarea/storefront/review_requests_integration_test.rb +63 -0
  148. data/test/integration/workarea/storefront/reviews_integration_test.rb +82 -0
  149. data/test/javascripts/fixtures/product_review_ajax_submit.html.haml +4 -0
  150. data/test/javascripts/product_review_ajax_submit_spec.js +28 -0
  151. data/test/javascripts/spec_helper.js +23 -0
  152. data/test/models/workarea/review/request_test.rb +18 -0
  153. data/test/models/workarea/review_test.rb +94 -0
  154. data/test/models/workarea/search/storefront/product_test.decorator +14 -0
  155. data/test/queries/workarea/review_request_params_test.rb +64 -0
  156. data/test/services/workarea/create_review_test.rb +131 -0
  157. data/test/system/workarea/admin/review_system_test.rb +96 -0
  158. data/test/system/workarea/storefront/review_request_system_test.rb +34 -0
  159. data/test/system/workarea/storefront/review_system_test.rb +113 -0
  160. data/test/teaspoon_env.rb +10 -0
  161. data/test/test_helper.rb +11 -0
  162. data/test/workers/workarea/create_review_requests_test.rb +38 -0
  163. data/test/workers/workarea/schedule_review_requests_test.rb +115 -0
  164. data/test/workers/workarea/send_review_requests_test.rb +30 -0
  165. data/workarea-reviews.gemspec +22 -0
  166. metadata +241 -0
@@ -0,0 +1,44 @@
1
+ - @title = t('workarea.storefront.reviews.write_a_review_title', name: @product.browser_title)
2
+
3
+ .write-review.view
4
+ %h1= t('workarea.storefront.reviews.write_a_review_title', name: @product.name)
5
+
6
+ .grid
7
+ .grid__cell.grid__cell--25
8
+ .product-summary{ itemscope: true, itemtype: 'http://schema.org/Product' }
9
+ = render 'workarea/storefront/products/summary', product: @product
10
+
11
+ .grid__cell.grid__cell--75
12
+ - unless request.get?
13
+ - if @review.errors.any?
14
+ - @review.errors.full_messages.each do |message|
15
+ = render_message 'error', message, data: { analytics: validation_error_analytics_data('review').to_json }
16
+
17
+ = form_tag complete_review_request_path(@request.token), method: 'post', id: 'review_request_form' do
18
+ .property
19
+ = label_tag 'review[rating]', nil, class: 'property__name' do
20
+ %span.property__text= t('workarea.storefront.reviews.rating')
21
+ %ul.write-review__ratings
22
+ - (1..5).each do |i|
23
+ %li.write-review__rating
24
+ = radio_button_tag 'review[rating]', i, (i == @review.rating || i == 5) ? true : false, class: 'write-review__rating-input', data: { rating_button: '' }
25
+ = label_tag "review[rating][#{i}]", nil, class: 'write-review__star' do
26
+ = inline_svg 'workarea/storefront/icons/star.svg', class: 'write-review__star-icon', title: t('workarea.storefront.reviews.stars', count: i)
27
+
28
+ .property
29
+ = label_tag 'review[title]', nil, class: 'property__name' do
30
+ %span.property__text= t('workarea.storefront.reviews.review_title')
31
+ %span.property__requirement.property__requirement--optional= t('workarea.storefront.forms.optional')
32
+ .value= text_field_tag 'review[title]', @review.title, class: 'text-box'
33
+
34
+ .property
35
+ = label_tag 'review[body]', nil, class: 'property__name' do
36
+ %span.property__requirement.property__requirement--required= t('workarea.storefront.forms.required')
37
+ %span.property__text= t('workarea.storefront.reviews.your_review')
38
+ .value= text_area_tag 'review[body]', @review.body, class: 'text-box text-box--multi-line', required: true
39
+
40
+ .property= recaptcha_tags
41
+
42
+ .grid.grid--auto
43
+ %p.grid__cell= button_tag t('workarea.storefront.reviews.submit'), value: 'submit_review', class: 'button', data: { disable_with: loading_indicator(t('workarea.storefront.reviews.submit_disabled')) }
44
+ %p.grid__cell= link_to t('workarea.storefront.forms.cancel'), product_path(@product), class: 'text-button', data: { dialog_close_button: '' }
@@ -0,0 +1,68 @@
1
+ - @title = t('workarea.storefront.reviews.write_a_review_title', name: @product.browser_title)
2
+
3
+ .write-review.view
4
+ %h1= t('workarea.storefront.reviews.write_a_review_title', name: @product.browser_title)
5
+
6
+ - unless request.get?
7
+ - if @create_review.errors.any?
8
+ - @create_review.errors.full_messages.each do |message|
9
+ = render_message 'error', message, data: { analytics: validation_error_analytics_data('review').to_json }
10
+
11
+ = form_tag create_product_review_path(@product), method: 'post', id: 'review_form', data: { product_review_ajax_submit: '' } do
12
+ = hidden_field_tag 'review[product_id]', @product.id
13
+
14
+ - if display_purchase_requirement_message
15
+ %span.write-review__purchase-requirement= t('workarea.storefront.reviews.purchase_requirement')
16
+
17
+ - if @review.requires_public_info?
18
+ .property
19
+ = label_tag 'review[first_name]', nil, class: 'property__name' do
20
+ %span.property__requirement.property__requirement--required= t('workarea.storefront.forms.required')
21
+ %span.property__text= t('workarea.storefront.users.first_name')
22
+ .value
23
+ = text_field_tag 'review[first_name]', @review.first_name, class: 'text-box', required: true
24
+ .value__note= t('workarea.storefront.reviews.notes.display_name')
25
+ .property
26
+ = label_tag 'review[last_name]', nil, class: 'property__name' do
27
+ %span.property__requirement.property__requirement--required= t('workarea.storefront.forms.required')
28
+ %span.property__text= t('workarea.storefront.users.last_name')
29
+ .value
30
+ = text_field_tag 'review[last_name]', @review.last_name, class: 'text-box', required: true
31
+ .value__note= t('workarea.storefront.reviews.notes.display_name')
32
+
33
+ - if @review.anonymous?
34
+ .property
35
+ = label_tag 'review[email]', nil, class: 'property__name' do
36
+ %span.property__text= t('workarea.storefront.users.email')
37
+ %span.property__requirement.property__requirement--optional= t('workarea.storefront.forms.optional')
38
+ .value
39
+ = email_field_tag 'review[email]', @review.email, class: 'text-box', autocapitalize: 'off', autocorrect: 'off', autocomplete: 'email'
40
+ .value__note= t('workarea.storefront.reviews.notes.email')
41
+
42
+ .property
43
+ = label_tag 'review[rating]', nil, class: 'property__name' do
44
+ %span.property__text= t('workarea.storefront.reviews.rating')
45
+ %ul.write-review__ratings
46
+ - (1..5).each do |i|
47
+ %li.write-review__rating
48
+ = radio_button_tag 'review[rating]', i, (i == @review.rating || i == 5) ? true : false, class: 'write-review__rating-input', data: { rating_button: '' }
49
+ = label_tag "review[rating][#{i}]", nil, class: 'write-review__star' do
50
+ = inline_svg 'workarea/storefront/icons/star.svg', class: 'write-review__star-icon', title: t('workarea.storefront.reviews.stars', count: i)
51
+
52
+ .property
53
+ = label_tag 'review[title]', nil, class: 'property__name' do
54
+ %span.property__text= t('workarea.storefront.reviews.review_title')
55
+ %span.property__requirement.property__requirement--optional= t('workarea.storefront.forms.optional')
56
+ .value= text_field_tag 'review[title]', @review.title, class: 'text-box'
57
+
58
+ .property
59
+ = label_tag 'review[body]', nil, class: 'property__name' do
60
+ %span.property__requirement.property__requirement--required= t('workarea.storefront.forms.required')
61
+ %span.property__text= t('workarea.storefront.reviews.your_review')
62
+ .value= text_area_tag 'review[body]', @review.body, class: 'text-box text-box--multi-line', required: true
63
+
64
+ .property= recaptcha_tags
65
+
66
+ .grid.grid--auto
67
+ %p.grid__cell= button_tag t('workarea.storefront.reviews.submit'), value: 'submit_review', class: 'button', data: { disable_with: loading_indicator(t('workarea.storefront.reviews.submit_disabled')) }
68
+ %p.grid__cell= link_to t('workarea.storefront.forms.cancel'), product_path(@product), class: 'text-button', data: { dialog_close_button: '' }
@@ -0,0 +1,13 @@
1
+ %p With reviews:
2
+ .style-guide__example-block
3
+ .product-summary
4
+ %p.product-summary__media
5
+ = link_to('#', class: 'product-summary__media-link') do
6
+ = image_tag('workarea/storefront/style_guide_product.jpg', alt: 'Product Name', class: 'product-summary__media-image')
7
+ .product-summary__info
8
+ %p.product-summary__name= link_to('Product Name', '#')
9
+ .product-summary__price-group
10
+ %p.product-summary__price
11
+ %span= number_to_currency(109.99)
12
+ = rating_stars(3.5, aggregate: true)
13
+ %p.product-summary__quickview= link_to('Quick View', '#', class: 'button button--small', rel: 'nofollow', data: { dialog_button: '' })
@@ -0,0 +1,17 @@
1
+ #rating.style-guide__subsection
2
+
3
+ %h3= link_to_style_guide('components', 'rating')
4
+
5
+ %p Displays a graphical rating scale for a numerical rating (1.0 - 5.0). Use the <code>rating_stars(rating, options = {})</code> helper to generate the rating stars.
6
+
7
+ %h4 Options
8
+ %ul
9
+ %li
10
+ <code>aggregate</code> (default: false)
11
+ %ul
12
+ %li Passing <code>aggregate: true</code> will change schema.org rating type from `reviewRating` to `aggregateRating` (an average rating from multiple reviews). See <a href="https://schema.org/AggregateRating">https://schema.org/AggregateRating</a> for more info.
13
+
14
+ - [1.5, 2.0, 3.75, 4.1, 4.8].each do |rating|
15
+ %p Example: rating of #{rating}
16
+ .style-guide__example-block
17
+ = rating_stars(rating, aggregate: true)
@@ -0,0 +1,33 @@
1
+ module Workarea
2
+ class CreateReviewRequests
3
+ include Sidekiq::Worker
4
+ include Sidekiq::CallbacksWorker
5
+
6
+ sidekiq_options(
7
+ enqueue_on: {
8
+ Order => :place,
9
+ only_if: -> { Workarea.config.send_transactional_emails }
10
+ },
11
+ queue: 'low'
12
+ )
13
+
14
+ def perform(order_id)
15
+ order = Order.find(order_id)
16
+ product_ids = order.items
17
+ .sort_by(&:original_unit_price)
18
+ .reverse
19
+ .map(&:product_id)
20
+ .uniq
21
+ .take(Workarea.config.review_requests_per_order)
22
+
23
+ product_ids.each do |product_id|
24
+ Review::Request.create(
25
+ email: order.email,
26
+ order_id: order.id,
27
+ user_id: order.user_id,
28
+ product_id: product_id
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ module Workarea
2
+ class ScheduleReviewRequests
3
+ include Sidekiq::Worker
4
+
5
+ sidekiq_options queue: 'low'
6
+
7
+ def perform(order_id, shipped_items)
8
+ requests = Review::Request.by_order(order_id).to_a
9
+ return if requests.all?(&:send_after)
10
+
11
+ order = Order.find(order_id)
12
+ product_ids =
13
+ shipped_items.map do |item|
14
+ order_item = order.items.detect { |i| i.id.to_s == item['id'] }
15
+ order_item&.product_id
16
+ end
17
+ .compact
18
+
19
+ schedule_requests(
20
+ requests.select { |request| request.product_id.in?(product_ids) },
21
+ requests
22
+ )
23
+ end
24
+
25
+ def schedule_requests(affected_requests, all_requests)
26
+ affected_requests.each do |request|
27
+ next if request.send_after.present?
28
+
29
+ last_scheduled =
30
+ all_requests.select(&:send_after).sort_by(&:send_after).last
31
+
32
+ date_to_send =
33
+ if last_scheduled.present?
34
+ last_scheduled.send_after +
35
+ Workarea.config.review_request_secondary_delivery_delay
36
+ else
37
+ Workarea.config.review_request_initial_delivery_delay.from_now
38
+ end
39
+
40
+ request.update_attributes(send_after: date_to_send)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ module Workarea
2
+ class SendReviewRequests
3
+ include Sidekiq::Worker
4
+
5
+ def perform
6
+ Review::Request.ready_to_send.each do |request|
7
+ Storefront::ReviewMailer.review_request(request).deliver_now
8
+ request.send!
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ module Workarea
2
+ class UpdateProductReviewData
3
+ include Sidekiq::Worker
4
+ include Sidekiq::CallbacksWorker
5
+
6
+ sidekiq_options(
7
+ enqueue_on: {
8
+ Workarea::Review => [:save, :destroy],
9
+ with: -> { [product_id] }
10
+ },
11
+ unique: :until_and_while_executing
12
+ )
13
+
14
+ def perform(product_id)
15
+ product = Workarea::Catalog::Product.find(product_id)
16
+ review_data = Review.find_single_aggregates(product_id)
17
+
18
+ product.update_attributes!(
19
+ total_reviews: review_data[:count],
20
+ average_rating: review_data[:average]
21
+ )
22
+ rescue Mongoid::Errors::DocumentNotFound
23
+ # we're ok if the product doesn't exist
24
+ end
25
+ end
26
+ end
data/bin/rails ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
6
+ ENGINE_PATH = File.expand_path('../../lib/workarea/reviews/engine', __FILE__)
7
+
8
+ # Set up gems listed in the Gemfile.
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
10
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
11
+
12
+ require 'action_controller/railtie'
13
+ require 'action_view/railtie'
14
+ require 'action_mailer/railtie'
15
+ require 'rails/test_unit/railtie'
16
+ require 'sprockets/railtie'
17
+
18
+ require 'rails/engine/commands'
@@ -0,0 +1,70 @@
1
+ module Workarea
2
+ #
3
+ # Storefront
4
+ #
5
+ #
6
+
7
+ Plugin.append_stylesheets(
8
+ 'storefront.components',
9
+ 'workarea/storefront/reviews/components/rating',
10
+ 'workarea/storefront/reviews/components/reviews',
11
+ 'workarea/storefront/reviews/components/reviews_aggregate',
12
+ 'workarea/storefront/reviews/components/write_review',
13
+ )
14
+
15
+ Plugin.append_javascripts(
16
+ 'storefront.templates',
17
+ 'workarea/storefront/reviews/templates/sort_by_property'
18
+ )
19
+
20
+ Plugin.append_javascripts(
21
+ 'storefront.modules',
22
+ 'workarea/storefront/reviews/modules/product_review_ajax_submit',
23
+ 'workarea/storefront/reviews/modules/product_reviews_sort_menus',
24
+ 'workarea/storefront/reviews/modules/rating_buttons',
25
+ )
26
+
27
+ Plugin.append_partials(
28
+ 'storefront.style_guide_product_summary',
29
+ 'workarea/storefront/style_guides/reviews_product_summary_docs'
30
+ )
31
+
32
+ Plugin.append_partials(
33
+ 'storefront.product_details',
34
+ 'workarea/storefront/products/reviews_aggregate'
35
+ )
36
+
37
+ Plugin.append_partials(
38
+ 'storefront.product_show',
39
+ 'workarea/storefront/products/reviews'
40
+ )
41
+
42
+ Plugin.append_partials(
43
+ 'storefront.product_summary',
44
+ 'workarea/storefront/products/reviews_summary'
45
+ )
46
+
47
+ #
48
+ # Admin
49
+ #
50
+ #
51
+ Plugin.append_partials(
52
+ 'admin.marketing_menu',
53
+ 'workarea/admin/reviews/menu'
54
+ )
55
+
56
+ Plugin.append_partials(
57
+ 'admin.catalog_product_aux_navigation',
58
+ 'workarea/admin/reviews/catalog_products_aux_link'
59
+ )
60
+
61
+ Plugin.append_partials(
62
+ 'admin.status_report_mailer',
63
+ 'workarea/admin/status_report_mailer/reviews'
64
+ )
65
+
66
+ Workarea.append_partials(
67
+ 'admin.marketing_dashboard_navigation',
68
+ 'workarea/admin/reviews/dashboard_navigation'
69
+ )
70
+ end
@@ -0,0 +1 @@
1
+ Rails.application.config.assets.precompile += %w( workarea/**/icons/*.svg )
@@ -0,0 +1,29 @@
1
+ Workarea.configure do |config|
2
+ config.seeds << 'Workarea::ReviewSeeds'
3
+
4
+ config.jump_to_navigation.merge!('Product Reviews' => :reviews_path)
5
+
6
+ config.product_copy_default_attributes ||= {}
7
+ config.product_copy_default_attributes.merge!(
8
+ total_reviews: 0,
9
+ average_rating: nil
10
+ )
11
+
12
+ config.data_file_ignored_fields << %w(total_reviews average_rating)
13
+
14
+ # The amount of time before a Review::Request will auto expire
15
+ # and be removed from the collection
16
+ config.review_request_ttl = 6.months
17
+
18
+ # The maximum number of review requests to send for each order
19
+ # Each request will be for a different order item, selected by
20
+ # most expensive.
21
+ config.review_requests_per_order = 3
22
+
23
+ # The amount of time after an order is shipped before the first review
24
+ # requests will be sent.
25
+ config.review_request_initial_delivery_delay = 14.days
26
+
27
+ # The amount of time after the last request was sent to send another.
28
+ config.review_request_secondary_delivery_delay = 5.days
29
+ end
@@ -0,0 +1,11 @@
1
+ class Rack::Attack
2
+ # Throttle POST requests to /reviews by email address
3
+ #
4
+ # Key: "rack::attack:#{Time.now.to_i/:period}:reviews/email:#{req.email}"
5
+ throttle('reviews/email', limit: 2, period: 2.minutes) do |req|
6
+ if req.path == '/reviews' && req.post?
7
+ # return the email if present, nil otherwise
8
+ req.params['email'].presence
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ recaptcha =
2
+ if Rails.application.secrets.recaptcha.present?
3
+ Rails.application.secrets.recaptcha.symbolize_keys
4
+ elsif Rails.env.development? || Rails.env.test?
5
+ # These are keys provided by google for the purpose of testing.
6
+ # See https://developers.google.com/recaptcha/docs/faq
7
+ {
8
+ site_key: '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI',
9
+ secret_key: '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe'
10
+ }
11
+ end
12
+
13
+ if recaptcha.present?
14
+ Recaptcha.configure do |config|
15
+ config.site_key = recaptcha[:site_key]
16
+ config.secret_key = recaptcha[:secret_key]
17
+ config.proxy = ENV.fetch('HTTP_PROXY', (recaptcha[:proxy] if recaptcha[:proxy].present?))
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ Sidekiq::Cron::Job.create(
2
+ name: 'Workarea::SendReviewRequests',
3
+ klass: 'Workarea::SendReviewRequests',
4
+ cron: "0 0 * * * #{Time.zone.tzinfo.identifier}",
5
+ queue: 'low'
6
+ )
@@ -0,0 +1,104 @@
1
+ en:
2
+ workarea:
3
+ admin:
4
+ activities:
5
+ review: Review
6
+ review_create_html: Created a %{name}
7
+ review_destroy_html: Removed a %{name}
8
+ review_update_html: updated %{fields} on the %{name} review
9
+ catalog:
10
+ dashboard_link: Dashboard link
11
+ catalog_products:
12
+ reviews_link: All Reviews
13
+ pending: (%{pending} Pending)
14
+ dashboards:
15
+ marketing:
16
+ reviews: Product reviews
17
+ fields:
18
+ body: Body
19
+ title: Title
20
+ rating: Rating
21
+ approved: Approved
22
+ auto_approve: Approve All Reviews
23
+ verified: Verified Purchaser
24
+ reviews:
25
+ approved: Approved
26
+ catalog_products_cards:
27
+ title: Reviews
28
+ edit:
29
+ review: Review
30
+ review_for_html: Review for %{product_link}
31
+ save_review: Save Review
32
+ title: Title
33
+ view_product: View Product
34
+ view_user: View User
35
+ flash_messages:
36
+ destroyed: Review has been removed.
37
+ updated: Review has been saved
38
+ index:
39
+ dashboard_link: Marketing dashboard
40
+ heading: Reviews
41
+ page_title: Reviews
42
+ review: Review
43
+ review_for: Review for %{product}
44
+ menu:
45
+ product_reviews:
46
+ one: Product Reviews (%{count})
47
+ other: Product Reviews (%{count})
48
+ zero: Product Reviews
49
+ pending: Pending
50
+ summary:
51
+ type: Review
52
+ unverified: Unverified
53
+ verified: Verified
54
+ status_report_mailer:
55
+ reviews:
56
+ pending_html: "%{count} awaiting approval."
57
+ review: review
58
+ recent_html: "%{count} submitted within the last day."
59
+ title: Reviews
60
+ sorts:
61
+ top_rated: Top Rated
62
+ highest_rating: Highest Rating
63
+ lowest_rating: Lowest Rating
64
+ storefront:
65
+ email:
66
+ review_request:
67
+ subject: Review your recent purchase of %{product}
68
+ link: Write a review
69
+ fields:
70
+ username: Username
71
+ review_requests:
72
+ flash_messages:
73
+ already_submitted: This review has already been submitted.
74
+ reviews:
75
+ flash_messages:
76
+ created: Your review has been submitted. Thanks!
77
+ failure: There was a problem saving your review.
78
+ heading: Reviews
79
+ out_of: out of
80
+ purchase_requirement: A purchase is required before you can post a review
81
+ rating: Rating
82
+ read_reviews: Read Reviews
83
+ review_for: Review for %{product}
84
+ review_title: Review Title
85
+ reviews:
86
+ one: "%{count} review"
87
+ other: "%{count} reviews"
88
+ zero: "%{count} reviews"
89
+ stars:
90
+ one: 1 star
91
+ other: "%{count} stars"
92
+ submit: Submit Review
93
+ submit_disabled: Submitting Review...
94
+ total_spent_validation: must make a purchase before creating a review
95
+ verified_purchaser: Verified Purchaser
96
+ write_a_review: Write a Review
97
+ write_a_review_title: Write a Review for "%{name}"
98
+ your_review: Your Review
99
+ notes:
100
+ display_name: Only the first letter of your name will be displayed.
101
+ email: Will only be used if we need to get in touch with you.
102
+ users:
103
+ first_name: First name
104
+ last_name: Last name
data/config/routes.rb ADDED
@@ -0,0 +1,21 @@
1
+ Workarea::Admin::Engine.routes.draw do
2
+ scope '(:locale)', constraints: Workarea::I18n.routes_constraint do
3
+ resources :reviews, except: [:new, :create]
4
+ resources :catalog_products, only: [] do
5
+ member do
6
+ get :reviews
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ Workarea::Storefront::Engine.routes.draw do
13
+ scope '(:locale)', constraints: Workarea::I18n.routes_constraint do
14
+ get 'reviews/:product_id/new' => 'reviews#new', as: :new_product_review
15
+ post 'reviews/:product_id' => 'reviews#create', as: :create_product_review
16
+
17
+ resources :review_requests, only: :show do
18
+ member { post :complete }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ namespace :workarea do
2
+ namespace :reviews do
3
+ desc 'Assign verified to each review based on whether the user has any orders with the product being reviewed'
4
+ task verify: :environment do
5
+ Workarea::Review.all.each_by(100) do |review|
6
+ user = Workarea::User.find(review.user_id) rescue nil
7
+
8
+ next unless user.present?
9
+
10
+ creator = Workarea::CreateReview.new(review, user: user)
11
+
12
+ review.update!(verified: creator.verified?)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ module Workarea
2
+ module Storefront
3
+ class ReviewMailerPreview < ActionMailer::Preview
4
+ def review_request
5
+ request = Review::Request.ready_to_send.first || Review::Request.first
6
+ ReviewMailer.review_request(request.id.to_s)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module Workarea
2
+ module Reviews
3
+ class Engine < ::Rails::Engine
4
+ include Workarea::Plugin
5
+ isolate_namespace Workarea::Reviews
6
+
7
+ config.to_prepare do
8
+ Storefront::ApplicationController.helper(Storefront::ReviewsHelper)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ module Workarea
2
+ module Reviews
3
+ VERSION = '3.0.8'.freeze
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ require 'workarea'
2
+ require 'workarea/storefront'
3
+ require 'workarea/admin'
4
+
5
+ require 'recaptcha/rails'
6
+
7
+ module Workarea
8
+ module Reviews
9
+ end
10
+ end
11
+
12
+ require 'workarea/reviews/version'
13
+ require 'workarea/reviews/engine'
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative 'config/application'
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,4 @@
1
+
2
+ //= link_tree ../images
3
+ //= link_directory ../javascripts .js
4
+ //= link_directory ../stylesheets .css
File without changes
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .