Silverstripe forbid editing of page but allow to create subpages - php

I have a NewsholderPage and NewsPage. NewsPages are subpages of NewsholderPage.
I need users of a certain group to be able to create NewsPages, but not to be able to edit the NewsholderPage.
If I put the following code into the NewsholderPage...
public function canEdit($member = null){
if(permission::check('SUPERUSER')){
return true;
}
return false;
}
... then a not-admin cannot edit the NewsholderPage but also gets a "forbidden" message, when he is trying to create a NewsPage as child of the NewsholderPage.
What is the best way to allow the creation of subpages, while not allowing to edit the parent page?

You'll want to override the canAddChildren method on NewsholderPage to return something other than the default (which is simply $this->canEdit()). To get the default behaviour back, you can use something like:
public function canAddChildren($member = null) {
// Call SiteTree::canEdit rather than NewsholderPage::canEdit
return parent::canEdit($member);
}

Related

Dynamic formConfig in same controller in October CMS

I am trying to make a car rental agency, and in the backend I want to be able to set the rates. However there are two different (but related) ways to do this, either by individual date, or in bulk, by selecting a date range and looping over the individual dates.
In the controller I have two actions defined to do this, calendar() and bulk() respectively. I also choose the form fields yaml file to be loaded by setting the $formConfig public property. My controller looks something like this:
class AvailableCars extends Controller
{
public $formConfig = 'config_form.yaml';
public function bulk($recordId = null, $context = null)
{
$this->pageTitle = "Bulk update rates";
$model = $this->formFindModelObject($recordId);
$this->initForm($model);
}
public function calendar($recordId = null, $context = null)
{
$this->pageTitle = "Update rates for single date on calendar";
$model = $this->formFindModelObject($recordId);
$this->initForm($model);
}
public function onSave($recordId = null, $context = null)
{
$this->formFindModelObject($recordId)->save();
Flash::success("Rates saved successfully");
}
}
The problem is that this works for one of the actions, however if I put, for example:
$this->formConfig = 'alternate_fields.yaml';
in either of the bulk() or calendar() methods, it does not override the behavior and load a different form config yaml file; and it even errors out if it is not previously defined as a class property. So I can assume this yaml file is loaded before either of these methods are called.
So my question is, is there any way to load a dynamic formConfig yaml file based on the entry point? Or, is this even good practice in Laravel / October, or should each controller only be responsible to do one thing, and have only one way to create/read/update/destroy a model?
You can set configuration file manually as per your need. but we also need to follow some rules and add required methods with proper naming conventions for it.
and yes every thing is good practice if we do it properly :)
You can use this code for update existing records.
public function updatebulk($recordId = null, $context = null)
{
$this->pageTitle = "Bulk update rates";
$this->asExtension('FormController')->setConfig('bulk_config.yaml');
$this->asExtension('FormController')->update($recordId, $context);
}
public function updatebulk_onSave($recordId = null, $context = null) {
$this->asExtension('FormController')->setConfig('bulk_config.yaml');
$this->asExtension('FormController')->update_onSave($recordId, $context);
// or custom logic (if you add custom logic please remove above lines)
}
Now you can navigate to http://localhost/backend/author/plugin/controller_name/updatebulk/1 it will render form based on new bulk_config.yaml configuration file.
and when you save it, it will call updatebulk_onSave to update record. we must follow rule and let FormController handle all control to make it work correctly.
If you need to save record differently then you need to add custom logic in updatebulk_onSave method its up to you.
#Note
If you also need creation functionality you need additional methods. For ex.
public function createbulk($context = null)
{
$this->pageTitle = "Bulk create rates";
$this->asExtension('FormController')->setConfig('bulk_config.yaml');
$this->asExtension('FormController')->create($context);
}
public function createbulk_onSave($context = null) {
$this->asExtension('FormController')->setConfig('bulk_config.yaml');
$this->asExtension('FormController')->create_onSave($context);
}
if any doubts please comment.

Laravel 4.2 is_online article

I'm confronted to a little problem, i try to add a method to show only online article, but i want to know how do implement this method.
In my DB i have a row is_online (Int) for 0=> offline, 1=>online, how do implement that for my view.
in my models with
public function isonline(){}
or in my PostController in my request of post find.
And after need to add in my admin panel a check box in Post create to change the status off article (online or offline-draft).
You should use Eloquent scope in your code by creating online scope in your model.
public function scopeOnline($query)
{
return $query->where('is_online', 1);
}
Draft posts
public function scopeDrafts($query)
{
return $query->where('is_online', 0);
}
Then in your code you can simply use it like this.
$onlinePosts = Post::online()->get();
$draftPosts = Post::drafts()->get();
You just need to find all records who have flag is_online = 1.
You can write one method in your PostController like
Public function getOnlineRecords{
$records = YourModel::where('is_online','=',1)->get();
return View::make('your_view_path',['records'=>$records]);
}
In your View file, you need to write:-
{{ Form::checkbox('your_field_name', 'value', true) }}
If you want to default the value as checked, pass true as the third argument.

Exceeded maximum time error when overriding the newQuery on Laravel 4.0

So, I was trying to implement this answer for my other question on the same subject... and it keeps givin me the exceeded time error. Any clues?
This is on my product model. It inherits from Eloquent.
public function newQuery($excludeDeleted = true)
{
$user_permission = Auth::user()->permissions;
if( $user_permission->master )
return parent::newQuery();
else if( $user_permission->web_service )
{
$allowed_ids = array();
foreach( $user_permission->allowed_products()->get() as $allowed)
$allowed_ids[] = $allowed->id;
return parent::newQuery()->whereIn('id', $allowed_ids);
}
return parent::newQuery();
}
If the user is master there is no need to query scope on the request. But, if it isn't then I need to filter by the logged user's permissions.
UPDATE:
I tried the following code in a controller and it works alright:
$user_permission = Auth::user()->permissions;
echo "<PRE>"; print_r($user_permission->allowed_products()->get()); exit;
UPDATE 2:
Guys, I just found out that the problem was in this peace of code:
$allowed = Auth::user()->permissions()->first()->allowed_products()->get()->list('id');
It somehow give me an Maximum execution time of 30 seconds exceeded. If I put the exact same code in a controller, works like a charm, though! I also tried to put it in a scope, also worked. This it's really grinding my gears!
Elloquent has a function called newQuery. Controller does not. When you implement this function in a Model you are overriding the one in Elloquent. If you then invoke Elloquent methods that need a new query for your model before they can return, like ->allowed_products()->get(). Then you are calling your own newQuery() method recursively. Since the user permissions have not changed, this results in infinite recursion. The only outcome can be a timeout because it will keep on trying to determine a filtered product list which causes your newQuery() method to be called, which tries to determine the filtered product list before returning the query, and so on.
When you put the method into a Controller, it is not overriding the Elloquent newQuery method so there is no infinite recursion when trying to get the allowed_product list.
It would be more efficient to apply the filter to the product query based on whether the id is in the user's allowed_products() list using ->whereExists() and build up the same query as allowed_products() except now add condition that id from the query you are filtering is equal to the product id in the allowed products query. That way the filtering is done in the database instead of PHP and all is done in the same query so there is no recursion.
I don't see how your update code works. Illuminate\Database\Eloquent\Collection does not have any magic methods to call the relation functions, you should get an undefined method error trying to do that.
Can you try something like
public function newQuery($excludeDeleted = true)
{
// Returns `Illuminate\Database\Eloquent\Collection`
$user_permission = Auth::user()->permissions;
if ($user_permission->master)
{
return parent::newQuery();
}
else if ($user_permission->web_service)
{
// If here you was to call $user_permission->allowed_products()->get() not much is going to happen, besides probably getting an undefined method error.
$allowed_ids = Auth::user()->permissions()->allowed_products()->get()->lists('id');
return parent::newQuery()->whereIn('id', $allowed_ids);
}
return parent::newQuery();
}
Update: as per comments below I believe the problem is due to newQuery() being called multiple times as the code works just fine when called once in a controller. When this is applied to every query there is no need to collect all the IDs over and over again (assuming they're not going to change each time you call for them). Something such as the below will allow you to store these and only process them once per request rather than every time a query is run.
private $allowed_ids_cache = null;
public function newQuery($excludeDeleted = true)
{
$user_permission = Auth::user()->permissions;
if ($user_permission->master)
{
return parent::newQuery();
}
else if ($user_permission->web_service)
{
if ($this->allowed_ids_cache === null)
{
$this->allowed_ids_cache = Auth::user()->permissions()->allowed_products()->get()->lists('id');
}
return parent::newQuery()->whereIn('id', $this->allowed_ids_cache);
}
return parent::newQuery();
}

conditional Hide/show the new link action in symfony 1.4

I want to show/hide the new link action of the admin generator list depending of some db condition.
For example:
A "Group" have many "Evaluation", a teacher may create new evaluations only if the Group status is not ended. I want to hide the "new" link of the symfony admin generator list depending on that. How can I do it?, I tried editing the _list_actions file with no success until now.
thanks.
Yoan
I think you can make it in a few ways.
You can hide link to the new action, but this is not very good,because users can create new evaluations with direct link.
So I recommend you next way.
Go to cache/backend/prod/modules/autoNamemodule/action/action.class.php
Copy to apps/backend/modules/Namemodule/action/action.class.php
next
public function executeNew(sfWebRequest $request)
{
$this->form = $this->configuration->getForm();
$this->product = $this->form->getObject();
}
Than you need to check status. I do not now yon DB table name, so for example
public function executeNew(sfWebRequest $request)
{
$id = $request->getParameter('id', false);
if (ctype_digit($id)) {
$group = Doctrine::getTable('Group')->findOneById($id);
$group_status=$group->getStatus();
if($group_status== 0){
$this->form = $this->configuration->getForm();
$this->product = $this->form->getObject();
}
else {
$this->getUser()->setFlash('notice', 'Group status ended!You can not create new evaluations ' );
$this->redirect('#yourmodulenamerout');
}
}
So if Group status ended you redirect user to the index of you backend module and show user why he can not create new evaluations). You can also hide link to the new actions. In the same way but you must make it in _list_actions file so it is not very good practice.

Manage groups in different page with sfDoctrineGuardPlugin

i'm using sfDoctrineGuardPlugin, but i would like a little change, i have 2 groups(admins,users) and i want to manage them separately, what i mean is a page that only shows me the "admins" group and another one the "users" group, i want this because users in "users" group will have some additional info and behavior, i already create a "sfGuardUser" empty module in my backend app, so i can overwrite and add everything i want, i create de actions.class.php
class sfGuardUserActions extends autoSfGuardUserActions
{
public function executeAdmins(sfWebRequest $request)
{
// sorting
if ($request->getParameter('sort') && $this->isValidSortColumn($request->getParameter('sort')))
{
$this->setSort(array($request->getParameter('sort'), $request->getParameter('sort_type')));
}
// pager
if ($request->getParameter('page'))
{
$this->setPage($request->getParameter('page'));
}
$this->pager = $this->getPager();
$this->sort = $this->getSort();
}
}
i copied exactly the "executeIndex" function from "autoSfGuardUserActions" class in cache, and now i can go to guard/admin and it acts like the default one, but now, how can i show only de users from "admins" group?
You have to modify buildQuery() method.

Categories