-
Martin7483 Crossfire CMS
   
-
Posts: 373
Threads: 14
Joined: Sep 2015
Reputation:
20
Hi,
As I mentioned in this thread, I am in the process of creating some ready to use features for CodeIgniter 3.x that together form a simple base project. With this thread I would like to keep you all updated on the progress.
Remember this project is not a CMS. I will be using this base project to build my own CMS and will share created modules that easily plug in to this base project.
No designs will be included. So you will be free to use your own images/CSS/JS and design framework. The form builder library will have it's own default views that work with Bootstrap. You can change, or add your own custom views to use any design framework you wish to use.
Once the base project is completed I will create a GitHub repo for it. For now you can visit my Github page here which has one repo at this time. It's a nice jQuery password strength meter.
The project will contain:
The following list has already been completed:
- Simple HMVC
- Extended router class for DB page routing
- Honey pot form extension
- Assets library & path handler
- Mobile detection library and helper
- Assets directory containing the following structure
- cache
- cms
- shared
- bootstrap335
- fontawesome440
- javascript
- website
At the moment I am building the form builder library. This library will take care of all the tedious work that comes with using and creating forms in CodeIgniter.
In short, this library will create a form based on an array that is passed to the generate_form method. In this array you define what field types you want, the rules and error messages for each field, a label and the field attributes.
Here is an example of such an array:
PHP Code: $this->load->library('formbuilder');
$form = array(); $fields = array(); $form['open'] = array(); $form['open']['method'] = 'post'; $form['open']['class'] = 'form contact'; $form['open']['id'] = 'contact'; $form['open']['title'] = 'Contactformulier'; $fieldNr = 0; $attributes = array(); $attributes['id'] = 'myform'; $attributes['name'] = 'myform'; $attributes['value'] = ''; $fields[$fieldNr]['element'] = 'text'; $fields[$fieldNr]['hidden'] = TRUE; $fields[$fieldNr]['field']['attributes'] = $attributes; $fields[$fieldNr]['field']['label'] = 'Titel formulier'; $fields[$fieldNr]['field']['rules'] = 'trim|required|xss_clean'; $fields[$fieldNr]['field']['messageRequired'] = 'Some value is required'; $fieldNr++; $attributes = array(); $attributes['id'] = 'first_name'; $attributes['name'] = 'first_name'; $attributes['class'] = 'formInput'; $attributes['value'] = ''; $attributes['placeholder'] = 'Enter your first name'; $fields[$fieldNr]['element'] = 'text'; $fields[$fieldNr]['field']['attributes'] = $attributes; $fields[$fieldNr]['field']['label'] = 'First name'; $fields[$fieldNr]['field']['rules'] = 'trim|required|xss_clean'; $fields[$fieldNr]['field']['messageRequired'] = 'Your first name is required'; $fieldNr++; $attributes = array(); $attributes['id'] = 'password'; $attributes['name'] = 'password'; $attributes['class'] = 'formInput'; $attributes['value'] = '!ThisIsApassword12'; $attributes['placeholder'] = 'Create a password'; $fields[$fieldNr]['element'] = 'password'; $fields[$fieldNr]['field']['attributes'] = $attributes; $fields[$fieldNr]['field']['label'] = 'Password'; $fields[$fieldNr]['field']['rules'] = 'trim|required|xss_clean'; $fields[$fieldNr]['field']['messageRequired'] = 'A password is required'; $fields[$fieldNr]['field']['config'] = array(); $fields[$fieldNr]['field']['config']['use_strength'] = TRUE; $fields[$fieldNr]['field']['config']['confirm_password'] = TRUE; $fieldNr++; $attributes = array(); $attributes['id'] = 'submit'; $attributes['name'] = 'submit'; $attributes['type'] = 'submit'; $attributes['class'] = 'formButton'; $attributes['value'] = 'Send'; $fields[$fieldNr]['type'] = 'submit'; $fields[$fieldNr]['field']['attributes'] = $attributes; $fields[$fieldNr]['field']['label'] = '*) Mandatory'; $fields[$fieldNr]['field']['config'] = array('add_form_reset'=>TRUE); $form['elements'] = $fields; $data['form'] = $this->formbuilder->generate_form($form, 'form_container');
The library will contain a method for the handling of posted forms. Validation will takes place here and will return an array containing 'success'=>$post_dataset on success and 'failed'=>$form_fields when validation fails.
When validation fails the passed in form fields are updated with their errors so that they can be displayed when the form is reloaded.
PHP Code: $fields[$fieldNr]['field']['error'] = 'Your first name is required';
By default the library will use jQuery Form Validation for client side form validation. The jQuery validation rules and messages will be set based on the passed in CodeIgniter rules and messages. Each element will take care of any assets that are required for it to function. Some elements can be passed a config array so that it's JavaScript assets or extra element options can be configured dynamically.
Here is an example of a form builder element:
PHP Code: <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Element_Password extends Element { public $element; // Config variables protected $use_strength = FALSE; protected $hide_password = TRUE; protected $confirm_password = FALSE; // Config strength variable, used in strength.js protected $strengthButtonText = 'Show password'; protected $strengthButtonTextToggle = 'Hide Password'; protected $showPasswordToggle = 'true'; protected $allowCopyPasteCut = 'false'; protected $copyPasteCutMessage = 'You must (re)-enter your password manually'; protected $confirm_attributes = array(); protected $rule; protected $msg; public function __construct($input_array, $hidden, $form_id, $view_path) { log_message('debug', "Class Input Password Initialized."); parent::__construct($input_array, $hidden); $this->form_id = $form_id; $this->view = $view_path; foreach ($this->config as $config => $value) { if( property_exists($this, $config) ) { $this->{$config} = $value; } } } public function build() { $this->element['element_css'] = ' password'; // Do we want password strength? if($this->use_strength) { $this->assets->_load_javascript('strength.js' ,'shared'); $this->_init_strength(); $this->assets->_load_css('strength.css' ,'shared'); $this->css_class .= ' strength'; if( array_key_exists('class', $this->attributes) ) { $this->attributes['class'] .= ' password strength'; } else { $this->attributes['class'] = 'password strength'; } } if($this->hide_password) { $this->element['element'] = form_password($this->attributes); } else { $this->element['element'] = form_input($this->attributes); } if($this->hidden) { $this->attributes['type'] = 'hidden'; return form_input($this->attributes); } else { $inputfield = $this->container($this->element); if( $this->confirm_password ) { $this->rule[] = parent::set_rules(); $this->msg[] = parent::set_message(); $this->confirm_attributes = $this->attributes; $this->confirm_attributes['id'] .= '_confirm'; $this->confirm_attributes['name'] .= '_confirm'; $this->confirm_attributes['value'] = ''; $this->confirm_attributes['class'] = 'formInput'; $this->confirm_attributes['placeholder'] = 'Repeat your password'; $this->rules .= '|matches['.$this->attributes['id'].']'; $field['attributes'] = $this->confirm_attributes; $field['label'] = 'Confirm'; $field['rules'] = $this->rules; $field['messageRequired'] = 'You must confirm your password'; $field['messageMatches'] = 'Your passwords do not match'; parent::__construct($field, FALSE); $this->confirm_attributes['class'] = 'formInput form-control'; $element['element'] = form_password($this->confirm_attributes); $element['element_css'] = ' password_confirm'; $inputfield .= $this->container($element); } } return $inputfield; } public function set_rules() { $rules = NULL; $this->rule[] = parent::set_rules(); foreach($this->rule as $key => $rule) { $rules .= $rule.",\n"; } $validation = rtrim($rules, ",\n"); return $rules; } public function set_message() { $msgs = NULL; $this->msg[] = parent::set_message(); foreach($this->msg as $key => $msg) { $msgs .= $msg.",\n"; } $msgs = rtrim($msgs, ",\n"); return $msgs; } private function _init_strength() { $script = "$('#".$this->attributes['id']."').strength({"."\n"; $script .= "\t\t\t\t"."strengthClass: 'password strength formInput form-control',"."\n"; $script .= "\t\t\t\t"."strengthMeterClass: 'strength_meter',"."\n"; $script .= "\t\t\t\t"."strengthButtonClass: 'button_strength',"."\n"; $script .= "\t\t\t\t"."strengthButtonText: '".$this->strengthButtonText."',"."\n"; $script .= "\t\t\t\t"."strengthButtonTextToggle: '".$this->strengthButtonTextToggle."',"."\n"; $script .= "\t\t\t\t"."showPasswordToggle: ".$this->showPasswordToggle.","."\n"; $script .= "\t\t\t"."});"."\n"; $this->assets->_load_inline_javascript('password_strength_'.$this->attributes['id'], $script); } }
And this is the base element at this time:
PHP Code: <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Element { // The form id protected $form_id; /* Element config * Some elements, like password, can have * extra config items. See the element for available * configuartions */ protected $config = array(); /* The definition of the element * Add any valid attribute of an input type to this array */ protected $attributes; // The rules protected $rules; protected $setMessage; // The message types protected $messageRequired; // Added protected $messageMatches; // Added protected $messageUnique; // Added protected $messageMin; // Added protected $messageMax; // Added protected $messageAlpha; // Added protected $messageAlphaNumeric; // Added protected $messageAlphaDash; // Added protected $messageNumeric; // Added protected $messageInt; // Added protected $messageDecimal; // Added protected $messageEmail; // Added /* Still need to add the following types */ protected $messageNatural; // Missing protected $messageNaturalZero; // Missing protected $messageEmails; // Missing protected $messageExact; // Missing protected $messageGreater; // Missing protected $messageLess; // Missing protected $messageIp; // Missing protected $messageBase64; // Missing protected $required; protected $error; // Values for element placement protected $css_id; protected $css_class; protected $fullwidth; protected $label; protected $label_for = TRUE; protected $label_width = 'col-sm-4'; protected $field_width = 'col-sm-8'; protected $description; protected $field; protected $hidden; public $use_multipart = FALSE; public $view; public function __construct($input_array, $hidden) { log_message('debug', "Class Element Initialized."); $this->config = $this->load->config('formbuilder'); $this->hidden = $hidden; $this->_init($input_array); } /** * __get * * Enables the use of CI super-global without having to define an extra variable. * * @access public * @param $var * @return mixed */ public function __get($var) { return get_instance()->$var; } public function _init($input_array) { // Set the definitions foreach($input_array as $key => $value) { if(property_exists($this, $key)) { $this->$key = $value; } else { die('The property ' .$key. ' does not exist'); } } if( ! is_array($this->config) ) { $this->config = array(); } if(array_key_exists('id', $this->attributes)) { $this->css_id = $this->attributes['id']; } if(array_key_exists('class', $this->attributes)) { $this->css_class = $this->attributes['class']; } if( array_key_exists('class', $this->attributes) ) { $this->attributes['class'] .= ' form-control'; } else { $this->attributes['class'] = ' form-control'; } $this->required = $this->is_required($this->rules); // If the element is not hidden and no placeholder set use the label value if( ! $this->hidden && ! array_key_exists('placeholder', $this->attributes) ) { $this->attributes['placeholder'] = $this->label; } } public function is_required($rules) { $rules_array = explode('|', $rules); if(in_array('required',$rules_array)) { return '<span class="star">*</span>'; } else { return NULL; } } public function set_rules() { $rule = NULL; if( is_null($this->rules) ) { return NULL; } $rules_array = explode('|', $this->rules); if(count($rules_array) > 0) { $this->setMessage = TRUE; $rule .= "\t\t\t\t\t"."'".$this->attributes['name']."'".": {"."\n"; if( in_array('required', $rules_array) ) { $rule .= "\t\t\t\t\t\t"."required: true"; } else { $rule .= "\t\t\t\t\t\t"."required: false"; } // Must match a given field if( strpos($this->rules, 'matches') ) { preg_match('/matches\[((?>[^\[\]]+)|(?R))*\]/', $this->rules, $matches); $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."equalTo: '#".$matches[1]."'"; } // Only letters allowed if( strpos($this->rules, 'alpha') ) { $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."lettersonly: true"; } // Only letters and numbers allowed if( strpos($this->rules, 'alpha_numeric') ) { $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."alphanumeric: true"; } // Only letters, numbers, - & _ allowed if( strpos($this->rules, 'alpha_dash') ) { $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."alphanumericdash: true"; } // Only numbers allowed if( strpos($this->rules, 'numeric') ) { $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."number: true"; } // Only intergers allowed if( strpos($this->rules, 'integer') ) { $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."digits: true"; } // Number must match set value if( strpos($this->rules, 'decimal') ) { preg_match('/decimal\[((?>[^\[\]]+)|(?R))*\]/', $this->rules, $matches); $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."decimals: ".$matches[1]; } // Minimal length or checkbox selection if( strpos($this->rules, 'min_length') ) { preg_match('/min_length\[((?>[^\[\]]+)|(?R))*\]/', $this->rules, $matches); $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."minlength: ".$matches[1]; } // Maximum length or checkbox selection if( strpos($this->rules, 'max_length') ) { preg_match('/max_length\[((?>[^\[\]]+)|(?R))*\]/', $this->rules, $matches); $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."maxlength: ".$matches[1]; } // Must be a valid email if( strpos($this->rules, 'valid_email') ) { $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."email: true"; } // The value must be unique if( strpos($this->rules, 'is_unique') ) { preg_match('/is_unique\[((?>[^\[\]]+)|(?R))*\]/', $this->rules, $matches); list($table,$column) = explode('.', $matches[1]); $clause = FALSE; if( strpos($this->rules, 'clause') ) { preg_match('/clause\[((?>[^\[\]]+)|(?R))*\]/', $this->rules, $values); $clause = TRUE; } $rule .= ","; $rule .= "\n\t\t\t\t\t\t"."remote: {"; $rule .= "\n\t\t\t\t\t\t\t"."url: '".validate_url(),"; $rule .= "\n\t\t\t\t\t\t\t"."type: 'post'"; $rule .= ","; $rule .= "\n\t\t\t\t\t\t\t"."data: {"; $rule .= "\n\t\t\t\t\t\t\t\t"."value: function() {"; $rule .= "\n\t\t\t\t\t\t\t\t\t"."return $('#".$this->attributes['id']."').val();"; $rule .= "\n\t\t\t\t\t\t\t\t"."},"; if($clause) { $rule .= "\n\t\t\t\t\t\t\t\t"."clause: function() {"; $rule .= "\n\t\t\t\t\t\t\t\t\t"."return $('#".$values[1]."').val()+'|".$values[1]."';"; $rule .= "\n\t\t\t\t\t\t\t\t"."},"; } $rule .= "\n\t\t\t\t\t\t\t\t"."table: function() {"; $rule .= "\n\t\t\t\t\t\t\t\t\t"."return '".$table."';"; $rule .= "\n\t\t\t\t\t\t\t\t"."},"; $rule .= "\n\t\t\t\t\t\t\t\t"."column: function() {"; $rule .= "\n\t\t\t\t\t\t\t\t\t"."return '".$column."';"; $rule .= "\n\t\t\t\t\t\t\t\t"."},"; $rule .= "\n\t\t\t\t\t\t\t\t"."id: function() {"; $rule .= "\n\t\t\t\t\t\t\t\t\t"."return $('#id').val();"; $rule .= "\n\t\t\t\t\t\t\t\t"."}"; $rule .= "\n\t\t\t\t\t\t\t"."}"; $rule .= "\n\t\t\t\t\t\t"."}"; } $rule .= "\n\t\t\t\t\t"."}"; } return $rule; } public function set_message() { $msg = NULL; if( $this->setMessage ) { $msg .= "\t\t\t\t\t"."'".$this->attributes['name']."'".": {"."\n"; $msg .= "\t\t\t\t\t\t"."required: '".$this->messageRequired."'"; if( ! is_null($this->messageMatches) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."equalTo: '".$this->messageMatches."'"; } if( ! is_null($this->messageMin) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."minlength: jQuery.validator.format('".$this->messageMin."')"; } if( ! is_null($this->messageMax) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."maxlength: jQuery.validator.format('".$this->messageMax."')"; } if( ! is_null($this->messageAlpha) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."lettersonly: jQuery.validator.format('".$this->messageAlpha."')"; } if( ! is_null($this->messageAlphaNumeric) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."alphanumeric: jQuery.validator.format('".$this->messageAlphaNumeric."')"; } if( ! is_null($this->messageAlphaDash) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."alphanumericdash: jQuery.validator.format('".$this->messageAlphaDash."')"; } if( ! is_null($this->messageNumeric) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."number: jQuery.validator.format('".$this->messageNumeric."')"; } if( ! is_null($this->messageDecimal) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."decimals: jQuery.validator.format('".$this->messageDecimal."')"; } if( ! is_null($this->messageInt) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."digits: jQuery.validator.format('".$this->messageInt."')"; } if( ! is_null($this->messageEmail) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."email: jQuery.validator.format('".$this->messageEmail."')"; } if( ! is_null($this->messageUnique) ) { $msg .= ","; $msg .= "\n\t\t\t\t\t\t"."remote: jQuery.validator.format('".$this->messageUnique."')"; } $msg .= "\n\t\t\t\t\t"."}"; } return $msg; } public function container($element) { $data = array(); $data['css_id'] = $label_for = $this->css_id; $data['css_class'] = trim($this->css_class." ".$this->fullwidth); if( ! $this->label_for) { $label_for = ''; } $data['label'] = form_label($this->label.' '.$this->required, $label_for, array('class'=>'control-label col-sm-4')); $data['field_width'] = $this->field_width; if( ! is_null($this->required) ) { $data['css_required'] = 'required'; } else { $data['css_required'] = 'not-required'; } $data['error'] = $this->error; if(is_array($element)) { foreach($element as $key => $value) { $data[$key] = $value; } } else { $data['element'] = $element; } // Return the container return $this->load->view($this->view, $data, true); } }
It is still a work in progress, but I hope to have the first version finished in a few days. The form builder library uses my Assets library for the handling of all required assets.
-
ignitedcms IgnitedCMS Developer
   
-
Posts: 505
Threads: 72
Joined: Jul 2015
Reputation:
17
Looks like a good overall start. Here are my thoughts from my own experiences.
-Your form builder looks useful but I take it it is all hand coded, why not consider adding these forms from a database and dropping them in
-Consider using widgets (stored in views) to build the front end for the forms, as you can already tell your code is becoming quite messy.
-Try not to use a combination of single quotes and double quotes where you you can. Use double quotes and you can pass variable right into the string without have to break out using '.'
-Just an all round no to javascript client validation. I was fooling around with clientside validation and found on the whole it just made things more complex. With server side validation you can't really go wrong and there is no duplication of the codebase. I've eradicated all clientside validation from my pet project altogether.
Overall good start. Keep up the good work.
-
Martin7483 Crossfire CMS
   
-
Posts: 373
Threads: 14
Joined: Sep 2015
Reputation:
20
(10-30-2015, 08:36 AM)iamthwee Wrote: Looks like a good overall start. Here are my thoughts from my own experiences.
-Your form builder looks useful but I take it it is all hand coded, why not consider adding these forms from a database and dropping them in
-Consider using widgets (stored in views) to build the front end for the forms, as you can already tell your code is becoming quite messy.
-Try not to use a combination of single quotes and double quotes where you you can. Use double quotes and you can pass variable right into the string without have to break out using '.'
-Just an all round no to javascript client validation. I was fooling around with clientside validation and found on the whole it just made things more complex. With server side validation you can't really go wrong and there is no duplication of the codebase. I've eradicated all clientside validation from my pet project altogether.
Overall good start. Keep up the good work.
Thanks for the feedback. As I said it is still a work in progress.
Defining a form is hand coded, but once it is done I will be creating a module to build and generate forms via an UI. These form definitions can then be stored in a database or as JSON files on the disk.
Could you point out where you think the code is getting messy? The way it works now, is that each form element has a default view. When the generate_form method is called all the views are collected a put into one main view file. This as a whole is returned.
Did you look at my assets library? This makes it possible for me to load any needed JS or CSS at any given point. So using jQuery validation is not making things complicated at all if you ask me. In any situation, using Javascript is an extra and features should not rely on it. And using it in my library is optional. You will be able to disable it via the libraries config.
-
ignitedcms IgnitedCMS Developer
   
-
Posts: 505
Threads: 72
Joined: Jul 2015
Reputation:
17
10-30-2015, 11:45 AM
(This post was last modified: 10-30-2015, 11:47 AM by ignitedcms.)
Let me just say, as your project grows and your client side validation rules are not just looping through input values on a form, but nested and conditional values on a form depending on different options selected prior to this you will absolutely understand why doing both client and server side validation for both is going to be a major ballache. After the patented 'grid' for ignitedCMS Pro has been fully vetted I'll show you, but it is still in beta at the moment.
But I'll wait till you get that far into it to find out
Quote:What do you mean here? Are you talking about the required rule of CodeIgniter?
Not specifically to do with codeigniter but more of a design issue. Consider you're creating dynamic form fields, it doesn't make sense to assign them a required or not required tag at this early stage.
ONLY after you have assigned a field to a section or page (if you like) should you consider assigning it a required or non required tag.
Hope that helped.
-
ignitedcms IgnitedCMS Developer
   
-
Posts: 505
Threads: 72
Joined: Jul 2015
Reputation:
17
10-30-2015, 02:43 PM
(This post was last modified: 10-30-2015, 03:14 PM by ignitedcms.)
No you're right codeigniter doesn't have a default validation rule for conditionals which is why you have to add nested (one within another) form validation calls, which gets rather untidy. That's why I ended up writing my own which makes use of json to save the form data in the database, but can be fetched to regenerate the form when input is bad.
Still my problems is that using both jquery and server side validation is rather redundant, especially if the user can just turn off javascript altogether. That's why I got rid of client side validation and just concentrated on creating a really robust server side method. I didn't like the idea of one not exactly mirroring the other.
Here's my biggest problem with the validation pattern. Let's say you have some jquery which opens a hidden div when you check a checkbox and the user inputs something wrong, how do you validate server side then reload the view but open that div with bad values filled in? The only robust way I could think about doing it was temporarily saving the form as a json array.
But who knows maybe you might convince me.
|