Hot questions for Using Spree in devise

Question:

It seems I must be doing something wrong here (I'm in my infancy with Spree) I followed this tutorial http://guides.spreecommerce.com/developer/authentication.html I have default devise setup and are trying now to add SpreeCommerce to it.

module Spree
  module AuthenticationHelpers
    def self.included(receiver)
      receiver.send :helper_method, :spree_login_path
      receiver.send :helper_method, :spree_signup_path
      receiver.send :helper_method, :spree_logout_path
      receiver.send :helper_method, :spree_current_user
    end

    def spree_current_user
      current_user
    end

    def spree_login_path
      # main_app.login_path
      main_app.new_user_session_path
    end

    def spree_signup_path
      main_app.new_user_registration_path
    end

    def spree_logout_path
      main_app. destroy_user_session_path
    end
  end
end

Spree::BaseController.send :include, Spree::AuthenticationHelpers
ApplicationController.send :include, Spree::AuthenticationHelpers

This is my code in authentication_helpers.rb under lib/spree

I have also added following to my spree.rb initializer

Spree.user_class = "User"

Rails.application.config.to_prepare do
  require_dependency 'spree/authentication_helpers'
end

I mounted spree engine under "/store"

Now. Forms for user obviously work in my application. When I go to store and I hit "Logout" that works as expected - logs me out from spree and my main app. Login form in Spree gives me following error:

> RuntimeError in Spree::UserSessionsController#create Could not find a
> valid mapping for nil

And when I look at session dump:

flash: {"discard"=>[], "flashes"=>{"success"=>"Logged in successfully"}}
session_id: "b71cbba980b1375c241d432920865fb6"
warden.user.spree_user.key: [[1], "fL1Ls3yoi8fg7yPFADbx"]

User obviously doesn't get logged in anywhere.

Registration form takes the input, saves record in Spree::User class but doesn't log user is neither allowed for login later. Login button is still there but when clicked doesn't do anything. I'm not logged in for the rest of the website either.

Additionally, when I try to open the cart I get (although /account gives no error):

ActiveRecord::AssociationTypeMismatch in Spree::OrdersController#edit
Spree::User(#70363670728860) expected, got User(#70363741439840) 

Answer:

Right. Answer turns out to be quite straightforward:

Instead of adding dedicated authentication strategy to the gem file

gem 'spree_auth_devise', :git => 'https://github.com/spree/spree_auth_devise.git', :branch => '2-3-stable'

Simply use

gem 'devise'

I had them both included at the same time.

Question:

At the moment, I'm building a Rails platform using Spree Commerce where I need two Devise login forms.

The default Devise login forms are already implemented. For the second (custom) Devise form, I created the following view:

global_login.html.erb

<div class="inner">
  <h2 class="large-margin">
    Login
   </h2>
   <%= form_for(Spree::User.new, :url => spree_login_path) do |f| %>
     <%= f.error_messages %>
     <%= f.email_field :email, {:value => params[:email], :placeholder => 'Email address', :class => 'input'} %>
     <%= f.password_field :password, {:placeholder => 'Password', :class => 'input'} %>
     <%= f.submit 'Login', :class => 'submit' %>
  <% end %>
</div>

This view does display a Devise login form. However, every time I submit the form, I'm being redirected to the default Devise view where the validation errors are shown.

Does somebody know a solution to this problem?

Cheers!


Answer:

I found a workaround for this issue. I added a param to one of the forms named "inside_cart". When the request is send to the #create method, I check if the param is present.

I overwrote the Spree::UserSessionsController#create method as follows:

Spree::UserSessionsController.class_eval do
  layout false

  def create
    authenticate_spree_user!
      if spree_user_signed_in?
        respond_to do |format|
          format.html {
            flash[:success] = Spree.t(:logged_in_succesfully)
            redirect_back_or_default(after_sign_in_path_for(spree_current_user))
          }
          format.js {
            user = resource.record
            render :json => {:ship_address => user.ship_address, :bill_address => user.bill_address}.to_json
          }
        end
      else
        if params[:spree_user] && params[:spree_user][:inside_cart]
          flash.now[:error] = t('devise.failure.invalid')
          render :new, layout: false
        else
          flash.now[:error] = t('devise.failure.invalid')
          render 'spree/users/global_login', layout: 'spree/layouts/spree_application'
        end
      end
    end
  end

Question:

I've installed spree on my app and have currently running live.

On my local machine the app seems to work fine and when I try to use forgot password I go to the confirmation page. But on my live server, I get the error 500 screen.

I Installed the spree mailer gem for my backed and put in the smpth information, but when I try to test it I get the following error (The change you wanted was rejected. Maybe you tried to change something you didn't have access to.)

Can anyone tell me how to setup the spree mail and forgotten passwords?


Answer:

Contacted server provider and they added a server certificated to my mail server resolving the problem.

Question:

I've been reading through the SpreeSocial documentation here. I can't figure out how to remove providers through the config. I've tried to just pop the most recent provider off off of the list but that's not working.

  SpreeSocial::OAUTH_PROVIDERS.pop

I managed to make the 'Sign in with LinkedIn' appear in the views, but I'd like to remove the 'Sign in with Google+' link.

SpreeSocial::OAUTH_PROVIDERS << ['LinkedIn', 'linkedin', 'true']
SpreeSocial.init_provider('linkedin')

If I want to open up the classes in SpreeSocial with .class_eval (I assume that this may help me edit which providers are available), where would I place those files so that they would autoload, or where would I need to configure them? Pretty new to Spree/Rails so not too familiar with configurations and initializers. Thanks for the help!

I'd like to be able to call .class_eval on the SpreeSocial Module here


Answer:

SpreeSocial::OAUTH_PROVIDERS is going to be redefined when the code initializes the application so just using pop won't be sufficient. Since SpreeSocial is a module not a class you'll need to either:

1) Use a module_eval not a class_eval to redefine the SpreeSocial::OAUTH_PROVIDERS

SpreeSocial.module_eval do OAUTH_PROVIDERS = [ %w(Facebook facebook true), %w(Twitter twitter false), %w(Github github false), %w(Amazon amazon false) ] end

or

2) You could override the methods that determine active authentication methods to not return google: https://github.com/spree-contrib/spree_social/blob/master/app/models/spree/authentication_method.rb#L4-L12

Question:

I have added name, surname and birthdate fields to devise registration. Here is the link to my original question.

Adding name to Spree Devise registration

It seems that on the frontend side everything works. However I now get an Authorization Failure alert when I try to access my admin.

Here is the console log:

Started GET "/admin/orders" for ::1 at 2015-07-21 13:25:16 -0500
Processing by Spree::Admin::OrdersController#index as HTML
  Spree::Preference Load (0.3ms)  SELECT  "spree_preferences".* FROM "spree_preferences" WHERE "spree_preferences"."key" = $1 LIMIT 1  [["key", "spree/backend_configuration/locale"]]
  Spree::User Load (0.5ms)  SELECT  "spree_users".* FROM "spree_users" WHERE "spree_users"."deleted_at" IS NULL AND "spree_users"."id" = $1  ORDER BY "spree_users"."id" ASC LIMIT 1  [["id", 6]]
   (0.7ms)  SELECT COUNT(*) FROM "spree_roles" INNER JOIN "spree_roles_users" ON "spree_roles"."id" = "spree_roles_users"."role_id" WHERE "spree_roles_users"."user_id" = $1 AND "spree_roles"."name" = $2  [["user_id", 6], ["name", "admin"]]
Redirected to http://localhost:3000/unauthorized
Completed 302 Found in 88ms (ActiveRecord: 4.1ms)

I have tried to override app/controllers/spree/admin/users_controller_decorator.rb

module Spree
  module Admin
   UsersController.class_eval do

    private
     def user_params
         params.require(:user).permit(:email, :password, :password_confirmation, :spree_role_ids, :name, :surname, :birthdate)

     end

   end
  end
end

Answer:

You Cant asign a role admin to any user from frontend side. Please register a user from front end and change it role from backend.

Question:

Hello fellow Spree users,

I'd like to understand a little more about the spree_auth_devise gem that works with Spree. If I have Devise set up already for user authentication, do I even need this? It's been giving me problems in terms of integrating my existing user model from Devise with its user class. I've read the documentation several times (link here)

Following the spree guides I modified the spree.rb file so that it references my User class. app/config/initializers/spree.rb:

Spree.user_class = "User"

      Rails.application.config.to_prepare do
        require_dependency 'spree/authentication_helpers'
      end

I then ran the migration to add the necessary spree columns to my User table.

class AddSpreeFieldsToCustomUserTable < ActiveRecord::Migration
  def up
    add_column "users", :spree_api_key, :string, :limit => 48
    add_column "users", :ship_address_id, :integer
    add_column "users", :bill_address_id, :integer
  end
end

and it migrated fine. The server starts, but when I navigate to where I mounted Spree, I get this error:

undefined method `last_incomplete_spree_order' for #User:0x007fb1e51824c0

Turns out that the Spree.user_class that I had changed to "User" gets changed back to Spree::User for some reason when I added spree_auth_devise. I verified this in my rails console. So that would make sense why I'm getting that error since all those Spree methods are defined by the Spree user class.

When I delete the spree_auth_devise gem, then suddenly everything works and my Spree.user_class is correctly defined as I set it ("User").

So I figured I would trash that gem and go on with my life. But apparently getting others to pull down my repo causes problems in the Spree migrations if they don't have that spree_auth_devise gem installed. So I've had to tell them add it for the migrations then delete it again when the migrations finish.

Also found the code that sets the user class in spree_auth_devise gem. I'm afraid to change it here.

spree_auth_devise/lib/spree/auth/engine.rb:

initializer "spree_auth_devise.set_user_class", :after => :load_config_initializers do
  Spree.user_class = "Spree::User
end

To sum up:

Why exactly do I need this gem? How can I keep it but change it so that it knows my User class? Thanks.


Answer:

Spree Auth Devise is just for Spree Authentication. Since you already have Devise setup, I would simply use Spree Auth Devise as an example codebase for any Spree specific user issues and continue with User as your spree user class.

# config/initializers/spree.rb
Spree.user_class = "User"

For the migrations, I would remove any spree_auth_devise specific migrations. You'll know they're SAD migrations since Rails namespaces migrations at the end by the Rails engine. For example:

20141124203907_add_confirmable_to_users.spree_auth.rb

Hope this helps.

EDIT:

I was looking at the undefined method last_incomplete_spree_order and it appears it gets defined inside of Spree Core when the Spree user class is set. Source code here.

Question:

I have a Spree 3.7 app and am trying to use the storefront API v2 to get a token. I've been recommended to use their SDK, but I'm failing to understand how to use the SDK

Example below:

Identifies a guest user's cart and order.

const response = await client.cart.create()

const orderToken: string = response.data.attributes.token

It isn't clearly stated where the response.data.attributes.token comes from or how to get it.

Does anyone have an example how to use the SDK to get an API token? Currently not able to do so. It's stated that using /cart I can get a guest token button trying to get a response ends with a 404

const orderToken: string = response.data.attributes.token returns data as unkown. Where do I get the data value from?

  import { makeClient } from '@spree/storefront-api-v2-sdk/dist/client';


async function asyncCall() {
  console.log('calling');

  // When using the SDK in a <script> tag or as part of a Webpack bundle
  // targeted for the browser, instead use:
  // import { makeClient } from '@spree/storefront-api-v2-sdk/dist/client'

  const client = makeClient({
    host: 'https://stern-telecom-react-salman15.c9users.io/'
  });

  console.log(client)

  const createCart = await client.cart.create()


  const orderToken = response.data.attributes.token

  const addToCart = await client.cart.addItem({ orderToken }, {
    variant_id: '1',
    quantity: 1
  })

  console.log('orderToken',orderToken,'createCart',createCart);
  // expected output: 'resolved'
}

asyncCall();

Answer:

I've managed to get the API token using the SDK by adding .succes() before calling the data

const client = makeClient({
   host: 'yourwebsite or localhost:3000'
 });
 console.log('cliet',client);

const cartCreateResponse = await client.cart.create()
 console.log('cartCreateResponse',cartCreateResponse.success().data);


const orderToken = cartCreateResponse.success().data.attributes.token
 console.log('orderToken', orderToken);

 const addToCart = await client.cart.addItem({ orderToken }, {
   variant_id: '1',
   quantity: 1
 })

Question:

I am trying to setup spree commerce with API and backend only. I can get the site to start properly and I can login with the admin credentials, but if I click to view my account via the header menu I get the following error

ActionController::RoutingError (uninitialized constant Spree::UsersController):

I cant figure out why it is trying to use the Spree::UsersController and not the Spree::Admin::UsersController

Am I missing something?

GEMFILE

source 'https://rubygems.org'
gem 'rails', '4.2.0'
gem 'sqlite3'
gem 'sass-rails', '~> 4.0.3'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.0.0'
gem 'therubyracer',  platforms: :ruby
gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder', '~> 2.0'
gem 'sdoc', '~> 0.4.0',          group: :doc
gem 'spring',        group: :development
gem 'spree_core'
gem 'spree_api'
gem 'spree_backend'
gem 'spree_gateway', github: 'spree/spree_gateway', branch: '3-0-stable'
gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: '3-0-stable'

ROUTES

Rails.application.routes.draw do
  get 'home/index'
  mount Spree::Core::Engine, :at => '/s/'
  root to: redirect('/')
end

Answer:

The "Your Account" link takes you to your front end account page not the admin account page. We should probably hide that link if spree_frontend is not in use or direct it to your admin account page instead (feel free to submit a PR to contribute).

If you want to view your users account info go to the users section in the left column of the admin panel and search for them.

Question:

I an deleting items from spree cart using the following spree api url

/api/orders/#{current_order.number}/line_items/#{line_iem.id}?line_item[variant_id]=#{line_item.variant.id}&line_item[quantity]=0&token=MyToken

the token used here is of a single user. I want to implement this in generic way so that it can be used for both guest and registered user.. is it possible?

any help would be highly appreciated

Regards


Answer:

Whenever you create an order using the Spree API, you get an order_token in the response. From the Spree API Guide:

The order_token parameter will work for authorizing any action for an order within Spree’s API.

So instead of appending &token=... you should append &order_token=...

It will work for both registered users as well as guests.

Question:

So I tried to add the login link on my Spree Rails app by following the documentation in http://guides.spreecommerce.org/developer/authentication.html However I haven't been able to get the link on my app.

I created app/overrides/auth_login_bar.rb file following the documentation adding the following code to the file.

Deface::Override.new(:virtual_path => "spree/shared/_nav_bar",
  :name => "auth_shared_login_bar",
  :insert_before => "li#search-bar",
  :partial => "spree/shared/login_bar",
  :disabled => false,
  :original => 'eb3fa668cd98b6a1c75c36420ef1b238a1fc55ad')

Answer:

Your override file at app/overrides/auth_login_bar.rb is telling Spree to insert a partial view called spree/shared/login_bar into the nav bar. Did you create this partial in your views?

Here's my partial view (written in HAML), located at spree/shared/_login_bar.html.haml

- if spree_current_user
  %li= link_to(Spree.t(:logout), destroy_spree_user_session_path, method: :delete)
- else
  %li= link_to(Spree.t(:login), login_path)
  %li= link_to(Spree.t(:signup), signup_path)

You could also remove the method: :delete from the second line to make it a get request, which I think is how Spree is set up now.

Question:

I am using spree_social gem, for social media logins using facebook and google.

How ever while I am trying to create an app in twitter, they say call back url is invalid

http://localhost:3000/users/auth/twitter This is what I am giving as the call back url. What should I give the call back url to make it work


Answer:

As it's mentioned in spree_social extension the callback url for

  • Twitter, http://localhost:3000 (development) or http://your-site.com (production) (mentioned here).
  • Facebook, no callback url required (see here).
  • Google, http://localhost:3000/users/auth/google_oauth2/callback (development) or http://your-site.com/users/auth/google_oauth2/callback (production).

Question:

I have an existing Spree project that is functioning. I'm not try to include Alchemy CMS in the project. I followed the guide on the Github Repo. The only exception is that I skipped the auth step because I already have spree_auth_devise configured. When trying to install Alchemy, bundle exec rake alchemy:install. I get the error below.

error
rake aborted!
Bundler::GemRequireError: There was an error while trying to load the gem 'alchemy_spree'.
/Users/atbyrd/dev/distinct-existence/config/application.rb:7:in `<top (required)>'
/Users/atbyrd/dev/distinct-existence/Rakefile:4:in `require'
/Users/atbyrd/dev/distinct-existence/Rakefile:4:in `<top (required)>'
NameError: uninitialized constant Alchemy::AuthEngine
/Users/atbyrd/dev/distinct-existence/config/application.rb:7:in `<top (required)>'
/Users/atbyrd/dev/distinct-existence/Rakefile:4:in `require'
/Users/atbyrd/dev/distinct-existence/Rakefile:4:in `<top (required)>'
(See full trace by running task with --trace)
Gemfile
ruby '2.2.4'
source 'https://rubygems.org'

gem 'rails', '4.2.5'
gem 'pg', '~> 0.15'
gem 'sass-rails'
gem 'uglifier'
gem 'coffee-rails'
gem 'jquery-rails'
gem 'turbolinks'
gem 'active_model_serializers'
gem 'sdoc', '~> 0.4.0', group: :doc
gem 'spree', github: 'spree/spree'
gem 'spree_auth_devise', github: 'spree/spree_auth_devise'
gem 'puma'
gem 'paperclip'
gem 'aws-sdk', '< 2.0'
gem 'delayed_job_active_record'

gem 'alchemy_spree'
gem 'alchemy_cms'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

group :development, :test do
  gem 'byebug'
end

group :development do
  gem 'web-console', '~> 2.0'
  gem 'spring'
end
config/initializers/alchemy.rb
# Tell Alchemy to use the Spree::User class
Alchemy.user_class_name = 'Spree::User'
Alchemy.current_user_method = :spree_current_user

# Load the Spree.user_class decorator for Alchemy roles
require 'alchemy/spree/spree_user_decorator'

# Include the Spree controller helpers to render the
# alchemy pages within the default Spree layout
Alchemy::BaseHelper.send :include, Spree::BaseHelper
Alchemy::BaseController.send :include, Spree::Core::ControllerHelpers::Common
Alchemy::BaseController.send :include, Spree::Core::ControllerHelpers::Store

Answer:

Looks like outdated gem versions. Which version of Spree are you using? Please check your Gemfile.lock. If it's 3.x then please try to use the GitHub version of this gem by using gem 'alchemy_spree', github: 'magiclabs/alchemy_spree', branch: 'master' in your Gemfile and run bundle update alchemy_spree.

And, regarding to the README you need to add an initializer into your app, if you use spree_auth_devise. Just skipping this step will not work.

Everything I mentioned above is stated right in the README of the project. Did you follow it?

Question:

i am learning rails and creating a web app which also got ecommerce in it There is a Form which user can fill only if he is logged in, For that i was using Devise, then for e-commerce i installed Spree Spree got its own login authentication, and there is no authenticate_user! in controllers too, i removed devise and having a tough time finding how to use Spree's authentication with my Form

here is UPDATED Form's controller: complaints_controller.rb

module Spree
class ComplaintsController < Spree::StoreController
  before_action :require_login

  before_action :set_complaint, only: [:show, :edit, :update, :destroy]

  # GET /complaints
  # GET /complaints.json



 def require_login
      redirect_to spree_login_path unless current_spree_user
    end 


      def index
        @complaints = Complaint.all
      end

  # GET /complaints/1
  # GET /complaints/1.json
  def show
  end

  # GET /complaints/new
  def new
    @complaint = Complaint.new
  end

  # GET /complaints/1/edit
  def edit
  end

  # POST /complaints
  # POST /complaints.json
  def create
    @complaint = Complaint.new(complaint_params)

    respond_to do |format|
      if @complaint.save
        format.html { redirect_to @complaint, notice: 'Complaint was successfully created.' }
        format.json { render :show, status: :created, location: @complaint }
      else
        format.html { render :new }
        format.json { render json: @complaint.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /complaints/1
  # PATCH/PUT /complaints/1.json
  def update
    respond_to do |format|
      if @complaint.update(complaint_params)
        format.html { redirect_to @complaint, notice: 'Complaint was successfully updated.' }
        format.json { render :show, status: :ok, location: @complaint }
      else
        format.html { render :edit }
        format.json { render json: @complaint.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /complaints/1
  # DELETE /complaints/1.json
  def destroy
    @complaint.destroy
    respond_to do |format|
      format.html { redirect_to complaints_url, notice: 'Complaint was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_complaint
      @complaint = Complaint.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def complaint_params
      params.require(:complaint).permit(:id_society, :id_user, :heading, :text, :active, :action, :IsDelete, :flat_number)
    end
end
end
<% end %>

index.html.erb

  <% if spree_current_user %>
  <p id="notice"><%= notice %></p>

<h1>Listing Complaints</h1>

<table>
  <thead>
    <tr>
      <th>Id society</th>
      <th>Id user</th>
      <th>Heading</th>
      <th>Text</th>
      <th>Active</th>
      <th>Action</th>
      <th>Isdelete</th>
      <th>Flat number</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @complaints.each do |complaint| %>
      <tr>
        <td><%= complaint.id_society %></td>
        <td><%= complaint.id_user %></td>
        <td><%= complaint.heading %></td>
        <td><%= complaint.text %></td>
        <td><%= complaint.active %></td>
        <td><%= complaint.action %></td>
        <td><%= complaint.IsDelete %></td>
        <td><%= complaint.flat_number %></td>
        <td><%= link_to 'Show', complaint %></td>
        <td><%= link_to 'Edit', edit_complaint_path(complaint) %></td>
        <td><%= link_to 'Destroy', complaint, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Complaint', new_complaint_path %>

<% else %>
<h1> please login</h1>
<% end %>

This works, as it verifies user's authentication in View, is there any way to check it in controller? Like if user is logged in it will be sent to action or else redirected to login?

Thank you


Answer:

Spree uses devise authentication through a extension:

https://github.com/spree/spree_auth_devise  

For authenticate your actions at controller(your own controllers) level, you need to define your own authentication filter. So you can manage something like this:

before_action :require_login

def require_login
  redirect_to login_url unless current_spree_user
end 

Question:

I am new to spree and ruby on rails. while creating a custom controller in my spree app, I can successfully add link to it in spree admin panel using deface. but when I go to that link, it gives me following error

NoMethodError in Spree::Admin::Societies#new
Showing app/views/spree/admin/societies/_form.html.erb where line #1 raised:
undefined method `societies_path' for #<#<Class:0x007f19cb636898>:0x007f19c5ecacf8>

I don't know from where it is looking for 'societies_path' as I already have updated app/views/spree/admin/societies/new.html.erb to look for 'admin_societies_path', here it is

<%= render 'form' %>
<%= link_to 'Back', admin_societies_path %>

and app/views/spree/admin/societies/_form.html.erb contains

    <%= form_for(@society) do |f| %>
     <% if @society.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@society.errors.count, "error") %> prohibited this society from being saved:</h2>
      <ul>
      <% @society.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :address %><br>
    <%= f.text_field :address %>
  </div>
  <div class="field">
    <%= f.label :area %><br>
    <%= f.text_field :area %>
  </div>
  <div class="field">
    <%= f.label :postcode %><br>
    <%= f.number_field :postcode %>
  </div>
  <div class="field">
    <%= f.label :city %><br>
    <%= f.text_field :city %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
    <% end %>

I tried removing link to back also, but it's again giving same error.

config/routes.rb is

mount Spree::Core::Engine, :at => '/'
Spree::Core::Engine.add_routes do
 namespace :admin do
    resource :societies
  end
end

and my app/controllers/spree/admin/societies_controller.rb is

module Spree
 module Admin
 class SocietiesController < Spree::Admin::BaseController
  before_action :set_society, only: [:show, :edit, :update, :destroy]

  def index
    @societies = Society.all
  end

  def show
  end

  def new
    @society = Society.new
  end

  def edit
  end

  def create
    @society = Society.new(society_params)

    respond_to do |format|
      if @society.save
        format.html { redirect_to @society, notice: 'Society was successfully created.' }
        format.json { render :show, status: :created, location: @society }
      else
        format.html { render :new }
        format.json { render json: @society.errors, status: :unprocessable_entity }
      end
    end
  end
  def update
    respond_to do |format|
      if @society.update(society_params)
        format.html { redirect_to @society, notice: 'Society was successfully updated.' }
        format.json { render :show, status: :ok, location: @society }
      else
        format.html { render :edit }
        format.json { render json: @society.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @society.destroy
    respond_to do |format|
      format.html { redirect_to societies_url, notice: 'Society was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    def set_society
      @society = Society.find(params[:id])
    end

    def society_params
      params.require(:society).permit(:name, :url, :building_number, :address, :area, :postcode, :city, :active, :IsDelete)
    end
end
end
end

Any help would be greatly appreciated.


Answer:

I would suspect that it is this line in _form partial

<%= form_for(@society) do |f| %>

You need to reference the namespace here, so maybe somthing like

<%= form_for([:admin, @society]) do |f| %>

or add your own url

<%= form_for(@society, url: admin_societies_path) do |f| %>

Question:

I added inheritance to my Spree::User model class with STI. I have a :type column which can be (Spree::Guest, Spree::Writer, or Spree::Reader).

In my authentication in the admin side I want to authenticate only writer and reader. What would be the best option to solve this issue?

I tried to override the create action to something like:

def create
  authenticate_spree_user!

  if spree_user_signed_in? && (spree_current_user.role?(:writer) || spree_current_user.role?(:reader))
    respond_to do |format|
      format.html {
        flash[:success] = Spree.t(:logged_in_succesfully)
        redirect_back_or_default(after_sign_in_path_for(spree_current_user))
      }
      format.js {
        user = resource.record
        render :json => {:ship_address => user.ship_address, :bill_address => user.bill_address}.to_json
      }
    end
  else
    flash.now[:error] = t('devise.failure.invalid')
    render :new
  end
end

In this case when trying to authenticate with user of type :guest, it redirects to the new action with invalid failure message (ok) but somehow the user get authenticated (nok).


Answer:

I don't think that is a good way to solve that, controller should be just a controller. I'd rather go that way:

Spree uses cancancan (or cancan in older branches) for authorization and that's how Spree implements that. I don't know why you want that STI solution - I would simply create new custom Spree::Role for that but as I said I don't know why you chose STI way - that should work fine too. Anyway, you can either just add a decorator for that ability file with additional checks for something like user.is_a? Spree::Guest and so on or register new abilities via register_ability - something like this.

Most important part of third link (or in case it goes off):

# create a file under app/models (or lib/) to define your abilities (in this example I protect only the HostAppCoolPage model):

Spree::Ability.register_ability MyAppAbility

class MyAppAbility
  include CanCan::Ability

  def initialize(user)
    if user.has_role?('admin')
      can manage, :host_app_cool_pages
    end
  end

end

Personally I would go with decorator option (code seems a bit unclear but is cleaner when it comes to determine what can be managed by who - remember about abilities precedence) but it is up to you. If you have any specific questions feel free to ask, I will help if I will be able to.

Edit: so if you want to disable authentication for some users maybe just leverage existing Devise methods? Something like this(in your user model):

def active_for_authentication?
  super && self.am_i_not_a_guest? # check here if user is a Guest or not
end

def inactive_message
  self.am_i_not_a_guest? ? Spree.t('devise.failure.invalid') : super # just make sure you get proper messages if you are using that module in your app
end