Adobe: no more money for you

Today Adobe announced it would no longer continue selling boxed products of it’s creative suite. So in response I say fuck off. I’ll say straight up that this is not a cost issue for me. I’ve been a paid (Australian/double cost) customer since CS3.

The issue for me is Adobe has shown such arrogance – highlighted by it’s CEO’s deplorable response to a question about boxed pricing in Aus – that I refuse out of principle to give one more cent to the ‘creative’ giant. Subscription models are all about control and ‘eco-systems’. They are designed to lock you in so as any kind of competition has no chance.

Well, despite Adobe’s apparent dominance of the design/creative industry, there are alternatives to most of their software offerings. Luckily I’m a web dev, so most of the ‘developer’ tools are mildly amusing at best.

I haven’t used Flash pro personally for over a year. Don’t even bother asking about Dreamweaver. Their attempts at code editors are a joke. Sublime Text 2 and Flash Develop are super fast and free/donateware and not in some shit locked down ‘cloud’.

GIMP does everything I need bitmap wise, and the new docked workspace finally fixes the terrible floating window UI.

So Adobe, you’ve just lost a loyal customer. Not only that, I now have as much contempt for you as it appears you have for your customers. With any luck Adobe will go broke.

/rant

Posted in rant | Leave a comment

A look into the possible future of computing

Check out this transcript. Very interesting. If you haven’t thought about the future of general purpose computing or don’t even know what that means, you should.

http://boingboing.net/2012/08/23/civilwar.html

Posted in philosophy | Comments Off

Day 1 as a web vagrant

Last week we gave up our inner city apartment to finalise our move to the country. However, my partner and I are both still heavily dependent on the Melbourne for a source of income. I’m still on a 3 day a week contract, while the other 2 days i’ll be on the farm, or if work calls, floating around the CBD somewhere on a random internet connections.

So today was my first real day of baselessness. Here is the low down of my situation at the start of the day:

  • Staying at a friends place this week some 10km from the city
  • No fixed Internet during the day
  • Only 700MB of data on my phone per recharge which I want to last 1 month.
  • In my bag I have my laptop and a raspberry pi (that’s just a coincidence)
  • Rock into the CBD at 8:30 am

I started by heading into Federation Square knowing that it provides free wifi, somewhere to sit down and some cover as Melbourne just decided to kick summer out on it’s arse and bring winter straight in. The wifi at fed square decided it wouldn’t work at that time of day. It wouldn’t surprise me if they throttle it like silly outside of business hours to stop hackers abusing the system. So I grabbed a coffee at the brilliant Little King cafe and headed towards the State Library.

It’d been a while since I’d worked in the State Library so when I got there I was reminded it doesn’t open until 10am. It was now around 9:00ish and I’m generally impatient with these things so I decided to go and buy some USB keys that I planned to use for two different linux distro installs (ubuntu and backtrack for those interested). I headed up to La Trobe St to a PC shop after a tip off from the internet and got the gear. Then I needed some headphones so headed to JB to grab a cheapo pair. I still had time to kill so decided to see if my very old student password would still get me in to RMIT’s wifi.

RMIT had actually updated their network software so apparently my student account had finally been disabled (last time I checked that it worked was about 5 years after I had graduated and ceased being a student). I decided anyway to hang out for a bit and try and get some work done. Bad Mistake. Withing 5 mins of me sitting down in the student lounge, I was accosted by 4 hopeful business/marketing students who surrounded me on my table and tried to sell me a $5 paintball package. Apparently I still look like a student so I guess that’s a wine, but these guys were unaware of fairly extensive knowledge of marketing and it’s pathetic techniques. I attentively listened to their shpeal including their ‘nearly empty’ booklet of deals and fancy printed flyer. Then when it came to the money, I announced there is no chance they are getting a cent from me. Calling them out on their scheme, they backed off and left tails between legs. I don’t really miss uni life.

The library was open by now so I headed over for free but dicey and locked down wifi. At this stage, I needed to download cygwin for windows. Fail. Filetype blocked. So being unable to work, I decided to try and download my linux distros. Fail. File size too big. I tried a torrent. Fail. File type blocked. It was time to revert to the mobile net again.

Before today, I’d had the foresight to realise I’m going to need a base of some sort pretty soon if I was to be productive at all. I’d had a look on the net for some co-working spaces and I’ve even worked in two before but never really found the right place. See I’m kind of unusual. I’m after 3 days after work hours and maybe 1-2 days normal hours. Not many places will give after hours access to part-timers, let alone web vagrants. But then I found the pretty nice Electron Workshop in North Melbourne. I had a chat to the founder Nick, and hopefully I’ll be back soon. Seemed like a cool place to do web.

Then It was back to Fed Square to give it another shot. Lo and behold the wi-fi now worked quite well. I can even download exes and torrents (although the default torrent port seems to be blocked – might work on that next time). As I type now I’m knocking back a beer in the ACMI Lounge while setting up my machine environment for my next job.

Summary

  • Being early days, it’s pretty fun rambling around searching for good wifi.
  • Good free wifi with no restrictions probably doesn’t exist
  • Office space is looking more and more likely
  • I ended up getting about 2-3 hours of actual work done. Probably about as productive as your average employee :)

 

Posted in general, hacking, linux | Comments Off

Raspberry pi GET!!!

so these little computers are all the rage right now. as I type, i’m installing RetroArch – some software for running retro emulators on a raspberry pi. check it out here: http://petrockblog.wordpress.com/retropie/.

don’t know what a pi is? it’s a AU$41 computer. you can load Linux on it. you can load android on it. you can do cool shit. it’s the size of a credit card and spits out audio and video via HDMI. check it http://www.raspberrypi.org/

I’ve gotten myself one in order to create my own dropbox clone. i’ll be following the tutorial I found here although my case will be made of lego. I actually ordered two so me and my mate could hack an pi-in-the-sky (catchy huh?) together. so i’ll have an update once it’s done. so far I’ve just installed the raspbian wheezy distro for a play. I used a Samsung Galaxy Y mobile charger as a power supply (5V 0.7A) and an 8gb SD card I had lying around the place. so far so good. check the pics:

pi and my brewery membership card

pi initial config screen

Posted in hacking, linux, raspberry pi | Comments Off

Building a CMS with CodeIgniter: Part 10 modularise

yep i’m an aussie so mind the spelling of modularise. :)

so i’m going to go back through my current code and do some organisation so it’s more useful and reusable as at the moment it’s all pretty much hard coded and a bit of a mess.

Controller super class

first, i’m going to create a class for all secure (admin) pages to sub class from. this will handle common functionality. so far this just consists of checking for an active sessions and redirecting if required. so apparently i’m supposed to create this MY_Controller but i don’t really like the MY prefix so i’m going to go into the config and change it to CMS:

$config['subclass_prefix'] = 'CMS_';

not really sure what to do if you wanted to have multiple controllers extend the CI Controller. maybe hack CI.
EDIT: found this good article and decided to implement the solution. http://philsturgeon.co.uk/blog/2010/02/CodeIgniter-Base-Classes-Keeping-it-DRY

so i create my CMS_Controller like so:

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

	class CMS_Controller extends CI_Controller
	{
		function __construct()
		{
			parent::__construct();
			
			// do stuff here that happens in all controllers
		}
	}

?>

then i create the admin controller which extends the CMS_Controller. later i can add a public controller using the same method.

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

	class Admin_Controller extends CMS_Controller
	{
		function __construct()
		{
			parent::__construct();
			
			$this->output->set_header("Cache-Control: no-store, no-cache, must-revalidate");
		
			$this->load->library('authenticate');
			$this->authenticate->checkLoggedIn();
		}
	}

?>

at this point CI has no idea about Admin_Controller so i’ve got to autoload it using PHP5. in the config file i add the following code (thanks to the link above) at the bottom. i really think it is magic.

/*
| -------------------------------------------------------------------
|  Native Auto-load
| -------------------------------------------------------------------
| 
| Nothing to do with config/autoload.php, this allows PHP autoload to work
| for base controllers and some third-party libraries.
|
*/
function __autoload($class)
{
	if(strpos($class, 'CI_') !== 0)
	{
		@include_once( APPPATH . 'core/'. $class . EXT );
	}
}

once that was done i went through and changed all my admin controllers to extend Admin_Controller instead. i also did some extra nice stuff which will be in the final files i’ll provide when this monster is done.

Make those UI elements libraries

so i want to make the wysiwyg controller more portable so i could perhaps use it in another unrelated project. so i’d want to make it a library. i’ll also do this with the upload script.

aaaaand this is where the tutorial ends. sorry I wrote this back in june and only just got around to finishing the post now in December! but I did end up kind of compiling this into one downloadable zip that you can play with. thanks for your patience.

download here

Posted in CMS, CodeIgnitor, php | Comments Off

Building a CMS with CodeIgniter: Part 9 CRUD

so the CMS currently only lets you modify (update) one page. i’m going to add in a create button so i can make a new page in my CMS. i’ll also display a list of all pages that currently exist so i can select which one to edit. the list will also have delete functionality.

Create

the create button is simply a link to a create controller. i made the controller ‘create_page.php’ which only has one function at the moment and that is to load the create view v_create.php. in this view, i’ll be asking the user for the title of the page, the uri of the page and the template to use. then the user can enter the page data on the next screen.

first for the title, i’ll need to modify the pages table in the db to have a title field. then i create a basic form for the user to enter this information. something like this in v_create_page.php:

<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>Create Page</title>
	</head>
	
	<body>
		<?php
			// TODO: ajax validation to make sure url is available.
		
			echo form_open('create_page/newPage');
			echo form_label('uri', "Enter URI:");
			echo form_input('uri');
			echo form_label('title', "Enter Title:");
			echo form_input('title');
			
			// hard code templates here for now
			$templates = array(
				'single_column' => 'Single Column',
				'double_column' => 'Double Column' 
				);
			
			echo form_label('template', "Choose template:");
			echo form_dropdown('template', $templates, 'single_column');
			echo form_submit( 'submit', 'Create' );
			echo form_close();
		?>
	</body>
</html>

i’ve put in some code comments to remind me to do some validation and replace the hard coded templates with database driven ones.

so now i create the newPage function in the controller like so:

public function newPage()
{
	$this->load->library('form_validation');
			
	$this->form_validation->set_rules('uri', 'URI', 'trim|required|is_unique[pages.uri]|xss_clean');
	$this->form_validation->set_rules('title', 'Title', 'trim|required|xss_clean');
	$this->form_validation->set_error_delimiters('<div class="error">', '</div>');
			
	if ($this->form_validation->run() == false)
	{
		$this->load->view('v_create_page');
	}
	else
	{
		echo 'success';
				
		$uri = $this->input->post('uri');
		$title = $this->input->post('title');
		$template = $this->input->post('template');
				
		$this->load->model('m_create_page');
		$response = $this->m_create_page->insertPage( $uri, $title, $template );
				
		if ( $response )
		{
			redirect( 'admin/hub' );
		}
		else
		{
			echo 'error!';
			print_r( $response );
		}
	}			
}

i’ve added in validation as after i ran my earlier code, i got database errors when you had duplicate uris. so after adding in this validation code i went back to the view and added in the form error fields and altered the input fields to contain the posted data.

echo form_input('uri', set_value('uri'));
echo form_error('uri');

Read

the list on the hub page only shows dummy data at the moment so i’ll modify that to show actual pages from the db. i created a model called m_hub with a rather simple function

<?php
class M_Hub extends CI_Model
{	
	public function getPages()
	{
		$query = $this->db->get('pages');
		
		if ($query->num_rows() > 0 )
		{
			return $query;
		}
	}
}
?>

then updated the view to spit out the pages instead of dummy data:

<table>
	<tr>
		<th class="col_page">Page</th>
		<th class="col_uri">URL</th>
		<th class="col_modified">Modified</th>
		<th class="col_actions">Actions</th>
	</tr>
	<?php 
		foreach( $pages->result() as $page )
		{
	?>
			<tr>
				<td class="col_page"><? echo $page->title; ?></td>
				<td class="col_uri"><? echo $page->uri; ?></td>
				<td class="col_modified"><? echo $page->modified ?></td>
				<td class="col_actions"><a href="">Edit</a> <a href="">Publish</a> <a href="">Delete</a></td>
			</tr>
	<?
		}
	?>
</table>

and finally i update the controller to hook it all together

public function index()
{
	$this->load->model('m_hub');
	$data['pages'] = $this->m_hub->getPages();
	
	$this->load->view('v_hub', $data);
}

Update

i think this will be the trickiest. for now, i’ll just get the edit page to display a text field for title and uri, the wysiwyg editor and the image uploader. each field will be an independent form for now. later, i might modularise them and combine them into one form – i’ll see.

==== NOTE ====

i just discovered my routing method was getting way out of hand so i’ve updated it. here is the new routing rules:

$route['default_controller'] = 'admin';
$route['404_override'] = '';
$route['admin'] = 'admin';
$route['admin/logout'] = 'admin/logout';
$route['admin/(:any)'] = "$1";
$route[':any'] = 'master';

the point of this was to separate all the admin controllers with the public facing pages. so now anything with admin in front of it will be routed to the correct controller. this means i don’t need a separate rule for all admin pages so they don’t look up the db for it. this also means that any links to controllers in the admin must be updated to have a preceding ‘admin/’.

BUG FIXES!

i’ve had a big session of bug fixes to get the edit working so i’ll just dump the code for the various files here:

update_page.php

modified the content variable to be checked for it’s existence.

// check there is content
if ( count( $content ) > 0 )
{
	$data['content'] = $content[0];
}

v_update_page.php

- prefixed ‘admin’ to the form submit urls to pass the new route test
- added a check to see if there is any content and to load appropriately. this might be cleaned up later

// if the page has content
if ( isset( $content->text ) && $content->text != '' )
{
	echo form_textarea(array( 'name' => 'content', 'class' => 'wysiwyg', 'value' => set_value('content', $content->text )));
}
else
{
	echo form_textarea(array( 'name' => 'content', 'class' => 'wysiwyg', 'value' => set_value('content' )));
}

...


if ( isset( $content ) && $content->image != '' )
{
	echo '<img src="' . base_url() . $content->image . '" />';
}

m_single_column

made a bunch of changes, entire source here

<?php
	class M_Single_Column extends CI_Model
	{	
		public function exists( $id )
		{
			$this->db->select( 'id' );
			$query = $this->db->where( 'id', $id )->get('single_column_pages');
			
			if ( $query->num_rows > 0 )
			{
				return true;
			}
			
			return false;
		}
		
		
		public function getImageURI( $id )
		{
			$this->db->select( 'image' );
			$query = $this->db->where( 'id', $id )->get('single_column_pages');
			
			if ( $query->num_rows > 0 )
			{
				return $query->row()->image;
			}
			
			return false;
		}
		
		
		public function getText( $id )
		{
			$this->db->select( 'text' );
			$query = $this->db->where( 'id', $id )->get('single_column_pages');
			
			if ( $query->num_rows > 0 )
			{
				return $query->row()->text;
			}
			
			return false;
		}
		
		
		public function getAll( $id )
		{
			return $this->db->where( 'id', $id )->get( 'single_column_pages' );
		}
		
		
		
		public function insert( $id, $field, $data )
		{
			$data = array(
			   'id' => $id,
			   $field => $data
			);
			return $this->db->insert( 'single_column_pages', $data );
		}
		
		
		public function update( $id, $field, $data )
		{
			$this->db->set( $field, $data );
			$this->db->where( 'id', $id );
			return $this->db->update( 'single_column_pages' );
		}
	}
	
?>

upload.php and wysiwyg.php

added an exists check for content

// check if entry for id exists already
$exists = $this->{ $model }->exists( $data['id'] );

if ( !$exists )
{
	$success = $this->{$model}->insert( $data['id'], $data['field'], $dbdata );
}
else
{
	$success = $this->{ $model }->update( $data['id'], $data['field'], $dbdata );
}

now i also needed to make sure that it knows whether you are updating a page or template, so i’ve made some changes.
the updateField function in the update_page controller now looks like this:

// check the posted data - ignore standard keys
foreach( $this->input->post() as $key => $value )
{
	// store table
	if ( $key == 'table' )
	{
		$table = $value;
	}
	
	// store field data
	if ( $key != 'table' && $key != 'field' && $key != 'id' && $key != 'submit' && $key != 'template' )
	{
		$data = $value;
	}
}
		
if ( $table == 'pages' )
{
	// update pages
	$this->load->model( "m_page" );
	$response = $this->m_page->updatePage( $this->input->post( 'id' ), $this->input->post( 'field' ), $data );
}
else
{
	// update template
	$this->load->model( "m_" . $this->input->post('template') );
	$response = $this->{"m_" . $this->input->post('template')}->update( $this->input->post( 'id' ), $this->input->post( 'field' ), $data );
}

and m_page model now has an update function

public function updatePage( $id, $field, $data )
{
	$this->db->set( $field, $data );
	$this->db->where( 'id', $id );
	return $this->db->update( 'pages' );
}

there was also an error in the v_update_page view so i updated the form_open for title to read like this:

echo form_open('admin/update_page/updateField');

and with that, updating is done!

Delete

the easiest one! well it’s not that easy…
i created a delete_page controller which looks like this:

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

	class Delete_Page extends CI_Controller
	{
		public function __construct()
		{
			parent::__construct();
		}
		
		
		public function index()
		{
			// run model code to delete page and associated models here
			$this->load->model( "m_" . $template );
			$templateResponse = $this->{"m_" . $template}->delete( $id );
			
			if ( $templateResponse )
			{
				$this->load->model( "m_page" );
				$pageResponse = $this->m_page-> deletePage( $id );
				
				if ( $pageResponse )
				{
					echo 'deleted!';
				}
				else
				{
					echo 'page delete error';
				}
			}
			else
			{
				echo 'template delete error';
			}
		}
	}

?>

and added this href to the link in v_hub.php:

<? echo base_url() . "admin/delete_page/index/" . $page->id . "/" . $page->controller ?>

m_page.php then got itself a delete function:

public function deletePage( $id )
{
	return $this->db->where( 'id', $id )->delete( 'pages' );
}

as did m_single_column.php:

public function delete( $id )
{
	return $this->db->where( 'id', $id )->delete( 'single_column_pages' );
}

done!
NB: you should add some validation in because if the user enters a URI that already exists, it will fail with a db error.

Posted in CMS, CodeIgnitor, php | 5 Comments

Building a CMS with CodeIgniter: Part 8 CMS UI

i’m going to modify the welcome files i setup early on as they are currently being bypassed by the routes. first i rename the welcome controller to ‘hub’. the hub will be my admin home page. in the hub.php file i then update the index function to load v_hub instead of v_welcome. i also rename v_welcome to v_hub. while i’m at the rename game, i head to the admin controller and change the redirect to ‘admin/hub’. this is just because i want a friendly url.

without updating the routes file, i get a 404. so i open up routes and add a route for hub like so:

$route['admin/hub'] = 'hub';

now my old welcome page opens up. for now i’m going to get this to edit my one page i have. after this is working, i’ll create a CRUD (Create, Read, Update, Delete) module for managing pages although it will be able to manage any object you like. i quickly changed the ‘welcome’ heading to ‘hub’. for now i’ll be handling updating. in the v_hub view, i change the form control to open welcome/save.

i need to add hidden fields to the forms so the model knows what id i’m updating. add the following hidden fields to both form controls.

wysiwyg

echo form_hidden('id', 1);
echo form_hidden('template', 'single_column');
echo form_hidden('field', 'text');

image upload

echo form_hidden('id', 1);
echo form_hidden('template', 'single_column');
echo form_hidden('field', 'image');

opening the wysiwyg controller, i add a new function called save. this will be used to save the wysiwyg data to database.

public function save()
{
	$data['id'] = $this->input->post('id');
	$data['template'] = $this->input->post('template');
	$data['field'] = $this->input->post('field');
	$data['content'] = $this->input->post('content');
		
	$model = 'm_' . $data['template'];
	$dbdata = $data['content'];
	$this->load->model($model);
	$success = $this->{$model}->set( $data['id'], $data['field'], $dbdata );

	if ( $success )
	{
		echo "updated!";
	}
	else
	{
		echo "an error occured";
	}
}

i also edit my routes again so the wysiwyg save function can be reached.

$route['wysiwyg/save'] = 'wysiwyg/save';

and lastly, i updated the m_single_column model to give it a set function like this:

public function set( $id, $field, $data )
{
	$this->db->set( $field, $data );
	$this->db->where( 'id', $id );
	return $this->db->update( 'single_column_pages' );
}

now i have a working CMS! albeit a pretty limited one…so i wanted a source button in my wysiwyg editor and have discovered that tinymce is rubbish. the steps needed to add a simple code button is stupid. so i’ve switched entirely to ckeditor which took about 5 mins.

i also repeated these steps for the image upload. here’s the modified upload script.

<?php

class Upload extends CI_Controller 
{
	function do_upload()
	{
		$config['upload_path'] = FCPATH . 'uploads/';
		$config['allowed_types'] = 'gif|jpg|png';
		$config['max_size']	= '100'; // in KB
		$config['max_width']  = '1024';
		$config['max_height']  = '768';
		// additional parameters
		/*
		$config['file_name']
		$config['overwrite']
		$config['max_filename']
		$config['encrypt_name']
		$config['remove_spaces']
		*/

		$this->load->library('upload', $config);
		$this->upload->initialize($config);
		
		// if there is an error, load error view
		if ( ! $this->upload->do_upload())
		{
			$error = array('error' => $this->upload->display_errors());

			$this->load->view('v_upload_fail', $error);
		}
		else
		{
			// store path in db
			$imagedata = $this->upload->data();
			
			$data['id'] = $this->input->post('id');
			$data['template'] = $this->input->post('template');
			$data['field'] = $this->input->post('field');
			$data['image'] = 'uploads/' . $imagedata['file_name'];
			
			$model = 'm_' . $data['template'];
			$dbdata = $data['image'];
			$this->load->model($model);
			$success = $this->{$model}->set( $data['id'], $data['field'], $dbdata );

			if ( $success )
			{
				$this->load->view('v_upload_success', $data);
			}
			else
			{
				$this->load->view('v_upload_fail', $error);
			}
			
			
		}
	}
}
?>

and that’s it. definitely time for bed. next up i’m going do the rest of the CRUD letters – create and delete. i’ll attempt to make a generic interface for creating, reading, updating and deleting stuff.

Posted in CMS, CodeIgnitor, php | 3 Comments

Building a CMS with CodeIgniter: Part 7 Data

now to hook up the database with the page view. i’ve created a table for the single_column template called single_column_pages. the table has three columns: id, text and image. id will match up with the corresponding id in the pages table while text and image will store the data for the page. ideally i should create an image table that contains some meta data like width and height but that’s an extension for later.

i then cut the data that i previously had hard-coded into the template library and put that into a new entry in the database. now to replace the data with a model.

i first modified my master controller to pass through the id of the page to the render function.

$this->{$page->controller}->render( $page->id );

next i updated the single_column library like so:

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

	class Single_column 
	{
		public function render( $id )
		{
			$CI =& get_instance();
			
			// DB calls
			// search for uri in db
			$CI->load->model('m_single_column');
			$image = $CI->m_single_column->getImageURI( $id );
			$text = $CI->m_single_column->getText( $id );
			
			// ==== HEADER
			// eventually, this will be loaded from the db
			$header_data['title'] = "Single Column Demo";
			$header_data['css'][] = "single_column";
			$CI->load->view('v_header', $header_data);
			
			// ==== CONTENT
			$content_data['content'] = '<div class="content-block">';
			$content_data['content'] .= '<div class="image">';
			$content_data['content'] .= '<img src="' . base_url() . $image . '" width="200" height="133" alt="my tractor"/>';
			$content_data['content'] .= '</div>';
			$content_data['content'] .= $text;
			$content_data['content'] .= '</div><!-- end content-block-->';
			
			$CI->load->view('v_content', $content_data);
			
			// ==== FOOTER
			$CI->load->view('v_footer');
		}
	}

?>

and finally i created a model for the template called m_single_column.php which is responsible for getting all the data from the database.

<?php

	class M_Single_Column extends CI_Model
	{	
		public function getImageURI( $id )
		{
			$this->db->select( 'image' );
			$query = $this->db->where( 'id', $id )->limit(1)->get('single_column_pages');
			
			if ( $query->num_rows > 0 )
			{
				return $query->row()->image;
			}
			
			return false;
		}
		
		
		public function getText( $id )
		{
			$this->db->select( 'text' );
			$query = $this->db->where( 'id', $id )->limit(1)->get('single_column_pages');
			
			if ( $query->num_rows > 0 )
			{
				return $query->row()->text;
			}
			
			return false;
		}
	}
	
?>

that’s it! one step closer to a CMS. i’ve now got pages dynamically loading from the database. next up is actually making the CMS UI!

Posted in CMS, CodeIgnitor, php | 1 Comment

Building a CMS with CodeIgniter: Part 6 Templates

my CMS is now loading pages from the database. but it isn’t loading actual data to display to the user. on top of that, i don’t have a template yet that i can insert this data into. in order to make a template, i’ll split it into 3 sections: header, body and footer. the header and footer are likely to consist of one view while the body may be a collection of views and libraries depending on what it will show.

the example template i’ll create will be a single column template. it will have some global navigation in the header – generated dynamically by the database, a column of wysiwyg text wrapping around an image, and a footer.

1. i created a view file called v_header.php. for now this will be very basic and just contain simple html header code. later you could extend this to accept scripts and meta data. the only thing dynamic at this stage will be the title.

<html lang="en">
	<head>
		<meta charset="utf-8">
		<title><? echo $title; ?></title>
	</head>
	
	<body>

2. i created a footer called v_footer.php which is just closing tags at the moment. later i’ll add in an actual footer.

	</body>
</html>

3. for the template i created a library called single_column.php, inside this library i put the following code:

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

	class Single_column 
	{
		public function render()
		{
			$CI =& get_instance();
			
			// ==== HEADER
			// eventually, this will be loaded from the db
			$header_data['title'] = "Single Column Demo";
			$header_data['css'][] = "single_column";
			$CI->load->view('v_header', $header_data);
			
			// ==== CONTENT
			$content_data['content'] = '<div class="content-block">';
			$content_data['content'] .= '<div class="image">';
			$content_data['content'] .= '<img src="' . base_url() . 'images/dummy.jpg" width="200" height="133" alt="my tractor"/>';
			$content_data['content'] .= '</div>';
			$content_data['content'] .= "He's got a massive bathers also as cunning as a dero. Come a gyno flamin she'll be right fair dinkum. Come a ridgy-didge to we're going christmas. As stands out like nipper no dramas as stands out like blow in the bag. Lets get some dinky-di to as dry as a khe sanh. Gutful of vee dub heaps she'll be right cane toad.
He's got a massive ace! when gutful of back of bourke. Gutful of rip snorter no dramas as stands out like bodgy. Built like a frog in a sock you little ripper vee dub. As stands out like ute no worries as dry as a uluru. Stands out like a dob bloody flat out like a joey. As busy as a sook heaps she'll be right ford. You little ripper cut lunch heaps as stands out like tucker. She'll be right bail out she'll be right cockie. She'll be right pint how it'll be dunny rat. Grab us a tucker-bag flamin as dry as a ute. He's got a massive bushie how he hasn't got a milk bar.
Built like a galah also stands out like a parma. mongrel flamin built like a show pony. Come a ankle biter bloody lets get some mate. As dry as a bluey he hasn't got a going off. As busy as a franger no dramas lets throw a bikkie. Stands out like a perve with she'll be right rollie. Mad as a pot piece of piss bottlo. Gutful of knock as stands out like mickey mouse mate. As dry as a captain cook no worries she'll be right rack off. As busy as a boogie board when you little ripper barbie.";
			$content_data['content'] .= '</div><!-- end content-block-->';
			
			$CI->load->view('v_content', $content_data);
			
			// ==== FOOTER
			$CI->load->view('v_footer');
		}
	}

?>

there’s quite a bit going on here. firstly, i have a function called render to show the page. i grabbed an instance of code igniter so i could actually use it. then i just load the header, content and footer views which the code for is listed below. for now, i’m just using hardcoded data for testing. next up i’ll be hooking it into the database. so i created an image called dummy.jpg and put it in the images folder. i also created a style sheet and put that in a css folder.

v_header.php (UPDATED)

<html lang="en">
	<head>
		<meta charset="utf-8">
		<title><? echo $title; ?></title>
		<? 
			foreach( $css as $stylesheet )
			{
		?>
				<link rel="stylesheet" href="<?=base_url() . 'css/' . $stylesheet ?>.css" type="text/css">
		<?
			}
		?>
		
	</head>
	
	<body>

v_content.php

<?
// ==== CONTENT WRAPPER
// useful for centred pages and other style stuff
?>

<div id="content-wrapper">
<?
// ==== NAVIGATION
// load here eventually
?>
<div id="nav">navigation will go here</div>
<?
	echo $content;
?>

</div><!-- end content-wrapper -->

single_column.css

#content-wrapper
{
	position: relative;
	width: 700px;
	margin-left: -350px;
	left: 50%;
}

#nav
{
	background-color: #ccc;
}

.content-block
{
	float: left;
	padding: 10px;
}

.image
{
	float: right;
	margin: 0px 0px 10px 10px;
}

next i’ll create a table to store page data and load it into the single_column template

Posted in CMS, CodeIgnitor, php | Comments Off

Building a CMS with CodeIgniter: Part 5 Dynamic URLs

for my CMS, i’d like the entire site structure to be determined by what pages are in the database. what this means is that i can’t have a controller for each page because i don’t know what each page is. so what i had to do was setup a route that would send all requests to a master controller. the master controller then analyses the uri, and then takes action based on that. eventually it will look up the database for a matching URI and serve that data. dynamic URLs are quite easy to do as it turns out, however there are some extra things you should do for larger sites where performance is an issue such as caching and dynamic route construction, but i might come back to that later.

1. open up the routes.php file in the config directory.
2. at the bottom of the route list add

$route[':any'] = 'master';

this will take all uris to the master controller except for the listings above this line.

3. modify the above listings so the admin and logout pages will still load without using the master controller so the final route listing is as follows:

$route['default_controller'] = 'admin';
$route['404_override'] = '';
$route['admin'] = 'admin';
$route['logout'] = 'admin/logout';
$route[':any'] = 'master';

now you just need to create the master controller. what this will do is grab the uri, search for it in the database and then do some stuff. but i don’t even have a database yet! first here’s the master controller:

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

	class Master extends CI_Controller
	{
		function __construct()
		{
			parent::__construct();
		}
		
		
		public function _remap( $method )
		{
			// remap overrides function calls
			
			// this is the uri. search for this in the db
			echo uri_string();
		}
	}

?>

now for the database. the schema for the page table is as follows:

int id
varchar uri
varchar controller
datetime created
timestamp modified

so the master controller will search for the uri in the table db. if found it will load the corresponding controller or template if you like. this template will load the data it requires from another table in the database using the id and spit it out into a view.

to automate the time fields, i set the timestamp to default to current_timestamp and to update the timestamp on update. then i ran the following trigger in mysql:

CREATE TRIGGER timecontrol
BEFORE INSERT ON pages 
FOR EACH ROW 
BEGIN 
IF NEW.created = "1900-01-01 00:00:00" THEN 
SET NEW.created = NOW(); 
END IF; 
END 

here’s some info links:
triggers in phpmyadmin
created and modified timestamp

i put some dummy data in the pages table and checked to make sure the update trigger was working. next i update the master controller to search the db

public function _remap( $method )
{
	// remap overrides function calls
			
	// search for uri in db
	$this->load->model('m_master');
	$uri_id = $this->m_master->findURI( uri_string() );
			
	if ( $uri_id != false )
	{
		// uri exists! load template controller
	}
	else
	{
		show_404( uri_string() );
	}
}

and i created a model for the master controller like so:

[php]
<?php

	class M_Master extends CI_Model
	{
		public function findURI( $uri )
		{
			$query = $this->db->where( 'uri', $uri )->limit(1)->get('pages');
			
			if ( $query->num_rows > 0 )
			{
				echo '<pre>';
				print_r($query->row());
				echo '</pre>';
				return $query->row();
			}
			
			return false;
		}
	} 
 ?>

so now if i type in the urls in my database into the browser, i’ll get the db info spat back at me. next up is to get the master controller to load templates which for this CMS we’ll store as library/view combos. so eventually when my base CMS is complete, i’ll create new templates by making a new library, view, model and database table.

Posted in CMS, CodeIgnitor, php | Comments Off