kartik-v / yii2-tree-manager

An advanced tree management module using nested sets for Yii 2.
http://demos.krajee.com/tree-manager
Other
150 stars 107 forks source link

Enable the error callback to display the specified error like 403 #199

Closed buttflattery closed 6 years ago

buttflattery commented 6 years ago

Prerequisites

Steps to reproduce the issue

  1. Install Treemanager and initialize TreeView widget.

  2. I have RBAC integrated and I want to allow a certain user to create and edit the nodes and for certain users I want to show the treeview with limited functionality, depending on the allowed permissions in the database.

  3. For that i have extended the NodeController and added a beforeAction() in the extended controller

    
    use kartik\tree\controllers\NodeController as BaseNodeController;
    use Yii;

/**

}


4. Used the following for the `treeViewSettings`

'nodeActions' => [ Module::NODE_MANAGE => Url::to(['/node/manage']), Module::NODE_SAVE => Url::to(['/node/save']), Module::NODE_REMOVE => Url::to(['/node/remove']), Module::NODE_MOVE => Url::to(['/node/move']), ],



## Expected behavior and actual behavior

When I follow those steps, I see...

Now the user who is not allowed to access the `manage` action if clicks on the `Add New` icon `+` under the treeview he should be presented with `403 Forbidden Error` but the loader keeps spinning. 

![image](https://user-images.githubusercontent.com/1536454/42638730-d31398c2-8607-11e8-9474-cf1457f9f0f0.png)

I was expecting...

The error should be presented via a simple alert or bootstrap alert, or at the same place where the loading spinner is displayed.

Looking in ajax call that is in the `kv-tree.js` file in the `renderForm` function the `error` callback is there and looks like it **should** raise the error as the line there is 

`self.raise('treeview.selectajaxerror', [key, jqXHR, textStatus, errorThrown]);`

which i think raises the event `treeview.selectajaxerror` which means the event needs to bind manually via adding javascript code inside the view it would be more easier if we have the `clientEvents` options so that the event binding is provided via the widget.

 OR

There should an option like `allowNewNodes` just like we have `allowNewRoots` which removes the icon from the toolbar to disallow addition of the nodes.

I am sorry if there is an option to control/manage the above described scenario.

## Environment
Php 5.6
Yii 2.0.15.1
Ubuntu 14.04.5 LTS

#### Browsers

- [x] Google Chrome
- [x] Mozilla Firefox
- [ ] Internet Explorer
- [ ] Safari

#### Operating System

- [ ] Windows
- [ ] Mac OS X
- [x] Linux
- [ ] Mobile

#### Libraries

- jQuery version: 2.2.4
- yii2-tree-manager version: dev-master 4f69d87

## Isolating the problem

- [ ] This bug happens [on the demos page](http://demos.krajee.com/tree-manager-demo/tree-view)
- [x] The bug happens consistently across all tested browsers
- [] This bug happens when using yii2-tree-manager without other plugins.
kartik-v commented 6 years ago

Check if you can control this instead through the Tree model or the TreeTrait

There is a new method isChildAllowed in the Tree model or the TreeTrait which controls whether children nodes can be added or created. This isChildAllowed method returns by default the child_allowed database field value for each tree node. You can override this method. For example:

class YourCustomTree extends \kartik\tree\models\Tree {
    public function isChildAllowed() {
        // your $route and $module variables
        if (Yii::$app->user->can($route)) {
            return parent::isChildAllowed();
        } else {
            return false;
        }
    }
}
buttflattery commented 6 years ago

Well after running the composer update it upgraded with the message

kartik-v/yii2-tree-manager dev-master (4f69d87 => 57c0e7b): Checking out 57c0e7b048

and then added the field child_allowed via migration, after that i also removed the custom controller that I was extending and kept the default one to see if things are smooth with the default functionality after the update, and commented out the nodeActions in my TreeView widget

'nodeActions' => [
    Module::NODE_MANAGE => Url::to(['/node/manage']),
    Module::NODE_SAVE => Url::to(['/node/save']),
    Module::NODE_REMOVE => Url::to(['/node/remove']),
    Module::NODE_MOVE => Url::to(['/node/move']),
],

Now although I haven't implemented the logic that you have recommended I have started receiving the error

Operation Disallowed Invalid request signature detected during tree data manage action! Please refresh the page and retry.

OLD HASH: 748f5e3eed5ca4993d4658c2bc2cfd626325611008f40aafd8b2a523f7fb1dafcommon\models\Campaign111/manage-campaigns@backend/views/campaign/_formw0-nodesel{"id":"w0-nodeform"}{"1":"","2":"","3":"","4":"","5":""}"none"{"activeCss":"active","depth":"","glue":" » ","untitled":"Untitled"} NEW HASH: 6ebc205bcfa5932d8b95c97f42748c15a237e8228275b4125484ec2b0cfc9f7fcommon\models\Campaign111/manage-campaigns@backend/views/campaign/_formw0-nodesel{"id":"w0-nodeform"}{"1":"","2":"","3":"","4":"","5":""}{"submit":"","reset":" "}"none"{"activeCss":"active","depth":"","glue":" » ","untitled":"Untitled"}

Strange thing is that by default when I land on the page it loads the form by default image

but if clicked on any of the nodes it shows the above error even if i click the same node/campaign that loads the form by default also shows the error, i removed the assets to be generated again but still the same. Any Ideas why is it doing that or if i am missing something?

buttflattery commented 6 years ago

UPDATE i am using the customized view by using the nodeView options in the TreeView Widget 'nodeView' => '@backend/views/campaign/_form', and looks like that if i remove it, it starts working correctly and does not show the error? does it mean i would have to revise all of my view too in future if i am using the customized form for Node view?

kartik-v commented 6 years ago

Yes your hashes are not matching (extra json from nodeViewButtonLabels)... Note that nodeViewButtonLabels is a new property added in TreeView widget and is also a part of the hash and exists as part of the hashData in the nodeView and the NodeController.

So if you have overridden any of the above - cross check and correct/update as per the default code in the extension.

buttflattery commented 6 years ago

Yes, the button labels sections needed to be added to the custom view, i just added it and all good now thanks for the heads-up once again @kartik-v , i will be closing this issue now. The code that works correctly to implement the original logic is below if someone else lands here for the same reason.

public function isChildAllowed() {
        $appId = Yii::$app->id;
        $module = (isset(Yii::$app->module->id)) ? Yii::$app->module->id : '';
        $controller = Yii::$app->controller->id;
        $actionName = Yii::$app->controller->action->id;
        $route = $appId . "/" . $controller . "/" . $actionName;

        // your $route and $module variables
        if (Yii::$app->user->can($route)) {
            return parent::isChildAllowed();
        } else {
            return false;
        }
    }
buttflattery commented 6 years ago

looks like it wont work if i override isChildAllowed() in the model as by default it will always pick up the default route on my controller where i am loading the TreeView which is campaign/index which is allowed for both admin and CRM user but the routes that need to be checked are

node/save node/manage node/remove node/move

So the button add new isnt disabled when the treeview loads initially and when clicked it ends up with the same loader when that was presented initially when i submitted the issue although the console shows the error log for 403 Permission denied :(

buttflattery commented 6 years ago

What i have done is hardcoded the route specific to the method i am overriding

//route used is node/manage
public function isChildAllowed() {
        $appId = Yii::$app->id;
        $controller = 'node';
        $actionName = 'manage';
        $route = $appId . "/" . $controller . "/" . $actionName;

        // your $route and $module variables
        if (Yii::$app->user->can($route)) {
            return parent::isChildAllowed();
        }
        return false;
    }
//route used is node/move
public function isMovable($dir) {
        $appId = Yii::$app->id;
        $controller = 'node';
        $actionName = 'move';
        $route = $appId . "/" . $controller . "/" . $actionName;

        // your $route and $module variables
        if (Yii::$app->user->can($route)) {
            return parent::isMovable($dir);
        }
        return false;
 }