Override core single page within a package
Permalink
I want to add some custom functionality to the page_not_found single page, which I know can be done by just copying the controller to the root /controllers directory, but I would like to do it within the context of a package.
Is there any way to get a single page to look within package for the controller?
Is there any way to get a single page to look within package for the controller?
After doing a little bit of research, I don't think this is actually possible. If someone knows for sure otherwise let me know...
This is still a top hit on Google, so I'll share how I got around this problem. First, you can't change the package id of a page through the update method.
This code will, at the time of this post, never update pkgID. In my package controller I had to write custom code to update it, since I didn't want to delete and re-create it.
The other problem is that Concrete5 has an order of precedence when overriding a single page. It checks in DIR_FILES_CONTENT, DIR_FILES_CONTENT_REQUIRED and then finally in the page's package. That means you can never override anything in the concrete/single_pages folder at a package level. I got around this by renaming these files in the controller. Here is my example code, it's highly situational, so please don't copy and paste. Ideally Concrete5 would just change its order of evaluation so that package single pages are looked at first.
You can call it like this:
Your executing user has to have rights to the folder. There are cleaner ways to do this! This is really just a proof of concept to illustrate the problem and a potential solution.
public function install() { $pkg = parent::install(); $pkgID = $pkg->getPackageID(); $p = Page::getByPath('page_not_found'); $p->update(array('pkgID' => $pkgID)); }
This code will, at the time of this post, never update pkgID. In my package controller I had to write custom code to update it, since I didn't want to delete and re-create it.
The other problem is that Concrete5 has an order of precedence when overriding a single page. It checks in DIR_FILES_CONTENT, DIR_FILES_CONTENT_REQUIRED and then finally in the page's package. That means you can never override anything in the concrete/single_pages folder at a package level. I got around this by renaming these files in the controller. Here is my example code, it's highly situational, so please don't copy and paste. Ideally Concrete5 would just change its order of evaluation so that package single pages are looked at first.
private function takeOverSinglePage(\Package $pkg, $page_path) { $db = \Loader::db(); $core_file = DIR_FILES_CONTENT_REQUIRED . rtrim($page_path, '/') . '.php'; if(file_exists($core_file)) { if(!rename($core_file, $core_file.'.naz-common-overridden.php')) { throw new \Exception("Could not override {$core_file}. Please ensure the user has permissions to the directory."); } } $db->query("update Pages set pkgID = ? where cID = ?", array($pkg->getPackageID(), Page::getByPath($page_path)->getCollectionID())); }
You can call it like this:
$this->takeOverSinglePage($pkg, '/page_not_found');
Your executing user has to have rights to the folder. There are cleaner ways to do this! This is really just a proof of concept to illustrate the problem and a potential solution.
Your install function should look something like:
Not tested, but should work. Just change the path to the page you want to change.
A dashboard page would be something like:
- IJ
public function install() { $pkg = parent::install(); $pkgID = $pkg->getPackageID(); $p = Page::getByPath('page_not_found'); $p->update(array('pkgID' => $pkgID)); }
Not tested, but should work. Just change the path to the page you want to change.
A dashboard page would be something like:
Page::getByPath('dashboard/reports/logs');
- IJ
Yeah, I tried that actually. I also tried
but apparently there's some hard-coded stuff in the dispatcher that causes it to not look at the package when loading the 404 page.
$p = Page::getByPath('page_not_found'); $p->delete(); //Add a new single page name with page /page_not_found
but apparently there's some hard-coded stuff in the dispatcher that causes it to not look at the package when loading the 404 page.
Hmmmm... look at the Pages table and look to see if the code actually changed the pkgID of the page in question.
If the code didn't work than I would editing the DB manually through the install script.
If the code didn't work than I would editing the DB manually through the install script.
After deleting the page, did you reinstall the new single page pointing to /page_not_found with your pagckage as well? If so and it's still not working then you're right, we probably have a bug.
my question, what if package 1 installs a page_not_found singlepage that for examples adds a logo, then how would package 2 for example add some text in the center,
how would we do this without manually merging the files
how would we do this without manually merging the files
In theory, I don't think this is really possible. A single page can only be associated with one package (or with no package).
Yes, andrew, I did re-install the page with the following code:
I checked the DB and confirmed that the package ID for the single page did match up, but it still did not work as expected.
$pkg = parent::install(); $pageNotFound = SinglePage::add('/page_not_found', $pkg);
I checked the DB and confirmed that the package ID for the single page did match up, but it still did not work as expected.
I made this happen, but I feel dirty. Maybe you could tell me if I've horribly overstepped...
I added the file to my package, then did a require_once() to get it from the file at {docroot}/single_pages/same_directory/same_script
Thusly:
I realize that this doesn't allow it to be installed automatically, but that doesn't matter in my case.
I added the file to my package, then did a require_once() to get it from the file at {docroot}/single_pages/same_directory/same_script
Thusly:
require_once($_SERVER["DOCUMENT_ROOT"]."/packages/packagename/single_pages/dashboard/users/search.php");
I realize that this doesn't allow it to be installed automatically, but that doesn't matter in my case.
I've been trying to override the login page. Have been able to theme it with a nasty hard-coded entry in a config file but couldn't even do that from a package installer that I could find :o(
I seriously hate these little manual tweaks, esp when you have to replicate them all on local/dev/staging/prod servers every time and remember to do it. It shouldn't be necessary. Anyhoo, I digress...
Could the single page be re-routed to your own single page which could then submit back to where the original single page submitted (e.g. in my case for login) ?
Just a thought, will maybe give it a try today and see what happens. I find it a bit dumb that so much of C5 is super-flexible but the most obvious things like 404, search, login, etc have become _harder_ to customise over time. I remember these sorts of overrides being more straightforward in 5.6.
I seriously hate these little manual tweaks, esp when you have to replicate them all on local/dev/staging/prod servers every time and remember to do it. It shouldn't be necessary. Anyhoo, I digress...
Could the single page be re-routed to your own single page which could then submit back to where the original single page submitted (e.g. in my case for login) ?
Just a thought, will maybe give it a try today and see what happens. I find it a bit dumb that so much of C5 is super-flexible but the most obvious things like 404, search, login, etc have become _harder_ to customise over time. I remember these sorts of overrides being more straightforward in 5.6.
OK so far this seems to work:
Route::register('/some/nice/url', function() {
View::element('viewName', null, 'your_package');
I did, in the package controllers on_start:
\Route::register('/login', function() { \View::element('login', null, $this->package_handle); } );
and created login.php in my <package>/elements directory.
Route::register('/some/nice/url', function() {
View::element('viewName', null, 'your_package');
I did, in the package controllers on_start:
\Route::register('/login', function() { \View::element('login', null, $this->package_handle); } );
and created login.php in my <package>/elements directory.
I agree with you that this 'basic' stuff like overriding single pages is too hard (ever tried creating a custom maintenance page?).
This used to work in version 7:
You'd put that in your package controller's on_start method. Maybe the array needs a second item that specifies the background image for the login page (I don't remember).
This used to work in version 7:
$app = \Concrete\Core\Support\Facade\Application::getFacadeApplication(); $config = $app->make('config'); $config->save('app.theme_paths./login', array('your_theme_handle'));
You'd put that in your package controller's on_start method. Maybe the array needs a second item that specifies the background image for the login page (I don't remember).
Thanks jakobfuchs, I shall try that. The more that I can define from the package installer the better, after all you'd think that was it's job!
Your post led me in the right direction, it would seem to be even easier than that in V8.1
I did try using ::set instead of ::save (which is permanent) to override in the on_start() but that didn't behave as expected... it did nothing.
Useful page discovered relating to config:
https://documentation.concrete5.org/developers/packages/storing-conf...
Now to find out what keys exist...
use \Config; public function install() { ... \Config::save('app.theme_paths./login', 'ffw_assessment'); ... } public function uninstall() { ... \Config::clear('app.theme_paths./login', 'ffw_assessment'); ... }
I did try using ::set instead of ::save (which is permanent) to override in the on_start() but that didn't behave as expected... it did nothing.
Useful page discovered relating to config:
https://documentation.concrete5.org/developers/packages/storing-conf...
Now to find out what keys exist...
In case you aren't aware of this, you can see the available single pages in /concrete/config/app.php
And it looks like the maintenance page has been added to this list in v8.
And it looks like the maintenance page has been added to this list in v8.
/* * Route themes */ 'theme_paths' => array( '/dashboard' => 'dashboard', '/dashboard/*' => 'dashboard', '/account' => VIEW_CORE_THEME, '/account/*' => VIEW_CORE_THEME, '/install' => VIEW_CORE_THEME, '/login' => array( VIEW_CORE_THEME, VIEW_CORE_THEME_TEMPLATE_BACKGROUND_IMAGE, ), '/register' => VIEW_CORE_THEME, '/frontend/maintenance_mode' => VIEW_CORE_THEME,
Viewing 15 lines of 17 lines. View entire code block.
That reminds me, I ought to be able to do the login redirection page using that too :D
I hate the way login redirect doesn't get reverted when you uninstall a package that login has a page redirected to, although I've become adept at manually fixing the C5 file, lol.
I hate the way login redirect doesn't get reverted when you uninstall a package that login has a page redirected to, although I've become adept at manually fixing the C5 file, lol.