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

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.

Related

Prestashop 1.6 Create Module to Display Carrier Filter

My Prestashop-based site is currently having an override for AdminOrdersController.php, I have placed it in override folder.
From the link provided below, it is perfectly working fine to add a Carrier filter which is not available in Prestashop 1.6 now. I have tried the solution and it is working perfectly.
Reference: Adding carrier filter in Orders page.
Unfortunately, for production site, I have no access to core files and unable to implement as such. Thus, I will need to create a custom module. Do take note that I already have an override in place for AdminOrdersController.php. I would like to tap on this override and insert the filter.
I have managed to create a module and tried placing an override (with the code provided in the URL) in mymodule/override/controller/admin/AdminOrdersController.php with the carrier filter feature.
There has been no changes/effect, I am baffled. Do I need to generate or copy any .tpl file?
Any guidance is greatly appreciated.
Thank you.
While the answer in the linked question works fine the same thing can be achieved with a module alone (no overrides needed).
Admin controllers have a hook for list fields modifications. There are two with the same name however they have different data in their params array.
actionControllernameListingFieldsModifier executes before a filter is applied to list.
actionControllernameListingFieldsModifier executes before data is pulled from DB and list is rendered.
So you can add fields to existing controller list definition like this in your module file:
public function hookActionAdminOrdersListingFieldsModifier($params) {
if (isset($params['select'])) {
$params['select'] .= ', cr.name';
$params['join'] .= ' LEFT JOIN `'._DB_PREFIX_.'carrier` cr ON (cr.`id_carrier` = a.`id_carrier`)';
}
$params['fields']['carrier'] = array(
'title' => $this->l('Carrier'),
'align' => 'text-center',
'filter_key' => 'cr!name'
);
}
Because array data is being passed into $params array by reference you can modify them in your hook and changes persist back to controller. This will append carrier column at the end of list.
It is prestashop best practice to try and solve problems through module hooks and only if there is really no way to do it with hooks, then do it with overrides.
Did you delete /cache/class_index.php ? You have to if you want your override to take effect.
If it still does not work, maybe you can process with the hook called in the AdminOrderControllers method with your new module.

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()

Sending more data to changeIdentity in CWebUser by overriding it?

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',
),
...
),

Combining DataSource and Local Data in SmartGWT ListGrid

I have extended a ListGrid to create a list of saved searches grouped by type of search, whether public or private. This list is populated through a standard SmartGWT datasource.
In addition, I would like to add to this list a grouping of historical searches, that would be available to a user as they create searches on a session-by-session basis (IE. a user creates a new search - until they close the browser, that search will display in the search list, under the grouping 'Historical Searches').
Long story short, I would like to be able to populate the ListGrid from two separate sources - from the already existing datasource and ideally from a RecordList saved in memory. I tried something similar to this:
#Override
public void fetchData() {
invalidateCache();
discardAllEdits();
super.fetchData();
setCanEdit(true);
for(Record r : histSearches.toArray()) {
startEditingNew(r);
endEditing();
}
setCanEdit(false);
markForRedraw();
};
While this code does get executed, it does not in any way perform the functionality that I'm hoping for it to do. Does anybody have any suggestions on how to perform this functionality? Any help would be greatly appreciated.
If you call DataSource.fetchData(), in the callback you can get the selected data as a RecordList. You can then add your per-session searches via recordList.add(), and provide the modified RecordList to a ListGrid via setData().
By the way, there is also an article on the public wiki showing a sample implementation of saved search (though different from what you want):
http://wiki.smartclient.com/display/Main/Saved+Search+%28Smart+GWT%29

Displaying Custom Attributes in Documentum - Webtop

I am following an article that explains how to use the ICustomAttributeDataHandler class.
I am creating a custom column for the inbox screen, but the problem is that the value I set for my custom attribute is not being reflected on the screen.
As a test I am changing the task name to "whoKnows". But this code is not effecting what is output on the screen:
ICustomAttributeRecordSet.setCustomAttributeValue(i, "taskName", "whoKnows");
(I am able to print debug lines from my custom class when the inbox is viewed, so I know my code is being run.)
Someone on the comments of that article wrote:
the user must call the
"setCustomAttributesInQuery() method
on the dataprovider passing in a
string array of the custom attributes
...what does that meen? Could this be my problem?
thanks.
To be honest, I have already used Webtop, but just as an user. I found a post in the dm developer discussion group that can be useful, though:
For creating a custom column in the
doclist you dont need to go through
this complex procedures. You can use
custom attribute datahandlers for
this.
First in your object list component xml file add your custom column
definition in the "columns" tag. You
can even add static columns instead of
the documentum attributes.
Now create a class which implements the ICustomAttributeDataHandler.
Implement the default the methods getRequiredAttributes and the getData
function.
In getRequiredAttributes add attributes of the object that you are
looking for.
In your getdata method retrieve each row and then based on the
attribute that you see, just set the
value that you want to. 6) Finally
define your class in the app.xml file
There is a section in WDK developement
guide regarding
ICustomAttribuetDataHandlers. Look for
the topic named "Adding custom
attributes to a datagrid".
I'm not sure if this is the final solution, but I hope it helps!
To answer you question about setCustomAttributesInQuery()
every datagrid in WDK is backed by an underlying data provider. You can get this proivder by using the following code.
Datagrid datagrid = (Datagrid)getControl("doclist_grid",com.documentum.web.form.control.databound.Datagrid.class);
DataProvider dp = datagrid.getDataProvider();
Once you've done that, you can call
dp.setCustomAttributesInQuery(myArr);
I'm not actually sure if this is part of the solution to your problem, but you could try this and see where it gets you.
You have to configure the inbox component.
if using classic view, go to inboxlist component and add your custom attribute.
<column>
<attribute>CustomAttributeName</attribute>
<label>Custom Attribute Label</label>
<visible>true</visible>
</column>
Your custom attribute has to be in a custom type that is a sub type of dmi_queue_item, because inboxlist shows only dmi_queue_item objects.
Hope this helps,
Regards,
Tejas.
This may be a non-issue, but based on your code, I can't tell if you're doing this:
ICustomAttributeRecordSet.setCustomAttributeValue(i, "taskName", "whoKnows");
or this:
ICustomAttributeRecordSet rs;
rs.setCustomAttributeValue(i, "taskName", "whoKnows");
You should be calling the setCustomAttributeValue method on the rs object instance, not on the interface.