Yii2 DetailView widget - access data with role - yii

This is the query I am using to find user:
$model = User::find()->with('role')->where(['id' => $id])->one();
And the DetailView widget:
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'first_name',
'email:email',
'password',
],
]) ?>
I need to access role name. How can I do this?

You could write a getRole in your user model:
public function getRole()
{
$role = Yii::app()->db->createCommand()
->select('itemname')
->from('AuthAssignment')
->where('userid=:id', array(':id'=>$this->id))
->queryScalar();
return $role;
}
And then use direct in your widget $model->getRole()
Hope it will solve your problem.

Related

diiimonn/yii2-widget-checkbox-multiple need to implement

<?= $form->field($model, 'template_id')->widget(CheckboxMultiple::className(), [
'dataAttribute' => 'template_id',
scriptOptions' => [450=>'abc',452=>'xyz'],
'placeholder' => Yii::t('app', 'Select ...'),
]) ?>
I try to implement this => https://github.com/diiimonn/yii2-widget-checkbox-multiple
but i am not getting the result.
When I have done code I am getting blank page.
What is the mistake here ?
Are there any errors in js console? I think that problem seems to be with assets bundle of CheckboxMultiple widgets. There are missing depends property for JQuery Assets. Try to register jQuery manually before render widget. dataAttribute property also seems to be unknown in latest version of this widget... This works for me:
$this->registerAssetBundle(yii\web\JqueryAsset::className());
echo $form->field($model, 'templates')->widget(CheckboxMultiple::className(), [
'attributeLabel' => 'templates',
'placeholder' => Yii::t('app', 'Select ...'),
'ajax' => [
'url' => Url::toRoute(['/site/templates']),
],
]);
Where templates attribute is relation in model, like this:
public function getTemplates()
{
return $this->hasMany(TemplateModel::className(), ['owner_id' => 'id']);
}
and in SiteController action templates:
public function actionTemplates()
{
Yii::$app->response->format = 'json';
$json = new \stdClass();
$query = new Query();
$query->select([
'id' => 'id',
'text' => 'name'
]);
$query->from(TemplateModel::tableName());
if ($search = Yii::$app->request->post('search', '')) {
$query->where(['like', 'name', $search]);
}
$query->orderBy([
'name' => SORT_ASC
]);
if ($itemsId = Yii::$app->request->post('itemsId', [])) {
$query->andWhere(['not in', 'id', $itemsId]);
}
$query->limit(20);
$command = $query->createCommand();
$data = $command->queryAll();
$json->results = array_values($data);
return $json;
}
In this example I use table templates with columns: id, name and owner_id.
You should modify above script to yours names of models and attributes.

CakePHP3 login auth to admin side always false

I know theres a few similar question, but i dont find answer which resolved my problem. My problem is authentication is always false. This is my code:
My AdminController
class AdminController extends AppController
{
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'userModel' => 'Admins',
'fields' => [
'username' => 'email',
'password' => 'password'
]
]
],
'loginAction' => [
'controller' => 'Innerlogin',
]
]);
}
}
InnerloginController:
namespace App\Controller\Admin;
use App\Controller\AdminController;
use Cake\Event\Event;
class InnerloginController extends AdminController
{
public function index()
{
$this->loadModel('Admins');
if( $this->request->is('POST') )
{
$admin = $this->Auth->identify();
if( $admin )
{
$this->Auth->setUser( $admin );
return $this->redirect([ 'controller' => 'admin' ]);
}
else
{
$this->Flash->error( 'Incorrect username or password' );
}
}
}
}
Entity/Admin
namespace App\Model\Entity;
use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Entity;
class Admin extends Entity
{
protected $_accessible = [
'*' => true,
'admins_id' => false
];
protected function _setPassword( $password )
{
return( new DefaultPasswordHasher() )->hash( $password );
}
}
Innerlogin/index.ctp
<?= $this->Form->create() ?>
<?= $this->Form->input('username', ['label' => false, 'class' => 'input-cst' ]) ?>
<?= $this->Form->input('password', ['label' => false, 'class' => 'input-cst', 'type' => 'password' ]) ?>
<?= $this->Form->button(__('Login'), ['class' => 'btn btn-success right']) ?>
<?= $this->Form->end() ?>
My admins table
Field Type Null Key Default Extra
admins_id int(11) NO PRI NULL auto_increment
name varchar(40) NO NULL
email varchar(50) NO NULL
password varchar(255) NO NULL
sex varchar(1) NO NULL
birthday varchar(40) NO NULL
created varchar(40) NO NULL
deleted varchar(40) NO 0
Any idea what am i doing wrong ?
And by the way i have another related question. I want to make two authentication, one for users another for admins (I dont want to keep role column in one table and check is user or admin). Can i do that? It will be works properly ?
The fields option is not a "input field to database column" map, it is used to configure what fields/columns to use instead!
You have configured the form authenticator to use the email field/column instead of the username field/column, and so you have to change that in your form accordingly to use email too, ie
<?= $this->Form->input('email', ['label' => false, 'class' => 'input-cst' ]) ?>

How to check if an item belongs to a hasMany association before updating in CakePHP 3.2

What I am trying to do:
I have Estimates and Estimates have items "EstimateItems". When updating a Estimate the EstimateItems changed should update. (using patchEntity)
This is working with my current code, my only problem is that other users can edit the Estimate Items of other users when changing the primary key of a EstimateItem in the edit form, because when patching the existing EstimateItems CakePHP only looks at the primary key of the EstimateItem and doesn't take the association in consideration. Also it's still possible to edit the estimate_id of a EstimateItem while $protected estimate_id is set to false.
So what I need is CakePHP to validate that this EstimateItem belongs to the current association before updating or while trying to update.
I hope some one can tell me what I am doing wrong or what I am missing.
Current Query
UPDATE
estimate_items
SET
data = 'Test Query 1',
amount = 123456789,
tax_id = 3
WHERE
id = 3
Expected Query
UPDATE
estimate_items
SET
data = 'Test Query 1',
amount = 123456789,
tax_id = 3
WHERE
id = 3 AND estimate_id = 1
Current code:
Estimates -> Edit.ctp
<?php $this->Form->templates($formTemplates['default']); ?>
<?= $this->Form->create($estimate, ['enctype' => 'multipart/form-data']) ?>
<fieldset>
<legend><?= __('Offerte') ?></legend>
<?= $this->Form->input('reference', ['label' => __('#Referentie'), 'autocomplete' => 'off']) ?>
<?= $this->Form->input('client_id',
[
'type' => 'select',
'empty' => true,
'label' => __('Klant'),
'options' => $clients
]
)
?>
<?php
foreach($estimate->estimate_items as $key => $item){
?>
<div class="item">
<legend>Item</legend>
<?= $this->Form->hidden('estimate_items.'. $key .'.id') ?>
<?= $this->Form->input('estimate_items.'. $key .'.data', ['type' => 'text', 'label' => __('Beschrijving')]) ?>
<?= $this->Form->input('estimate_items.'. $key .'.amount', ['type' => 'text', 'label' => __('Bedrag'), 'class' => 'input-date']) ?>
<?= $this->Form->input('estimate_items.'. $key .'.tax_id',
[
'type' => 'select',
'empty' => true,
'label' => __('Belasting type'),
'options' => $taxes
]
)
?>
</div>
<?php
}
?>
<legend>Informatie</legend>
<?= $this->Form->input('date', ['type' => 'text', 'label' => __('Offerte datum'), 'autocomplete' => 'off']) ?>
<?= $this->Form->input('expiration', ['type' => 'text', 'label' => __('Verloop datum'), 'autocomplete' => 'off']) ?>
</fieldset>
<?= $this->Form->button(__('Save')); ?>
<?= $this->Form->end() ?>
Estimates Controller
namespace App\Controller;
use App\Controller\AppController;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
class EstimatesController extends AppController
{
public function edit($id){
$associated = ['EstimateItems'];
$estimate = $this->Estimates->get($id, ['contain' => $associated]);
$this->log($estimate);
if($this->request->is(['patch', 'post', 'put'])) {
$estimate = $this->Estimates->patchEntity($estimate, $this->request->data, [
'associated' => $associated
]);
$estimate->total = '0';
$this->log($estimate);
$this->log($this->request->data);
if($this->Estimates->save($estimate, ['associated' => $associated])){
$this->Flash->success(__('De offerte is bijgewerkt'));
return $this->redirect(['action' => 'index']);
}
}
$this->set('taxes', $this->Estimates->Taxes->find('list', [ 'keyField' => 'id', 'valueField' => 'tax_name' ]));
$this->set('clients', $this->Estimates->Clients->find('list', [ 'keyField' => 'id', 'valueField' => 'companyname' ]));
$this->set('estimate', $estimate);
}
}
EstimatesTable
<?php
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\ORM\RulesChecker;
use Cake\ORM\Rule\IsUnique;
class EstimatesTable extends Table
{
public function initialize(array $config)
{
$this->addAssociations([
'hasOne' => ['Taxes'],
'belongsTo' => ['Companies', 'Clients'],
'hasMany' => ['EstimateItems' => [
'foreignKey' => 'estimate_id'
]]
]);
}
public function buildRules(RulesChecker $rules){
// A Node however should in addition also always reference a Site.
$rules->add($rules->existsIn(['estimate_id'], 'EstimateItems'));
return $rules;
}
}
EstimateItem Entity
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class EstimateItem extends Entity
{
protected $_accessible = [
'*' => false,
'data' => true,
'amount' => true,
'tax_id' => true,
'unit_id' => true
];
}
EstimateItemsTable
<?php
namespace App\Model\Table;
use Cake\ORM\Entity;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\ORM\RulesChecker;
use Cake\ORM\Rule\IsUnique;
use Cake\ORM\Query;
class EstimateItemsTable extends Table
{
public function initialize(array $config)
{
$this->addAssociations([
'belongsTo' => ['Estimates' => ['foreignKey' => 'estimate_id']],
'hasOne' => ['Taxes' => ['foreignKey' => 'tax_id']]
]);
}
Estimate Entity
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Estimate extends Entity
{
/**
* Fields that can be mass assigned using newEntity() or patchEntity().
*
* Note that when '*' is set to true, this allows all unspecified fields to
* be mass assigned. For security purposes, it is advised to set '*' to false
* (or remove it), and explicitly make individual fields accessible as needed.
*
* #var array
*/
protected $_accessible = [
'*' => false,
'id' => false,
];
}
Looks like you need to implement the check via a beforeSave callback function if you don't trust that setting fields hidden / not editable is not enough.
In the callBack you can check if the relation was already there before before you overwrite them with a wrongly edited value.
Markstory Replied to me on github with a solution credits to him:
https://github.com/cakephp/cakephp/issues/9527
In Model/Table/EstimateItemsTable.php
<?php
namespace App\Model\Table;
use Cake\ORM\RulesChecker;
....
class EstimateItemsTable extends Table
{
....
public function buildRules(RulesChecker $rules){
$rules->addUpdate(function($entity) {
if (!$entity->dirty('estimate_id')) {
return true;
}
return $entity->estimate_id == $entity->getOriginal('estimate_id');
}, 'ownership', ['errorField' => 'estimate_id']);
return $rules;
}
}

Why i getting an error "Call to a member function formName() on a non-object"

i try to save multilanguaged content
My About model
...
public function rules() {
return [
[['status', 'date_update', 'date_create'], 'integer'],
[['date_update', 'date_create'], 'required'],
];
}
...
public function getContent($lang_id = null) {
$lang_id = ($lang_id === null) ? Lang::getCurrent()->id : $lang_id;
return $this->hasOne(AboutLang::className(), ['post_id' => 'id'])->where('lang_id = :lang_id', [':lang_id' => $lang_id]);
}
My AboutLang model
public function rules()
{
return [
[['post_id', 'lang_id', 'title', 'content'], 'required'],
[['post_id', 'lang_id'], 'integer'],
[['title', 'content'], 'string'],
];
}
My About controller
public function actionCreate()
{
$model = new About();
$aboutLang = new AboutLang();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,'aboutLang'=>$aboutLang]);
}
}
and my view (create form)
...
<?= $form->field($model, 'status')->textInput() ?>
<?= $form->field($aboutLang, 'title')->textInput() ?>
<?= $form->field($aboutLang, 'content')->textInput() ?>
enter code here
And when i put $aboutLang in create form i get an error "Call to a member function formName() on a non-object"
It looks like the views you are using were generated by Gii. In that case, Gii generates a partial view for the form (_form.php) and two views both for create and update actions (create.php and update.php). These two views perform a rendering of the partial view.
The problem you might have is that you are not passing the variable $aboutLang from create.php to _form.php, that must be done in create.php, when you call renderPartial():
$this->renderPartial("_form", array(
"model" => $model,
"aboutLang" => $aboutLang, //Add this line
));
Hope it helps.
Check your $aboutLang type.
It looks like it is null.
if ($aboutLang) {
echo $form->field($aboutLang, 'title')->textInput();
echo $form->field($aboutLang, 'content')->textInput();
}

CakePHP 2, how to AuthComponent::login() with plain-text password?

I would like to implement CakePHP 2 website over existing database with plain-text password field.
This is my AppController
class AppController extends Controller {
public $components = array(
'Session',
'Auth' => array(
'loginRedirect' => array('controller' => 'users', 'action' => 'index'),
'logoutRedirect' => array('controller' => 'users', 'action' => 'home'),
'authError' => 'You cannot view this page',
'authorize' => array('Controller'),
'authenticate' => array(
'Form' => array(
'userModel' => 'User',
'fields' => array('username' => 'user_id', 'password' => 'user_password')
)
)
)
);
public function isAuthorized($user) {
return true;
}
function beforeFilter() {
$this->Auth->allow('home');
//$this->Auth->authenticate = $this->User;
parent::beforeFilter();
}
This is my UserController.
class UsersController extends AppController {
public $paginate = array(
'fields' => array('user_id', 'user_desc', 'user_password'),
'limit' => 25,
'order' => array(
'user_id' => 'asc'
)
);
function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash('Cannot Login');
}
}
}
}
This is my User model
class User extends AppModel {
public $name = 'User';
public $primaryKey = 'user_id';
public $belongsTo = 'Group';
}
According to those files above, when I pressed the Login button on login.ctp, I saw
select * from users where user_password = 'this_is_hashing_password'
on the sql dump section.
So, how to turn-off the automatic hashing algorithm, so the login() will compare the user input to the database stored password as plain-text???
I have tried lots of reading on the CakePHP book but I cannot find any, also using hashPasswords($data) technique which found from the internet is not working.
Please help.
Kongthap.
The best answer really is to batch-process your stored passwords so they are hashed, however there are cases where you may be adding a Cake app to an existing application that hashes passwords differently (say by not hashing them at all), so the question is valid even if the goal in this case is not.
Try these resources for modifying Cake's password hashing function, depending on your Cake version:
Cake 2.x
Cake 1.3
I got plain text password working in Cakephp 3, this should only be use for DEVELOPMENT purpose, you should never store password in plain text in production.
That being said, during development, plain text password allows me to focus on login instead of implementing a fully functional user encrypt/decrypt logic. Which is going to be replaced by an OAuth / SAML module anyway...
OK here comes the source code:
ROOT/src/Auth/PlainTextPasswordHasher.php
<?php
namespace App\Auth;
use Cake\Auth\AbstractPasswordHasher;
/**
* Plain text password for demo use, DO NOT PUSTH THIS TO PROD
*/
class PlainTextPasswordHasher extends AbstractPasswordHasher
{
public function hash($password)
{
return $password;
}
public function check($password, $hashedPassword)
{
return $password === $hashedPassword;
}
}
ROOT/src/Controller/PagesController.php
<?php
class PagesController extends AppController
{
public function initialize()
{
parent::initialize();
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'username',
'password' => 'password',
],
'passwordHasher' => [
'className' => 'PlainText',
],
'userModel' => 'YourUsers',
]
],
'loginAction' => [
'controller' => 'Logins',
'action' => 'login'
]
]);
}
}
Source: This video https://www.youtube.com/watch?v=eASSNS1f3V4 and this section of the official doc: https://book.cakephp.org/3.0/en/controllers/components/authentication.html#creating-custom-password-hasher-classes