e9_vendors 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/README.markdown +6 -0
  4. data/Rakefile +1 -0
  5. data/app/controllers/e9_vendors/settings_controller.rb +11 -0
  6. data/app/controllers/e9_vendors/vendor_categories_controller.rb +36 -0
  7. data/app/controllers/e9_vendors/vendor_members_controller.rb +61 -0
  8. data/app/controllers/e9_vendors/vendor_proxies_controller.rb +38 -0
  9. data/app/controllers/e9_vendors/vendors_controller.rb +22 -0
  10. data/app/decorators/vendor_category_decorator.rb +10 -0
  11. data/app/decorators/vendor_member_decorator.rb +55 -0
  12. data/app/decorators/vendor_proxy_decorator.rb +74 -0
  13. data/app/decorators/vendors_decorator.rb +27 -0
  14. data/app/models/vendor.rb +23 -0
  15. data/app/models/vendor_category.rb +17 -0
  16. data/app/models/vendor_member.rb +21 -0
  17. data/app/models/vendor_proxy.rb +39 -0
  18. data/app/observers/vendor_member_observer.rb +17 -0
  19. data/app/observers/vendor_observer.rb +18 -0
  20. data/app/uploaders/logo_uploader.rb +14 -0
  21. data/app/views/e9_vendors/settings/_form.html.haml +18 -0
  22. data/app/views/e9_vendors/settings/show.html.haml +2 -0
  23. data/app/views/e9_vendors/vendor_categories/_form.html.haml +7 -0
  24. data/app/views/e9_vendors/vendor_categories/_table.html.haml +21 -0
  25. data/app/views/e9_vendors/vendor_categories/destroy.js.erb +3 -0
  26. data/app/views/e9_vendors/vendor_categories/edit.html.haml +2 -0
  27. data/app/views/e9_vendors/vendor_categories/index.html.haml +4 -0
  28. data/app/views/e9_vendors/vendor_categories/index.js.erb +1 -0
  29. data/app/views/e9_vendors/vendor_categories/new.html.haml +2 -0
  30. data/app/views/e9_vendors/vendor_members/_form.html.haml +49 -0
  31. data/app/views/e9_vendors/vendor_members/_table.html.haml +21 -0
  32. data/app/views/e9_vendors/vendor_members/_vendors.html.haml +24 -0
  33. data/app/views/e9_vendors/vendor_members/destroy.js.erb +3 -0
  34. data/app/views/e9_vendors/vendor_members/edit.html.haml +2 -0
  35. data/app/views/e9_vendors/vendor_members/index.html.haml +4 -0
  36. data/app/views/e9_vendors/vendor_members/index.js.erb +1 -0
  37. data/app/views/e9_vendors/vendor_members/new.html.haml +2 -0
  38. data/app/views/e9_vendors/vendor_members/widget_code.html.haml +3 -0
  39. data/app/views/e9_vendors/vendor_proxies/_form.html.haml +28 -0
  40. data/app/views/e9_vendors/vendor_proxies/_table.html.haml +16 -0
  41. data/app/views/e9_vendors/vendor_proxies/_table_row.html.haml +11 -0
  42. data/app/views/e9_vendors/vendor_proxies/destroy.js.erb +3 -0
  43. data/app/views/e9_vendors/vendor_proxies/edit.html.haml +2 -0
  44. data/app/views/e9_vendors/vendor_proxies/index.html.haml +1 -0
  45. data/app/views/e9_vendors/vendor_proxies/index.js.erb +1 -0
  46. data/app/views/e9_vendors/vendor_proxies/new.html.haml +2 -0
  47. data/app/views/e9_vendors/vendor_proxies/update.js.erb +7 -0
  48. data/app/views/e9_vendors/vendors/_form.html.haml +91 -0
  49. data/app/views/e9_vendors/vendors/_table.html.haml +20 -0
  50. data/app/views/e9_vendors/vendors/destroy.js.erb +3 -0
  51. data/app/views/e9_vendors/vendors/edit.html.haml +2 -0
  52. data/app/views/e9_vendors/vendors/index.html.haml +4 -0
  53. data/app/views/e9_vendors/vendors/index.js.erb +1 -0
  54. data/app/views/e9_vendors/vendors/new.html.haml +2 -0
  55. data/config/locales/vendorboon.en.yml +61 -0
  56. data/config/routes.rb +38 -0
  57. data/e9_vendors.gemspec +20 -0
  58. data/lib/e9_vendors.rb +24 -0
  59. data/lib/e9_vendors/controller.rb +18 -0
  60. data/lib/e9_vendors/model.rb +13 -0
  61. data/lib/e9_vendors/version.rb +3 -0
  62. data/lib/generators/e9_vendors/install_generator.rb +31 -0
  63. data/lib/generators/e9_vendors/templates/javascript.js +16 -0
  64. data/lib/generators/e9_vendors/templates/migration.rb +84 -0
  65. data/lib/generators/e9_vendors/templates/stylesheets/vb-widget-ie.scss +21 -0
  66. data/lib/generators/e9_vendors/templates/stylesheets/vb-widget.scss +151 -0
  67. metadata +129 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in e9_directory.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,6 @@
1
+ E9 Vendors
2
+ ============
3
+
4
+ Vendor directory plugin for use with the e9 CMS
5
+
6
+ Although it doesn't speficy requirements, this gem is dependent on e9_base and its environment.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,11 @@
1
+ class E9Vendors::SettingsController < Admin::SettingsController
2
+ include E9Vendors::Controller
3
+
4
+ defaults :instance_name => 'settings', :resource_class => Settings
5
+
6
+ skip_before_filter :add_admin_settings_breadcrumb
7
+
8
+ def add_index_breadcrumb
9
+ add_breadcrumb! e9_t(:index_title)
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ class E9Vendors::VendorCategoriesController < Admin::ResourceController
2
+ include E9Vendors::Controller
3
+ include E9Rails::Controllers::Sortable
4
+
5
+ respond_to :html, :js
6
+
7
+ add_resource_breadcrumbs
8
+
9
+ def create
10
+ create! { collection_path }
11
+ end
12
+
13
+ def update
14
+ update! { collection_path }
15
+ end
16
+
17
+ def destroy
18
+ destroy! do |format|
19
+ format.html { redirect_to collection_path }
20
+ format.js
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ ##
27
+ # IR
28
+ #
29
+ def collection
30
+ get_collection_ivar || set_collection_ivar(end_of_association_chain.order(:position).all)
31
+ end
32
+
33
+ def resource
34
+ get_resource_ivar || set_resource_ivar(end_of_association_chain.find(params[:id]))
35
+ end
36
+ end
@@ -0,0 +1,61 @@
1
+ class E9Vendors::VendorMembersController < Admin::ResourceController
2
+ include E9Vendors::Controller
3
+
4
+ respond_to :json, :only => :show
5
+ respond_to :html, :js, :except => :show
6
+
7
+ carrierwave_column_methods :logo, :context => :admin
8
+
9
+ skip_before_filter :authenticate_user!, :filter_access_filter, :only => :show
10
+
11
+ add_resource_breadcrumbs
12
+ before_filter :add_widget_code_breadcrumb, :only => :widget_code
13
+
14
+ def show
15
+ show! do |format|
16
+ format.html { render_404 }
17
+ format.json { render :json => { :type => 'member', :resource => resource }, :callback => params[:jsonp] }
18
+ end
19
+ end
20
+
21
+ def create
22
+ create! { collection_path }
23
+ end
24
+
25
+ def update
26
+ update! { collection_path }
27
+ end
28
+
29
+ def destroy
30
+ destroy! do |format|
31
+ format.html { redirect_to collection_path }
32
+ format.js
33
+ end
34
+ end
35
+
36
+ protected
37
+
38
+ def resource
39
+ get_resource_ivar || set_resource_ivar(decorate end_of_association_chain.send(resource_lookup, params[:id]))
40
+ end
41
+
42
+ def resource_lookup
43
+ params[:action] == 'show' ? :find_by_md5_hash! : :find
44
+ end
45
+
46
+ def collection
47
+ get_collection_ivar || set_collection_ivar(decorate end_of_association_chain.order(:name))
48
+ end
49
+
50
+ def decorate(records)
51
+ VendorMemberDecorator.decorate(records)
52
+ end
53
+
54
+ def add_widget_code_breadcrumb
55
+ add_breadcrumb! e9_t(:widget_code_title)
56
+ end
57
+
58
+ def determine_layout
59
+ request.xhr? ? false : super
60
+ end
61
+ end
@@ -0,0 +1,38 @@
1
+ class E9Vendors::VendorProxiesController < Admin::ResourceController
2
+ include E9Vendors::Controller
3
+
4
+ belongs_to :vendor_member
5
+ defaults :instance_name => :vendor
6
+
7
+ add_resource_breadcrumbs
8
+
9
+ def update
10
+ update! do |format|
11
+ format.html { redirect_to collection_path }
12
+ format.js
13
+ end
14
+ end
15
+
16
+ protected
17
+
18
+ def determine_layout
19
+ request.xhr? ? false : super
20
+ end
21
+
22
+ def add_index_breadcrumb
23
+ add_breadcrumb parent.class.model_name.human.pluralize, polymorphic_path(parent.class)
24
+ add_breadcrumb (@index_title = e9_t(:index_title, :member => parent.name)), collection_path
25
+ end
26
+
27
+ def decorate(records)
28
+ VendorProxyDecorator.decorate(records)
29
+ end
30
+
31
+ def collection
32
+ get_collection_ivar || set_collection_ivar(decorate end_of_association_chain.joins(:vendor).order('vendors.name').all)
33
+ end
34
+
35
+ def resource
36
+ get_resource_ivar || set_resource_ivar(decorate end_of_association_chain.find(params[:id]))
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ class E9Vendors::VendorsController < Admin::ResourceController
2
+ include E9Vendors::Controller
3
+
4
+ respond_to :html, :js
5
+
6
+ carrierwave_column_methods :logo, :context => :admin
7
+
8
+ add_resource_breadcrumbs
9
+
10
+ def create; create! { collection_path } end
11
+ def update; update! { collection_path } end
12
+ def destroy; destroy! { collection_path } end
13
+
14
+ protected
15
+
16
+ ##
17
+ # IR
18
+ #
19
+ def collection
20
+ @vendors ||= end_of_association_chain.order(:name).all
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ class VendorCategoryDecorator < VendorsDecorator
2
+ decorates :vendor_category
3
+
4
+ def as_json(opts = {})
5
+ {
6
+ :id => self.id,
7
+ :name => self.name
8
+ }
9
+ end
10
+ end
@@ -0,0 +1,55 @@
1
+ class VendorMemberDecorator < VendorsDecorator
2
+ decorates :vendor_member
3
+
4
+ def as_json(options = {})
5
+ {
6
+ :name => model.name,
7
+ :address_1 => model.address_1,
8
+ :address_2 => model.address_2,
9
+ :admin_notes => model.admin_notes,
10
+ :categories => VendorCategoryDecorator.decorate(VendorCategory.widget_visible),
11
+ :city => model.city,
12
+ :contact_email => model.contact_email,
13
+ :contact_full_name => model.contact_full_name,
14
+ :contact_phone => model.contact_phone,
15
+ :contact_title => model.contact_title,
16
+ :country => model.country,
17
+ :logo => model.logo_url,
18
+ :nickname => model.nickname,
19
+ :state => model.state,
20
+ :vendors => VendorProxyDecorator.decorate(model.vendor_proxies.widget_visible),
21
+ :website => model.website,
22
+ :widget_form_text => config_render(:vendorboon_widget_form_text),
23
+ :widget_form_title => config_render(:vendorboon_widget_form_title),
24
+ :widget_title => config_render(:vendorboon_widget_title),
25
+ :zipcode => model.zipcode
26
+ }
27
+ end
28
+
29
+ def widget_code
30
+ <<-CODE
31
+ <script type="text/javascript" src="#{script_url}"></script>
32
+ <script type="text/javascript">
33
+ try {
34
+ new VB.Widget({
35
+ code: "#{model.md5_hash}"
36
+ }).render();
37
+ } catch (e) {}
38
+ </script>
39
+ CODE
40
+ end
41
+
42
+ protected
43
+
44
+ def script_url
45
+ dir = Rails.env.development? && 'javascripts' || 'assets'
46
+ Linkable.urlify_path("/#{dir}/widget.js")
47
+ end
48
+
49
+ def liquid_context
50
+ super.merge({
51
+ 'member_name' => self.name,
52
+ 'member_nickname' => self.nickname
53
+ })
54
+ end
55
+ end
@@ -0,0 +1,74 @@
1
+ class VendorProxyDecorator < VendorsDecorator
2
+ decorates :vendor
3
+
4
+ def as_json(options={})
5
+ {
6
+ :id => self.id,
7
+ :address_1 => vendor.address_1,
8
+ :address_2 => vendor.address_2,
9
+ :admin_notes => vendor.admin_notes,
10
+ :city => vendor.city,
11
+ :categories => VendorCategoryDecorator.decorate(vendor.vendor_categories),
12
+ :contact_email => vendor.contact_email,
13
+ :contact_full_name => vendor.contact_full_name,
14
+ :contact_phone => vendor.contact_phone,
15
+ :contact_title => vendor.contact_title,
16
+ :country => vendor.country,
17
+ :discount_code => self.discount_code,
18
+ :discount_percentage => self.discount_percentage,
19
+ :discount_percentage => vendor.discount_percentage,
20
+ :display_on_widget => self.display_on_widget,
21
+ :display_on_widget_contact_form => vendor.display_on_widget_contact_form,
22
+ :landing_page => self.landing_page,
23
+ :logo => vendor.logo_url,
24
+ :long_description => liquid_render(vendor.long_description),
25
+ :member_compensation => vendor.member_compensation,
26
+ :name => vendor.name,
27
+ :nickname => vendor.nickname,
28
+ :sales_email => self.sales_email,
29
+ :sales_email => vendor.sales_email,
30
+ :sales_full_name => self.sales_full_name,
31
+ :sales_phone => self.sales_phone,
32
+ :sales_title => self.sales_title,
33
+ :short_description => liquid_render(vendor.short_description),
34
+ :state => vendor.state,
35
+ :website => vendor.website,
36
+ :zipcode => vendor.zipcode
37
+ }
38
+ end
39
+
40
+ def sales_info_array
41
+ [model.sales_full_name, model.sales_title, model.sales_phone, model.sales_email].reject(&:blank?)
42
+ end
43
+
44
+ def landing_page_url
45
+ model.landing_page =~ /^https?:/ ? model.landing_page : "http://#{model.landing_page}"
46
+ end
47
+
48
+ def label_with_default(column)
49
+ "#{model.class.human_attribute_name(column)}".tap do |retv|
50
+ if (v = model.vendor.send(column)) && v.present?
51
+ retv << ' '
52
+ retv << h.e9_t(:vendor_default, :default_value => v)
53
+ end
54
+ end
55
+ end
56
+
57
+ protected
58
+
59
+ def liquid_context
60
+ super.merge({
61
+ 'vendor_discount_code' => model.discount_code,
62
+ 'vendor_sales_full_name' => model.sales_full_name,
63
+ 'vendor_sales_title' => model.sales_title,
64
+ 'vendor_sales_phone' => model.sales_phone,
65
+ 'vendor_sales_email' => model.sales_email,
66
+ 'vendor_landing_page' => model.landing_page,
67
+ 'vendor_discount_percentage' => model.discount_percentage,
68
+ 'vendor_name' => vendor.name,
69
+ 'vendor_nickname' => vendor.nickname,
70
+ 'member_name' => vendor_member.name,
71
+ 'member_nickname' => vendor_member.nickname
72
+ })
73
+ end
74
+ end
@@ -0,0 +1,27 @@
1
+ class VendorsDecorator < BaseDecorator
2
+ allows :id, :role, :remove_logo!
3
+
4
+ protected
5
+
6
+ class << self
7
+ delegate :lookup_ancestors, :i18n_scope, :to => :model_class
8
+ end
9
+
10
+ # simply send it all over to the model
11
+ def method_missing(*args)
12
+ model.send(*args)
13
+ end
14
+
15
+ def liquid_render(template)
16
+ Liquid::Template.parse(template || '').render(liquid_context)
17
+ end
18
+
19
+ def config_render(key)
20
+ liquid_render(E9::Config[key])
21
+ end
22
+
23
+ def liquid_context
24
+ # from base_controller
25
+ h.liquid_env
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ class Vendor < ActiveRecord::Base
2
+ include E9Vendors::Model
3
+
4
+ has_and_belongs_to_many :vendor_categories
5
+ has_many :vendor_proxies, :dependent => :destroy
6
+ has_many :vendor_members, :through => :vendor_proxies
7
+
8
+ validates :name, :presence => true
9
+ validates :logo, :presence => true
10
+ validates :contact_email, :presence => true, :email => { :allow_blank => true }
11
+ validates :sales_email, :presence => true, :email => { :allow_blank => true }
12
+ validates :short_description, :presence => true
13
+ validates :long_description, :presence => true
14
+ validates :discount_percentage, :presence => true, :numericality => { :allow_blank => true, :greater_than_or_equal_to => 0, :less_than => 100 }
15
+ validates :member_compensation, :presence => true, :numericality => { :allow_blank => true, :greater_than_or_equal_to => 0, :less_than => 100 }
16
+ validates :landing_page, :presence => true
17
+
18
+ mount_uploader :logo, LogoUploader
19
+
20
+ def self.all_proxies
21
+ all.map {|vendor| vendor.vendor_proxies.build }
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ class VendorCategory < ActiveRecord::Base
2
+ include E9Vendors::Model
3
+
4
+ has_and_belongs_to_many :vendors
5
+
6
+ validates :name, :presence => true
7
+
8
+ scope :widget_visible, lambda {
9
+ joins(:vendors => :vendor_proxies) & VendorProxy.widget_visible
10
+ }
11
+
12
+ # On updates, all members are touched, ensuring that widget JSON requests
13
+ # for the associations are pulling the most recent information
14
+ after_save :on => :update do
15
+ VendorMember.touch_all
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ class VendorMember < ActiveRecord::Base
2
+ include E9Vendors::Model
3
+
4
+ def self.add_vendor_proxy(proxy_association)
5
+ all.each {|member| member.vendor_proxies << proxy_association.build }
6
+ end
7
+
8
+ has_many :vendor_proxies, :dependent => :destroy do
9
+ def by_category
10
+ includes(:vendor => :vendor_categories)
11
+ .order('vendor_categories.position')
12
+ .group_by {|vd| vd.vendor.vendor_category.name }
13
+ end
14
+ end
15
+ has_many :vendors, :through => :vendor_proxies
16
+
17
+ mount_uploader :logo, LogoUploader
18
+
19
+ validates :name, :presence => true
20
+ validates :logo, :presence => true
21
+ end
@@ -0,0 +1,39 @@
1
+ class VendorProxy < ActiveRecord::Base
2
+ include E9Vendors::Model
3
+
4
+ belongs_to :vendor, :touch => true
5
+ belongs_to :vendor_member, :touch => true
6
+
7
+ before_validation :populate_default_discount_code, :on => :create
8
+
9
+ validates :discount_code, :presence => true, :on => :update
10
+ validates :discount_percentage, :numericality => { :greater_than_or_equal_to => 0, :less_than => 100 }
11
+ validates :sales_email, :email => { :allow_blank => true }
12
+
13
+ delegate :name, :to => :vendor, :prefix => true
14
+
15
+ scope :widget_visible, lambda { where(:display_on_widget => true) }
16
+
17
+ PROXIED_COLUMNS = %w(
18
+ discount_percentage
19
+ landing_page
20
+ sales_full_name
21
+ sales_title
22
+ sales_phone
23
+ sales_email
24
+ )
25
+
26
+ PROXIED_COLUMNS.each do |column|
27
+ class_eval %Q[def #{column}; vendor_fallback(:#{column}) end]
28
+ end
29
+
30
+ protected
31
+
32
+ def populate_default_discount_code
33
+ self.discount_code = "V-#{self.vendor.id}-#{self.vendor_member.id}"
34
+ end
35
+
36
+ def vendor_fallback(column)
37
+ read_attribute(column).presence || vendor.send(column)
38
+ end
39
+ end