Overriding /controllers/element

Permalink
Hi,

I keep falling while trying to override some of the core files... In this case the /controllers/elements/navigation/dashbboard_menu.php.

I found this in the documentation (https://documentation.concrete5.org/tutorials/override-almost-any-core-file-in-5-7)
....
Controllers
Core Path: /concrete/controllers/single_page/page_name.php
Package Path: /packages/package_handle/controllers/single_page/page_name.php
Override Path: /application/controllers/single_page/page_name.php
Override Class Name: \Application\Controller\SinglePage\PageName
....

So as simple as to duplicate the file, rename the class and upload this in the application folder.... But it's not working! Cleared cache, did some dies... But the file isn't loaded or processed. In the Environment log I see that the override is registered, but no joy!

Then I tried to add the "override" as a alias in the config/app.php. Found some code on stackoverflow. But that doens't work either.

Then I changed it from an alias to a provider. Then at last I got some response from C5. Only errors though, but It seems to notice my override. I can't get it working without overriding much more files if I register them as "new" providers...

Also tried this:https://documentation.concrete5.org/developers/extending-concrete5-w...
But that only seems to work for /src files.

So... My question....
How do I override the /concrete/controllers/element/navigation/dashboard_menu.php in my /application/controllers/element/navigation/dashboard_menu.php, without any other overrides!? Just that one file please....

Thanks for the help!

Best,

Corretje

DeWebmakers
 
mnakalay replied on at Permalink Reply
mnakalay
Sometimes an alternate way might work. Could you tell us what exactly you want to change in that class that makes you want to override it? That might bring up some ideas.
DeWebmakers replied on at Permalink Reply
DeWebmakers
Hi,

Thank you for answering...

In this case the dashboard menu on the right isn't displaying aliases. I want to add an alias in the "Permissions & Access" tab.

It's very strange that all the permissions are there to set, except for the file permissions. So I wanted to create an alias in the parent so that all permissions are grouped.

Also I want to change the behaviour of the "Reports" page. It's redirecting to the Form Results and that is something that I don't want. I think this last one isn't in the dashboard menu managed, but it will need some overriding of core controllers...

Best,

Corretje
mnakalay replied on at Permalink Reply
mnakalay
For the first one, why don't you simply create a single page, call It File permissions and place it where you want it. ANd in its controller, just redirect to the actual file permissions page?
mnakalay replied on at Permalink Reply
mnakalay
As for the second one, that's actually easy enough. Use laravel's bind() function to bind your own page controller to the existing one. Yo do that for the single page "reports" controller. All that controller has in it is a redirect to the forms page. Once your bind() override is in place you just remove the redirect and send it to wherever you want. The reports single page itself is empty.
DeWebmakers replied on at Permalink Reply
DeWebmakers
Hi,

Thanks again. Seems a way to do this.
Is there any documentation about binding within Concrete? The basics will do...

Still wondering why the "normal" overriding (like 5.6) isn't working. In my opinion it was so much easier...
mnakalay replied on at Permalink Reply
mnakalay
bind() is a Laravel thing so you should be reading Laravel's doc. But here what you can do. I do it from a package's controller on_start() function but you can do it from the file application/bootstrap/app.php and add an on_start event to it.
Then the code to bind your controller to the core controller is this
$app->bind(
            '\Concrete\Controller\SinglePage\Dashboard\Reports', 
            'Namespace\For\Your\Own\Controller\In\The\Application\Folder'
);

And that's it, now you controller should be used instead of the core one every time the reports page is called
DeWebmakers replied on at Permalink Reply
DeWebmakers
Hi,

Sorry... Another question...
I've a package installed, and I want to override a class within the src. So I have the following code in my bootstrap/app.php:
$app->bind('Concrete\Package\PackageName\Src\Folder\With\Class', function($app) {
    return new Application\PackageName\Folder\MyOverwrite\Class();
});

So in my opinion the named class should be overwritten with my custom class. But for some reason its not loaded or overwritten. Can you tell me why?

Thank you
mnakalay replied on at Permalink Reply
mnakalay
Often, in packages, the Src is ommited from class namespaces. Make sure you got the namespace as it is declared in the class you want to override.

Also instead of new yourclass() I'd do $app->make(yourclass::class);
DeWebmakers replied on at Permalink Reply
DeWebmakers
Thanks again, but still there is some weird stuff going on....
I've the following code:
$classLoader = new \Symfony\Component\ClassLoader\Psr4ClassLoader();
$classLoader->addPrefix('Application\\User', DIR_APPLICATION . '/' . DIRNAME_CLASSES . '/User');
$classLoader->register();
// This should hande the override
$app->bind('\Concrete\Core\User\User', 'Application\User\User');
$user = new Application\User\User();
var_dump($user->inGroup([]));  // Returns "TRUE"! So this works!
$user = new Concrete\Core\User\User();
var_dump($user->inGroup([]));  // Returns error....


So in this case Core\User can't be overwritten? Or am I doing something wrong?
mnakalay replied on at Permalink Reply
mnakalay
That's the thing. That only works because you let Laravel handle it.
So when you do
$app->bind('\Concrete\Core\User\User', 'Application\User\User');

You're telling Laravel to do the binding.
But then when you instantiate your class like this
$user = new Concrete\Core\User\User();

You are totally bypassing Laravel.
You must instantiate it like this
$user = $app->make(\Concrete\Core\User\User::class);

So keeping that in mind, understand that for classes that Concrete5 itself instantiates using new class() there isn't much you can do. So anywhere in code you see for instance new User() well you're out of luck.
For single pages, typically, Concrete5 instantiate them using Laravel container so then you can override them the way described above.
In summary, not everything will be overridable this way but a lot will
DeWebmakers replied on at Permalink Reply
DeWebmakers
Didn't get any further with this... Instead I just overwritten the core User-class. It's the worst solution ever in my opinion. This way there is no way to update / upgrade in the right way....

I know it's not your fault, so don't get me wrong....but why isn't overwriting based on a simple config file? Just like the concrete/app.php aliases? That way you don't have to "read" entire directories. You, as programmer, deside what file is overwritten and where....

Another overriding issue that I'm having, and I'm a little scared of the answer...., is there a way to override the Javascript within a package (/packages/package_name/js/package_js.js)?
mnakalay replied on at Permalink Reply
mnakalay
To stop that redirect this is what I did and it worked.

I added the file application\controllers\single_page\dashboard\reports.php

In it I put this code:
<?php
namespace Application\Controller\SinglePage\Dashboard;
use Concrete\Core\Page\Controller\DashboardPageController;
class Reports extends DashboardPageController
{
}

Then, in application\bootstrap\app.php I added this code
Events::addListener(
    'on_start',
    function ($event) use ($app) {
        $app->bind(
            '\Concrete\Controller\SinglePage\Dashboard\Reports', 
            '\Application\Controller\SinglePage\Dashboard\Reports'
        );
        return $event;
    }
);

And that's it, no more redirect.

One problem you will have is you won't be able to display your own content on the reports page. If you need to do that you should create a new single page, put it under the main reports page and redirect to that one instead.

Concerning your JS question. You can only override it if you're using a template.

So go to application\blocks create a folder named with your block's handle. Inside add a new folder name templates. Inside add a new folder and give it the name you want (only small caps and underscores, no spaces like any handle)
Then go back to your block, copy the file view.php and the folder js and put them in the ast folder you created.

Then apply this template to your block and the JS file used will be the one from your application folder.

You can also set up your theme to automatically use that template every time you add one of those blocks to a page so it's easier.

Concerning why it's not made easier to override files, I guess you'll have to ask the core team about it.
mnakalay replied on at Permalink Reply
mnakalay
Oups I misread your JS question It's in a package, not in a block.

To load a JS file that's at a package level, the package must call some registerAsset() function and then the requireAsset() function.

All you need to do is, in your theme, use the providesAsset() function (look how they do it in Elemental) and then register and request your own JS instead.
mnakalay replied on at Permalink Reply
mnakalay
Concerning why C5 doesn't do overrides the old way, it's a matter of performance. The old way forced C5 to always check multiple places before loading a file. Server operations can be expensive so it was slow. it also means that for 1 file there could only be one override.

Now with the Laravel system, by using extend() instead of bind() for instance, several different packages can add their own stuff to the same core class. It's much more flexible and powerful. Not as easy and not all classes can be overridden but still better.

Having said so, some things can still be overridden the old way. Open the core in your editor and look for "overrideable_core_class" Every time you'll find this it will reference a file you can override the old way.

For instance, you can override the external form block's controller by putting a new one in the application directory
DeWebmakers replied on at Permalink Reply
DeWebmakers
Thank you! Very helpful! I'll get working on this. :)