Create a mobile version of my CodeIgniter site - mobile

I know this has been asked on here many, many times before, but I am trying to optimize our company gateway for mobile. This was contracted out in 2016 but the guy that started building it wasn't building it for mobile. And he didn't finish it. Now, please know I cut my teeth on ASP.Net so PHP is not my forte, but I inherited this project to finish it. It was built with CodeIgniter 2.2 and I can't upgrade it. Everything mobile (that I work on) will be built using Bootstrap (latest version), as I love Bootstrap and how easy it makes building things with "mobile-first" in mind. So I don't want to try changing all the layout and files that he built because everything is working and displaying really good in all the browsers, but not for mobile. Also know that I have looked at EVERY example that I could find on here (SO). I have tried everything. Problem is, I CAN get my "mobile" login view to display, but what I DON'T know is where to go from there. Do I create separate models and controllers just for the mobile pages that I will be building out? I will show example code of what I've tried below and what has worked. Has anyone tried anything like this and have a working example? At least have enough code that you could point me in the right direction? I would HIGHLY appreciate it. Wracking my brains here. Oh yeah, and I did update the user_agent code from the latest version of CodeIgniter (3.1.11) to catch all the latest mobile browsers because I know CI 2.2 is a little outdated. Btw, I tried to supply as much information as possible as it is working if you visit with a regular computer it shows the regular login page and it logs in just fine. If however you visit with a mobile device, it displays the mobile login page, but will not log in. Just refreshes the page. No matter if I go the user_agent route and use the same URL, or if I go the Detect_Mobile.php route and load the mobi.gatewayurl.com page.
Ok, so here's the layout that's on the host. Sorry I know this is complicated, but all this layout was already done, all I added was the mobile and mobi directories.
|--public_html
|--gateway
|--gw_application
|--controllers
|--account.php
|--administrator.php
|--login.php
|--core
|--MY_Loader.php
|--helpers
|--hooks
|--models
|--account_model.php
|--administrator_model.php
|--login_model.php
|--other models as well
|--third_party
|--Mobile_Detect.php <== Was also playing around with this as well.
|--views
|--account
|--dashboard.php
|--All the other views for the regular users.
|--administrator <== Depending on what role they play. A few users will be an Administrator.
|--dashboard.php
|--All the other views for the Administrators. Which should only be a few people. Most will fall under "account".
|--mobile <== Do I put another set of models and controllers in here?
|--account <== Copied all views from the account directory for regular users in here as well.
|--templates <== The mobile login page loads by putting in a blank header.php, heading.php, and footer.php in here. Otherwise it throws errors if I don't have these in here.
|--login.php <== Or I tried even naming it mlogin.php for mobile, but it won't login. It loads, but just refreshes the page.
|--templates
|--admin
|--header.php
|--heading.php
|--footer.php
|--header.php
|--heading.php
|--footer.php
|--login.php <== The MAIN login page when visiting through a normal browser.
|--gw-system
|--core
|--blah blah
|--mobi <== Also this works using Detect_Mobile.php redirecting it to here with the URL as https://mobi.ourgatewayurl.com.
And here's my MY_Loader.php file that I got from SO article 13928677.
<?php if (! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Loader extends CI_Loader
{
//overides existing view function
function view($view, $vars = array(), $return = FALSE)
{
$CI =& get_instance();
$CI->load->library("user_agent");
if($CI->agent->is_mobile()){
$view = 'mobile/'.$view;// <== This does diplay my mobile login page without changing the URL.
}
return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
}
}
//This works good to keep the same URL, such as https://gateway.ourgatewayurl.com, but where to go from here? Otherwise using Mobile_Detect.php, I
//can redirect to https://mobi.ourgatewayurl.com. That would be no problem as well, I can do the subdomain thing, but again, where to go from here?
?>
Here's the login.php controller. Again, I didn't write this, it was already done.
<?php
class Login extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->library('session');
$this->load->model('login_model');
$this->load->model('setting_model');
$this->load->model('producer_model');
$this->load->model('company_model');
$this->load->model('report_model');
$this->load->model('administrator_model');
}
public function index()
{
$data['settings'] = $this->setting_model->get_setting();
$data['bad_login'] = 0;
if ($this->input->post('username')) {
$data['login'] = $this->login_model->get_login($this->input->post('username'),$this->input->post('password'));
if ($data['login'] == '0') {
$level = $this->session->userdata('user_level');
if ($level == 'administrator') {
$data['message_list'] = $this->administrator_model->get_message();
$this->load->view('templates/admin/heading', $data);
$this->load->view('templates/admin/header', $data);
$this->load->view('administrator/dashboard', $data);
$this->load->view('templates/admin/footer', $data);
$this->load->view('administrator/leftmenu', $data);
} else {
$data['company'] = $this->company_model->get_company($this->session->userdata('company_id'));
$data['producer'] = $this->producer_model->get_producer($this->session->userdata('producer_id'));
$data['message_list'] = $this->administrator_model->get_message();
$this->load->view('templates/admin/heading', $data);
$this->load->view('templates/admin/header', $data);
$this->load->view('account/dashboard', $data);
$this->load->view('templates/admin/footer', $data);
$this->load->view('account/leftmenu', $data);
}
} else {
$data['bad_login'] = 1;
$data['login'] = '1';
$this->load->view('templates/heading', $data);
$this->load->view('templates/header', $data);
$this->load->view('login', $data);
$this->load->view('templates/footer', $data);
}
} else {
$data['login'] = '1';
$this->load->view('templates/heading', $data);
$this->load->view('templates/header', $data);
$this->load->view('login', $data);
$this->load->view('templates/footer', $data);
}
}
}
?>
Here's part of the account.php controller. I left a lot of it out.
<?php
//Only showing for index and dashboard, but he has a public function for every page he has on the gateway.
class Account extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->library('session');
$this->load->library('My_PHPMailer');
$this->load->model('account_model');
$this->load->model('setting_model');
$this->load->model('user_model');
$this->load->model('producer_model');
$this->load->model('company_model');
$this->load->model('email_model');
$this->load->model('report_model');
$this->load->model('pagenate_model');
$this->load->model('staff_model');
$this->load->model('signup_model');
if (!$this->session->userdata('user_id') || ($this->session->userdata('user_level') != 'corporate' && $this->session->userdata('user_level') != 'branch' && $this->session->userdata('user_level') != 'producer')) {
header("Location: " . base_url() . 'index.php?/login/logout/');
die();
}
}
public function index()
{
$data['settings'] = $this->setting_model->get_setting();
$data['company'] = $this->company_model->get_company($this->session->userdata('company_id'));
$data['producer'] = $this->producer_model->get_producer($this->session->userdata('producer_id'));
$data['message_list'] = $this->account_model->get_message();
$this->load->view('templates/admin/heading', $data);
$this->load->view('templates/admin/header', $data);
$this->load->view('account/dashboard', $data);
$this->load->view('templates/admin/footer', $data);
$this->load->view('account/leftmenu', $data);
}
public function dashboard()
{
$data['settings'] = $this->setting_model->get_setting();
$data['company'] = $this->company_model->get_company($this->session->userdata('company_id'));
$data['producer'] = $this->producer_model->get_producer($this->session->userdata('producer_id'));
$data['message_list'] = $this->account_model->get_message();
$this->load->view('templates/admin/heading', $data);
$this->load->view('templates/admin/header', $data);
$this->load->view('account/dashboard', $data);
$this->load->view('templates/admin/footer', $data);
$this->load->view('account/leftmenu', $data);
}
}
?>
And here's the login model.
<?php
class Login_model extends CI_Model {
public function __construct()
{
$this->load->database();
}
public function get_login($username, $password)
{
$this -> db -> select('*');
$this -> db -> from('user');
$this -> db -> where('user_email = ' . "'" . $this->db->escape_str($username) . "'");
$this -> db -> where('user_password = ' . "'" . $this->db->escape_str(do_hash($password, 'md5')) . "'");
$this -> db -> where('user_is_deleted = 0');
$this -> db -> limit(1);
$query = $this -> db -> get();
$user = $query->row_array();
if($query -> num_rows() == 1) {
$this -> db -> select('*');
$this -> db -> from('user_level');
$this -> db -> where('user_level_id = ' . $user['user_level_id']);
$this -> db -> where('user_level_is_deleted = 0');
$this -> db -> limit(1);
$query2 = $this -> db -> get();
$user_level = $query2->row_array();
$pid = '';
$coid = '';
$ctype = '';
if ($user_level['user_level_name'] != 'administrator') {
$this -> db -> select('*');
$this -> db -> from('producer');
$this -> db -> where('user_id = ' . $user['user_id']);
$this -> db -> limit(1);
$query2 = $this -> db -> get();
$producer = $query2->row_array();
$this -> db -> select('*');
$this -> db -> from('company');
$this -> db -> where('company_id = ' . $producer['company_id']);
$this -> db -> limit(1);
$query3 = $this -> db -> get();
$company = $query3->row_array();
$pid = $producer['producer_id'];
$coid = $producer['company_id'];
$ctype = $company['company_type_id'];
}
$newdata = array(
'user_id' => $user['user_id'],
'producer_id' => $pid,
'company_id' => $coid,
'company_type_id' => $ctype,
'user_level_id' => $user['user_level_id'],
'user_level' => $user_level['user_level_name'],
'user_name' => $user['user_first_name'] . ' ' . $user['user_last_name']
);
$this->session->set_userdata($newdata);
return '0';
} else {
$_SESSION['user_id'] = '';
$_SESSION['producer_id'] = '';
$_SESSION['company_id'] = '';
$_SESSION['company_type_id'] = '';
$_SESSION['user_level_id'] = '';
$_SESSION['user_level'] = '';
$_SESSION['user_name'] = '';
return '1';
}
}
}
?>
Sorry guys this is SO LONG. I just wanted everyone to see how this was laid out and how he had the code written. ANY help will be HIGHLY appreciated. Thanks so much.

Nevermind, I've decided to convert the whole thing to Bootstrap. Its just better if I do that. It needs to be anyway. Bootstrap works so good for mobile anyway.

Related

CakePHP 2.5 Datasource, create and return response

I have a specific task to connect CakePHP web application to a remote restful server . I create a datasource, read method works great, but the api after save data return an array of processed data.
Looking for a way to return the data array and use in controller.
My Controller code
public function admin_generate()
{
$data = $this->request->data;
$data['path'] = 'special/generate';
$this->Tool->create();
if($this->Tool->save($data)){
// handle response ????
}
$this->set('data',$data);
$this->set('_serialize','data');
}
In datasource file
public function create(Model $model, $fields = null, $values = null)
{
$data = array_combine($fields, $values);
$api = $this->config['api_path'].$data['path'].'?auth_key='.$this->config['auth_key'];
$json = $this->Http->post($api, $data);
$response = json_decode($json, true);
if (is_null($response)) {
$error = json_last_error();
throw new CakeException($error);
}
return $response; // ??????
}
Can someone show me the correct way to use the api response data in the controller?
I found a solution, a few minutes after a post question. This can help one of you.
datasource
....
if (is_null($response)) {
$error = json_last_error();
throw new CakeException($error);
}
// SOLUTION
$model -> code = $response['code'];
$model -> key = $response['key'];
$model -> code_id = $response['code_id'];
return true;
.....
in controller
.....
if($this->Tool->save($data)){
unset($data['path']);
$data['code'] = $this->Tool->code;
$data['key'] = $this->Tool->key;
$data['code_id'] = $this->Tool->code_id;
}
.....

CakePHP 2.4 Forgotten Password

I have just started using CakePHP and love using it! I have created a login system and registration system, however am really struggling with the "forgotten password" section.
I want to use a tokenhash and expiry date in the Users DB so that it cant be abused, users would need to enter username and email to get an activation link emailed to them with a newly generated tokenhash
There are quite a few tutorials out there but I find most of them work for the first part e.g. emailing the activation link/ resetting token and timer but all seem to fail on the change of the password.
Please help me, either with a working tutorial from the net or a solution that applies the above required things.
Thanks in advance
Steve
Below I am writing the code that I wrote for one of my project, this might help you out.
1- I created a new table which contains the unique token for every user.
Table Name:- user_password_resets
Columns : userclient_id, token
2- A email template name as:- change_password.html inside /webroot/template/change_password.html
public function login_send() {
$this->isLoggedIn(); //Check if the user is logged in
if($this->request->is('post')) { #if the form is submitted
$login = $this->data['User']['login'];
$conditions = array('User.login'=>$login);
if($this->User->hasAny($conditions)) {
$users = $this->User->find('first', array('conditions'=>$conditions));
#Generate the token
$token = md5(uniqid(rand(),true));
#Save token and other details in user_password_reset_links table
$users = $this->User->find('first', array('conditions'=>array('User.login'=>$login)));
$my_name = $users['User']['first_name'];
$reset_links = array();
$reset_links['UserPasswordReset']['userclient_id'] = $users['User']['client_id'];
$reset_links['UserPasswordReset']['token'] = $token;
$conditions = array('UserPasswordReset.userclient_id'=>$users['User']['client_id']);
if($this->UserPasswordReset->hasAny($conditions)) {
$user_id = $users['User']['client_id'];
$this->UserPasswordReset->updateAll(array('UserPasswordReset.token'=>"'$token'"), array("UserPasswordReset.userclient_id"=>"$user_id"));
} else {
$this->UserPasswordReset->create();
$this->UserPasswordReset->save($reset_links);
}
$password_reset_link = BASE_URL."users/reset_password/$token";
#Send Welcome Email
$mailContent = file_get_contents(BASE_URL . "templates/change_password.html");
$rootlink = BASE_URL;
$arrMail = array(
"{NICK}" => ucfirst($my_name),
"{rootlink}" => BASE_URL,
"{SITE_TITLE}" => SITE_TITLE,
"{PASSWORD_RESET_LINK}"=>$password_reset_link
);
$mails = explode(',', $users['User']['email']);
$msg = #str_replace(array_keys($arrMail), array_values($arrMail), $mailContent);
$data = array();
$data['to'] = #$mails[0];
$data['body'] = $msg;
$data['subject'] = SITE_TITLE.'- Reset Password.';
$this->send_mail($data);
$this->Session->setFlash('A password reset link has been sent to the email address.', 'default', array('class'=>'successMsg'));
$this->redirect(array('controller'=>'users', 'action'=>'login'));
exit;
} else {
$this->Session->setFlash('The Username entered is not registered with Captain Marketing.', 'default', array('class'=>'errorMsg'));
$this->redirect(array('controller'=>'users', 'action'=>'login_send'));
exit;
}
}
$this->set('title_for_layout', '-Send password reset link');
}

Find all in CakePHP is dumping a semicolon in my view

I don't know what exactly is happening with my CakePHP app. It worked last week and I have literally changed nothing with that particular file.
When I use find 'all' in my controller it dumps a semi-colon in my view even if there is nothing in the view file.
Here is my code
$evts = $this->Event->find('all');
There is nothing in my view file. I don't know if it makes a difference but I'm using a json view.
As requested here is the complete code for the action
public function search(){
$this->Event->recursive = 2;
$conditions = array();
if(!empty($this->request->query['name'])){
$conditions = array('Event.name LIKE ' => "%" . str_replace(" ","%", $this->request->query['name']) . "%");
}
if(!empty($this->request->query['home'])){
$conditions = array('Event.home_team_id' => $this->request->query['home']);
}
if(!empty($this->request->query['away'])){
$conditions = array('Event.away_team_id' => $this->request->query['away']);
}
$limit = 25;
if(!empty($this->request->query['limit'])){
$limit = $this->request->query['limit'];
}
//$evts = $this->Event->find('all',array('conditions'=>array($conditions),'order' => array('Event.start_time'),'limit'=>$limit));
$evts = $this->Event->find('all');
$this->set('events',$evts);
}
Everything in the view has been commented out ... but here is the code anyway
$results = array();
$i = 0;
foreach ($events as $event) {
$results[$i]['id'] = $event['Event']['id'];
$results[$i]['label'] = $event['Event']['name'] . "(" . date_format(date_create($event['Event']['start_time']), 'D, jS M Y') . ")";
$results[$i]['value'] = $event['Event']['name'];
$results[$i]['home_team_name'] = $event['HomeTeam']['name'];
$results[$i]['away_team_name'] = $event['AwayTeam']['name'];
$results[$i]['sport_name'] = $event['Sport']['name'];
$results[$i]['tournament_name'] = $event['Tournament']['name'];
$results[$i]['start_time'] = $event['Event']['start_time'];
$results[$i]['img'] = $event['Event']['img_path'];
$results[$i]['listener_count'] = 0; //TODO Get the follower count
$i++;
}
echo json_encode($results);
Display
If you changed nothing with that particular file then perhaps you have installed callbacks (either in the controller or in the model) that echo that ";". Look for 'afterFind' calls...
Search your model and your controller folders for "echo statement". In fact you should search your entire app folder except for the Views folder.

Codeigniter getting result from model

i have a controller called main...
here i have this code.
$data['companies'] = $this->companies->getAllCompanies();
$this->load->view('main_view',$data);
i have loaded the model called companies in the contructer like this:
$this->load->model('companies');
and this is my model:
class Companies extends CI_Model{
function getAllCompanies()
{
$this -> db -> select('*');
//$this -> db -> from('companies');
$query = $this -> db -> get('companies');
if($query -> num_rows() > 0)
{
return $query->result();
}
else
{
return false;
}
}}
i am getting this error:
( ! ) Fatal error: Call to a member function execute() on a non-object in C:\wamp\www\awt\system\database\drivers\pdo\pdo_driver.php on line 193
Call Stack
5 0.0299 4303144 Companies->getAllCompanies( ) ..\main.php:32
whats wrong in this code plz help me!
Sounds like that you have some kind of sql error. Try enabling the db_debug flag in application/config/database.php and check the table names and such. You can try to print the generated sql query at where at the place of the error too (under system/database/drivers/pdo/pdo_driver.php:191).
class Companies extends CI_Model{
function getAllCompanies()
{
$this -> db -> select('*');
//$this -> db -> from('companies');
$result = $this -> db -> get('companies');
if($query -> num_rows() > 0)
{
return $result;
}
else
{
return false;
}
}}
make the above changes.

Cakephp, i18n, Retrieve translation records for associated models

Quoting from the cakephp Book (ver 1.3):
Note that only fields of the model you are directly doing find on will be translated. Models attached via associations won't be translated because triggering callbacks on associated models is currently not supported.
Has anyone come up with a solution for this?
If not could you give me some pointers concerning the following simple scenario.
I have 2 models:
Project, Category.
Project HABTM Category
I have properly set up i18n table and I have a few entries in the db, all translated. When I retrieve a project it does retrieve the translation but not the translated category because as it says in the cakephp book models attached via associations won't be translated.
I have another workaround; I don't know if it is any better or worse performance- or style-wise, only that it suits the "fat models, skinny controllers" goal:
AppModel.php
public function getTranslatedModelField($id = 0, $field) {
$res = false;
$db = $this->getDataSource();
$tmp = $db->fetchAll('SELECT content from s2h_i18n WHERE model = ? AND locale = ? AND foreign_key = ? AND field = ? LIMIT 1',
array($this->alias, Configure::read('Config.language'), $id, $field)
);
if (!empty($tmp)) {
$res = $tmp[0]['s2h_i18n']['content'];
}
return $res;
}
SomeModel.php
public function afterFind($results, $primary = false) {
foreach ($results as $key => $val) {
if (isset($val['SomeOtherModel']) && isset($val['SomeOtherModel']['id'])) {
$results[$key]['SomeOtherModel']['name'] =
$this->SomeOtherModel->getTranslatedModelField($val['SomeOtherModel']['id'], 'name');
}
// other possible queries for other models and/or fields
}
return $results;
}
OK I found a solution. Which is mostly a workaround. I should have thought of that earlier.
What I'm doing is the following. I'm finding all projects and recursively all categories associated with projects. Now since cakephp does not translate categories I am using the results from the initial query and I am performing a second one only for categories but using the category id values that I found on the first query. Now cakephp translates categories since I'm only searching for them and I can have their data translated.
At the moment I'm OK with this solution but it would be nice if first cakephp makes the translate behavior out of the box ready or secondly if someone had a behavior that could support retrieval of translation on associated models.
I generalized the afterFind part a bit, so that it automatically grabs the fields to translate from the associated models' actsAs["Translate"] array, and uses an array of associated models to (potentially) translate:
public function afterFind($results, $primary = false){
$modelsToTranslate = array("SomeModel", "AnotherModel");
foreach ($results as $key => $val){
foreach($modelsToTranslate as $mtt){
if (isset($val[$mtt])){
foreach($val[$mtt] as $fieldname => $fieldval){
foreach ($this->$mtt->actsAs["Translate"] as $fieldToTranslate){
$results[$key][$mtt][$fieldname][$fieldToTranslate] = $this->$mtt->getTranslatedModelField($val[$mtt][$fieldname]['id'], $fieldToTranslate);
}
}
}
}
}
return $results;
}
I took above solution and generalized both functions a bit, now it needs to be used together with the translate behaviour and both functions need to go into the model.php - everything else should work by itself:
public function getTranslatedModelField($id = 0, $field) {
$res = false;
$translateTable = (isset($this->translateTable))?$this->translateTable:"i18n";
$db = $this->getDataSource();
$tmp = $db->fetchAll(
"SELECT content from {$translateTable} WHERE model = ? AND locale = ? AND foreign_key = ? AND field = ? LIMIT 1",
array($this->alias, Configure::read('Config.language'), $id, $field)
);
if (!empty($tmp)) {
$res = $tmp[0][$translateTable]['content'];
}
return $res;
}
public function afterFind($results, $primary = false) {
if($primary == false && array_key_exists('Translate', $this->actsAs)) {
foreach ($results as $key => $val) {
if (isset($val[$this->name]) && isset($val[$this->name]['id'])) {
foreach($this->actsAs['Translate'] as $translationfield) {
$results[$key][$this->name][$translationfield] =
$this->getTranslatedModelField($val[$this->name]['id'], $translationfield);
}
} else if($key == 'id' && is_numeric($val)) {
foreach($this->actsAs['Translate'] as $translationfield) {
$results[$translationfield] =
$this->getTranslatedModelField($val, $translationfield);
}
}
}
}
return $results;
}

Resources