Admin's delete user link not working with Devise and Rails 7 - devise

I want to allow admins to delete users from a link on the users index.
Desired behavior: When logged in admin clicks "delete" next to a username on the index page, that admin sees a confirmation message. Upon confirming, the user is deleted, admin sees updated index with flash success message.
I've tried two different ways, both failing. All things being equal, I'd prefer to do this using Devise out of the box functionality (e.g. the user_registration_path destroy action, and default flash message).
First way - adding a destroy action to custom user controller:
users/index.html.erb
<% provide(:title, 'All users') %>
<h2>All users</h2>
<%= will_paginate %>
<ul class="users">
<%= render #users %>
</ul>
<%= will_paginate %>
users/_user.html.erb
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
<% if current_user.admin? && current_user != user %>
| <%= link_to "delete", user, data: { "turbo-method": :delete,
turbo_confirm: "You sure?" } %>
<% end %>
</li>
users_controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!, :only => [:index, :edit, :update, :destroy]
def show
#user = User.find(params[:id])
end
def index
#users = User.paginate(page: params[:page])
end
def destroy
#user = User.find(params[:id])
#user.destroy
flash[:success] = "User deleted"
redirect_to users_url, status: :see_other
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users, controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations'
}
root "static_pages#home"
get "/about", to: "static_pages#about"
get "/contact", to: "static_pages#contact"
resources :users
end
Using this code, when I click "delete" next to the username, the user show page is loaded with these params:
#<ActionController::Parameters {"controller"=>"users", "action"=>"show", "id"=>"3"} permitted: false>
Second way - using the destroy action in the registrations_controller:
users/_user.html.erb
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
<% if current_user.admin? && current_user != user %>
| <%= link_to "delete", user_registration_path, data: { "turbo-method": :delete,
turbo_confirm: "You sure?" } %>
<% end %>
</li>
registrations_controller.rb
# frozen_string_literal: true
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
before_action :configure_account_update_params, only: [:update]
before_action :authenticate_user!, :only => [:destroy]
# GET /resource/sign_up
# def new
# super
# end
# POST /resource
# def create
# super
# end
# GET /resource/edit
# def edit
# super
# end
# PUT /resource
# def update
# super
# end
# DELETE /resource
def destroy
super
end
# GET /resource/cancel
# Forces the session data which is usually expired after sign
# in to be expired now. This is useful if the user wants to
# cancel oauth signing in/up in the middle of the process,
# removing all OAuth session data.
# def cancel
# super
# end
# protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
end
# If you have extra params to permit, append them to the sanitizer.
def configure_account_update_params
devise_parameter_sanitizer.permit(:account_update, keys: [:name])
end
# The path used after sign up.
def after_sign_up_path_for(resource)
super(resource)
user_url(#user)
end
# The path used after sign up for inactive accounts.
# def after_inactive_sign_up_path_for(resource)
# super(resource)
# end
end
Using this code, when I click "delete" next to the username, the users index page is reloaded with params:
#<ActionController::Parameters {"controller"=>"users", "action"=>"index"} permitted: false>

This worked for me:
<%= button_to "Delete user", user, form: {data: { turbo_confirm: "Are you sure?" }}, method: :delete %>

Related

Clearance failure when forbidden password reset

I’m using clearance and love it, but I'm having trouble resetting passwords. I type in my email to reset the password, which works, but then when I try to navigate to the edit password page using the reset token, I get the failure when forbidden flash error “Please double check the URL or try submitting the form again” and it redirects me back. I get the same error in my tests.
I think this has something to do with my before_action statements, but I just don’t know how to fix them. I have researched questions like this to no avail.
I'm sure it's a stupid question, but I'm new so I really appreciate any help. Please let me know if this isn't enough code.
class UsersController < Clearance::UsersController
before_action :require_login, only: [:create] # does this need to be in both user controllers?
...
def user_params
params.require(:user)
end
end
And here is the clearance controller.
class Clearance::UsersController < ApplicationController
before_action :require_login, only: [:create]
require 'will_paginate/array'
def new
#user = user_from_params
render template: 'users/new'
end
def create
#user = user_from_params
#user.regenerate_password
if #user.save
sign_in #user unless current_user
UserMailer.welcome_email(#user).deliver!
redirect_to users_path
else
render template: 'users/new'
end
end
def edit
#user = User.friendly.find(params[:id])
end
def update
#user = User.friendly.find(params[:id])
if #user.update(permit_params)
redirect_to #user
flash[:success] = "This profile has been updated."
else
render 'edit'
end
end
private
def avoid_sign_in
redirect_to Clearance.configuration.redirect_url
end
def url_after_create(user)
dashboards_path(user)
end
def user_from_params
user_params = params[:user] || Hash.new
is_public = check_public_params(user_params)
first_name = user_params.delete(:first_name)
last_name = user_params.delete(:last_name)
email = user_params.delete(:email)
password = user_params.delete(:password)
parish = user_params.delete(:parish)
division = user_params.delete(:division)
admin = user_params.delete(:admin)
Clearance.configuration.user_model.new(user_params).tap do |user|
user.first_name = first_name
user.last_name = last_name
user.password = password
user.email = email
user.is_public = is_public
user.parish_id = parish.to_i
user.division = division
user.admin = admin
end
end
def permit_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :is_public, :parish_id, :division, :admin)
end
end
EDIT: relevant portions of routes.rb
Rails.application.routes.draw do
resources :passwords, controller: "clearance/passwords", only: [:create, :new]
resource :session, controller: "clearance/sessions", only: [:create]
resources :users, controller: "clearance/users", only: [:create] do
resource :password,
controller: "clearance/passwords",
only: [:create, :edit, :update]
end
get "/sign_in" => "clearance/sessions#new", as: "sign_in"
delete "/sign_out" => "clearance/sessions#destroy", as: "sign_out"
get "/sign_up" => "clearance/users#new", as: "sign_up"
constraints Clearance::Constraints::SignedOut.new do
root to: 'high_voltage/pages#show', id: 'landing'
end
constraints Clearance::Constraints::SignedIn.new do
# root to: 'dashboards#index', as: :signed_in_root
root to: 'high_voltage/pages#show', id: 'parish_dashboard', as: :signed_in_root
end
# constraints Clearance::Constraints::SignedIn.new { |user| user.admin? } do
# root to: 'teams#index', as: :admin_root
# end
resources :users do
collection { post :import }
end
It turns out there was a conflict between the way I was finding the user instance in the password reset link. Clearance finds users simply by using #user, but since I'm using FriendlyId I needed to change that to #user.id.
So instead of...
<%= link_to 'Change My Password', edit_user_password_url(#user, token: #user.confirmation_token.html_safe) %>
I did
<%= link_to 'Change My Password', edit_user_password_url(#user.id, token: #user.confirmation_token.html_safe) %>
Thanks, Thoughbot, for this great gem!

What are the steps to implement Google Button subscribe for rails

I implemented Facebook Button with omniauth and It works. I don't find a tutorial to implement Google Button. Could you guide me or share some steps to integate it. Thank you very much.
Here you can find a tutorial:
Gemfile:
gem "omniauth-google-oauth2", "~> 0.2.1"
config/initializers/omniauth.rb:
OmniAuth.config.logger = Rails.logger
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, 'my Google client id', 'my Google client secret', {client_options: {ssl: {ca_file: Rails.root.join("cacert.pem").to_s}}}
end
Terminal Commands:
rails g model user provider uid name oauth_token oauth_expires_at:datetime
rake db:migrate
Terminal Commands:
rails g controller home show
rails g controller Sessions create destroy
config/routes.rb:
GoogleAuthExample::Application.routes.draw do
get 'auth/:provider/callback', to: 'sessions#create'
get 'auth/failure', to: redirect('/')
get 'signout', to: 'sessions#destroy', as: 'signout'
resources :sessions, only: [:create, :destroy]
resource :home, only: [:show]
root to: "home#show"
end
app/models/user.rb:
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
app/controllers/sessions_controller.rb:
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_path
end
def destroy
session[:user_id] = nil
redirect_to root_path
end
end
app/views/layouts/application.html.erb:
<!DOCTYPE html>
<html>
<head>
<title>Google Auth Example App</title>
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
</head>
<body>
<div>
<% if current_user %>
Signed in as <strong><%= current_user.name %></strong>!
<%= link_to "Sign out", signout_path, id: "sign_out" %>
<% else %>
<%= link_to "Sign in with Google", "/auth/google_oauth2", id: "sign_in" %>
<% end %>
</div>
<div>
<%= yield %>
</div>
</body>
</html>

link_to post ID in Rails

In my feed I have this:
<span class="title"><strong><%= link_to feed_item.title, #micropost %></strong></span><br>
I cannot figure out how to make it link to the individual pages for posts.
Right now it links to: http://localhost:3000/microposts and I get an error: No route matches [GET] "/microposts"
if I manually type the URL: http://localhost:3000/microposts/25 I can see the indivual page for the post.
This works fin linking to a user profile, but I can't get the link working to a micropost's page.
<%= link_to feed_item.user.name, feed_item.user %>
I'm new to rails and I'm trying to figure this out. Any help would be appreciated.
microposts_controller.rb
class MicropostsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
before_filter :correct_user, only: :destroy
def index
end
def show
#micropost = Micropost.find(params[:id])
end
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#micropost.destroy
redirect_to root_url
end
private
def correct_user
#micropost = current_user.microposts.find_by_id(params[:id])
redirect_to root_url if #micropost.nil?
end
end
config/routes.rb
SampleApp::Application.routes.draw do
resources :users do
member do
get :following, :followers
end
end
resources :sessions, only: [:new, :create, :destroy]
resources :microposts, only: [:create, :destroy, :show]
resources :relationships, only: [:create, :destroy]
root to: 'static_pages#home'
match '/signup', to: 'users#new'
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
match '/help', to: 'static_pages#help'
match '/about', to: 'static_pages#about'
match '/contact', to: 'static_pages#contact'
end
_feed_item.html.erb
<li id="<%= feed_item.id %>">
<%= link_to gravatar_for(feed_item.user), feed_item.user %>
<span class="title"><strong><%= link_to feed_item.title, micropost_path(#micropost) %></strong></span><br>
<span class="user">
<p><small>Created by: <%= link_to feed_item.user.name, feed_item.user %><br>
<%= feed_item.loc1T %><br>
<%= feed_item.startTime.strftime('%A, %B %d, %Y') %></small></p>
</span>
<span class="content"><%= feed_item.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
</span>
<% if current_user?(feed_item.user) %>
<%= link_to "delete", feed_item, method: :delete,
data: { confirm: "Are you sure?" },
title: feed_item.content %>
<% end %>
</li>
feed.html.erb
<% if #feed_items.any? %>
<ol class="microposts">
<%= render partial: 'shared/feed_item', collection: #feed_items %>
</ol>
<%= will_paginate #feed_items %>
<% end %>`
static_pages_controller
class StaticPagesController < ApplicationController
def home
if signed_in?
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
def help
end
def about
end
def contact
end
end
Long answer
Rails generates a number of handy named routes for you when you add routes to your routes.rb file. Usually when in doubt for routes I take a look at my rake routes task which shows you a list of all available routes. Try running rake routes > routes.txt and open up the corresponding routes.txt file.
The resulting file will list out a series of requests for you, in your case you should see something similar to this for your microposts controller:
POST /microposts(.:format) microposts#create
micropost GET /microposts/:id(.:format) microposts#show
DELETE /microposts/:id(.:format) microposts#destroy
Rake routes produces the following information for each of your routes (where applicable):
The route name (if any)
The HTTP verb used (if the route doesn’t respond to all verbs)
The URL pattern to match
The routing parameters for the route
With that information in mind be can simply look at the urls provided in the routes.txt file for the url we're trying to get to (/microposts/25). You'll see that the listed /microposts/:id(.:format) url pattern handles that perfectly. Lo and behold it also maps to the microposts#show action that you want so now to get the named route just look at the first column to appear and you'll see the "microposts" keyword. Simply add _path` to this and you'll have your named route usable in views to generate link urls. Since this particular route requires an id parameter (as detailed in the url pattern) you have to pass the named route helper and id argument as well.
Also in your routes.rb file when you add resources :something it generates routes for each of the default seven RESTful routes (new, create, edit, update, delete, index, show). In your case you're explicitly telling rails to generate default routes for the actions create, destroy and show so you can erase the line at the bottom
match "/microposts/:id" => "microposts#show" because that's already being handled.
Short answer
Change this:
<%= link_to feed_item.title, #micropost %>
To this:
<%= link_to feed_item.title, micropost_path(feed_item) %>
See Ruby on Rails Guides: Rails routing from the Outside In for all you need to know about routes.

Nested resources create Couldn't find Topic without an ID

I wanna make an application which User can create a topic and others can make posts after that. I nested my resources in my routes.rb:
MyPedia2::Application.routes.draw do
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :topics, only: [:show, :create, :destroy]
resources :posts
resources :topics do
resources :posts, only: [:create, :show, :new]
end
In my topic show page , I want to show topic.title and sended Posts and post.form.html.erb.
Everything works accept when i create a post , I get mistake
ActiveRecord::RecordNotFound in PostsController#create
Couldn't find Topic without an ID..
This is my posts_controller.rb:
class PostsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
before_filter :correct_user, only: :destroy
def new
#topic= Topic.find_by_id(params[:id])
#post = #topic.posts.build(params[:post])
end
def show
#topic = Topic.find(params[:id])
#posts = #topic.posts.paginate(page: params[:page])
end
def create
#topic = Topic.find(params[:id])
#post = #topic.posts.build(params[:post])
#post.topic = #topic
if #post.save
flash[:success] = "Konu oluşturuldu!"
redirect_to :back
else
render 'static_pages/home'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def correct_user
#post = current_user.posts.find_by_id(params[:id])
redirect_to root_path if #post.nil?
end
end
and _post_form.html.erb:
<%= form_for #new_post do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "yorumunuzu girin..." %>
</div>
<%= f.submit "Gönder", class: "btn btn-large btn-primary" %>
<% end %>
There are a few things that should solve things for you.
First and foremost, your create action in the posts controller is a bit wrong - it should look something like this:
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.build(params[:post])
# This is unnecessary as you're already adding
# the post to the topic with the build statement.
# #post.topic = #topic
if #post.save
flash[:success] = "Konu oluşturuldu!"
redirect_to :back
else
render 'static_pages/home'
end
end
This controller action assumes that you're going to use a put request to a post resource that is nested in topics, so you'll have to clean up your routes.
You have routes to posts#create both nested and unnested.
If posts are ALWAYS supposed to be nested within a topic, which your controller logic is stating, then you should add this to the unnested posts resource:
resources :posts, except: [:new, :create]
and then change that form_for tag to this:
<%= form_for [#topic, #post] do |f| %>
This tells the form builder that you're using a nested resource and will use the correct url for the http request.
Also - it looks like you're using loading all of your topics using Topic.find(params[:id]). This isn't going to work - you're in the posts controller, this is a post id. You should be loading posts with the id param like this: Post.find(params[:id]) and then the topic like this: topic = post.topic

CanCan polymorphic resource access problem

i don't quite understand how to restrict access to links in this particular case with CanCan. I always get "Edit" link displayed.
So i believe the problem is in my incorrect definition of cancan methods(load_ and authorize_).
I have CommentsController like that:
class CommentsController < ApplicationController
before_filter :authenticate_user!
load_resource :instance_name => :commentable
authorize_resource :article
def index
#commentable = find_commentable #loading our generic object
end
......
private
def find_commentable
params.each { |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.includes(:comments => :karma).find(value)
end }
end
end
and i have in comments/index.html.erb following code that render file from other controller:
<%= render :file => "#{get_commentable_partial_name(#commentable)}/show.html.erb", :collection => #commentable %>
you can think about "#{get_commentable_partial_name(#commentable)}" like just "articles" in this case.
Content of "articles/show.html.erb":
<% if can? :update, #commentable %>
<%= link_to 'Edit', edit_article_path(#commentable) %> |
<% end %>
my ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.role? :admin
can :manage, :all
elsif user.role? :author
can :read, [Article, Comment, Profile]
can :update, Article, :user_id => user.id
end
end
end
i have tried debug this issue like that
user = User.first
article = Article.first
ability = Ability.new(user)
ability.can?(:update, article)
and i always get "=> true" in ability check
Note: user.role == author and article.user_id != user.id
if you need more information please write
thank's for your time && sorry for my english
okay i figure it out, redetermined rules in ability.rb so now order is like guest->author->moderator->admin and problem is solved. I believe root of problem was in cancan logic which assumes that i need to redefine rules or do it in order i've show before