Creating nested form in yii - yii

following this link I am trying to create a register form and connect the form to tables "user" and "profile". In my controller I have copied the same code as follows:
public function actionRegistration()
{
$form = new CForm('application.views.user.registerForm');
$form['user']->model = new Users;
$form['profile']->model = new Profile;
if($form->submitted('register') && $form->validate())
{
$user = $form['user']->model;
$profile = $form['profile']->model;
if($user->save(false))
{
$profile->userID = $user->id;
$profile->save(false);
$this->redirect(array('/user/login'));
}
}
var_dump($form->submitted('register'));
$this->render('registration', array('form'=>$form));
}
I actually don't know what is $form->submitted('register') for and why it returns false!
Can anyone explain me what is that and what is 'register' value which is passed to the submitted function!? Also why it should return false while posting the form?

the traditional way to get form data is
$model = new User;
if(isset($_POST["register"])){ //get the form data
...
$model->attributes=$_POST["register"]; //set model's attributes
...
}
for more examples you can go: http://www.yiiframework.com/doc/blog/1.1/en/comment.create

Related

User getting created before custom user validator runs in Identity core

I am using identity core for user management in .net core 3.1 web api. Now, I want to check the users email for something and if it meets the requirement only then he will be created. The code below tells a lot about what I want to achieve
I have a custom user validator as below:
public class CustomEmailValidator<TUser> : IUserValidator<TUser>
where TUser : User
{
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager,
TUser user)
{
User userFromEmail = null;
if(!string.IsNullOrEmpty(user.Email))
userFromEmail = manager.FindByEmailAsync(user.Email).Result;
if (userFromEmail == null)
return Task.FromResult(IdentityResult.Success);
return Task.FromResult(
IdentityResult.Failed(new IdentityError
{
Code = "Err",
Description = "You are already registered with us."
}));
}
}
I add the validator in my startup as below:
services.AddDbContext<DataContext>(x => x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
IdentityBuilder builder = services.AddIdentityCore<User>(opt =>
{
opt.User.RequireUniqueEmail = false;
opt.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz0123456789._-";
opt.Password.RequireDigit = true;
opt.Password.RequiredLength = 6;
opt.Password.RequireNonAlphanumeric = true;
opt.Password.RequireUppercase = true;
opt.Password.RequireLowercase = true;
})
.AddUserValidator<CustomEmailValidator<User>>();
builder = new IdentityBuilder(builder.UserType, typeof(Role), builder.Services);
builder.AddEntityFrameworkStores<DataContext>();
builder.AddRoleValidator<RoleValidator<Role>>();
builder.AddRoleManager<RoleManager<Role>>();
builder.AddSignInManager<SignInManager<User>>();
As can be seen, I want to use the default user validation and my custom validation too. The problem being the user gets created right after the default validation and the email always turns out as exists in my custom validation. I don't really want to override my default validations.
Creating the user as below:
[HttpPost("Register")]
public async Task<IActionResult> Register(UserForRegisterDto userForRegister)
{
var userToCreate = _mapper.Map<User>(userForRegister);
var result = await _userManager.CreateAsync(userToCreate, userForRegister.Password);
if (result.Succeeded)
{
var roleresult = await _userManager.AddToRoleAsync(userToCreate, "Member");
return Ok(roleresult);
}
return BadRequest(result.Errors);
}
Note This is not my actual use case. I know I can check for unique email in my default validation by making opt.User.RequireUniqueEmail = true. This is just to clear a concept for further development.
Update After further debugging, I see that the custom validation method is called twice. Once before user creation and once after creation for some reason. I insert a new unique email and the custom validation passes success and after user creation, custom validation is called again and find the email registered already and throws an error message. This is weird
Found out that AddToRoleAsync was calling the custom validator again and was finding the user present in the table. Had to include a check whether the user found in the table with the same email is the same as user getting getting updated.
Code below:
public class CustomEmailValidator<TUser> : IUserValidator<TUser>
where TUser : User
{
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager,
TUser user)
{
User userFromEmail = null;
if(!string.IsNullOrEmpty(user.Email))
userFromEmail = manager.FindByEmailAsync(user.Email).Result;
if (userFromEmail == null)
return Task.FromResult(IdentityResult.Success);
else {
if(string.Equals(userFromEmail.Id, user.Id))
{
return Task.FromResult(IdentityResult.Success);
}
}
return Task.FromResult(
IdentityResult.Failed(new IdentityError
{
Code = "Err",
Description = "You are already registered with us."
}));
}
}
This should help a lot of people

yii generatepasswordhash not working

For some strange reasons, i am finding it difficult to login with yii->$app->generatePasswordhash($password.) I have a backedn where i register users and also change password. Users can login successfully when i created them but when i edit user password, the system keeps telling me invalid username or password. Below is my code.
//Model
Class Adminuser extends ActiveRecord
{
public $resetpassword
public function activateuser($id,$newpassword)
{
//echo Yii::$app->security->generatePasswordHash($newpassword); exit;
$user = Adminuser::find()->where(['id' =>$id])->one();
$user->status = self::SET_STATUS;
$user->password_reset_token = null;
$user->password = Admin::genPassword($this->resetpassword); // this returns yii::$app->security->generatePasswordHash($password)
return $user->save();
}
}
//controller action
public function actionActivate($id)
{
$model = new Adminuser();
$model->scenario = 'adminactivate';
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
if($model->activateuser($id,$model->password))
{
//send a mail
Yii::$app->session->setFlash('success', 'New user has been activated.');
return $this->redirect(['index']);
}
else
$errors = $model->errors;
}
return $this->render('activate', [
'model' => $model,
]);
}
Please i need help
Your activateuser() method has $newpassword as an incoming parameter. Anyway you are using $this->resetpassword in Admin::genPassword(). Looks like that is the reason of the problem and all your passwords are generated based on null value. So try to use $user->password = Admin::genPassword($newpassword); instead.

Enhance Exporting Report to csv/excel using yii

My system is working fine for small database and my report is generates from at least 5 table of phpmyadmin after certain limit of data load '500 internal server error' will come.I want enhance exporting a report to csv/excel from phpmyadmin using yii for larger database.
I use this extension to export to CSV. http://www.yiiframework.com/extension/csvexport/
I have created an action that I can attach to any controller that I need to export.
<?php
class Csv extends CAction {
public $field_list;
public function run() {
$controller = $this->getController();
/* Disable the logging because it should not run on this function */
foreach (\Yii::app()->log->routes as $route) {
if ($route instanceof \CWebLogRoute) {
$route->enabled = false;
}
}
\Yii::import('core.extensions.ECSVExport.ECSVExport');
//use the existing filters
$model_name = $controller->modelName();
$model = new $model_name('search');
$dataProvider = $model->search();
$criteria = $dataProvider->criteria;
//remove the pagination
$dataProvider->setPagination(false);
//changing the criteria to only select what we want
$criteria->select = implode(',', $this->field_list);
$dataProvider->setCriteria($criteria);
//export to CSV
$csv = new \ECSVExport($dataProvider);
if(isset($_GET['test'])) {
echo $csv->toCSV();
} else {
\Yii::app()->getRequest()->sendFile($controller->modelName() . '_'.date('Y-m-d').'.csv', $csv->toCSV(), "text/csv", false);
exit();
}
}
}
field_list are the fields that I need to export.
For the controller I add
/**
* #return array actions to be mapped to this controller
*/
public function actions(){
return array(
'csv'=>array(
'class'=>'core.components.actions.Csv',
'field_list'=>array('t.id', 't.name', 't.status'),
),
);
}
/**
I use the same search as in the controller because it suits me and because I use http://www.yiiframework.com/extension/remember-filters-gridview/ I actually can export exactly what is on the screen. Change the field list to what you need. Remember to give access to the csvAction function.
You can use yiiexcell extension for that: http://www.yiiframework.com/extension/yiiexcel/ it is simple wrapper for PHPExcel http://phpexcel.codeplex.com/releases/view/107442

Edit profile page with 3 tables - Yii frameworks

I am new on Yii framework, so please i need some help where to start.
What i need is, action and module to display a form to a user, which his will be able to edit is own profile (with profile picture), so i have 3 table
user_account || user_personal || user_general
how can i build a form that insert and update those 3 table at once?
i try this:
This is what i did, but its still not saving the data even into 1 table.
public function actionUpdate() {
$model = new ProfileUpdate();
if(isset($_POST['ProfileUpdate']))
{
$model->attributes = $_POST['ProfileUpdate'];
if($model->validate())
{
$account = new Profile();
$account->userid = Yii::app()->user->id;
$account->name = $model->name;
$account->website = $model->website;
$account->category = $model->category;
$account->save();
$this->redirect('profile');
}
}
model:
class Profile extends CActiveRecord
{
public $userid;
public $name;
public $website;
public $category;
public static function model()
{
return parent::model(__CLASS__);
}
public function tableName()
{
return 'userinfo';
}
public function primaryKey()
{
return 'id';
}
public static function userExists($user)
{
return self::model()->countByAttributes( array('username'=>$user) ) > 0;
}
}
You can use all three models in a single function
for example:
In create function
$model_account = new user_account;
$model_personal= new user_personal;
$model_general = new user_general;
$this->render('create',array(
'model_account'=>$model_account, 'model_personal' => $model_personal, 'model_general' => $model_general
));
here the all three models pass by render to create page.
in form page you can use the every model attributes as fields
Like this
echo $form->textField($model_account,'account_name');
echo $form->textField($model_personal,'email');
echo $form->textField($model_general,'address');
In create function / Update function
$model_account->attributes = $_POST['user_account'];
$model_personal->attributes = $_POST['user_personal'];
$model_general->attributes = $_POST['user_general'];
if($model_account->validate() && $model_personal->validate() && $model_general->validate())
{
$model_account->save();
$model_personal->save();
$model_general->save();
}

Developing a user search page in asp.net MVC 4 using Activedirectory

I am developing a ASp.net MVC 4 Intranet application.
So when I login I can see my account getting displayed.
Now to authorise the account I have developed a custom authorization provider by following the below link :http://www.codeproject.com/Articles/607392/Custom-Role-Providers
now my problem is after the initial authorization I add myself to superadmin role who has acess to Index.cstml where there is a text box and a search button.
basically here on this page I want to search user from active directory and display it so that I can assign some roles to the user.
How do I do that?
please provide the code how to connect to Active directory, where to provide the connection string for LDAP and the excat method which will help me in searching the user from active directory and dispaly it on the index view.
Below is what I have tried:
Entities: ADuser->property UserLoginName ,UserDispalyName
Model: has a method that returns the serch user from AD and add it to entity
public class ADUserModel
{
public List<ADUser> GetUserFromAD(string name) //Get Network Users (AD)
{
//ArrayList searchUser = new ArrayList();
var domainContext = new PrincipalContext(ContextType.Domain);
var groupPrincipal = GroupPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, "Domain Users");
UserPrincipal user = new UserPrincipal(domainContext);
user.Enabled = true;
user.Name = name + "*";
PrincipalSearcher pS = new PrincipalSearcher();
pS.QueryFilter = user;
PrincipalSearchResult<Principal> results = pS.FindAll();
var list = new List<ADUser>();
//searchUser.Add(results);
foreach (var item in results)
{
var users = new ADUser();
users.UserLoginName = item.SamAccountName;
users.UserDisplayName = item.DisplayName;
list.Add(users);
//searchUser.Add(users);
}
return list;
}
}
Controller: has an action method that will return the view
public class ManageUsersController : Controller
{
//
// GET: /ManageUsers/
public ActionResult Search(string searchString)
{
ADUserModel model = new ADUserModel();
var list = new List<ADUser>();
if (searchString != null)
{
list = model.GetUserFromAD(searchString);
return View("Search");
}
else
{
return View("Search");
}
}
}
View: Search.cshtml
#model IEnumerable<FMSKendoUI.Entities.ADUser>
#{
ViewBag.Title = "Search";
}
#Html.BeginForm("Search","ManageUsers",FormMethod.Get)
{
#Html.TextBox("Search") <input type="submit" value="Search" />
}
#if (Model.Count() != 0)
{
#foreach (FMSKendoUI.Entities.ADUser model in Model)
{
#Html.DisplayFor(m => model.UserLoginName)
#Html.DisplayFor(m => model.UserDisplayName)
}
}
But my problem is:
During my first page load I would need only the textbox and search button.
and on the search button click I need to call the controller search method.
Since during the first time my entity is null it is giving me null exception.
my entity get loaded only after I provide the search string and call the model method.
I am unable to achieve this. Please help me on this.