I created a form to allow people to register, it worked fine but weeks passed and it is not working anymore.
I have my module Inscription with index function:
public function executeIndex(sfWebRequest $request)
{
$this->form = new InscriptionForm();
}
In indexSuccess.php, I have:
<?php include_partial('form', array('form' => $form)) ?>
My partial form is like:
<form action="<?php echo url_for('Inscription/'.($form->getObject()->isNew() ? 'create' : 'update').(!$form->getObject()->isNew() ? '?id='.$form->getObject()->getId() : '')) ?>" method="post" <?php $form->isMultipart() and print 'enctype="multipart/form-data" ' ?>>
... // All my input for the inscription Form
</form>
And the create function:
public function executeCreate(sfWebRequest $request)
{
$this->forward404Unless($request->isMethod(sfRequest::POST));
$this->form = new InscriptionForm();
$this->processForm($request, $this->form);
}
My form is displayed properly, but if I submit the form, the create function is not called anymore.
$form->getObject()->isNew()
return true.
The update case works, just the create does not. I have no errors, just in config -> request, I have:
parameterHolder:
action: index
module: Inscription
Seems like my form call the action index instead of create.
I'm searching for hours now and I can't get the reason why.
Edit:
I got this when I submit my form:
options:
path_info_key: PATH_INFO
path_info_array: SERVER
http_port: null
https_port: null
default_format: null
logging: '1'
relative_url_root: null
formats: { txt: text/plain, js: [application/javascript, application/x-javascript, text/javascript], css: text/css, json: [application/json, application/x-json], xml: [text/xml, application/xml, application/x-xml], rdf: application/rdf+xml, atom: application/atom+xml }
no_script_name: false
parameterHolder:
action: index
module: Inscription
sf_guard_user: { id: '', _csrf_token: 2a4bd03a7c7cb1cfd8a41653fddabcc3, username: '', email_address: admin, password: admin, password_confirmation: '', Informations: { sexe_id: '1', date_naissance: { month: '', day: '', year: '' }, id: '', token: '0', etat_id: '2' }, Avatar: { id: '', valide: '0' } }
attributeHolder:
sf_route: 'sfRoute Object()'
This is like create function is not called!
And in my logs I have:
# type message
1 sfPatternRouting Connect sfRoute "sf_guard_signin" (/guard/login)
2 sfPatternRouting Connect sfRoute "sf_guard_signout" (/guard/logout)
3 sfPatternRouting Match route "sf_guard_signin" (/:module) for /Inscription with parameters array ( 'module' => 'Inscription', 'action' => 'index',)
4 sfFilterChain Executing filter "sfRenderingFilter"
5 sfFilterChain Executing filter "sfExecutionFilter"
6 InscriptionActions Call "InscriptionActions->executeIndex()"
7 sfPHPView Set component "sf_twitter_bootstrap_permanent_slot" (/)
8 sfPHPView Render "sf_app_dir\modules/Inscription/templates/indexSuccess.php"
9 sfPartialView Render "sf_app_dir\modules/Inscription/templates/_form.php"
10 sfPHPView Decorate content with "sf_app_dir\templates/layout.php"
11 sfPHPView Render "sf_app_dir\templates/layout.php"
12 sfWebResponse Send status "HTTP/1.1 200 OK"
13 sfWebResponse Send header "Content-Type: text/html; charset=utf-8"
Normally, when I submit my form, it checks for a value from an InscriptionForm, but I can set every value that I want; there is no control anymore and the index function calls the index again.
Related
I am trying to validate User inputs on server side in a Rails Application with React as view. Basically I make axios calls to the Rails API like this:
const Script = props => {
const [script, setScript] = useState({})
const [scene, setScene] = useState({})
const [loaded, setLoaded] = useState(false)
useEffect(() => {
const scriptID = props.match.params.id
const url = `/api/v1/scripts/${scriptID}`
axios.get(url)
.then( resp => {
setScript(resp.data)
setLoaded(true)
})
.catch(resp => console.log(resp))
}, [])
const handleChange = (e) => {
e.preventDefault()
setScene(Object.assign({}, scene, {[e.target.name]: e.target.value}))
}
const handleSubmit = (e) => {
e.preventDefault()
const csrfToken = document.querySelector('[name=csrf-token]').content
axios.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken
const script_id = script.data.id
axios.post('/api/v1/scenes', {scene, script_id})
.then(resp => {
const included = [...script.included, resp.data.data]
setScript({...script, included})
})
.catch(err => {
console.log(err.response.data.error)
})
.finally(() => {
setScene({name: '', description: ''})
})
}
All data gets passed into a react component with a form.
return (
<div className="wrapper">
{
loaded &&
<Fragment>
.
.
.
<SceneForm
handleChange={handleChange}
handleSubmit={handleSubmit}
attributes={script.data.attributes}
scene={scene}
/>
</Fragment>
}
</div>
)
In this form I have a name field and the corresponding name in Rails has a validation uniqueness: true. everything works fine if I enter a valid (unique) name.
I tried to implement a validation but I am not happy with the outcome. (It works in general: my no_errors? method does what is is supposed to do and I get a 403 status) This is the controller part:
def create
scene = script.scenes.new(scene_params)
if no_error?(scene)
if scene.save
render json: SceneSerializer.new(scene).serialized_json
else
render json: { error: scene.errors.messages }, status: 422
# render json: { error: scene.errors.messages[:name] }, status: 423
end
else
render json: { error: "name must be unique" }, status: 403
end
end
.
.
.
private
def no_error?(scene)
Scene.where(name: scene.name, script_id: scene.script_id).empty?
end
If I enter an existing name I get a console.log like this:
screenshot
Here is my concern: I am not happy with my approach of error handling in general. I do not want to get the 403 message logged to the console (I do want to avoid this message in the first place).
My idea is to take the "simple form" approach: Make the border of my field red and post an error message under the field, without any console output...
And on a side note: Is 403 the correct status? I was thinking about 422 but wasn't sure...
Thank you for your ideas in advance!
403 is the wrong status code. What you need is to return a 422 (unprocessable entity). 403 is more about policy and what you are authorized to do.
Then when you deal with http request it's a standard to have a request and status code printed in browser console. Not sur to get your issue here.
If it's about to display the error you could just have a function that colorize (or whatever fireworks you want) your input if the status code response is a 422.
I am writing an invitation form with Vue.js. The invites should POST to a Rails 5 API. I am using devise_invitable for the invitation logic and email dispatch. However, I am having a problem with intercepting the create method, as I will be sending the invitation to multiple users so I want to perform a User.invite! for each user in the params.
My invite.vue file, which contains the form to post:
<template>
<b-row>
<b-col>
Invite {{form.length}} members
<b-form #submit.prevent="submitStaffInvite" id='staffInvite'>
<div v-for="(row, idx) in form">
<b-row>
<b-col cols="3">
<b-form-group id="firstNameGroup" label="First name">
<b-form-input id="firstNameInput" name="user[first_name][]" type="text" v-model="row.firstName" autofocus></b-form-input>
</b-form-group>
</b-col>
<b-col cols="3">
<b-form-group id="lastNameGroup" label="Last name">
<b-form-input id="lastNameInput" name="user[last_name][]" type="text" v-model="row.lastName"></b-form-input>
</b-form-group>
</b-col>
<b-col cols="3">
<b-form-group id="emailGroup" label="Staff email">
<b-form-input id="emailInput" name="user[email][]" type="text" v-model="row.email"></b-form-input>
</b-form-group>
</b-col>
<b-col cols="3">
<b-button #click='removeRow(idx)'>Remove invitation</b-button>
</b-col>
</b-row>
</div>
<br />
<b-button-group>
<b-button #click.prevent='addRow'>Add invitation</b-button>
</b-button-group>
<br />
<b-button-group>
<b-button type="submit" variant="primary">Submit</b-button>
</b-button-group>
</b-form>
</b-col>
</b-row>
</template>
<script>
export default {
data: () => {
return {
form: [
{
email: '',
firstName: '',
lastName: ''
}
]
}
},
methods: {
addRow: function () {
this.form.push({
email: '',
firstName: '',
lastName: ''
})
},
removeRow: function (idx) {
this.form.splice(idx, 1)
},
submitStaffInvite: function () {
this.$axios.post('http://localhost:3001/auth/invitation', this.form)
.then((res) => {
if (res.status === 200) {
this.$notify({
text: res.data.message,
group: 'alerts',
type: 'success'
})
}
})
.catch(function (error) {
error.response.data.errors.forEach((err) => {
this.$notify({
text: err,
group: 'alerts',
type: 'warning'
})
})
})
}
}
}
</script>
my users/invitations_controller.rb
class Users::InvitationsController < Devise::InvitationsController
before_action :configure_permitted_parameters
def create
# TODO
# Send email to each user in the form.
end
def edit
sign_out send("current_#{resource_name}") if send("#{resource_name}_signed_in?")
set_minimum_password_length
resource.invitation_token = params[:invitation_token]
redirect_to "http://localhost:3001/auth/invitation/accept?invitation_token=#{params[:invitation_token]}"
end
def update
super do |resource|
if resource.errors.empty?
render json: { status: "Invitation Accepted!" }, status: 200
else
render json: resource.errors, status: 401
end
end
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:accept_invitation, keys: [:first_name, :last_name])
end
end
my routes.rb
Rails.application.routes.draw do
# Using devise_token_auth for the API
mount_devise_token_auth_for 'User', at: 'auth',
defaults: { format: :json },
controllers: {
invitations: 'users/invitations'
}
...
end
I can see that the following routes exist:
PUT /auth/invitation(.:format) devise/invitations#update
POST /auth/invitation(.:format) devise/invitations#create
The problem I can see here though is that I'd expect the routes to look like this:
PUT /auth/invitation(.:format) users/invitations#update
POST /auth/invitation(.:format) users/invitations#create
This behavior might be caused by the devise_token_auth gem, which I'm not sure how to correct.
Again, when I submit this form I'd expect to be able to intercept the create method, so to then call User.invite! on all the users that are listed in the form. I've tried adding a byebug or binding.pry inside the create method but the code doesn't appear to execute, which means it's being bypassed.
The error message I see is:
Processing by Devise::InvitationsController#create as HTML
Parameters: {"_json"=>[{"email"=>"asd#ad.com", "firstName"=>"Test", "lastName"=>"Last"}], "invitation"=>{"_json"=>[{"email"=>"asd#ad.com", "firstName"=>"Test", "lastName"=>"Last"}]}}
Completed 500 Internal Server Error in 0ms (ActiveRecord: 0.0ms)
ArgumentError (wrong number of arguments (given 1, expected 0)):
Am I not supposed to pass the form data to the devise_invitable's create method? Any help is much appreciated.
Thanks in advance!
Try changing your routes to
mount_devise_token_auth_for 'User', at: 'auth', skip: [:invitations]
devise_for :users, path: "auth", only: [:invitations],
controllers: { invitations: 'users/invitations' }
For more information you can refer to this article
I am getting an error of MethodNotAllowedHttpException in compiled.php line 8931. It was working fine but suddenly it started throwing this error. I created a view in directory resources/views/main/templates/submit-for-sale.blade.php and defined its route in routes.php as:
Route::get('/submit-for-sale', function () {
return view('main.templates.submit-for-sale');
});
This view submit-for-sale.blade.php contains a form whose form tag looks like:
<form id="submit-sale-form" action="{{route('main.submit-sale-form')}}" method="post" class="form-horizontal>
{{ csrf_field() }}
.............
</form>
View is loading perfectly.
Route to post form in routes.php
Route::post('/submit-sale-form', ['as' => 'main.submit-sale-form', 'uses' => 'MainController#submitSaleForm']);
Code in MainController.php
public function submitSaleForm(Request $request)
{
$parameters = Input::get();
foreach ([
'name' => 'Name',
'email' => 'Email',
] as $key => $label) {
if (!isset($parameters[$key]) || empty($parameters[$key])) {
return response()->json(
[
'success' => false,
'error' => "{$label} cannot be empty",
]
);
}
}
}
It worked fine 2 - 3 times same code but suddenly it started throwing this error. Your help and suggestions will be highly appreciated.
Thank You.
Many popular websites have a username field that, usually, turns red or blue as a user types in a new character, based on whether or not the characters entered correspond to an already-existing user.
Say I have the following username field:
<%= f.text_field :username, id:"username" %>
How could that functionality be added to this field?
Instead of checking the username and making request on every key, you can use the blur method to check the user name once the user leaves the username field, or else you need it on every key you can use keyup itself,
Your Javascript,
$( "#username" ).keyup(function() { # you can also try, $( "#username" ).blur(function() {
$.ajax({
url: '<%= check_duplicate_username_path %>', # your own route
type: "GET",
data: { username: $('#username').val() }, // This goes to Controller in params hash, i.e. params[:username]
complete: function() {},
success: function(data, textStatus, xhr) {
// do something with your returned data //
if (data.available == false)
{
$('#username').addClass("error"); // style the class with your required css
}
},
error: function() {
alert("Your Ajax error! message")
}
});
});
The route can be taken as,
get '/check_duplicate_username' => 'users#check_duplicate_username', as: :check_duplicate_username
The controller action can be something like,
def check_duplicate_username
#user = User.where('username = ?',params[:username]).first
if #user.present?
render json: {:success => 0, :message => 'User exists', :user_available => true}
else
render json: {:success => 1, :message => 'User Does not exist', :user_available => false}
end
end
You have to fire ajax request on textbox event.
write ajax function and add new function to you user_controller with GET http method and return suitable response for check availabilty of your username.
Trigger an ajax request while writing on the text box. Like:
$( "#username" ).keyup(function() {
$.ajax({
type: "GET",
url: '<%= username_availability_path %>', # replace by your route
data: {name: $('#username').prop('value')}
});
});
Create a new route on your routes.rb file with type GET. In the method access the typed name using params[:name] and then check if exists or not. Then do whatever you want.
I have a method with a signautre in rails:
def my_function(some_variable)
end
I call the method from jquery get function like this:
$.get('/controller/my_function', {data: mydata}, function(){
});
But I get an error because I need to send the argument also.
How can I do that?
You need to define an action in your controller and call your function from that action
def my_action
my_function(params[:data])
end
and your jquery script will be calling my_action
$.get('/controller/my_action', {data: mydata}, function(){
});
as #Henry pointed out - in your javascript code data: xxxx is HTTP parameters being sent from the browser to your rails controller action on the server, rails puts all HTTP parameters into the params hash, so if you had
var data = {
first_name: "Joe",
last_name: "Smith"
}
$.get('/controller/some_action', data, function() {
// ...
access those in the params hash on the server
def some_action
logger.debug params.inspect
# => { :first_name => "Joe", :last_name => "Smith", :action => "some_action" }
user.first_name = params[:first_name]
# ...
end