diff --git a/Gemfile b/Gemfile index 456234f554..2e03d65a35 100644 --- a/Gemfile +++ b/Gemfile @@ -42,6 +42,7 @@ gem 'gmaps4rails' gem 'spinjs-rails' gem 'rack-ssl', :require => 'rack/ssl' gem 'custom_error_message', :github => 'jeremydurham/custom-err-msg' +gem 'angularjs-file-upload-rails', '~> 1.1.0' gem 'foreigner' gem 'immigrant' diff --git a/Gemfile.lock b/Gemfile.lock index 9bb6e501db..c1a2c66522 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -153,6 +153,7 @@ GEM railties (>= 3.1) sprockets tilt + angularjs-file-upload-rails (1.1.0) angularjs-rails (1.2.13) ansi (1.4.2) arel (3.0.3) @@ -505,6 +506,7 @@ DEPENDENCIES active_model_serializers andand angular-rails-templates + angularjs-file-upload-rails (~> 1.1.0) angularjs-rails awesome_print aws-sdk diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee index f529ac3255..45acfd6523 100644 --- a/app/assets/javascripts/darkswarm/all.js.coffee +++ b/app/assets/javascripts/darkswarm/all.js.coffee @@ -15,6 +15,8 @@ #= require ../shared/bindonce.min.js #= require ../shared/ng-infinite-scroll.min.js #= require ../shared/angular-local-storage.js +#= require angularjs-file-upload + #= require angular-rails-templates #= require_tree ../templates diff --git a/app/assets/javascripts/darkswarm/controllers/enterprise_image_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/enterprise_image_controller.js.coffee new file mode 100644 index 0000000000..e12d55b50c --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/enterprise_image_controller.js.coffee @@ -0,0 +1,13 @@ +angular.module('Darkswarm').controller "EnterpriseImageCtrl", ($scope, EnterpriseImageService) -> + $scope.imageStep = 'logo' + + $scope.imageSteps = ['logo', 'promo'] + + $scope.imageUploader = EnterpriseImageService.imageUploader + + $scope.imageSelect = (image_step) -> + EnterpriseImageService.imageSrc = null + $scope.imageStep = image_step + + $scope.imageSrc = -> + EnterpriseImageService.imageSrc diff --git a/app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee index 84f133da54..fabc2c382a 100644 --- a/app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee @@ -12,4 +12,4 @@ Darkswarm.controller "RegistrationFormCtrl", ($scope, RegistrationService, Enter EnterpriseRegistrationService.update(nextStep) if $scope.valid(form) $scope.selectIfValid = (nextStep, form) -> - RegistrationService.select(nextStep) if $scope.valid(form) \ No newline at end of file + RegistrationService.select(nextStep) if $scope.valid(form) diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee index 1e58fe7294..e8ea9dce3c 100644 --- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee +++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee @@ -1,18 +1,19 @@ -window.Darkswarm = angular.module("Darkswarm", ["ngResource", - 'mm.foundation', - 'angularLocalStorage', - 'pasvaz.bindonce', - 'infinite-scroll', - 'angular-flash.service', +window.Darkswarm = angular.module("Darkswarm", ["ngResource", + 'mm.foundation', + 'angularLocalStorage', + 'pasvaz.bindonce', + 'infinite-scroll', + 'angular-flash.service', 'templates', 'ngSanitize', 'ngAnimate', 'google-maps', 'duScroll', + 'angularFileUpload', ]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) -> - $httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') - $httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') - $httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest' + $httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') + $httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') + $httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest' $httpProvider.defaults.headers.common.Accept = "application/json, text/javascript, */*" # This allows us to trigger these two events on tooltips @@ -20,4 +21,3 @@ window.Darkswarm = angular.module("Darkswarm", ["ngResource", # We manually handle our scrolling $anchorScrollProvider.disableAutoScrolling() - diff --git a/app/assets/javascripts/darkswarm/services/enterprise_image_service.js.coffee b/app/assets/javascripts/darkswarm/services/enterprise_image_service.js.coffee new file mode 100644 index 0000000000..c8cd64e93a --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/enterprise_image_service.js.coffee @@ -0,0 +1,12 @@ +Darkswarm.factory "EnterpriseImageService", (FileUploader, spreeApiKey) -> + new class EnterpriseImageService + imageSrc: null + + imageUploader: new FileUploader + headers: + 'X-Spree-Token': spreeApiKey + autoUpload: true + + configure: (enterprise) => + @imageUploader.url = "/api/enterprises/#{enterprise.id}/update_image" + @imageUploader.onSuccessItem = (image, response) => @imageSrc = response diff --git a/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee b/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee index 68915193ee..77b2204316 100644 --- a/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee +++ b/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService, CurrentUser, spreeApiKey, Loading, availableCountries, enterpriseAttributes) -> +Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService, EnterpriseImageService, CurrentUser, spreeApiKey, Loading, availableCountries, enterpriseAttributes) -> new class EnterpriseRegistrationService enterprise: user_ids: [CurrentUser.id] @@ -22,6 +22,7 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService, ).success((data) => Loading.clear() @enterprise.id = data + EnterpriseImageService.configure(@enterprise) RegistrationService.select('about') ).error((data) => Loading.clear() @@ -54,4 +55,4 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService, enterprise[key] = value enterprise.address_attributes = @enterprise.address if @enterprise.address? enterprise.address_attributes.country_id = @enterprise.country.id if @enterprise.country? - enterprise \ No newline at end of file + enterprise diff --git a/app/assets/javascripts/darkswarm/services/registration_service.js.coffee b/app/assets/javascripts/darkswarm/services/registration_service.js.coffee index a2a1fe2dc4..530d118025 100644 --- a/app/assets/javascripts/darkswarm/services/registration_service.js.coffee +++ b/app/assets/javascripts/darkswarm/services/registration_service.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.factory "RegistrationService", (Navigation, $modal, Loading)-> +angular.module('Darkswarm').factory "RegistrationService", (Navigation, $modal, Loading)-> new class RegistrationService constructor: -> @@ -20,4 +20,4 @@ Darkswarm.factory "RegistrationService", (Navigation, $modal, Loading)-> close: -> Loading.message = "Taking you back to the home page" - Navigation.go "/" \ No newline at end of file + Navigation.go "/" diff --git a/app/assets/javascripts/templates/registration/about.html.haml b/app/assets/javascripts/templates/registration/about.html.haml index 07e631345f..57f7c482e6 100644 --- a/app/assets/javascripts/templates/registration/about.html.haml +++ b/app/assets/javascripts/templates/registration/about.html.haml @@ -9,7 +9,7 @@ {{ enterprise.name }} %ng-include{ src: "'registration/steps.html'" } - %form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('social',about)" } } + %form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('images',about)" } } .row .small-12.columns .alert-box.alert{"data-alert" => ""} diff --git a/app/assets/javascripts/templates/registration/images.html.haml b/app/assets/javascripts/templates/registration/images.html.haml index efd3688076..889bc4596f 100644 --- a/app/assets/javascripts/templates/registration/images.html.haml +++ b/app/assets/javascripts/templates/registration/images.html.haml @@ -1,14 +1,20 @@ -.container#registration-images +.container#registration-images{ 'nv-file-drop' => true, uploader: "imageUploader", options:"{ alias: imageStep }", ng: { controller: "EnterpriseImageCtrl" } } .header %h2 Thanks! %h5 Let's upload some pretty pictures so your profile looks great! :) %ng-include{ src: "'registration/steps.html'" } - .row.content + %form{ name: 'images', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "select('social')" } } + .row{ ng: { repeat: 'image_step in imageSteps', show: "imageStep == image_step" } } + %ng-include{ src: "'registration/images/'+ image_step + '.html'" } - .row.buttons - .small-12.columns - %input.button.primary.left{ type: "button", value: "Back", ng: { click: "select('about')" } } -   - %input.button.primary.right{ type: "button", value: "Continue", ng: { click: "select('social')" } } - - \ No newline at end of file + .row.buttons.pad-top{ ng: { if: "imageStep == 'logo'" } } + .small-12.columns + %input.button.primary{ type: "button", value: "Back", ng: { click: "select('about')" } } +   + %input.button.primary{ type: "button", value: "Continue", ng: { click: "imageSelect('promo')" } } + + .row.buttons.pad-top{ ng: { if: "imageStep == 'promo'" } } + .small-12.columns + %input.button.primary{ type: "button", value: "Back", ng: { click: "imageSelect('logo')" } } +   + %input.button.primary{ type: "submit", value: "Continue" } diff --git a/app/assets/javascripts/templates/registration/images/logo.html.haml b/app/assets/javascripts/templates/registration/images/logo.html.haml new file mode 100644 index 0000000000..803b103a58 --- /dev/null +++ b/app/assets/javascripts/templates/registration/images/logo.html.haml @@ -0,0 +1,41 @@ +.small-12.medium-12.large-6.columns + .row + .small-12.columns.center + .row + .small-12.columns.center + %h4 + Step 1. Select Logo Image + .row + .small-12.columns.center + %span.small + Tip: Square images will work best, preferably at least 300×300px + .row.pad-top + .small-12.columns + .image-select.small-12.columns + %label.small-12.columns.button{ for: 'image-select' } Choose a logo image + %input#image-select{ type: 'file', hidden: true, 'nv-file-select' => true, uploader: "imageUploader", options: '{ alias: imageStep }' } + .row.show-for-large-up + .large-12.columns + %span#or.large-12.columns + OR + .row.show-for-large-up + .large-12.columns + #image-over{ 'nv-file-over' => true, uploader: "imageUploader" } + Drag and drop your logo here +.small-12.medium-12.large-6.columns + .row + .small-12.columns.center + .row + .small-12.columns.center + %h4 + Step 2. Review Your Logo + .row + .small-12.columns.center + %span.small + Tip: for best results, your logo should fill the available space + .row.pad-top + .small-12.columns.center + #image-placeholder.logo + %img{ ng: { show: "imageSrc()", src: '{{ imageSrc() }}' } } + .message{ ng: { hide: "imageSrc()" } } + Your logo will appear here for review once uploaded diff --git a/app/assets/javascripts/templates/registration/images/promo.html.haml b/app/assets/javascripts/templates/registration/images/promo.html.haml new file mode 100644 index 0000000000..f134834d5c --- /dev/null +++ b/app/assets/javascripts/templates/registration/images/promo.html.haml @@ -0,0 +1,39 @@ +.small-12.medium-12.large-12.columns + .row + .small-12.columns.center + %h4 + Step 3. Select Promo Image + .row + .small-12.medium-12.large-5.columns.center + .row + .small-12.columns.center + %span.small + Tip: Shown as a banner, preferred size is 1200×260px + .row.pad-top + .small-12.columns + .image-select.small-12.columns + %label.small-12.columns.button{ for: 'image-select' } Choose a promo image + %input#image-select{ type: 'file', hidden: true, 'nv-file-select' => true, uploader: "imageUploader", options: '{ alias: imageStep }' } + .large-2.columns + %span#or.horizontal.large-12.columns + OR + .large-5.columns + #image-over{ 'nv-file-over' => true, uploader: "imageUploader" } + Drag and drop your promo here +.small-12.medium-12.large-12.columns.pad-top + .row + .small-12.columns.center + %h4 + Step 4. Review Your Promo Banner + .row + .small-12.columns.center + .row + .small-12.columns.center + %span.small + Tip: for best results, your promo image should fill the available space + .row.pad-top + .small-12.columns.center + #image-placeholder.promo + %img{ ng: { show: "imageSrc()", src: '{{ imageSrc() }}' } } + .message{ ng: { hide: "imageSrc()" } } + Your logo will appear here for review once uploaded diff --git a/app/assets/javascripts/templates/registration/introduction.html.haml b/app/assets/javascripts/templates/registration/introduction.html.haml index 8a4f4f7e02..ccefdefbe0 100644 --- a/app/assets/javascripts/templates/registration/introduction.html.haml +++ b/app/assets/javascripts/templates/registration/introduction.html.haml @@ -6,35 +6,34 @@ .small-12.medium-3.large-2.columns.text-right.hide-for-small-only %img{:src => "/assets/potatoes.png"} .small-12.medium-9.large-10.columns - %p - Your profile gives you an online presence on the - %strong Open Food Network, + %p + Your profile gives you an online presence on the + %strong Open Food Network, allowing you to easily connect with potential customers or partners. You can always choose to update your info later, as well as choose to upgrade your Profile to and Online Store, where you can sell products, track orders and receive payments. Creating a profile takes about 5-10 minutes. .row{ 'data-equalizer' => true } .small-12.medium-6.large-6.columns.pad-top{ 'data-equalizer-watch' => true } %h5 You'll need the following: %ul.check-list - %li + %li Your enterprise address and contact details - %li + %li Your logo image - %li + %li A pretty picture for your profile header - %li + %li Some 'About Us' text .small-12.medium-6.large-6.columns{ 'data-equalizer-watch' => true} .highlight-box %h5 Your profile entitles you to: %ul.small-block-grid-1 - %li + %li %i.ofn-i_020-search A searchable listing - %li + %li %i.ofn-i_040-hub A pin on the OFN map .row .small-12.columns %hr %input.button.primary{ type: "button", value: "Let's get started!", ng: { click: "select('details')" } } - \ No newline at end of file diff --git a/app/assets/javascripts/templates/registration/social.html.haml b/app/assets/javascripts/templates/registration/social.html.haml index 2aa12bd08b..1b3490ffa0 100644 --- a/app/assets/javascripts/templates/registration/social.html.haml +++ b/app/assets/javascripts/templates/registration/social.html.haml @@ -30,6 +30,6 @@ .row.buttons .small-12.columns - %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('about')" } } + %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('images')" } }   - %input.button.primary{ type: "submit", value: "Continue" } \ No newline at end of file + %input.button.primary{ type: "submit", value: "Continue" } diff --git a/app/assets/stylesheets/darkswarm/registration.css.sass b/app/assets/stylesheets/darkswarm/registration.css.sass index 3e851709bc..3261fc0fd4 100644 --- a/app/assets/stylesheets/darkswarm/registration.css.sass +++ b/app/assets/stylesheets/darkswarm/registration.css.sass @@ -80,6 +80,53 @@ color: #333 @include box-shadow(inset 0 0 1px 0 #fff) + + .image-select + label + font-size: 18px + padding: 21px 0px + #logo-select + display: none + + #image-over + font-size: 18px + padding: 41px 0px + border: 3px dashed #494949 + text-align: center + font-weight: bold + color: #494949 + &.nv-file-over + background-color: #78cd91 + + #or + text-align: center + font-weight: bold + font-size: 18px + padding: 21px 0px + &.horizontal + padding: 41px 0px + + #image-placeholder + font-size: 18px + font-weight: bold + color: #373737 + background-color: #e1e1e1 + text-align: center + border: 3px dashed #494949 + margin-left: auto + margin-right: auto + &.logo + .message + padding-top: 6em + width: 306px + height: 306px + &.promo + .message + padding-top: 4em + width: 726px + height: 166px + + #registration-details #enterprise-types a.panel @@ -112,4 +159,3 @@ p clear: both font-size: 0.875rem - diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 64ba997950..bde7bff431 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -7,6 +7,7 @@ module Admin before_filter :check_bulk_type, only: :bulk_update before_filter :override_owner, only: :create before_filter :check_owner, only: :update + before_filter :check_bulk_owner, only: :bulk_update helper 'spree/products' include OrderCyclesHelper @@ -79,11 +80,19 @@ module Admin end def check_owner - unless spree_current_user == @enterprise.owner || spree_current_user.admin? + unless ( spree_current_user == @enterprise.owner ) || spree_current_user.admin? params[:enterprise].delete :owner_id end end + def check_bulk_owner + unless spree_current_user.admin? + params[:enterprise_set][:collection_attributes].each do |i, enterprise_params| + enterprise_params.delete :owner_id + end + end + end + # Overriding method on Spree's resource controller def location_after_save if params[:enterprise].key? :producer_properties_attributes diff --git a/app/controllers/api/enterprises_controller.rb b/app/controllers/api/enterprises_controller.rb index 9a3b715093..76320b0750 100644 --- a/app/controllers/api/enterprises_controller.rb +++ b/app/controllers/api/enterprises_controller.rb @@ -27,9 +27,9 @@ module Api end def update - authorize! :update, Enterprise - @enterprise = Enterprise.find(params[:id]) + authorize! :update, @enterprise + if @enterprise.update_attributes(params[:enterprise]) render text: @enterprise.id, :status => 200 else @@ -37,6 +37,19 @@ module Api end end + def update_image + @enterprise = Enterprise.find(params[:id]) + authorize! :update, @enterprise + + if params[:logo] && @enterprise.update_attributes( { logo: params[:logo] } ) + render text: @enterprise.logo.url(:medium), :status => 200 + elsif params[:promo] && @enterprise.update_attributes( { promo_image: params[:promo] } ) + render text: @enterprise.promo_image.url(:medium), :status => 200 + else + invalid_resource!(@enterprise) + end + end + private def override_owner diff --git a/app/controllers/registration_controller.rb b/app/controllers/registration_controller.rb index d58b10bd0b..47a1537b57 100644 --- a/app/controllers/registration_controller.rb +++ b/app/controllers/registration_controller.rb @@ -2,7 +2,7 @@ require 'open_food_network/spree_api_key_loader' class RegistrationController < BaseController include OpenFoodNetwork::SpreeApiKeyLoader - before_filter :load_spree_api_key, only: :index + before_filter :load_spree_api_key, only: [:index, :store] before_filter :check_user, except: :authenticate layout 'registration' diff --git a/app/controllers/spree/api/products_controller_decorator.rb b/app/controllers/spree/api/products_controller_decorator.rb index 65765e03ca..111f36962c 100644 --- a/app/controllers/spree/api/products_controller_decorator.rb +++ b/app/controllers/spree/api/products_controller_decorator.rb @@ -12,6 +12,7 @@ Spree::Api::ProductsController.class_eval do def bulk_products @products = OpenFoodNetwork::Permissions.new(current_api_user).managed_products. merge(product_scope). + order('created_at DESC'). ransack(params[:q]).result. page(params[:page]).per(params[:per_page]) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index fc6e356d78..30a5939b08 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -34,7 +34,7 @@ class Enterprise < ActiveRecord::Base path: 'public/images/enterprises/logos/:id/:style/:basename.:extension' has_attached_file :promo_image, - styles: { large: "1200x260#", thumb: "100x100>" }, + styles: { large: "1200x260#", medium: "720x156#", thumb: "100x100>" }, url: '/images/enterprises/promo_images/:id/:style/:basename.:extension', path: 'public/images/enterprises/promo_images/:id/:style/:basename.:extension' diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 79019f3e8a..08fc4407ce 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -2,12 +2,17 @@ class AbilityDecorator include CanCan::Ability def initialize(user) + add_base_abilities user if is_new_user? user add_enterprise_management_abilities user if can_manage_enterprises? user add_product_management_abilities user if can_manage_products? user add_relationship_management_abilities user if can_manage_relationships? user end + def is_new_user?(user) + user.enterprises.blank? + end + def can_manage_enterprises?(user) user.enterprises.present? end @@ -22,6 +27,9 @@ class AbilityDecorator can_manage_enterprises? user end + def add_base_abilities(user) + can [:create], Enterprise + end def add_enterprise_management_abilities(user) # Spree performs authorize! on (:create, nil) when creating a new order from admin, and also (:search, nil) @@ -71,7 +79,7 @@ class AbilityDecorator can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Shipment can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Adjustment can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::ReturnAuthorization - + can [:create], OrderCycle can [:admin, :index, :read, :edit, :update, :bulk_update, :clone], OrderCycle do |order_cycle| user.enterprises.include? order_cycle.coordinator diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml index 44008b3bd2..03e5335155 100644 --- a/app/views/admin/enterprises/index.html.haml +++ b/app/views/admin/enterprises/index.html.haml @@ -16,7 +16,8 @@ %col{style: "width: 5%;"}/ - if spree_current_user.admin? %col{style: "width: 12%;"}/ - %col{style: "width: 18%;"}/ + - if spree_current_user.admin? + %col{style: "width: 18%;"}/ %col{style: "width: 25%;"}/ %thead %tr{"data-hook" => "enterprises_header"} @@ -25,7 +26,8 @@ %th Visible? - if spree_current_user.admin? %th Type - %th Owner + - if spree_current_user.admin? + %th Owner %th %tbody = f.fields_for :collection do |enterprise_form| @@ -41,7 +43,8 @@ %td= enterprise_form.check_box :visible - if spree_current_user.admin? %td= enterprise_form.select :type, Enterprise::TYPES, {}, class: 'select2 fullwidth' - %td= enterprise_form.select :owner_id, enterprise.users.map{ |e| [ e.email, e.id ] }, {}, class: "select2 fullwidth" + - if spree_current_user.admin? + %td= enterprise_form.select :owner_id, enterprise.users.map{ |e| [ e.email, e.id ] }, {}, class: "select2 fullwidth" %td{"data-hook" => "admin_users_index_row_actions"} = render 'actions', enterprise: enterprise - if @enterprises.empty? diff --git a/app/views/layouts/registration.html.haml b/app/views/layouts/registration.html.haml index 8946a27de1..d6122cdc3b 100644 --- a/app/views/layouts/registration.html.haml +++ b/app/views/layouts/registration.html.haml @@ -31,6 +31,6 @@ %section{ role: "main" } = yield - + #footer %loading diff --git a/config/initializers/spree.rb b/config/initializers/spree.rb index bbee30379a..f722c60f1a 100644 --- a/config/initializers/spree.rb +++ b/config/initializers/spree.rb @@ -23,6 +23,11 @@ Spree.config do |config| #config.override_actionmailer_config = false end +# TODO Work out why this is necessary +# Seems like classes within OFN module become 'uninitialized' when server reloads +# unless the empty module is explicity 'registered' here. Something to do with autoloading? +module OpenFoodNetwork +end # Add calculators category for enterprise fees module Spree @@ -38,7 +43,7 @@ module Spree end # Forcing spree to always allow SSL connections -# Since we are using config.force_ssl = true +# Since we are using config.force_ssl = true # Without this we get a redirect loop: see https://groups.google.com/forum/#!topic/spree-user/NwpqGxJ4klk SslRequirement.module_eval do protected diff --git a/config/routes.rb b/config/routes.rb index f95b0c07df..3e063641dd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -67,6 +67,7 @@ Openfoodnetwork::Application.routes.draw do namespace :api do resources :enterprises do + post :update_image, on: :member get :managed, on: :collection get :accessible, on: :collection end diff --git a/lib/open_food_network/spree_api_key_loader.rb b/lib/open_food_network/spree_api_key_loader.rb index 36fa4b9961..5eb7236221 100644 --- a/lib/open_food_network/spree_api_key_loader.rb +++ b/lib/open_food_network/spree_api_key_loader.rb @@ -9,4 +9,4 @@ module OpenFoodNetwork end end end -end \ No newline at end of file +end diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index 44421aad8e..a522624489 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' module Admin describe EnterprisesController do + include AuthenticationWorkflow let(:distributor_owner) do user = create(:user) user.spree_roles = [] @@ -110,36 +111,52 @@ module Admin end describe "bulk updating enterprises" do - let(:profile_enterprise1) { create(:enterprise, type: 'profile') } - let(:profile_enterprise2) { create(:enterprise, type: 'profile') } + let!(:original_owner) do + user = create_enterprise_user + user.enterprise_limit = 2 + user.save! + user + end + let!(:new_owner) do + user = create_enterprise_user + user.enterprise_limit = 2 + user.save! + user + end + let!(:profile_enterprise1) { create(:enterprise, type: 'profile', owner: original_owner ) } + let!(:profile_enterprise2) { create(:enterprise, type: 'profile', owner: original_owner ) } context "as manager" do - it "does not allow 'type' to be changed" do - profile_enterprise1.enterprise_roles.build(user: user).save - profile_enterprise2.enterprise_roles.build(user: user).save - controller.stub spree_current_user: user - bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, type: 'full' }, '1' => { id: profile_enterprise2.id, type: 'full' } } } } + it "does not allow 'type' or 'owner' to be changed" do + profile_enterprise1.enterprise_roles.build(user: new_owner).save + profile_enterprise2.enterprise_roles.build(user: new_owner).save + controller.stub spree_current_user: new_owner + bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, type: 'full', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, type: 'full', owner_id: new_owner.id } } } } spree_put :bulk_update, bulk_enterprise_params profile_enterprise1.reload profile_enterprise2.reload expect(profile_enterprise1.type).to eq 'profile' expect(profile_enterprise2.type).to eq 'profile' + expect(profile_enterprise1.owner).to eq original_owner + expect(profile_enterprise2.owner).to eq original_owner end end context "as super admin" do - it "allows 'type' to be changed" do - profile_enterprise1.enterprise_roles.build(user: user).save - profile_enterprise2.enterprise_roles.build(user: user).save + it "allows 'type' and 'owner' to be changed" do + profile_enterprise1.enterprise_roles.build(user: new_owner).save + profile_enterprise2.enterprise_roles.build(user: new_owner).save controller.stub spree_current_user: admin_user - bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, type: 'full' }, '1' => { id: profile_enterprise2.id, type: 'full' } } } } + bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, type: 'full', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, type: 'full', owner_id: new_owner.id } } } } spree_put :bulk_update, bulk_enterprise_params profile_enterprise1.reload profile_enterprise2.reload expect(profile_enterprise1.type).to eq 'full' expect(profile_enterprise2.type).to eq 'full' + expect(profile_enterprise1.owner).to eq new_owner + expect(profile_enterprise2.owner).to eq new_owner end end end diff --git a/spec/controllers/api/enterprises_controller_spec.rb b/spec/controllers/api/enterprises_controller_spec.rb new file mode 100644 index 0000000000..e21a879d4f --- /dev/null +++ b/spec/controllers/api/enterprises_controller_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +module Api + describe EnterprisesController do + include AuthenticationWorkflow + render_views + + let(:enterprise) { create(:distributor_enterprise) } + + before do + stub_authentication! + Enterprise.stub(:find).and_return(enterprise) + end + + describe "as an enterprise manager" do + let(:enterprise_manager) { create_enterprise_user } + + before do + enterprise_manager.enterprise_roles.build(enterprise: enterprise).save + Spree.user_class.stub :find_by_spree_api_key => enterprise_manager + end + + describe "submitting a valid image" do + before do + enterprise.stub(:update_attributes).and_return(true) + end + + it "I can update enterprise image" do + spree_post :update_image, logo: 'a logo' + response.should be_success + end + end + end + + describe "as an non-managing user" do + let(:non_managing_user) { create_enterprise_user } + + before do + Spree.user_class.stub :find_by_spree_api_key => non_managing_user + end + + describe "submitting a valid image" do + before do + enterprise.stub(:update_attributes).and_return(true) + end + + it "I can't update enterprise image" do + spree_post :update_image, logo: 'a logo' + assert_unauthorized! + end + end + end + end +end diff --git a/spec/controllers/registration_controller_spec.rb b/spec/controllers/registration_controller_spec.rb index 49efc005f6..13babdf89e 100644 --- a/spec/controllers/registration_controller_spec.rb +++ b/spec/controllers/registration_controller_spec.rb @@ -12,4 +12,28 @@ describe RegistrationController do response.should redirect_to registration_auth_path(anchor: "signup?after_login=/register/store") end end + + describe "loading data when user is logged in" do + let!(:user) { double(:user) } + + before do + controller.stub spree_current_user: user + user.stub spree_api_key: '12345' + user.stub last_incomplete_spree_order: nil + end + + describe "index" do + it "loads the spree api key" do + get :index + expect(assigns(:spree_api_key)).to eq '12345' + end + end + + describe "store" do + it "loads the spree api key" do + get :store + expect(assigns(:spree_api_key)).to eq '12345' + end + end + end end diff --git a/spec/factories.rb b/spec/factories.rb index d605b5b759..db035ba16a 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -10,21 +10,29 @@ FactoryGirl.define do after(:create) do |oc| # Suppliers + supplier1 = create(:supplier_enterprise) + supplier2 = create(:supplier_enterprise) + + # Incoming Exchanges ex1 = create(:exchange, :order_cycle => oc, :incoming => true, - :sender => create(:supplier_enterprise), :receiver => oc.coordinator) + :sender => supplier1, :receiver => oc.coordinator) ex2 = create(:exchange, :order_cycle => oc, :incoming => true, - :sender => create(:supplier_enterprise), :receiver => oc.coordinator) + :sender => supplier2, :receiver => oc.coordinator) ExchangeFee.create!(exchange: ex1, enterprise_fee: create(:enterprise_fee, enterprise: ex1.sender)) ExchangeFee.create!(exchange: ex2, enterprise_fee: create(:enterprise_fee, enterprise: ex2.sender)) - # Distributors + #Distributors + distributor1 = create(:distributor_enterprise) + distributor2 = create(:distributor_enterprise) + + # Outgoing Exchanges ex3 = create(:exchange, :order_cycle => oc, :incoming => false, - :sender => oc.coordinator, :receiver => create(:distributor_enterprise), + :sender => oc.coordinator, :receiver => distributor1, :pickup_time => 'time 0', :pickup_instructions => 'instructions 0') ex4 = create(:exchange, :order_cycle => oc, :incoming => false, - :sender => oc.coordinator, :receiver => create(:distributor_enterprise), + :sender => oc.coordinator, :receiver => distributor2, :pickup_time => 'time 1', :pickup_instructions => 'instructions 1') ExchangeFee.create!(exchange: ex3, enterprise_fee: create(:enterprise_fee, enterprise: ex3.receiver)) diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index 5db9eee86d..fe18f1a9bf 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -10,7 +10,7 @@ feature %q{ after { Warden.test_reset! } stub_authorization! - pending "listing orders" do + context "listing orders" do before :each do admin_user = quick_login_as_admin end @@ -92,7 +92,7 @@ feature %q{ end end - pending "altering line item properties" do + context "altering line item properties" do before :each do admin_user = quick_login_as_admin end @@ -140,7 +140,7 @@ feature %q{ end end - pending "using page controls" do + context "using page controls" do before :each do admin_user = quick_login_as_admin end @@ -562,7 +562,7 @@ feature %q{ end end - pending "as an enterprise manager" do + context "as an enterprise manager" do let(:s1) { create(:supplier_enterprise, name: 'First Supplier') } let(:d1) { create(:distributor_enterprise, name: 'First Distributor') } let(:d2) { create(:distributor_enterprise, name: 'Another Distributor') } diff --git a/spec/features/admin/orders_spec.rb b/spec/features/admin/orders_spec.rb index d45778635a..908ce4042e 100644 --- a/spec/features/admin/orders_spec.rb +++ b/spec/features/admin/orders_spec.rb @@ -110,10 +110,10 @@ feature %q{ let(:coordinator2) { create(:distributor_enterprise) } let!(:order_cycle1) { create(:order_cycle, coordinator: coordinator1) } let!(:order_cycle2) { create(:simple_order_cycle, coordinator: coordinator2) } - let(:supplier1) { order_cycle1.suppliers.first } - let(:supplier2) { order_cycle1.suppliers.last } - let(:distributor1) { order_cycle1.distributors.first } - let(:distributor2) { order_cycle1.distributors.last } + let!(:supplier1) { order_cycle1.suppliers.first } + let!(:supplier2) { order_cycle1.suppliers.last } + let!(:distributor1) { order_cycle1.distributors.first } + let!(:distributor2) { order_cycle1.distributors.reject{ |d| d == distributor1 }.last } # ensure d1 != d2 let(:product) { order_cycle1.products.first } before(:each) do @@ -132,19 +132,6 @@ feature %q{ expect(page).to have_content 'ADD PRODUCT' targetted_select2_search product.name, from: '#add_variant_id', dropdown_css: '.select2-drop' - puts "c1: " + coordinator1.id.to_s + " "+ coordinator1.name - puts "c2: " + coordinator2.id.to_s + " "+ coordinator2.name - puts "s1: " + supplier1.id.to_s + " "+ supplier1.name - puts "s2: " + supplier2.id.to_s + " "+ supplier2.name - puts "d1: " + distributor1.id.to_s + " "+ distributor1.name - puts "d2: " + distributor2.id.to_s + " "+ distributor2.name - order_cycle1.distributors.each do |distributor| - puts "oc1d: " + distributor.id.to_s + " "+ distributor.name - end - Enterprise.is_distributor.managed_by(@enterprise_user).each do |distributor| - puts "eud: " + distributor.id.to_s + " "+ distributor.name - end - click_link 'Add' page.has_selector? "table.index tbody[data-hook='admin_order_form_line_items'] tr" # Wait for JS expect(page).to have_selector 'td', text: product.name diff --git a/spec/features/consumer/registration_spec.rb b/spec/features/consumer/registration_spec.rb index 3500736100..7ac46a8970 100644 --- a/spec/features/consumer/registration_spec.rb +++ b/spec/features/consumer/registration_spec.rb @@ -60,15 +60,22 @@ feature "Registration", js: true do fill_in 'enterprise_acn', with: '54321' click_button 'Continue' - # Enterprise should be updated - expect(page).to have_content 'Last step!' + # Enterprise should be update + expect(page).to have_content "Let's upload some pretty pictures so your profile looks great!" e.reload expect(e.description).to eq "Short description" expect(e.long_description).to eq "Long description" expect(e.abn).to eq '12345' expect(e.acn).to eq '54321' + # Images + # Move from logo page + click_button 'Continue' + # Move from promo page + click_button 'Continue' + # Filling in social + expect(page).to have_content 'Last step!' fill_in 'enterprise_website', with: 'www.shop.com' fill_in 'enterprise_facebook', with: 'FaCeBoOk' fill_in 'enterprise_linkedin', with: 'LiNkEdIn' diff --git a/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee index b1b019276b..8f6bd64a5f 100644 --- a/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee @@ -7,7 +7,7 @@ describe "enterpriseCtrl", -> beforeEach -> module('admin.enterprises') - Enterprise = + Enterprise = enterprise: payment_method_ids: [ 1, 3 ] shipping_method_ids: [ 2, 4 ] @@ -15,7 +15,7 @@ describe "enterpriseCtrl", -> paymentMethods: [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 } ] ShippingMethods = shippingMethods: [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 } ] - + inject ($controller) -> scope = {} ctrl = $controller 'enterpriseCtrl', {$scope: scope, Enterprise: Enterprise, PaymentMethods: PaymentMethods, ShippingMethods: ShippingMethods} @@ -82,4 +82,4 @@ describe "enterpriseCtrl", -> describe "counting selected shipping methods", -> it "counts only shipping methods with selected: true", -> scope.ShippingMethods = [ { selected: true }, { selected: true }, { selected: false }, { selected: true } ] - expect(scope.selectedShippingMethodsCount()).toBe 3 \ No newline at end of file + expect(scope.selectedShippingMethodsCount()).toBe 3 diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index cf17d7fe3a..78ca1db909 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -13,6 +13,13 @@ module Spree let(:enterprise_single) { create(:enterprise, type: 'single') } let(:enterprise_profile) { create(:enterprise, type: 'profile') } + describe "creating enterprises" do + it "can create enterprises straight off the bat" do + subject.is_new_user?(user).should be_true + expect(user).to have_ability :create, for: Enterprise + end + end + describe "managing enterprises" do it "can manage enterprises when the user has at least one enterprise assigned" do user.enterprise_roles.create! enterprise: enterprise_full diff --git a/spec/support/matchers/table_matchers.rb b/spec/support/matchers/table_matchers.rb index 146002b751..053562b9e4 100644 --- a/spec/support/matchers/table_matchers.rb +++ b/spec/support/matchers/table_matchers.rb @@ -3,20 +3,15 @@ RSpec::Matchers.define :have_table_row do |row| match_for_should do |node| @row = row - false_on_timeout_error do - wait_until { rows_under(node).include? row } - end + node.has_selector? "tr", text: row.join(" ").strip # Check for appearance + rows_under(node).include? row # Robust check of columns end match_for_should_not do |node| @row = row - false_on_timeout_error do - # Without this sleep, we trigger capybara's wait when looking up the table, for the full - # period of default_wait_time. - sleep 0.1 - wait_until { !rows_under(node).include? row } - end + node.has_no_selector? "tr", text: row.join(" ").strip # Check for appearance + !rows_under(node).include? row # Robust check of columns end failure_message_for_should do |text| @@ -27,17 +22,7 @@ RSpec::Matchers.define :have_table_row do |row| "expected not to find table row #{@row}" end - def rows_under(node) node.all('tr').map { |tr| tr.all('th, td').map(&:text) } end - - def false_on_timeout_error - yield - rescue TimeoutError - false - else - true - end - end