Darren Nolan

Computer Tech… and stuff

Design boss got annoyed! YAY!

24 Mar 2012 T07:55:54am - Dazz - 5 Comment(s)

I think that’s pretty awesome, that when Karl gets annoyed with my site, he redoes it. =D While not a massive update, it is a fantastic opportunity for me to redo the site’s HTML and CSS. So stay tuned. It’ll be here shortly (and by ‘shortly’ I mean, when it’s done and ready in my own time).

Categories: Website

CodeIgniter Model Validation and Rule Sets

12 Feb 2012 T05:28:46pm - Dazz - 3 Comment(s)

Update 1.0.3 19/02/12: Updated checking for validation variables and methods before calling them.
Update 1.0.2 14/02/12: Removed static from libraries context. Might add static support later.
Update 1.0.1 13/02/12: Changed so CI Form_Validation rules are run first.

So, once again been tasked to play with the mighty CodeIgniter and having myself some fun. Except I still don't, for the love of god, understand why default validation is defined within controllers and not the models you're about to play with.

And so, I present some simple awesomeness to do two things.

Firstly we can define Form Validation rules within our Model class. Secondly, we can do all custom callbacks or complex rules within the model as well. So under application/models/User.php

class User extends Model
{
    // Required to store Form_validation error Messages in.
    public $model_validate_error;
    
    // When declared $model_validate_<fieldname> will add additional rules
    public $model_validate_username = 'required|min_length[10]|max_length[50]';
    
    // Rest of your model logic here.  This will even work awesomely with PHPActiveRecord
    //
    //

    public function model_validate_username($string)
    {
        if (!validate_string_length_between($string, 4, 15)) {
            $this->model_validate_error = 'Username not between 4 and 15 characters';
            return FALSE;
        } else {
            return TRUE;
        }
    }
}

So here we're defining the rules for "username" and these are normal CodeIgniter Form_validation rules. To create custom functions (or callbacks within the model itself) you can use the public static function model_validate_. Do your logic within that function, set the validation error message if you have one, and return true/false. validate_string_length_between() was a little testing function I added to the bottom of my form validation extender, just cause.

Within your controller application/controllers/signup.php for exmaple

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

class Signup extends MY_Controller
{
    function index()
    {
        $this->load->library('form_validation');
        
        $this->load->model('User');  // Loading for model here for validation is not required, but I assume right after validation passed you'll be saving stuff.
        
        // Call validate_model functions in the [Modelname.field] style (sort of like is_unique for table checking)
        $this->form_validation->set_rules('username', 'Username', 'validate_model[User.username]');
        $this->form_validation->set_rules('password', 'Password', 'validate_model[User.password]');
        $this->form_validation->set_rules('email', 'Email Address', 'validate_model[User.email]');
        
        if ($this->form_validation->run()) {
            $this->view_data['message'] = "pass";
        } elseif ($this->input->post()) {
            $this->view_data['message'] = "failed";
        } else {
            $this->view_data['message'] = "not run";
        }
    }
}

One nice thing is that even after you declare validate_model on the email field, you don't need the rules already saved in the Model nor the extended callback function there. If it's there, it will be used. If it's not, it won't. Means you can setup your controllers today, and populate the rules tomorrow.

Your extending function application/libraries/MY_Form_validation.php

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
 
class MY_Form_validation extends CI_Form_validation
{
     
    public function set_rules($field, $label = '', $rules = '')
    {
        // Check if "validate_model" is a rule within our set of rules.  If it is, load up the model's rules
        // within the rule-set and continue on to CI's class.
         
        if (strlen($rules) != 0) {
            $rule_list = explode('|', $rules);
            if ($rule_list != FALSE) {
                foreach ($rule_list as $rule) {
                    if (substr($rule, 0, 15) == 'validate_model[') {
                        // Found a validate_model ruling.  Grab the class name and field type.
                        $rule = substr($rule, 15);
                        $rule = substr($rule, 0, -1);
                        $functionName = explode ('.', $rule);
                        if (count($functionName == 2)) {
                            $modelName = $functionName[0];
                            $variableName = 'model_validate_' . $functionName[1];
                            
                            $this->CI->load->model($modelName, 'validate_model'); // Load the model
                            
                            if (isset($this->CI->validate_model->$variableName)) {
                                $rules = $this->CI->validate_model->$variableName . '|' . $rules;
                            }
                        }
                    }
                }
            }
        }
        // Always continue with the default CI set_rules even if we can't work anything additional out.
        parent::set_rules($field, $label, $rules);
    }
     
    public function validate_model($input = FALSE, $model_field = FALSE)
    {
        $functionName = explode('.', $model_field);
        if (count($functionName) != 2) {
            // Unable to work out 'model.function' to call.
            return;
        }
         
        $modelName = $functionName[0];
        $methodName = 'model_validate_' . $functionName[1];
         
        $this->CI->load->model($modelName, 'validate_model'); // Load the model if it's not already loaded.
         
        if (method_exists($this->CI->validate_model, $methodName)) {
            //$result = call_user_func(array($modelName, $methodName), $input);
            $result = $this->CI->validate_model->$methodName($input);
            if ($result) {
                return TRUE;
            } else {
                $this->set_message("validate_model", $this->CI->validate_model->model_validate_error);
                $this->CI->validate_model->model_validate_error = NULL;
                return FALSE;
            }
        } else {
            log_message('debug', "Unable to find validation rule: $modelName -> $methodName");
            return;
        }
    }
}

// Validation Functions
if (!function_exists('validate_string_length_between')) {
    function validate_string_length_between($string, $min, $max)
    {
        if (strlen($string) >= $min AND strlen($string) <= $max) {
            return TRUE;
        } else {
            return FALSE;
        }
    }
}

So! I hope with this simple stuff, makes your controllers much neater, and rules and validation far more reusable than either saving your rules in a config file, or copy/pasting your rules between controllers and extending Form_validation with custom function callbacks.

Feedback welcome, let me know!

Categories: CodeIgniter | PHP

cPanel + Redmine 1.2.1 – Pages delivered as text/plain when using rewrite

10 Oct 2011 T06:38:52pm - Dazz - 0 Comment(s)

Well.

Bit more of a trouble than I wish to deal with today, but apparently Mongrel has changed somewhat in the latest version and doesn't deliver content as HTML, instead delivering it as text/plain (source html).

If you are using Mongrel to deliver your Ruby Apps, and you're sure your versions of gems meet the requirements - take a look at the below to try.

Add this file in, restart your application - and hopefully away you go. Again there is a lot of read/take in when it comes to dealing with this issue - hopefully I'm saving you about 1.5 hours of research. Original Source for the Patch I've forked..

Add this to redmine/config/initializers/mongrel.rb (create a new file)

# Pulled right from latest rack. Old looked like this in 1.1.0 version.
  # 
  # def [](k)
  #   super(@names[k] ||= @names[k.downcase])
  # end
  # 
  module Rack
    module Utils
      class HeaderHash < Hash
        def [](k)
          super(@names[k]) if @names[k]
          super(@names[k.downcase])
        end
      end
    end
  end
  
  # Code pulled from the ticket above.
  # 
  class Mongrel::CGIWrapper
    def header_with_rails_fix(options = 'text/html')
      @head['cookie'] = options.delete('cookie').flatten.map { |v| v.sub(/^\n/,'') } if options.class != String and options['cookie']
      header_without_rails_fix(options)
    end
    alias_method_chain :header, :rails_fix
  end
  
  # Pulled right from 2.3.8 ActionPack. Simple diff was
  # 
  # if headers.include?('Set-Cookie')
  #   headers['cookie'] = headers.delete('Set-Cookie').split("\n")
  # end
  # 
  # to 
  # 
  # if headers['Set-Cookie']
  #   headers['cookie'] = headers.delete('Set-Cookie').split("\n")
  # end
  #       
  module ActionController
    class CGIHandler
      def self.dispatch_cgi(app, cgi, out = $stdout)
        env = cgi.__send__(:env_table)
        env.delete "HTTP_CONTENT_LENGTH"
        cgi.stdinput.extend ProperStream
        env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
        env.update({
          "rack.version" => [0,1],
          "rack.input" => cgi.stdinput,
          "rack.errors" => $stderr,
          "rack.multithread" => false,
          "rack.multiprocess" => true,
          "rack.run_once" => false,
          "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
        })
        env["QUERY_STRING"] ||= ""
        env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
        env["REQUEST_PATH"] ||= "/"
        env.delete "PATH_INFO" if env["PATH_INFO"] == ""
        status, headers, body = app.call(env)
        begin
          out.binmode if out.respond_to?(:binmode)
          out.sync = false if out.respond_to?(:sync=)
          headers['Status'] = status.to_s
          if headers['Set-Cookie']
            headers['cookie'] = headers.delete('Set-Cookie').split("\n")
          end
          out.write(cgi.header(headers))
          body.each { |part|
            out.write part
            out.flush if out.respond_to?(:flush)
          }
        ensure
          body.close if body.respond_to?(:close)
        end
      end
    end
  end

Categories: cPanel | Redmine

Fedora 15 – Making Gnome 3 more like Gnome 2

04 Oct 2011 T10:45:39pm - Dazz - 9 Comment(s)

I'm all for progress. But what I question is when workflow of the latest desktop interfaces are seeming to take a turn backwards (MetroUI, OS X, Gnome3, Ubuntu's Unity etc). Like using Windows 3.11 for Workgroups all over again. Where having one opening application is the thing to do, and switching between applications is now more complex then switching a tab in a web browser.

Now we're using things like gestures, swiping movements over the touchpad, mouse hover previews of open apps or new key-combos which simply adds more steps to get to the application you're after or that are already running.

For typical users, I'm sure they don't care. I mean, hey, it looks pretty, and if Apple (and other big names) have learnt anything over the years - it's that pretty sells.

(more...)

Categories: Gnome | Linux

MySQL: Return dates between two dates – Make Intervals Procedure

06 Sep 2011 T01:13:55pm - Dazz - 2 Comment(s)

Full credit to Ron Savage over at Stack Exchange. He's written this fantastic function which creates a temporary table with two columns, 'interval_start' and 'interval_end'.

You call this function with a MySQL datetime ('2011-02-23 05:00:00'), and a MySQL end datetime ('2011-02-23 16:00:00'). You then specify what you want returned back to you (Seconds, Minutes, Hours, Days etc. See the function below for the full list of options).

What is returned is below will be similar;

interval_start interval_end
2011-02-23 05:00:00 2011-02-23 05:59:59
2011-02-23 06:00:00 2011-02-23 06:59:59
2011-02-23 07:00:00 2011-02-23 07:59:59
2011-02-23 08:00:00 2011-02-23 08:59:59
2011-02-23 09:00:00 2011-02-23 09:59:59
2011-02-23 10:00:00 2011-02-23 10:59:59
2011-02-23 11:00:00 2011-02-23 11:59:59
2011-02-23 12:00:00 2011-02-23 12:59:59
2011-02-23 13:00:00 2011-02-23 13:59:59
2011-02-23 14:00:00 2011-02-23 14:59:59
2011-02-23 15:00:00 2011-02-23 15:59:59
(more...)

Categories: MySQL | Programming