I am developing a site in codeigniter divided into modules using HMVC. I want the modules to be enabled/disabled by an administrator, and following is how I am planning to achieve it. Since I don't have much experience in PHP/Codeigniter, the following way could be very wrong, so am looking for suggestions/feedbacks:
DB Table: Modules
ID | NAME | STATE
Above table will contain all the module information, and the state field will contain the disabled/enabled value(0 or 1).
I am going to extend the main CI_Controller, and have a function to check the status of the module:
class MY_Controller extends CI_Controller{
public function __construct()
{
parent::__construct();
$this->load->Model('Module_model'); //loads the module model
}
function check_module_state($module_name = '')
{
return $this->Module_model->getState($module_name); //the model returns TRUE or FALSE
}
Now, in the constructor of every controller, I will extend the MY_Controller class, and call the check_module_state and if it returns FALSE, will redirect the user to a "Section Disabled Page";
class Module1 extends MY_Controller{
public function __construct()
{
parent::__construct();
if($this->check_module_state('module1') == FALSE)
redirect('module_disabled', 'location');
}
}
Will the above work, is there a better and easier alternative?
Thanks
It looks ok. How you describe the functionality sounds good. I would however say that if a module is disabled it isn't necessary to tell people that it is disabled.
How I would approach this problem is as below.
I would use the module table to construct the navigation items. If a module is enabled allow a link to be displayed to that module. If the module is disabled no link is displayed. This would not mean you can get rid of security in your controllers as people may still URL Surf. In the case of URL Surfing to a disabled module I would silently re-direct to a default module, for example, the site root (Index).
Related
I'm currently working on CI for my website, and i'm having some trouble about extending Controller_CI.
I have one controller that deals with login/signin actions, which doesn't need authentication, and others controllers that check if a user session exists before loading content.
For that purpose, I created MY_Controller class and add authentication code in the constructor.
Then I made all my controller extend MY_Controller, except the first one that still extends Controller_CI
My question is : Is it the right way to deals with authentication ? Is it still possible to use Controller_CI even if it's extended ?
I found another pattern :
http://philsturgeon.co.uk/blog/2010/02/CodeIgniter-Base-Classes-Keeping-it-DRY
I guess it's better, but still, I don't understand why not using the first solution.
Thanks
Extending controller class for that purpose will work, but this solution is not much flexible. I would rather create a library that handles authentication, and run it from a controller when it is desired. Please read http://ellislab.com/codeigniter/user-guide/general/creating_libraries.html for details about creating custom libraries in CI.
Please remember you can only extend the CI_Controller with MY_Controller only once. In that aspects it's not a good idea. Suppose you want to implement another feature (e.g. a piece of code that makes a specific entry in the log) for some controllers, but not necessarily the controllers that need authentication you cannot make another MY_Controller.
Using a library is a better thing.
I'm using the flexi auth library on a big CI site. On every controller that requires authentication I just add the following:
public function __construct() {
parent::__construct();
$this->load->library('flexi_auth');
if (!$this->flexi_auth->is_logged_in())
redirect('auth/login');
}
I think a combination of what Phil Sturgeon suggests in that blog post and using a library would be best. So I would create a core controller (by that I mean a controller you place into application/core that extends CI_Controller) called MY_Controller which will look something like this
class MY_Controller extends CI_Controller
{
function __construct()
{
parent::__construct();
}
//Any other functions you want
}
Then judging by your question you currently have controllers that fit into two categories
Controllers that do require a logged in user before they do
anything
Controllers that don't require a logged in user before they do anything
So I would then create another controller in the /application/core directory that extends MY_Controller but in its constructor it checks to see if the user is logged in
class Auth_Controller extends My_Controller
{
function __construct()
{
parent::__construct();
//Check to see if the user is logged in
$this->load->library('authentication');
if(!$this->authentication->user_logged_in())
{
redirect('/login');
}
}
//Any other functions you want
}
Now when you create you controller you can choose which one of the core controllers you want to extend. If its a controller than doesn't require a logged in user you can extend MY_Controller but if it does required a logged in user you can extend Auth_Controller. That way it means you only need to do the user login check once in your code.
Like others have said if may be a good idea to place any authentication code into a library as that's a better place to put it than in a controller.
Summary
So to summarise by having all of your controllers extend core controllers rather than CI_Controller it should cut down on code repetition.
I also currently working on a CI project and had the same issue. I have came up with a different solution to deal with the authentication.
I extended the core controller as bellow,
class MY_Controller extends CI_Controller
{
public $data = array();
public $calledClass ;
public $calledMethod ;
public function __construct()
{
parent::__construct();
$authException['authentication']['login'] = true;
$authException['authentication']['logout'] = true;
$authException['welcome']['index'] = true;
$this->load->library("router");
$this->calledClass = $this->router->fetch_class();
$this->calledMethod = $this->router->fetch_method();
if(!#$authentication[$this->calledClass][$authentication->calledMethod] && !Auth::isUserLoggedIn())
{
# IS_AJAX is a contant defined in the contants.php
# define('IS_AJAX', isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
if(IS_AJAX)
{
# if this is an AJAX call, it sets a value in the header ,
# which can be captured in the AJAX response
# to redirect the user to the login page.
$this->output->set_header("auth-him:1");
exit;
}
else
{
redirect("authentication/login");
}
}
}
}
Hope the code above is clear and this helps.
To extend core controller more than one time, If you still need to use 2 controllers to handle authentication, I have followed this method
https://stackoverflow.com/a/22125436/567854
Hope this also helps :)
So, i have some problem.
I just want to create some website where visitor can interact with my site if they're registered.
Let say they've provided their username,email,password, blah..blah..blah...
And then after they provided their blah..blah..blah.. it will autologin (if their data is passed) to my site.
After they logged in my site, they must provided more data again, like they uploaded their profile picture, how they control their privacy in my site, like step by step registration.
I don't want they interact with my site, until they complete their registration.
How do i make every page in my site looks like registration page until they finished their registration.
It's not like i will give this kind of function right.
if(is_login()){
if(is_registration_complete()){
//you're free to go
} else {
// complete your registration first
}
} else {
//you're not logged in
}
In my every controller, if you know what I mean :)
How do I create this function globaly?
If their registration isn't complete, they will go to registration controller, in every routes.
If they complete their registration, they will go to the, yeah you know the default routes.
I'm so sorry if my English is bad, English isn't my native language, sorry for grammar mistakes :)
The easiest is probably to create a library with your checking function and then to include it in the Constructor of the impacted ControllerS :
class Blog extends CI_Controller {
public function __construct()
{
parent::__construct();
// Load the lib here or Autoload
$this->load->library('mylogincheckhelper');
$this->mylogincheckhelper->is_complete();
}
}
And then you do all the ckecks and routing in the Lib.
create a view with your post-registering stuff and make them conditionally visible. and include the view in your templates.
One way you can do it is to create a custom controller by extend the core CI_Controller. Then you can have your page controllers extend from your custom controllers. By extending, you inherit the functions of the parent, as well as run the parent constructor (or run it if you override it), making them "globally available" to whoever extends it.
//extend the core controller
class MY_Controller extends CI_Controller {
//override to provide additional functionality
public function __construct(){
//run the core controller
parent::__construct();
//then do your login and registration checks here
//you can put code here, call another function or load a helper class
//or better, redirect them to your registration page
}
}
//your page's controller extending from your custom controller
class Page extends MY_Controller {
//not overriding the constructor will execute the parent constructor
//every page that extends your extended controller will inherit it's functions
//and execute it's constructor
}
I'm going to do my first site in code ignitor, a fairly basic site like this:
home
login / register
members area
protected page 1
protected page 2
protected page 3
general info section
page 1
page 2
page 3 (dynamic table of reports)
about section
page 1
page 2
blog section
listing
article page
I've gone through a couple of basic tuts and have read some of the documentation but still feel unsure on what would be the best way to structure this. Could anyone that is experienced with CI show me an example of how they' do it?
some specific Qs are:
header with nav panel will be the same on all pages. normally i'd code that as an include with if/else to show highlighted current section. I guess I'd just keep this as an include (view) and either load it first via the controller or include it in every view?
I'd envisage having a model called 'user' which will handle the login and registration, a model called 'blog' and a model called 'reports'. Does that sound right?
for static sections like about, I guess there'd be no model and i'd just have a controller with a function for each static page? i.e. about.php with page1(), page2() and all they do is load static views?
1 -> To fix this problem, I decided to use my own controller like this
Using CI 2.x, create a file under app/core called MY_Controller.php like so :
<?php
class MY_Controller extends CI_Controller {
function __construct() {
parent::__construct();
}
public function loadView($view) {
$this->load->view('header');
$this->load->view($view);
$this->load->view('footer');
}
}
And then I extend this controller instead of the CI one. Be sure that your $config['subclass_prefix'] = 'MY_'; in the config.php file.
2-> yes
3-> thats about it :D
I'm a newbie here (codeigniter) but:
For headers/footers I adopted the template strategy from here (first alternative). Worked nice.
Before models, I'd plan the controllers -roughly one per each section. I made all them inherit from a MY_controller here I place common funcionalilty. And yours models seems about right to me. I think them rathar as DAOs, or "service objects" that provide access to the database and not much more. GEneral intelligence of the site (if needed) should be in a custom library, or inside the controllers.
Yes.
You should use an CI library to handle your user registration and per page authorisation.
Here's a very simple example on how you could do it. Keep in mind that CI uses the MVC pattern
class Reports extends CI_Controller {
public function __construct() {
parent::__construct();
// load database if needed
// load a model if needed
}
public function page() {
//get the page requested
$page_id = $this->uri->segments(2);
// based on the page_id do something.
$data['somedata'] = 'About us data here';
// this is an actual file loaded from the template view
$data['maincontent'] = 'my_page';
$this->load->view('template',$data);
}
}
class About extends CI_Controller {
public function __construct() {
parent::__construct();
// load database if needed for this page
}
public function page() {
// same here
//get the page requested
$page_id = $this->uri->segments(2);
// based on the page_id do something.
$data['somedata'] = 'About us data here';
// this is an actual file loaded from the template view
$data['main_content'] = 'my_about_page';
$this->load->view('template',$data);
}
}
in the template file
$this->load->view('template/header');
$this->load->view('template/nav');
$this->load->view($main_content);
$this->load->view('template/footer');
I have a question for you. I'm not sure how to structure a PHP Module for CMS maybe when I create a new Page I want to select the Module which are created, for example
News Module, Gallery Module etc..
How can I structure this and implement those modules in PHP CMS ?
In your database you should hold a modules table that consists of the following:
id
module_name
module_desc
module_folder
module_active
so that you can keep modules organized, in the table where you have module_folder this should be the location of the module such as
cms_root() . "/modules/%module_folder%/main.module.php"
This is where interfaces would come in handy :)
interface IModule
{
public function __run($param);
public function __version();
public function __shutdown();
//...
}
you should also have a class called Module where the module would extend and gather rights to templates/database ect.
class Module
{
public $DB,$TPL; /*...*/
/*
Functions ehre to help the module gain access to the instance of the page.
*/
}
Also the Module class should be able to execute modules and keep track of executed modules, so in your core code you can say $Module->RunAll() and it would run them.
A module file would probably look like:
class Gallery_Module extends Module implements IModule
{
public function __version()
{
return '1.0';
}
public function __run()
{
//Assign module data to the template :)
}
public function __shutdown()
{
//Clean old records etc from DB
}
}
And within your core as said above, you can get the Module class to read active modules from the database load the files from the folder and create an instance of the class, followed by there execution.
Hope this helps.
Let's suppose that we have poor MVC framework without modules support. Our aim is to implement admin panel with some functionality.
Url for all admin panel features will start with /admin (/admin/add_user, /admin/remove_user) etc.
As we don't have modules, so we have to create Admin controller (yes, this controller probably will be extra large).
<?
class AdminController extends Controller {
public function addUser() {
...
}
public function removeUser() {
...
}
}
?>
How can we protect this methods of being accessed by anyone? .htaccessing /admin folder is not a good idea, I think.
Thank you.
Make all functions private and implement a public function __call which checks whether the user is logged in and has appropriate rights and then either throws an error message or redirects to the correct method.
Well I don't know if your MVC model have it but if so you an use a pre-dispatch mechanism.
Or may be check it in the initialization.