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?

jgarcia
View Replies:
jgarcia replied on at Permalink Reply
jgarcia
After doing a little bit of research, I don't think this is actually possible. If someone knows for sure otherwise let me know...
nazweb replied on at Permalink Reply
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.

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.
ijessup replied on at Permalink Reply
ijessup
Your install function should look something like:
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
jgarcia replied on at Permalink Reply
jgarcia
Yeah, I tried that actually. I also tried

$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.
ijessup replied on at Permalink Reply
ijessup
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.
andrew replied on at Permalink Reply
andrew
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.
Mnkras replied on at Permalink Reply
Mnkras
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
jgarcia replied on at Permalink Reply
jgarcia
In theory, I don't think this is really possible. A single page can only be associated with one package (or with no package).
jgarcia replied on at Permalink Reply
jgarcia
Yes, andrew, I did re-install the page with the following code:

$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.
bobkennedy replied on at Permalink Reply
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:
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.
surefyre replied on at Permalink Reply
surefyre
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.
surefyre replied on at Permalink Reply
surefyre
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.
jakobfuchs replied on at Permalink Reply
jakobfuchs
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:

$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).
surefyre replied on at Permalink Reply
surefyre
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!
surefyre replied on at Permalink Reply
surefyre
Your post led me in the right direction, it would seem to be even easier than that in V8.1

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...
jakobfuchs replied on at Permalink Reply
jakobfuchs
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.
/*
     * 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,
surefyre replied on at Permalink Reply
surefyre
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.