Sending more data to changeIdentity in CWebUser by overriding it? - yii

Disclaimer: Complete beginner in Yii, Some experience in php.
In Yii, Is it OK to override the login method of CWebUser?
The reason i want to do this is because the comments in the source code stated that the changeIdentity method can be overridden by child classes but because i want to send more parameters to this method i was thinking of overriding the login method too (of CWebUser).
Also if that isn't such a good idea how do you send the extra parameters into the changeIdentity method.(By retrieving it from the $states argument somehow ??). The extra parameters are newly defined properties of UserIdentity class.

It is better to first try to do what you wish to do by overriding components/UserIdentity.php's authenticate method. In fact, that is necessary to implement any security system more advanced than the default demo and admin logins it starts you with.
In that method, you can use
$this->setState('myVar', 5);
and then access that anywhere in the web app like so:
Yii::app()->user->getState('myVar');
If myVar is not defined, that method will return null by default. Otherwise it will return whatever it was stored as, in my example, 5. These values are stored in the $_SESSION variable, so they persist as long as the session does.
UPDATE: Okay, took the time to learn how this whole mess works in Yii for another answer, so I'm sharing my findings here as well. The below is mostly copy pasted from a similar answer I just gave elsewhere. This is tested as working and persisting from page to page on my system.
You need to extend the CWebUser class to achieve the results you want.
class WebUser extends CWebUser{
protected $_myVar = 'myvar_default';
public function getMyVar(){
$myVar = Yii::app()->user->getState('myVar');
return (null!==$myVar)?$myVar:$this->_myVar;
}
public function setMyVar($value){
Yii::app()->user->setState('myVar', $value);
}
}
You can then assign and recall the myVar attribute by using Yii::app()->user->myVar.
Place the above class in components/WebUser.php, or anywhere that it will be loaded or autoloaded.
Change your config file to use your new WebUser class and you should be all set.
'components'=>
'user'=>array(
'class'=>'WebUser',
),
...
),

Related

silverstripe 3 - How to add access control to generated data objects?

Good afternoon,
Please let me know if this question is not clear enough, I'll try my best to make as straight-forward as possible.
How can I add access control to objects that are generated by an end-user using my data object?
Example: I have a class that extends a DataObject. Someone logs in the back-end; fills out the form that's generated by the CMS for the data object. A record is then created in the database by the CMS.
I would like to add an access control to that newly created record in the database.
For a code scenario you can take a look at one of my posts: Silverstripe 3 - Unable to implement controller access security from CMS
The only other way I can think of asking this question is: How to Dynamically (or programmatically) create permissions for records that are created by a DataObject extension via the CMS?
Thanks for your assistance.
Update - Sample Code
///>snippet, note it also has a Manager class that extends ModelAdmin which manages this!
class component extends DataObject implements PermissionProvider{
public static $db = array(
'Title' => 'Varchar',
'Description' => 'Text',
'Status' => "Enum('Hidden, Published', 'Hidden')",
'Weight' => 'Int'
);
///All the regular permission checks (overrides), for the interface goes here, etc...
///That is: canView, canDelete, canEdit, canCreate, providePermissions
}
Now, from the back-end an end-user can add components using the Manager Interface that's generated by extending ModelAdmin. How can I add individual permissions to those added components by the end-user?
Thanks.
Update 2
Example: Add Process Data Object that extends ModelAdmin will give you this in the back end
Then, when you click on the generated 'Add Process' button, you'll get this:
Finally, someone fills out the form and clicks on the 'Create' button, which saves the data in the database. That looks like this:
Now, on that record thats created in MySQL I'd like to add granular permissions to that record. Meaning, for every record created I want to be able to Deny/Allow access to it via a Group/Individual, etc.
Is that even possible with the SilverStripe framework? Thanks.
Implement the functions canView, canEdit, canDelete, and/or canCreate on your DataObject.
Each function will return true or false depending on the conditions you set - any conditions, not just what is defined in the CMS.
See the example code on the tutorial site.

How can I access query string parameters for requests I've manually dispatched in Laravel 4?

I'm writing a simple API, and building a simple web application on top of this API.
Because I want to "consume my own API" directly, I first Googled and found this answer on StackOverflow which answers my initial question perfectly: Consuming my own Laravel API
Now, this works great, I'm able to access my API by doing something like:
$request = Request::create('/api/cars/'.$id, 'GET');
$instance = json_decode(Route::dispatch($request)->getContent());
This is great! But, my API also allows you to add an optional fields parameter to the GET query string to specify specific attributes that should be returned, such as this:
http://cars.com/api/cars/1?fields=id,color
Now the way I actually handle this in the API is something along the lines of this:
public function show(Car $car)
{
if(Input::has('fields'))
{
//Here I do some logic and basically return only fields requested
....
...
}
I would assume that I could do something similar as I did with the query string parameter-less approach before, something like this:
$request = Request::create('/api/cars/' . $id . '?fields=id,color', 'GET');
$instance = json_decode(Route::dispatch($request)->getContent());
BUT, it doesn't seem so. Long story short, after stepping through the code it seems that the Request object is correctly created (and it correctly pulls out the fields parameter and assigns id,color to it), and the Route seems to be dispatched OK, but within my API controller itself I do not know how to access the field parameter. Using Input::get('fields') (which is what I use for "normal" requests) returns nothing, and I'm fairly certain that's because the static Input is referencing or scoping to the initial request the came in, NOT the new request I dispatched "manually" from within the app itself.
So, my question is really how should I be doing this? Am I doing something wrong? Ideally I'd like to avoid doing anything ugly or special in my API controller, I'd like to be able to use Input::get for the internally dispatched requests and not have to make a second check , etc.
You are correct in that using Input is actually referencing the current request and not your newly created request. Your input will be available on the request instance itself that you instantiate with Request::create().
If you were using (as you should be) Illuminate\Http\Request to instantiate your request then you can use $request->input('key') or $request->query('key') to get parameters from the query string.
Now, the problem here is that you might not have your Illuminate\Http\Request instance available to you in the route. A solution here (so that you can continue using the Input facade) is to physically replace the input on the current request, then switch it back.
// Store the original input of the request and then replace the input with your request instances input.
$originalInput = Request::input();
Request::replace($request->input());
// Dispatch your request instance with the router.
$response = Route::dispatch($request);
// Replace the input again with the original request input.
Request::replace($originalInput);
This should work (in theory) and you should still be able to use your original request input before and after your internal API request is made.
I was also just facing this issue and thanks to Jason's great answers I was able to make it work.
Just wanted to add that I found out that the Route also needs to be replaced. Otherwise Route::currentRouteName() will return the dispatched route later in the script.
More details to this can be found on my blog post.
I also did some tests for the stacking issue and called internal API methods repeatedly from within each other with this approach. It worked out just fine! All requests and routes have been set correctly.
If you want to invoke an internal API and pass parameters via an array (instead of query string), you can do like this:
$request = Request::create("/api/cars", "GET", array(
"id" => $id,
"fields" => array("id","color")
));
$originalInput = Request::input();//backup original input
Request::replace($request->input());
$car = json_decode(Route::dispatch($request)->getContent());//invoke API
Request::replace($originalInput);//restore orginal input
Ref: Laravel : calling your own API

Same paginator for every controller in ZF2

I'm writing my first app in ZF2, and I want to create pagination system.
Currently, I have something like this in my controllers:
$pagLimit = $this->params()->fromQuery('limit', 1000);
$pagPage = $this->params()->fromQuery('page', 1);
$orderDir = $this->params()->fromQuery('dir', 'ASC');
$orderBy = $this->params()->fromQuery('column', 'id');
$result = $this->getMapper()->getList($orderDir, $orderBy);
$paginator = new Paginator(new ArrayAdapter($result));
$paginator->setItemCountPerPage($pagLimit);
$paginator->setCurrentPageNumber($pagPage);
I think that my solution is not quite good..
If I want to change e.g. default limit of items per page, I have to modify all my controllers. Also, I have to remember to send two arguments for all mapper methods which are getting lists of data.
My first thought was to use inheritance ("MyController" with methods like: setPaginationParams(), and setPaginator($data)).
Then I would have to remember to invoke "my controller" methods in every controller.
But maybe there is a better way to implement the same paginator for every controller in my module? MVC event? Create custom class and use DI?
What is the best way to implement this functionality?
Could you just give me some hints?
I'm new to ZF2 and OOP concepts. :(
You could always extend the paginator with your own, and have default values set.
You could even just pass in the request object or params object and then let the Paginator internally handle some things for you to save setting up default values etc.

Yii global variables and setting issue

I am developing a social networking website using Yii. While frequently using the following things I am having great data manageability issue.
- User ID
- Current user ID (the user which profile is the owner viewing)
- Is owner???
where can I define these things.
I would something like
if(Yii::app()->owner==ME){
//do something
}
// and similarly
if($this->isMyFreind(<Current user ID>){
}
// $this(CanIView()){
}
I want these functions to be public for any page? But how?
In other words
Where can I put my library which contains my own favorite functions like text shortening, image cropping, date time format etc etc??
In Yii, you can do achieve this by making a class (under protected/compoents) which inherits
CApplicationComponent
class. And then you call any property of this class globally as a component.
class GlobalDef extends CApplicationComponent {
public $aglobalvar;
}
Define this class in main config under components as:
'globaldef' => array('class' => 'application.components.GlobalDef '),
And you can call like this:
echo Yii::app()->globaldef->aglobalvar;
Hope that can help.
According to the MVC model, things like image cropping or date time formats would go in models. You would simply create models for that.
I usually use a globals.php file with all my common functions
In the index.php (yip the one in the root):
$globals='protected/globals.php';
require_once($globals);
I can then call my global functions anywhere. For example, I shorten certain Yii functions like:
function bu($url=null){
static $baseUrl;
if ($baseUrl===null)
$baseUrl=Yii::app()->request->baseUrl;
return $url===null ? $baseUrl : $baseUrl.'/'.ltrim($url,'/');
}
So I can then call the Yii::app()->request->baseUrl by simply calling bu()

How do you check if the current page is the frontpage using YII?

Drupal has a function called "drupal_is_front_page". Does YII have something similar to deal with navigation in this way?
Unfortunately not. And while the information needed to piece this together is available, doing so is really more pain than it should be.
To begin with, the front page is defined by the CWebApplication::defaultController property, which can be configured as discussed in the definitive guide. But there's a big issue here: defaultController can in reality be any of the following:
a bare controller name, e.g. site
a module/controller pair, e.g. module/site
a controller/action pair, e.g. site/index
a module/controller/action tuple, e.g. module/site/index
If you have specified the defaultController as #4 (which is the same as #3 if your application does not include any modules) then everything is easy:
function is_home_page() {
$app = Yii::app();
return $app->controller->route == $app->defaultController;
}
The problem is that if defaultController is specified as #1 or #2 then you have to examine a lot of the runtime information to convert it to form #3 or #4 (as appropriate) so that you can then run the equality check.
Yii of course already includes code that can do this: the CWebApplication::createController method, which can accept any of the valid formats for defaultController and resolve that to a controller/action pair (where controller is dependent on the module, if applicable). But looking at the source doesn't make you smile in anticipation.
To sum it up: you can either assume that defaultController will always be fully specified and get the job done with one line of code, or borrow code from createController to determine exactly what defaultController points to (and then use the one line of code to check for equality).
I do not recommend looking into solutions based on URLs because the whole point of routes is that different URLs can point to the same content -- if you go that way, can never be sure that you have the correct result.
In my experience, there is no such function in Yii. However, you can retrieve the followings:
base url: Yii::app()->request->baseUrl
current URL : Yii::app()->request->requestUri.
current page controller with Yii::app()->getController()->getAction()->controller->id .
With these APIs, it should be possible to find out whether the current page is front page.
another simple idea:
in your action (that one you use to present your 'main front page'), you could set up a variable using a script in its view:
Yii::app()->getClientScript()->registerScript("main_screen",
"var main_front_page = true;",CClientScript::POS_BEGIN);
put that code in the "main view", (the rest view pages dont have this piece of code).
so when you need to check if a page is the "main page" you could check for it using javascript, quering for:
if(main_front_page){..do something..}.
if you need to recognize the main page in php (in server side), use the method proposed by Jon.
another solution, based on a common method for your controller:
Your controllers all of them must extend from CController, but, when you build a new fresh yii application Gii creates a base Controller on /protected/components/Controller.php so all your controllers derives from it.
So, put a main attribute on it, named:
<?php
class Controller extends CController {
public $is_main_front_page;
public function setMainFrontPage(){ $this->is_main_front_page = true; }
public function getIsMainFrontPage(){ returns $this->is_main_front_page==true; }
}
?>
well, when you render your main front page action, set up this core varible to true:
<?php
class YoursController extends Controller {
public function actionPrimaryPage(){
$this->setMainFrontPage();
$this->render('primarypage');
}
public function actionSecondaryPage(){
$this->render('secondarypage');
}
}
next, in any view, you could check for it:
<?php // views/yours/primaryview.php
echo "<h1>Main Page</h1>";
echo "is primary ? ".$this->getIsMainFrontPage(); // must say: "is primary ? true"
?>
<?php // views/yours/secondaryview.php
echo "<h1>Secondary Page</h1>";
echo "is primary ? ".$this->getIsMainFrontPage(); // must say: "is primary ? false"
?>