I created an extension which allows the user to sign up via the frontend. I couldn't use working ones because the client requested special tasks.
This is the code which detects taken usernames.
public function createAction(\Vendor\Feregister\Domain\Model\FeUserX $newFeUserX)
{
$uname = $newFeUserX->getUsername();
$select_query = '*';
$from_table = 'fe_users';
$where_clause = 'username="'.$uname.'"';
$test = $GLOBALS['TYPO3_DB']->exec_SELECTquery($select_query, $from_table, $where_clause);
if ($GLOBALS['TYPO3_DB']->sql_num_rows($test)) {
$this->addFlashMessage('Username is already taken.', '', \TYPO3\CMS\Core\Messaging\AbstractMessage::ERROR);
$this->redirect('new');
} else {
// do stuff when the username isn't taken yet
}
}
But unfortunately and obivously, when redirecting back to the new action, the fields are empty again.
Is there a way to pass the arguments back to the new action and fill the forms?
Yes, and extbase has a standardized way to do this. It works as follows:
If an action is called, its parameters are validated, except if validation is switched off in the doc comments. If validation fails, the previous action (the one whose view contained the submitted form) is called again, with the same parameters.
You can use this as follows:
/**
* #param \Vendor\Feregister\Domain\Model\FeUserX $newFeUserX
* #ignorevalidation $newFeUserX
*/
public function newAction(\Vendor\Feregister\Domain\Model\FeUserX $newFeUserX = null)
{
$this->view->assign('user', $newFeUserX);
// View renders form with name="newFeUserX" and object="{user}",
// action="create", fields use the property-attribute to fill
// in values and field names.
}
/**
* #param \Vendor\Feregister\Domain\Model\FeUserX $newFeUserX
* #validate $newFeUserX \Vendor\Feregister\Validator\UsernameDoesNotExistValidator
*/
public function createAction(\Vendor\Feregister\Domain\Model\FeUserX $newFeUserX)
{
// Do something with the user - you can be sure the username
// is not yet taken
}
The class \Vendor\Feregister\Validator\UsernameDoesNotExistValidator is a custom validator that implements the ValidatorInterface, or extends AbstractValidator. It should basically do the validation you are doing in your createAction (maybe using a repository instead of $GLOBALS['TYPO3_DB']). A validator returns errors in a standard way, making it easier to show nice error messages and localize them.
If the validation fails, extbase will try to forward to the action that rendered the form, in this case the new-action. In this case, it will work, because of the #ignorevalidation annotation on the new-action.
In addition, information about validation errors are available in the view, you can render them using the ViewHelper f:form.validationResults.
Related
I have a model in Yii that contains an array of another model type. I am then trying to validate that no duplicate emails are filled out in a form, where you can fill out for n number of persons at the same time.
My current approach is to trigger a custom validation of the "outer" model that holds all the entrants, however, that model is not accessible in the view, only the array of entrants is, and if I then trigger the error on the "outer" model, it will not be displayed to the user. Therefore I would like to trigger it for the first entrant that violates the rule, but how do I go about doing that?
My code that attempts this, looks like this so far:
/*
* Custom validation rule to hinder the same e-mail being used twice.
*/
public function noRepeatingEmails($attribute, $params)
{
if (!isset($attribute)) return;
$emails = array();
foreach($this->$attribute as $user)
{
if (isset($user) && strlen(trim($user->email)) != 0)
{
$emailToAdd = strtolower(trim($user->email));
if (in_array($emailToAdd, $emails))
{
$this->addError($user, '<my error message>');
return;
}
else
{
$emails[] = $emailToAdd;
}
}
}
}
This only results in a code 500 error though:
Illegal offset type
I presume that is because it is looking for the property "user" in my model, rather than adding an error to "$user" object.
How do I best accomplish this?
I have a .NET background, so I am probably doing loads wrong here however.
If I understood correctly from your comment, you want to validate your model before saving it. For this purpose, CActiveRecord provides beforeSave() method. You need to put this method inside your model:
protected function beforeSave()
{
if(parent::beforeSave())
{
if(/* Your validation goes here*/)
return true;
else
return false
}
else
return false;
}
When the result of this method is true, save() method will be called. Otherwise save() method won't be called and therefore no record will be saved into your database.
I'm working with Restler and the OAuth2 module written by Brent Shaffer. What I want to do is determine the user from the token they send, inside my app classes, not just the OAuth2Server classes.
There are two methods that I can see of doing this. Hopefully this explains what I am trying to do.
Method 1: I don't particularly like this method, but it works.
POST /v1/token
Returns my token including the user_id, for example
{
"access_token":"282090609b3407d981c2bea633a39739595ba426",
"expires_in":3600,
"token_type":"Bearer",
"scope":"basic",
"refresh_token":"b60a4e5f759168df857342380f3550bc120b6f9d",
"user_id": 5
}
Now that the client knows the user_id, it is sent with my request:
GET /v1/dashboard?id=5
My __isAllowed method takes care of checking that the user hasn't altered the id, requesting info that isn't theirs.
public function __isAllowed() {
$token = static::$server->getAccessTokenData(Request::createFromGlobals());
return (($token['user_id'] > 0) && ($token['user_id'] === $_GET['id']) && ($token['group_id'] == self::$group_id));
}
Dashboard class looks like this:
/*
* #version 1
* #access protected
*/
class Dashboard {
/**
* #param int $id Customer ID {#from query}
* #return type
*/
public function index($id) {
$s = Dao\ViewCustomerDaoObject::findId($id);
return array_merge($s->toJSON(), $widgets);
}
}
This is how I would prefer to be calling the API:
GET /v1/dashboard
When I request the above, join the oauth2_token table to my dashboard table. I think this might be a bit of a hack and I don't want this to cause problems down the road.
The info is already available in the OAuth2Server instance, as the OAuth2Server class does determine if the correct token is used and what their user_id is.
Can someone please guide me in the right direction for handling this situation, particularly with Restler?
I actually figured this out myself.
In the OAuth2Server->__isAllowed method, you must set the UserId in the static User class.
public function __isAllowed() {
$token = static::$server->getAccessTokenData(Request::createFromGlobals());
// If the user_id is valid, set static user class.
// *** This is not production code, add more checks here if you use this!
if ($token['user_id'] > 0) {
\Luracast\Restler\User::init();
\Luracast\Restler\User::setUniqueIdentifier($token['user_id']);
return true;
}
return false;
}
Now you can get the currently authenticated user in your class by calling:
\Luracast\Restler\User::getUniqueIdentifier(true)
In my Application the user can create Custom Fields for some entities and then set the values for this custom fields for each entity object when i display a form.
The implementation is like this:
1º) I created a Interface for the forms, and the forms that i want implement this Interface.
2º) I created a form extension for all forms:
app_core_form_builder.form_extension:
class: App\Core\Bundle\FormBuilderBundle\Form\FormExtension
arguments: ["#service_container", "#doctrine.orm.entity_manager"]
tags:
- { name: form.type_extension, alias: form }
3º) In this extension if the form implements the interface referenced in the step 1 i add a EventSubscriber:
if($formType instanceof \App\Core\Bundle\FormBuilderBundle\Model\IAllowCustomFieldsdInterface){
$builder->addEventSubscriber(new FormSubscriber($this->container, $this->em));
}
4º) This Form Subscriber subscribes the preSetData FormEvent. In this method i get the Entity associated with the form and i get all custom fields created for it.
Then i add this fields to the form with the help of Symfony2 Form Type.
Everything goes well, and when i display my form the custom fields are rendered correct. Just for the record, when i save the form the values inserted in the custom fields also are store well.
public function preSetData(FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
// During form creation setData() is called with null as an argument
// by the FormBuilder constructor. You're only concerned with when
// setData is called with an actual Entity object in it (whether new
// or fetched with Doctrine). This if statement lets you skip right
// over the null condition.
if (null === $data) {
return;
}
$formEntity = $form->getConfig()->getType()->getInnerType()->getEntity();
$DbEntity = $this->em->getRepository('AppCoreSchemaBundle:DbEntity')->findOneBy(array('id' => $formEntity));
if ($DbEntity && $DbEntity->getAllowCustomFields()) {
$organization = $this->container->get('app_user.user_manager')->getCurrentOrganization();
if (!$organization) {
throw $this->createNotFoundException('Unable to find Organization entity.');
}
$params = array(
'organization' => $organization,
'entity' => $DbEntity,
);
$entities = $this->em->getRepository('AppCoreSchemaBundle:DbCustomField')->getAll($params);
# RUN BY ALL CUSTOM FIELDS AND ADD APPROPRIATE FIELD TYPES AND VALIDATORS
foreach ($entities as $customField) {
# configurate customfield
FieldConfiguration::configurate($customField, $form);
# THE PROBLEM IS HERE
# IF OBJECT IS NOT NULL THEN MAKE SET DATA FOR APPROPRIATED FIELD
if ($data->getId()) {
$filters = array(
'custom_field' => $customField,
'object' => $data->getId(),
);
$DbCustomFieldValue = $this->em->getRepository('UebCoreSchemaBundle:DbCustomFieldValue')->getFieldValue($filters);
if ($DbCustomFieldValue) {
$form[$customField->getFieldAlias()]->setData($DbCustomFieldValue->getValue());
} else {
$form[$customField->getFieldAlias()]->setData(array());
}
}
}
}
}
The problem is when i try to edit a form. if you look at the part in the code above where says "THE PROBLEM IS HERE" you can understand.
If the object of the form has an ID, then i will get the values stored for the custom fields of that object, and i call $form[field_alias']->setData(value returned from database that is mapped as type Array).
But this dont work, and the Data is not set for the fields. But if in my controller i do the same, the data is set properly.
Does anybody have an idea where the problem can be? Can't i set the data in preSetData Event?
EDITED
The value field from the Entity DbCustomField is mapped in this way:
/**
* #var string
*
* #ORM\Column(name="value", type="array", nullable=true)
*/
protected $value;
`
var_dump($DbCustomFieldValue) -> object(Ueb\Core\Bundle\SchemaBundle\Entity\DbCustomFieldValue)
var_dump(DbCustomFieldValue->getValue())
-> string(11) "bruno valor"
But even if i try something like:
var_dump($customField->getFieldAlias()); = string(21) "testebruno-1383147874"
$form[$customField->getFieldAlias()]->setData('example1'); it doesnt work.
But in my controller if i do the following for the fieldAlias above:
$form['testebruno-1383147874']->setData('example2');
-> it does work
Any idea?
As metalvarez suggested in his/her comment and working as expected, use the postSetData event instead of the preSetData one:
public function postSetData(FormEvent $event) {
// ...
}
The preSetData event method is called before populating the form with default values, then Symfony2 will set the data and it may change from what you set before, thus the use of postSetData instead.
Figure from the doc
In my cake PHP application, I have a edit form where "email" field is readonly that means user can not update it.
NOw if I think according to security point of view, user can update the field by 'firebug' or some other browser plugins.
I am using $this->User->save($this->data) to save the updated data. By this function Email can be also be updated.
Do we have any way in cake php so that I can prevent this field to be update, like by passing here a argument or something like this?
You can simply remove the email field from $this->data:
unset($this->data['User']['email']);
$this->User->save($this->data);
You could do something like:
$dontUpdateField = array('email');
$this->Model->save(
$this->data,
true,
array_diff(array_keys($this->Model->schema()),$dontUpdateField)
);
If security is a concern, simply reject any data that has unexpected values. In cake you could do this, but it can be adapted for any framework/cms
/**
* Checks input array against array of expected values.
*
* Checks single dimension input array against array of expected values.
* For best results put this is in app_controller.
*
* #param array $data - 1 dimensional array of values received from untrusted source
* #param array $expected - list of expected fields
* #return boolean - true if all fields are expected, false if any field is unexpected.
*/
protected function _checkInput($data,$expected){
foreach(array_keys($data) as $key){
if (!in_array($key,$expected)){
return;
}
}
return true;
}
/**
* edit method.
*
* put this in <Model>_controller
* #param string $id
* #return void
* #todo create errors controller to handle incorrect requests
* #todo configure htaccess and Config/routes.php to redirect errors to errors controller
* #todo setup log functionality to record hack attempts
* #todo populate $expected with fields relevant to current model
*/
function edit($id=null){
$expected = ('expectedVal1', 'expectedVal2');
$this->Model->id = $id;
if (!$this->Model->exists()) {
throw new NotFoundException(__('Invalid model'));
}
if ($this->request->is('post')) {
if (!$this->_checkData($this->request->data['Model'], $expected)) {
//log the ip address and time
//redirect to somewhere safe
$this->redirect(array('controller'=>'errors','action'=>'view', 405);
}
if ($this->Model->save($this->request->data)) {
//do post save routines
//redirect as necessary
}
else {
$this->Session->setFlash(__('The model could not be saved. Please, try again.'));
}
}
$this->set('model',$this->Model->read($expected,$id));
}
You can use the security component and make the email hidden. While using this component, hidden fields cant be changed or cake will blackhole the form.
http://book.cakephp.org/1.3/en/view/1296/Security-Component
If your application is public it is strongly recommended that you use security, otherwise it is kinda trivial to inject data in your models by submitting extra fields on the form and when you do $this->Model->save($this->data)) the extra fields are saved, unless you do the extra work of validating every field of $this->data;
I would like to filter some fields in my form with strtolower() function. Unfortunately I can't find any example of doing that.
How can I write such filter, that will lowercase the input, check the database if element exists and then decide wheter to add the record or not?
1) new project custom validator (we will use it like value filter here):
/lib/validator/MyProjectStringLowerCase.class.php
<?php
class MyProjectStringLowerCase extends sfValidatorBase
{
/**
* #see sfValidatorBase
*/
protected function doClean($value)
{
return strtolower($value);
}
}
2) bound it to field:
$this->setWidget('my_field_name', new sfWidgetFormInputText());
$this->validatorSchema['my_field_name'] = new MyProjectStringLowerCase();
If you have some validator on that field already, you can merge them into combined validators this way:
$this->validatorSchema['my_field_name'] = new sfValidatorAnd(array(
$this->validatorSchema['my_field_name'], // the original field validator
new MyProjectStringLowerCase(),
));
The combined validators order influent how value will flow trough them, so if you want to have value filtrated in second validation, set MyProjectStringLowerCase as the first one.
There are 2 differences between this approach and using post processing (like doSave() for instance):
the value here will be filtered after each send (and will show
filtered in displaying of form errors)
You can reuse it very cleanly and easily in other fields or forms in
your project
In your Form, you can override the doSave() method to do any manual interventions that you need to do that aren't completed by the form validation methods.
For example:
public function doSave($con = null) {
$employee = $this->getObject();
$values = $this->getValues();
// do your filter
$this->values['name'] = strtolower($values['name']);
parent::doSave($con);
}