lonnieezell / Bonfire

Jumpstart your CodeIgniter web applications with a modular, HMVC-ready, backend.
http://cibonfire.com
1.39k stars 526 forks source link

Custom module menu breaks developer context display #1200

Open cdatwood opened 8 years ago

cdatwood commented 8 years ago

Hello,

I have a custom module built using the Module Builder in the Developer area of the admin, and defined a custom menu in the module's config/config.php file. Other contexts such as Reporting and Settings operate as expected, but when attempting to access the Developer context, it errors out saying it can't locate the menu file.

application/config/application.php (contexts):

//------------------------------------------------------------------------------
// !CONTEXTS
//------------------------------------------------------------------------------

// Contexts provide the main sections of the admin area.
// Only two are required: 'settings' and 'developer'.
// The name of the context displayed in the UI is determined by language strings
// as defined in application_lang.php. The string must follow the format:
//      context_content_name
// The icon displayed is chosen automatically from the file:
//      theme/images/context_context_name.png
$config['contexts'] = array('datamart','reports','settings','developer');

/application/modules/my_module/config/config.php

<?php defined('BASEPATH') || exit('No direct script access allowed');

$config['module_config'] = array(
    'description'   => 'Brand Management',
    'name'          => 'Brands',
     /*
      * Replace the 'name' entry above with this entry and create the entry in
      * the application_lang file for localization/translation support in the
      * menu
     'name'          => 'lang:bf_menu_brands',
      */
    'version'       => '0.0.1',
    'author'        => 'chris.atwood',
    'menus'         => array(
        'datamart'  => 'datamart/_top_sub_nav',
        'developer' => '', //added after the break to see if having a blank menu for the module would remove it. 
    ), 
);

Trying to go to /admin/developer (or any context index page) takes me back to /admin/datamart for the URL. If I remove my menus array from the module config, it looks like it reverses and everything goes back to working as expected. Any thoughts on getting that sub-menu to cooperate?

mwhitneysdsu commented 8 years ago

Can you post the actual error message you receive? I really wouldn't expect /admin/developer to do anything more than display a 404 message unless you set a home route for it. Does the datamart context menu display properly?

cdatwood commented 8 years ago

Within the datamart context page, it works exactly as expected.

Here's the error I get on any other context page: An Error Was Encountered

Unable to load the requested file: datamart/_top_sub_nav.php

That file is what defines the submenu, but within the datamart context it picks it up just fine.

mwhitneysdsu commented 8 years ago

Is the file /application/modules/datamart/views/_top_sub_nav.php or /public/themes/{admin_theme}/admin/datamart/_top_sub_nav.php?

As far as I can tell, it should work if it's in the /application/modules/datamart/views directory, but the themes directory will cause the error you're describing.

cdatwood commented 8 years ago

It's in /application/modules/datamart/views/_top_sub_nav.php — should I move it over to the public theme?

mwhitneysdsu commented 8 years ago

No, it should be working where it is.

mwhitneysdsu commented 8 years ago

I guess the first thing to do is to remove the blank entry for developer from the config file, so your menus array should just be:

'menus'         => array(
        'datamart'  => 'datamart/_top_sub_nav',
    ), 

What does your contexts routing look like? For example, the default contexts in /application/config/routes.php look like this:

// Contexts
Route::prefix(SITE_AREA, function(){
    Route::context('content', array('home' => SITE_AREA .'/content/index'));
    Route::context('reports', array('home' => SITE_AREA .'/reports/index'));
    Route::context('developer');
    Route::context('settings');
});

Also, have any other routes been modified related to SITE_AREA, 'developer', or 'datamart'?

cdatwood commented 8 years ago

Okay, modified the module config file to reflect that change.

On the routes file, I was able to solve the developer and settings areas forwarding back to datamart by giving them routes to an index page like reports and content. Datamart also has its own index page route, but no other modifications .

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/*
| -------------------------------------------------------------------------
| URI ROUTING
| -------------------------------------------------------------------------
| This file lets you re-map URI requests to specific controller functions.
|
| Typically there is a one-to-one relationship between a URL string
| and its corresponding controller class/method. The segments in a
| URL normally follow this pattern:
|
|   example.com/class/method/id/
|
| In some instances, however, you may want to remap this relationship
| so that a different class/function is called than the one
| corresponding to the URL.
|
| Please see the user guide for complete details:
|
|   http://codeigniter.com/user_guide/general/routing.html
|
| -------------------------------------------------------------------------
| RESERVED ROUTES
| -------------------------------------------------------------------------
|
| There are two reserved routes (three in CI3):
|
|   $route['default_controller'] = 'welcome';
|
| This route indicates which controller class should be loaded if the
| URI contains no data. In the above example, the "welcome" class
| would be loaded.
|
|   $route['404_override'] = 'errors/page_missing';
|
| This route will tell the Router which controller/method to use if those
| provided in the URL cannot be matched to a valid route.
|
| CI3:
|
|   $route['translate_uri_dashes'] = FALSE;
|
| This is not exactly a route, but allows you to automatically route
| controller and method names that contain dashes. '-' isn't a valid
| class or method name character, so it requires translation.
| When you set this option to TRUE, it will replace ALL dashes in the
| controller and method URI segments.
|
| Examples: my-controller/index -> my_controller/index
|       my-controller/my-method -> my_controller/my_method
*/

$route['default_controller'] = 'home';
$route['404_override'] = '';

// Authentication
Route::any(LOGIN_URL, 'users/login', array('as' => 'login'));
Route::any(REGISTER_URL, 'users/register', array('as' => 'register'));
Route::block('users/login');
Route::block('users/register');

Route::any('logout', 'users/logout');
Route::any('forgot_password', 'users/forgot_password');
Route::any('reset_password/(:any)/(:any)', 'users/reset_password/$1/$2');

// Activation
Route::any('activate', 'users/activate');
Route::any('activate/(:any)', 'users/activate/$1');
Route::any('resend_activation', 'users/resend_activation');

// Contexts
Route::prefix(SITE_AREA, function(){
    Route::context('content', array('home' => SITE_AREA .'/content/index'));
    Route::context('reports', array('home' => SITE_AREA .'/reports/index'));
    Route::context('datamart', array('home' => SITE_AREA .'/datamart/index'));
    Route::context('developer', array('home' => SITE_AREA .'/developer/index'));
    Route::context('settings', array('home' => SITE_AREA .'/settings/index'));
});

$route = Route::map($route);

if (defined(CI_VERSION) && substr(CI_VERSION, 0, 1) != '2') {
    $route['translate_uri_dashes'] = false;
}
mwhitneysdsu commented 8 years ago

It shouldn't really matter, but did you build controllers for Developer and Settings (typically /application/modules/admin/controllers/Developer.php and /application/modules/admin/controllers/Settings.php)?

If not, you should be getting a 404 error when you visit /admin/developer or /admin/settings, rather than a file not found error. However, if you did, maybe something else is causing the problem. For testing purposes, can you set the developer and settings contexts to point to the content home?

Route::prefix(SITE_AREA, function(){
    Route::context('content', array('home' => SITE_AREA .'/content/index'));
    Route::context('reports', array('home' => SITE_AREA .'/reports/index'));
    Route::context('datamart', array('home' => SITE_AREA .'/datamart/index'));
    Route::context('developer', array('home' => SITE_AREA .'/content/index'));
    Route::context('settings', array('home' => SITE_AREA .'/content/index'));
});

Without a home, they still shouldn't redirect to datamart, though maybe it would if you've set it as your role's default context. At least if they're set to the same home as content, they should behave the same as the Content context.

cdatwood commented 8 years ago

I have controllers for them in application/controllers/admin/ but not for the module since I didn't want it to appear under those contexts. Let me change that routing, and I'll create controllers for those two within the module and we'll see if that helps!

cdatwood commented 8 years ago

Okay, they both have controllers in the module now, and the module pages for those sections work with the menu enabled, but other areas of those contexts do not (including index). Still cites it cannot find the nav file.

Made the change to routes.php, and those contexts load as expected when the module's menu is commented out.

An Error Was Encountered

Unable to load the requested file: datamart/_top_sub_nav.php

Is there a way to specify a more specific path, and would that help?

mwhitneysdsu commented 8 years ago

I know that sometimes it's easy to start changing things all over the place to try to fix a problem, but it's really difficult to troubleshoot a problem that way, and often when the problem goes away, it's hard to determine what actually fixed it.

What I want you to do is return the site to the state it was in when you first filed the issue. Then I want you to only modify the config.php file for the module to remove the empty setting for the developer context in the menus array. Then the routes for the contexts should be configured to look like this:

Route::prefix(SITE_AREA, function(){
    Route::context('content', array('home' => SITE_AREA .'/content/index'));
    Route::context('reports', array('home' => SITE_AREA .'/reports/index'));
    Route::context('datamart', array('home' => SITE_AREA .'/datamart/index'));
    Route::context('developer');
    Route::context('settings');
});

Now, you won't be able to access the /admin/developer or /admin/settings pages directly like this, but /admin/content and /admin/reports should work. If the only context that works when you do this is the datamart context, we need to focus on determining why the other contexts aren't working, preferably one at a time, unless something has been done to the system that would specifically affect both (or all) contexts at the same time.

cdatwood commented 8 years ago

Okay, I've changed those files back to the way they were initially, and I've backed up/removed the controllers added a short while ago so they aren't associated with any context. It should be back to original state.

The config for the module has the menu code in the array as follows:

    'menus'         => array(
        'datamart'  => 'datamart/_top_sub_nav',
    ), 

The routes file for the application is as follows:

Route::prefix(SITE_AREA, function(){
    Route::context('content', array('home' => SITE_AREA .'/content/index'));
    Route::context('reports', array('home' => SITE_AREA .'/reports/index'));
    Route::context('datamart', array('home' => SITE_AREA .'/datamart/index'));
    Route::context('developer');
    Route::context('settings');
});

Result when trying to load admin/developer and admin/settings (tested some other areas, and admin/datamart, admin/content and admin/reports also do not load, all with the same error):

An Error Was Encountered

Unable to load the requested file: datamart/_top_sub_nav.php
mwhitneysdsu commented 8 years ago

With that routes file, admin/developer and admin/settings should give a 404 error or redirect to your role's default context. admin/content should display the view in /application/views/admin/content/index.php and admin/reports should display the view in /application/views/admin/reports/index.php.

If this is not the case, then something is interfering with the normal operation of the system. Have you changed any of the following:

cdatwood commented 8 years ago

Hi Mat, Appreciate your continued help here! Answers to your questions below. - C

the views listed above (either directly or by adding /application/modules/admin/views/{context}/index.php or /public/themes/{admin_theme}/{context}/index.php)

the controllers (/application/controllers/admin/{context}.php or adding /application/modules/admin/controllers/{context}.php or /application/modules/admin/controllers/Admin.php)

the base controllers (Admin_Controller, Authenticated_Controller, Base_Controller, or MX_Controller) the admin theme

the admin theme

Datamart.php under /application/controllers/admin/

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
 * Bonfire
 *
 * An open source project to allow developers get a jumpstart their development of CodeIgniter applications
 *
 * @package   Bonfire
 * @author    Bonfire Dev Team
 * @copyright Copyright (c) 2011 - 2013, Bonfire Dev Team
 * @license   http://guides.cibonfire.com/license.html
 * @link      http://cibonfire.com
 * @since     Version 1.0
 * @filesource
 */

// ------------------------------------------------------------------------

/**
 * Datamart context controller
 *
 * The controller which displays the homepage of the Content context in Bonfire site.
 *
 * @package    Bonfire
 * @subpackage Controllers
 * @category   Controllers
 * @author     Bonfire Dev Team
 * @link       http://guides.cibonfire.com/helpers/file_helpers.html
 *
 */
class Datamart extends Admin_Controller
{

    /**
     * Controller constructor sets the Title and Permissions
     *
     */
    public function __construct()
    {
        parent::__construct();

        Template::set('toolbar_title', 'Datamart');

        $this->auth->restrict('Brands.Datamart.View');
    }//end __construct()

    //--------------------------------------------------------------------

    /**
     * Displays the initial page of the Content context
     *
     * @return void
     */
    public function index()
    {
        Template::set_view('admin/datamart/index');
        Template::render();
    }//end index()

    //--------------------------------------------------------------------

}//end class
mwhitneysdsu commented 8 years ago

The module builder provided views for all the contexts under /application/modules/admin/views/ and I added several under /datamart as I was building out that module.

Module Builder shouldn't do anything in that location unless you build a module named 'admin', which, in itself, could be a problem.

/application/controllers/admin/{context}.php has one controller per context, and they are unmodified. The one for Datamart was added, and copied from the Settings.php file, with all instances of Settings changed to Datamart.

https://github.com/ci-bonfire/Bonfire/tree/develop/application/controllers/admin As you can see here, this directory would normally just contain Content.php, Home.php, and Reports.php. The Datamart controller you posted looks like it should be fine, though.

/application/modules/admin/controllers/ has one controller per context and was added by the module builder.

Again, this is where things get weird, because module builder shouldn't do this unless you build a module named 'admin'. These controllers will override the controllers in /application/controllers/admin/. More than likely, if you rename your 'admin' module, things will start working properly (though you may also need to rename some permissions).

cdatwood commented 8 years ago

Okay, I apologize I think I misread two of those paths and thought you meant under the new module that we have been looking at (which is what those answers apply to). Didn't mean to abuse that time.

Okay, so looking under the path: /application/modules/ I have an index.html file, and folders for two modules I built using the module builder. One of those has not been modified. There is not a module for /admin, which would preclude any files from being in /application/modules/admin/controllers and /applications/modules/admin/views. (Also just unzipped the Bonfire core files again from the one I downloaded, and under /application/modules there is only an index.html file).

The module builder provided views for all the contexts under /application/modules/admin/views/ and I added several under /datamart as I was building out that module.

The files I spoke of here are under /application/modules/{my_module}/views, not /application/modules/admin/views. Sorry again for the confusion. There are admin folders under /application/controllers and /application/views. (Just clarifying as I re- checked those folders based on your previous post to make sure I didn't mess up paths a second time.)

mwhitneysdsu commented 8 years ago

You definitely don't want an 'admin' module at this point. If you were to create one, anything in it would override the files I'm discussing below. You also should not have an Admin.php controller in /application/controllers/, as this could potentially cause some conflicts, as well.

Basically:

You could also have a datamart directory under /application/views/admin/, but the primary purpose of doing so would be to add an index.php file displayed when you visit /admin/datamart/.

cdatwood commented 8 years ago

Okay, we are on the same page with files, and back to originally reported behavior.

Menu in my module is still enabled: /admin/content, /admin/reports and /admin/datamart begin loading the page, but error out on the module's nav in the dropdown.

An Error Was Encountered Unable to load the requested file: datamart/_top_sub_nav.php

/admin/settings and /admin/developer automatically load /admin/datamart.

mwhitneysdsu commented 8 years ago

Has your admin theme been modified in some way? If /admin/datamart loads, the others should load. The only reason I can think of for it to stop on that file in one context but not another would be if something is trying to load it through the Template library, which the context menu itself does not do.

cdatwood commented 8 years ago

Just to clarify, /admin/datamart also has the same error about not finding the file. /admin/datamart/{my_module} DOES load and has the menu in place/rendered.

Just did a side-by-side comparison, and there were two stylesheets added to the header.php under /public/themes/admin and one javascript file for datatables added into footer.php. I replaced both of those files with stock, no change. The other files are all dated 8/21/15.

Here is the entire config file for my module (only place that references that nav).

config.php under /admin/modules/{my_module}/config:

<?php defined('BASEPATH') || exit('No direct script access allowed');

$config['module_config'] = array(
    'description'   => 'Brand Management',
    'name'          => 'Brands',
     /*
      * Replace the 'name' entry above with this entry and create the entry in
      * the application_lang file for localization/translation support in the
      * menu
     'name'          => 'lang:bf_menu_brands',
      */
    'version'       => '0.0.1',
    'author'        => 'chris@mycompany.com',
    'menus'         => array(
        'datamart'  => 'datamart/_top_sub_nav',
    ), 

);

Random thought: does that nav file need to be moved up a level and renamed? Or should it stay within the context folder under /application/module/{my_module}/views/admin?

mwhitneysdsu commented 8 years ago

I'm running out of ideas on this one. Is $config['template.admin_theme'] set to 'admin'? (Normally this is set in /application/config/application.php, but it is possible to override the setting elsewhere.)

Do any of the controllers change the theme by calling Template::set_theme() or Template::set_default_theme()?

I may not be able to get back to this until Monday. The nuclear option would be to backup your existing environment and overwrite everything with a clean download of Bonfire, get everything working, then start adding things back in from your modules until something breaks.

cdatwood commented 8 years ago

Checked, $config['template.admin_theme'] is set to admin, and none of the controllers I've written change the theme. They all just call ::render()

I really appreciate all the time you took today. Thank you Mat!