D7net Mini Sh3LL v1

 
OFF  |  cURL : OFF  |  WGET : ON  |  Perl : ON  |  Python : OFF
Directory (0775) :  /var/www/html/hpsc/../infor/supporto/less/../pages/

 Home   ☍ Command   ☍ Upload File   ☍Info Server   ☍ Buat File   ☍ Mass deface   ☍ Jumping   ☍ Config   ☍ Symlink   ☍ About 

Current File : /var/www/html/hpsc/../infor/supporto/less/../pages/lazy_mofo.php
<?php

// php crud datagrid for mysql and php5
// MIT License - http://lazymofo.wdschools.com/
// send feedback or questions lazymofo@wdschools.com
// version 2016-03-20


class lazy_mofo{

 

    public $dbh = false;                    // required, pass the PDO connection object into the constructor
    public $table = '';                     // required, table for use for sql_insert(), sql_update(), sql_update_grid() and sql_delete()
    public $identity_name = '';             // required, column name of id primary key 

    public $rename = array();               // associative array of column names and friendly names. example: array('prod_id' => 'Product ID') 

    public $uri_path = '';                  // to specify URI. Needed for some applications such as in admin WordPress Plugins.

    public $exclude_field = array();        // don't allow users to update or insert into these fields, even if data is posted. place the field name in the key of the array. example: $lm->exclude_field['is_admin'] = '';

    public $form_sql = '';                  // render form from fields retuned in sql statement. if blank then 'select * from table where identity_name = identity_id' is used. when no record is found then a blank form to ADD a record is displayed
    public $form_sql_param = array();       // associative array to bind named parameters to form_sql. use to pass in identity_id when specifiying form_sql.
    public $form_input_control = array();   // for form(), define inputs like select boxes, document uploads, etc... *info on usage below*
    public $form_default_value = array();   // for form(), define default values for columns when adding a record. if auto_populate_control = true then this array will be populated from the defaults in the database. 
    public $form_display_identity = true;   // display identity value when editing a record
    public $form_additional_html = '';      // add any addition html inside the <form> after the form buttons.
    public $form_text_input_size = 35;      // size of text inputs on form()

    public $grid_sql = '';                  // render grid with desired sql. defaults to 'select *, identity_name from table'. *important* to display [edit] and [delete] links, the identity must be the last column in the sql statement. this arangement allows grid to display data without showing the identity.
    public $grid_sql_param = array();       // associative array to bind variables to grid_sql. see php docs on PDOStatement->execute for more info.
    public $grid_default_order_by = '';     // free-form 'order by' clause. Not used if grid_sql is specified. Example: column1 desc, column2 asc
    public $grid_input_control = array();   // for grid(), define inputs like select boxes, checkboxes, etc... *info on usage below*
    public $grid_output_control = array();  // for grid(). define outputs like --email to make a clickable mailto or --document to make a link. *info on usage below*
    public $grid_multi_delete = false;      // display checkboxes on grid to allow for multiple record delete
    public $grid_show_search_box = false;   // display search field at the top - grid_sql must be altered to accomodate search
    public $grid_limit = 200;               // pagination limit number of records per page
    public $grid_repeat_header_at = 0;      // interval of records to repeat header column titles at
    public $grid_show_images = false;       // option to show images inside the grid, otherwise a link is displayed for --image type
    public $grid_ellipse_at = 30;           // limit number of characters displayed, set to 0 to disable truncation
    public $grid_text_input_size = 10;      // size of text input when input is displayed

    public $text_input_max_length_default = 0;  // global max_length attribute for input. 0 is disabled
    public $text_input_max_length = array();    // array of column names to max length integers, optional

    public $auto_populate_controls = true;      // have get_columns() populate input and output controls according to meta data for types --date, --datetime, --number and --textarea. also, populate default values.

    public $on_insert_validate = array();       // example : $lm->on_insert_validate['field_name'] = array(string $regexp_or_user_function, string $error_message[, string $tip_placeholder , boolean optional_input]); 
    public $on_update_validate = array();
    public $validate_tip_in_placeholder = true; // allow input placeholder to display validation tip
    public $validate_text_general = "Missing or Invalid Input"; // generic message displayed at the top when validation error occurs, optional

    public $on_insert_user_function = '';       // user function called before data is inserted, updated, or deleteed. return a string error message for server-side validation. Can be used to formating _POST data.
    public $on_update_user_function = '';
    public $on_delete_user_function = '';
    public $on_update_grid_user_function = '';

    public $after_insert_user_function = '';    // user function names to be called after data is inserted, updated, or deleteed.
    public $after_update_user_function = '';
    public $after_delete_user_function = '';
    public $after_update_grid_user_function = '';

    public $cast_user_function = array();       // user function for casting data, example : $lm->cast_function['field_name'] = 'my_casting_function'

    public $return_to_edit_after_insert = true; // redirect to edit screen after adding or updating a record. if false, user is sent back to grid view.
    public $return_to_edit_after_update = true; 

    // upload paths                             // relative path names only! paths are created at runtime as needed
    public $upload_path = 'uploads';            // required when using --image or --document input types
    public $thumb_path = 'thumbs';              // optional, leave blank if you don't need thumbnails

    // image settings    
    public $upload_width = 400;                 // 0 height or width means no resizing or cropping
    public $upload_height = 400;
    public $upload_crop = false;                // crop versus resize: resize keeps the original aspect ratio but limits the size of the image
    public $thumb_width = 100;
    public $thumb_height = 100;
    public $thumb_crop = true;

    public $image_quality = 80;                       // image quality when resizing and cropping, 1-100
    public $image_style = "style='height: 100px;'";   // apply style to all images displayed. limiting size is nice to keep things orderly.

    public $charset_mysql = 'utf8';                   // charset for mysql communications
    public $charset = 'UTF-8';                        // charset for output

    public $timezone = 'UTC';                         // if no timezone is set in the application, then this timezone is set for strtotime. http://php.net/manual/en/timezones.others.php

    public $date_in = 'Y-m-d';                        // input format into database, no need to change this
    public $datetime_in = 'Y-m-d H:i:s';              

    // US date format
    public $date_out = 'm/d/Y';                       // output date
    public $datetime_out = 'm/d/Y h:i A';             // output datetime

    // non-US date format
    //public $date_out = 'd/m/Y';
    //public $datetime_out = 'd/m/Y h:i A';

    // ISO-ish date format, or when using html5 date input
    //public $date_out = 'Y-m-d';
    //public $datetime_out = 'Y-m-d H:i';

    public $restricted_numeric_input = '/[^0-9\.\-]/';// optional, regular expression of what numbers are allowed to be sent to the database. helpful to remove dollar signs and spaces. many non-US countries use comma instead of decimal points and spaces instead of commas.

    public $upload_allow_list = '.mp3 .jpg .jpeg .png .gif .doc .docx .xls .xlsx .txt .pdf'; // space delimted file name extentions. include period

    public $query_string_list = '';            // comma delimited list of variable names carried around to maintain state of search, sort, and pagination

    public $delete_confirm      = 'Sei sicuro di voler eleminare questo elemento?';       // javascript popup confirmation
    public $update_grid_confirm = 'Are you sure you want to delete [count] record(s)?'; // javascript popup confirmation when deleting on the grid

    // public $form_add_button    = "<input type='submit' value='Add' class='lm_button'>";
    // public $form_update_button = "<input type='submit' value='Update' class='lm_button'>"; 
    // public $form_back_button   = "<input type='button' value='&lt; Back' class='lm_button dull' onclick='_back();'>"; // use type=button for delete and cancel so form presses the right button with enter key
    // public $form_delete_button = "<input type='button' value='Delete' class='lm_button error' onclick='_delete();'>"; 

	
	public $form_add_button    = "<button type='submit' value='Aggiungi' class='btn btn-primary' >Aggiungi</button>";
    public $form_update_button = "<button type='submit' value='Aggiorna' class='btn btn-success' >Aggiorna</button>";
    public $form_back_button   = "<button type='button' value='&lt; Indietro' class='btn btn-warning' dull' onclick='_back();'>Indietro</button>"; // use type=button for delete and cancel so form presses the right button with enter key
    public $form_delete_button = "<button type='button' value='Elimina' class='btn btn-danger' onclick='_delete();'>Elimina</button>"; 
	
	
/*     public $form_text_title_add    = 'Add Record';   // titles in the <th> of top of the edit form 
    public $form_text_title_edit   = 'Edit Record';
    public $form_text_record_saved = 'Record Saved'; // customize success messages
    public $form_text_record_added = 'Record Added'; */


    public $form_text_title_add    = 'Aggiungi una nuova elezione';   // titles in the <th> of top of the edit form 
    public $form_text_title_edit   = 'Modifica dati';
    public $form_text_record_saved = 'Dati salvati';  // customize success messages
    public $form_text_record_added = 'Nuova elezione aggiunta'; 

    public $grid_add_link    = "<a href='[script_name]action=edit&amp;[qs]' class='lm_grid_add_link'>Aggiungi una nuova elezione</a>";  // link at displayed at the top to add a new record. [script_name] placeholder  will be populated by grid()
    public $grid_edit_link   = "<a href='[script_name]action=edit&amp;[identity_name]=[identity_id]&amp;[qs]'>[Modifica]</a>"; // note special [identity_name] and [identity_id] placeholders that will be populated by grid()
    public $grid_delete_link = "<a href='#' onclick='return _delete(\"[identity_id]\");'>[Elimina]</a>";
    public $grid_export_link = "<a href='[script_name]_export=1&amp;[qs]' title='Download CSV'>Export</a>";

    public $grid_search_box = "<form action='[script_name]' class='lm_search_box'><input type='text' name='_search' value='[_search]' size='20' class='lm_search_input'><a href='[script_name]' style='margin: 0 10px 0 -20px; display: inline-block;' title='Clear Search'>x</a><input type='submit' value='Search' class='lm_button lm_search_button'><input type='hidden' name='action' value='search'></form>"; 

    public $grid_text_record_added     = "Nuova elezione aggiunta";
    public $grid_text_changes_saved    = "Dati salvati";
    public $grid_text_record_deleted   = "Elezione eliminata";
    public $grid_text_save_changes     = "Modifiche salvate";
    public $grid_text_delete           = "Elimina";
    public $grid_text_no_records_found = "Non sono stati trovati elementi";

    public $pagination_text_use_paging = '[use paging]';
    public $pagination_text_show_all   = '[show all]';
    public $pagination_text_records    = 'Records';
    public $pagination_text_go         = 'Go';
    public $pagination_text_page       = 'Page';
    public $pagination_text_of         = 'of';
    public $pagination_text_next       = 'Next&gt;';
    public $pagination_text_back       = '&lt;Back';

    public $text_delete_image = 'delete image';
    public $text_delete_document = 'delete document';

    public $export_csv_file_name = '';

    public $delim = '|'; // when using mutiple checkboxes or multipleselect, delimiter for values

    public $select_first_option_blank = true; // make first option blank on dropdown --select and --selectmultiple inputs

    private $set_names = false; 


    function __construct($dbh){

        if(!$dbh)
            die('Pass in a PDO object connected to the mysql database.');

        if(!(get_magic_quotes_gpc() == 0) && (get_magic_quotes_runtime() == 0))
            echo('Warning: lazy mofo requires magic_quotes be disabled.');

        $this->dbh = $dbh; 

        $timezone = @date_default_timezone_get();
        if($timezone == '' || $timezone == 'UTC')
            date_default_timezone_set($this->timezone);

        // avoid notices for this noonce token
        if(!isset($_SESSION['_csrf']))
            $_SESSION['_csrf'] = '';
		



    }

    
    function run(){

        // purpose: built-in controller 

        switch($this->get_action()){
            case "edit":          $this->edit();        break;
            case "insert":        $this->insert();      break;
            case "update":        $this->update();      break;
            case "update_grid":   $this->update_grid(); break;
            case "delete":        $this->delete();      break;
            default:              $this->index();
        }

    }

    
    function edit($error = ''){

        // purpose: called from contoller to display form() and add or edit a record

        echo $this->form($error);

    }


    function insert(){

        // purpose: called from contoller to display insert() data
        
        $error = '';

        // validation system
        $is_valid = $this->validate($this->on_insert_validate);
        if(!$is_valid)
            $error = $this->validate_text_general; //optional general error at the top

        // call user function to validate or whatever
        if($is_valid && $this->on_insert_user_function != '')
            $error = call_user_func($this->on_insert_user_function);

        // go back on validation error
        if($error != '' || !$is_valid){
            $this->edit($error);
            return;
        }

        // insert data
        $id = $this->sql_insert();

        // user function after insert
        if($this->after_insert_user_function != '')
            call_user_func($this->after_insert_user_function, $id);
        
        // send user back to edit screen if desired
        $action = '';
        if($this->return_to_edit_after_insert)
            $action = 'action=edit&';

        // redirect user
        $url = $this->get_uri_path() . "{$action}_success=1&$this->identity_name=$id";
        $this->redirect($url, $id);

    }


    function update(){

        // purpose: called from contoller to display update() data

        $error = '';

        // validation system
        $is_valid = $this->validate($this->on_update_validate);
        if(!$is_valid)
            $error = $this->validate_text_general; //optional general error at the top

        // call user function to validate or whatever
        if($is_valid && $this->on_update_user_function != '')
            $error = call_user_func($this->on_update_user_function);

        // go back on validation error
        if($error != '' || !$is_valid){
            $this->edit($error);
            return;
        }

        // update data
        $id = $this->sql_update();

        // user function after update
        if($this->after_update_user_function != '')
            call_user_func($this->after_update_user_function);
        
        // send user back to edit screen if desired
        $action = '';
        if($this->return_to_edit_after_update)
            $action = 'action=edit&';

        // redirect user
        $url = $this->get_uri_path() . "{$action}_success=2&$this->identity_name=$id&" . $this->get_qs();
        $this->redirect($url, $id);

    }


    function update_grid(){

        // purpose: called from contoller to display update() data

        // call user function to validate or whatever
        $error = '';
        if($this->on_update_grid_user_function != '')
            $error = call_user_func($this->on_update_grid_user_function);

        // go back on validation error
        if($error != ''){
            $this->index($error);
            return;
        }
        
        // update data
        $flag = $this->sql_update_grid();

        // user function after updates
        if($this->after_update_grid_user_function != '')
            call_user_func($this->after_update_grid_user_function);

        // redirect user
        $url = $this->get_uri_path() . "_success=2&" . $this->get_qs();
        $this->redirect($url, $flag);

    }


    function delete(){

        // purpose: called from contoller to display update() data

        // call user function to validate or whatever
        $error = '';
        if($this->on_delete_user_function != '')
            $error = call_user_func($this->on_delete_user_function);

        // go back on validation error
        if($error != ''){
            if($_POST['_called_from'] == 'form')
                $this->edit($error);
            else
                $this->index($error);

            return;
        }
        
        // delete data
        $flag = $this->sql_delete();

        // user function after delete
        if($this->after_delete_user_function != '')
            call_user_func($this->after_delete_user_function);

        // redirect user
        $url = $this->get_uri_path() . "_success=3&" . $this->get_qs();
        $this->redirect($url, $flag);

    }


    function index($error = ''){

        // purpose: called from contoller to display update() data

        echo $this->grid($error);

    }

    
    function sql_delete(){

        // purpose: delete the requested record
        // returns: false on error, true on success
        
        $identity_id = $this->cast_id($_POST[$this->identity_name]);

        if($identity_id == 0){
            $this->display_error("missing identity_value", 'delete()');
            return false;
        }

        if(!$this->upload_delete($this->table, $this->identity_name, $identity_id, '*', $this->form_input_control))
            return false;

        $sql_param = array(':identity_id' => $identity_id);
        $sql = "delete from `$this->table` where `$this->identity_name` = :identity_id limit 1";
        if($this->query($sql, $sql_param, 'delete()') === false)
            return false;

    }


    function sql_insert(){

        // purpose: generate insert sql statement from the data posted and table's meta data
        // returns: false on error, id returned on success

        $columns = $this->get_columns();

        if(mb_strlen($this->table) == 0 || count($columns) == 0){
            $this->display_error("missing tablename, or get_columns() failed", 'insert()');
            return false;
        }

        // format list of fields from posted data
        $sql_fields = '';
        foreach($_POST as $key => $val){

            // checkboxes require a special hidden field to identify unchecked values
            if(mb_substr($key, -9) == '-checkbox')
                if(mb_substr($key, 0, -9) != $prev_key)
                    $key = mb_substr($key, 0, -9);

            $prev_key = $key;

            if(!(array_search($key, $columns) === false))
                $sql_fields .= "`$key`, ";  
        }
        $sql_fields = rtrim($sql_fields, ', ');
        

        // format list of values from posted data
        $sql_param = array();
        $sql_values = '';
        foreach($_POST as $key => $val){

            // don't allow updates on certain fields
            if(array_key_exists($key, $this->exclude_field))
                continue;

            // checkboxes require a special hidden field to identify unchecked values
            if(mb_substr($key, -9) == '-checkbox')
                if(mb_substr($key, 0, -9) != $prev_key)
                    $key = mb_substr($key, 0, -9);

            if(!(array_search($key, $columns) === false)){
                $safe_np = $this->safe_np($key);
                $sql_values .= ":$safe_np, ";
                $sql_param[":$safe_np"] = $this->cast_value($val, $key);
            }

            $prev_key = $key;

        }

        $sql_values = rtrim($sql_values, ', '); 
        
        $sql = "insert into `$this->table` ($sql_fields) values ($sql_values);";
        $identity_id = $this->query($sql, $sql_param, 'insert()');
        if($identity_id === false)
            return false;

        if(!$this->get_upload($columns, $this->table, $this->identity_name, $identity_id))
            return false;

        return $identity_id;
    }


    function sql_update(){

        // purpose: generate and run update sql statement from the data posted and table's meta data
        // returns: false on error, true on success

        $columns = $this->get_columns();
        $identity_id = $this->cast_id($_POST[$this->identity_name]);

        if(mb_strlen($this->table) == 0 || $identity_id == 0 || count($columns) == 0){
            $this->display_error("missing tablename, or missing identity_value, or get_columns() failed", 'update()');
            return false;
        }

        // posted values are saved here for pdo execute
        $sql_param = array();
        $sql_param[':identity_id'] = $identity_id;

        // make sql statement from key and values in $_POST data
        $sql_set = '';
        foreach($_POST as $key => $val){

            // don't allow updates on certain fields
            if(array_key_exists($key, $this->exclude_field))
                continue;

            // checkboxes require a special hidden field so unchecked values are detectable
            if(mb_substr($key, -9) == '-checkbox')
                if(!array_key_exists(mb_substr($key, 0, -9), $_POST))
                    $key = mb_substr($key, 0, -9);
            
            if(!(array_search($key, $columns) === false)){
                $safe_np = $this->safe_np($key);
                $sql_set .= "`$key` = :$safe_np, "; 
                $sql_param[":$safe_np"] = $this->cast_value($val, $key);
            }

        }
        $sql_set = rtrim($sql_set, ', ');

        // run sql update
        if($sql_set != ''){

            $sql_final = "update `$this->table` set $sql_set where `$this->identity_name` = :identity_id;";
            
            if($this->query($sql_final, $sql_param, 'update()') === false)
                return false;
        }

        if(!$this->get_upload($columns, $this->table, $this->identity_name, $identity_id))
            return false;

        return $identity_id;

    }


    function sql_update_grid(){

        // purpose: generate multiple update sql statements from editable fields in grid()
        // returns: false on error, true on success

        $columns = $this->get_columns('grid');
        $skip_update_on_column_name = '';

        $table = $this->table;
        $identity_name = $this->identity_name;
        $input_control = $this->grid_input_control;

        if(mb_strlen($table) == 0 || count($columns) == 0 || (count($input_control) == 0 && $this->grid_multi_delete == false)){
            $this->display_error("missing table name, or get_columns(), or grid_input_control is empty", 'update_grid()');
            return false;
        }

        // optimization flag
        $run_upload = false;
        if(array_search('--image', $input_control) || array_search('--document', $input_control))
            $run_upload = true;

        // gather all identity ids from suffix of input_name-identity_id
        $arr_identity_id = array();
        $prev_identity_id = 0;
        $post = array_merge($_POST, $_FILES);
        foreach($post as $key => $val){
            
            if(!mb_strstr($key, '-'))
                continue;    
            
            $identity_id = $this->cast_id(mb_substr($key, mb_strrpos($key, '-') + 1));

            if($identity_id == 0)
                continue;

            if($identity_id != $prev_identity_id)
                array_push($arr_identity_id, $identity_id);

            $prev_identity_id = $identity_id;
        }

        // run updates
        $sql_final = '';
        $sql_param = array();
        $this->dbh->beginTransaction();
        foreach($arr_identity_id as $identity_id){

            $sql_set = '';
            $sql_param[':identity_id'] = $identity_id;

            // loop thur editable columns
            foreach($input_control as $column_name => $control){

                if(array_search($column_name, $columns) === false)
                    continue;

                if($column_name == $skip_update_on_column_name)
                    continue;

                if($this->is_upload($input_control, $column_name))
                    continue;

                $safe_np = $this->safe_np($column_name);
                $sql_set .= "`$column_name` = :$safe_np, ";
                $sql_param[":$safe_np"] = $this->cast_value(@$_POST["$column_name-$identity_id"], $column_name, 'grid');

            }

            // append statements
            if(mb_strlen($sql_set) > 0){
                
                $sql_set = rtrim($sql_set, ', ');
                $sql_final = "update `$table` set $sql_set where `$identity_name` = :identity_id;\n";

                if($this->query($sql_final, $sql_param, 'sql_update_grid()') === false)
                    return false;
                $sql_param = array();
            
            }

        }
        $this->dbh->commit();

        // upload files
        if($run_upload)
            foreach($arr_identity_id as $identity_id)
                $this->get_upload($columns, $table, $identity_name, $identity_id, $context);

        // get records to delete
        $arr_delete = @$_POST['_delete'];
        if(!is_array($arr_delete))
            $arr_delete = array();

        // delete records
        $sql = '';
        $sql_param = array();
        foreach($arr_delete as $identity_id){

            if($run_upload)
                $this->upload_delete($table, $identity_name, $identity_id, '*', $input_control);

            $sql = "delete from `$table` where `$identity_name` = :identity_id;";
            $sql_param = array(':identity_id' => $identity_id);
            if($this->query($sql, $sql_param, 'sql_update_grid() - delete') === false)
                return false;

        }

        return true;

    }

    
    function form($error = ''){

        // purpose: generate a form to edit or add a record
        // if a record is found the form will be populated for editing, otherwise the form is empty and form is for adding/inserting data
        // $error = error message to display before form, often from server-side validation
        // returns: html

        if(mb_strlen($this->identity_name) == 0 || (mb_strlen($this->grid_sql) && mb_strlen($this->table) == 0)){
            $this->display_error("missing grid_sql and table (one is required), or missing identity_name", 'form()');
            return;
        }

        $identity_id = $this->cast_id(@$_GET[$this->identity_name]);
        if($identity_id == 0)
            $identity_id = $this->cast_id(@$_POST[$this->identity_name]);

        $sql = $this->form_sql;
        $sql_param = $this->form_sql_param;
        
        // make sql statement from table name if no sql was provided
        if(mb_strlen($sql) == 0){
            $sql_param = array(':identity_id' => $identity_id);
            $sql = "select * from `$this->table` where `$this->identity_name` = :identity_id";
        }

        // run query
        $result = $this->query($sql, $sql_param, 'form()');

        // quit on error
        if($result === false)
            return;

        $columns = $this->get_columns('form');
        $count = count($result);
        $_posted = intval(@$_POST['_posted']);

        // success messages 
        $success = intval(@$_GET['_success']);
        if($success == 1)
            $success = $this->form_text_record_added;
        elseif($success == 2)
            $success = $this->form_text_record_saved; 
        else
            $success = '';

        // are we adding (blank form) or editing (populated form) a record
        if($count == 0){
            $action = 'add';
            $title = $this->form_text_title_add;
            $validate = $this->on_insert_validate;
        }
        else{
            $action = 'edit';
            $title = $this->form_text_title_edit;
            $validate = $this->on_update_validate;
        }

        // get 1 row of data if available
        $row = false;
        $identity_id = 0; // id fetched below
        if(count($result) > 0){
            $row = $result[0];
            $identity_id = $this->cast_id($row[$this->identity_name]);
        }

        if($action == 'edit' && $identity_id == 0)
            $error .= "Missing identity_id. If using a custom form_sql statement be sure to include the identity. ";
    
        // query string is used here in form() to maintain pagination and sort data so user can return back to the same place in grid results 
        $qs = $this->get_qs();
        if(mb_strlen($qs) > 0)
            $qs = "$qs";
        
        $uri_path = $this->get_uri_path();

        $html  = "<div id='lm'>\n";
        $html .= "<form action='$uri_path$qs' method='post' enctype='multipart/form-data'>\n";
        $html .= "<input type='hidden' name='_csrf' value='$_SESSION[_csrf]'>\n";
        $html .= "<input type='hidden' name='_posted' value='1'>\n";

        if(mb_strlen($error) > 0)
            $html .= "<div class='lm_error'><b>$error</b></div>\n";
        
        if(mb_strlen($success) > 0)
            $html .= "<div class='lm_success'><b>$success</b></div>\n";
        
        $html .= "<table cellpadding='2' cellspacing='1' border='0' class='lm_form'>\n";

        if(mb_strlen($title) > 0)
            $html .= "<tr>\n    <th colspan='2'>$title</th>\n</tr>\n";

        // loop thru fields
        foreach($columns as $column_name){

            if($column_name == $this->identity_name && ($this->form_display_identity == false || $action == 'add'))
                continue;

            // get data from database or repost
            if($_posted == 1 && !mb_stristr(@$this->form_input_control[$column_name], 'readonly'))
                $value = @$_POST[$column_name];
            elseif($count == 0)
                $value = @$this->form_default_value[$column_name];
            else
                $value = $row[$column_name];

            // field label
            $title = $this->format_title($column_name, @$this->rename[$column_name]);

            // render the html control according to the type of data
            $control = "";    

            if($column_name == $this->identity_name)
                $control = $this->clean_out($value);
            elseif(array_key_exists($column_name, $this->form_input_control))
                $control = $this->get_input_control($column_name, $value, $this->form_input_control[$column_name], 'form', $validate);
            else
                $control = $this->get_input_control($column_name, $value, '--text', 'form', $validate);
        
            $html .= "<tr>\n";
            $html .= "    <td>$title:</td>\n";
            $html .= "    <td>$control</td>\n";
            $html .= "</tr>\n";
        }

        $html .= "</table>\n";

        if($action == 'edit')
            $html .= "<input type='hidden' name='$this->identity_name' value='$identity_id'>\n";

        // action 
        if($action == 'edit')
            $html .= "<input type='hidden' name='action' value='update'>\n";
        else
            $html .= "<input type='hidden' name='action' value='insert'>\n";
        
        // add buttons
        if($action == 'edit')
            $html .= "<div class='lm_form_button_bar'>$this->form_back_button $this->form_delete_button $this->form_update_button</div>";
        else
            $html .= "<div class='lm_form_button_bar'>$this->form_back_button $this->form_add_button</div>";

        $html .= $this->form_additional_html;
        $html .= "</form>\n";
        $html .= "</div><!-- close #lm -->\n";
        $html .= $this->delete_js($identity_id, 'form');
        
        return $html;    

    }

    
    function grid($error = ''){

        // purpose: function to list a table of records. aka data grid
        // returns: html
        // populate_from_post tells inputs to populate from $_POST instead of the database. useful to preserve data when displaying validation errors.
        // in grid_sql, select the identity_id as the last column to display the edit and delete links
        // example: $lm->grid_sql = 'select title, create_date, foo_id from foo';

        if(mb_strlen($this->identity_name) == 0 || (mb_strlen($this->grid_sql) && mb_strlen($this->table) == 0)){
            $this->display_error("missing grid_sql and table (one is required), or missing identity_name", 'form()');
            return;
        }

        // local copies 
        $sql = trim($this->grid_sql);
        $sql_param = $this->grid_sql_param;
        $grid_limit = intval($this->grid_limit);
        $uri_path = $this->get_uri_path();
        $default_order_by = $this->grid_default_order_by;

        if($default_order_by == '')
            $default_order_by = "`$this->identity_name`";

        // remove line feeds which can cause problems with parsing
        $sql = preg_replace('/[\n\r]/', ' ', $sql);

        // default queries if only table and id names were provided
        if(mb_strlen($sql) == 0)
            $sql = "select *, `$this->identity_name` from `$this->table` order by $default_order_by";

        // inject funciton for counting
        $sql = preg_replace('/^select/i', 'select sql_calc_found_rows', $sql);

        // get input
        $_posted = intval(@$_REQUEST['_posted']);
        $_search = $this->clean_out(@$_REQUEST['_search']);
        $_pagination_off = intval(@$_REQUEST['_pagination_off']);
        $_order_by = abs(intval(@$_REQUEST['_order_by'])); // order by is numeric index to column
        $_desc = abs(intval(@$_REQUEST['_desc']));         // descending order flag
        $_offset = abs(intval(@$_REQUEST['_offset']));     // pagination offset
        $_export = intval(@$_REQUEST['_export']); 
        
        $qs = $this->get_qs();

        // header links - invert current sort
        $_desc_invert = 1;
        if($_desc == 1)
            $_desc_invert = 0;

        // success messages 
        $success = intval(@$_GET['_success']);
        if($success == 1)
            $success = $this->grid_text_record_added;
        elseif($success == 2)
            $success = $this->grid_text_changes_saved;
        elseif($success == 3)
            $success = $this->grid_text_record_deleted;
        else
            $success = '';

        // column array and column count 
        $columns = $this->get_columns('grid');
        $column_count = count($columns);
        if($column_count == 0)
            return;

        // alter order
        $desc_str = '';
        if($_order_by > 0){
            if($_desc == 1)
                $desc_str = 'desc'; 
            $sql = rtrim($sql, '; '); // remove last semicolon
            $sql = preg_replace('/order\s+by\s+.+$/i', '', $sql); // remove existing order
            $sql .= " order by $_order_by $desc_str"; // add requested sort order
        }

        // remove existing limit
        $sql = preg_replace('/\s+limit\s+[0-9,\s]+$/i', '', $sql); 

        // add limit and offset for pagination
        if($_pagination_off == 0 && $_export == 0)
            $sql .= " limit $_offset, $grid_limit"; 

        // run query
        $result = $this->query($sql, $sql_param, 'grid() run query');
        if(!is_array($result))
            $result = array();

        // get count
        $count = 0;
        $sql = 'select found_rows() as cnt';
        $result_count = $this->query($sql);
        foreach($result_count as $row)
            $count = $row['cnt'];

        // export data to CSV and quit 
        if($_export == 1 && $count > 0){
            $this->export($result, $columns);
            return;    
        }

        // populate link placeholders    
        $grid_add_link = $this->grid_add_link;
        $grid_edit_link = $this->grid_edit_link;
        $grid_delete_link = $this->grid_delete_link;
        $grid_export_link = $this->grid_export_link;
        $grid_add_link = str_replace('[script_name]', $uri_path, $grid_add_link);
        $grid_add_link = str_replace('[qs]', $qs, $grid_add_link);
        $grid_edit_link = str_replace('[script_name]', $uri_path, $grid_edit_link);
        $grid_edit_link = str_replace('[qs]', $qs, $grid_edit_link);
        $grid_edit_link = str_replace('[identity_name]', $this->identity_name, $grid_edit_link);
        $grid_delete_link = str_replace('[script_name]', $uri_path, $grid_delete_link);
        $grid_delete_link = str_replace('[qs]', $qs, $grid_delete_link);
        $grid_delete_link = str_replace('[identity_name]', $this->identity_name, $grid_delete_link);
        $grid_export_link = str_replace('[script_name]', $uri_path, $grid_export_link);
        $grid_export_link = str_replace('[qs]', $qs, $grid_export_link);
        $links = $grid_edit_link . ' ' . $grid_delete_link;

        // pagination and save changes link bar
		$pagination = $this->get_pagination($count, $grid_limit, $_offset, $_pagination_off);
        $button = '';
		if(count($this->grid_input_control) > 0 || $this->grid_multi_delete == true)
            $button = "<input type='submit' name='__update_grid' value='$this->grid_text_save_changes' class='lm_button lm_save_changes_button'>";
		$pagination_button_bar = "<table cellpadding='2' cellspacing='1' border='0' width='100%' class='lm_pagination'><tr><td style='text-align: left'>$pagination</td><td style='text-align: right'>$button</td></tr></table>\n";

        // search bar
        $search_box = '';
		if($this->grid_show_search_box){
            $search_box = $this->grid_search_box;
			$search_box = str_replace('[script_name]', $uri_path, $search_box);
			$search_box = str_replace('[_search]', $_search, $search_box);
			$search_box = str_replace('[_csrf]', $_SESSION['_csrf'], $search_box);
        }

		$add_record_search_bar = "<table cellpadding='2' cellspacing='1' border='0' width='100%' class='lm_add_search'><tr><td style='text-align: left'>$grid_add_link &nbsp; $grid_export_link</td><td style='text-align: right'>$search_box</td></tr></table>\n";

        // generate table header
        $head = "<tr>\n";
        if($this->grid_multi_delete)
            $head .= "<th><a href='#' onclick='return _toggle();' title='toggle checkboxes'>$this->grid_text_delete</a></th>\n";
        
        $i = 0;
        foreach($columns as $column_name){

            $title = $this->format_title($column_name, @$this->rename[$column_name]);

            if($column_name == $this->identity_name && $i == ($column_count - 1))
                $head .= "    <th></th>\n"; // if identity is last column then this is the column with the edit and delete links
            else
                $head .= "    <th><a href='{$uri_path}_order_by=" . ($i + 1) . "&_desc=$_desc_invert&" . $this->get_qs('_search') . "'>$title</a></th>\n";
        
            $i++;

        }
        $head .= "</tr>\n";
            
        // start generating output //
        $html = "<div id='lm'>\n";

        if(mb_strlen($success) > 0)
            $html .= "<div class='lm_success'><b>$success</b></div>\n";
        if(mb_strlen($error) > 0)
            $html .= "<div class='lm_error'><b>$error</b></div>\n";
        
        $html .= $add_record_search_bar;

        $html .= "<form action='$uri_path$qs' method='post' onsubmit='return _update_grid()' enctype='multipart/form-data'>\n";
        $html .= "<input type='hidden' name='_posted' value='1'>\n";
        $html .= "<input type='hidden' name='_csrf' value='$_SESSION[_csrf]'>\n";

        // quit if there's no data
        if($count <= 0){
            $html .= "<div class='lm_error'><b>$this->grid_text_no_records_found</b></div></form></div><!-- close #lm -->\n";
            return $html;    
        }    

        // buttons & pagination on top. only show if we have a lot of records
        if($count > 30)
            $html .= $pagination_button_bar;

        $html .= "<table cellpadding='2' cellspacing='1' border='0' width='100%' class='lm_grid'>\n";
        $html .= $head;

        // print rows
        $j = 0;
        foreach($result as $row){

            // highlight last updated or inserted row
            $shaded = '';
            if(@$_GET[$this->identity_name] == @$row[$this->identity_name] && mb_strlen(@$_GET[$this->identity_name]) > 0)
                $shaded = "class='lm_active'";

            // print a row
            $html .= "<tr $shaded>\n";

            // delete selection checkbox
            if($this->grid_multi_delete){
                $html .= "<td align='center'><label><input type='checkbox' name='_delete[]' value='{$row[$this->identity_name]}'></label></td>\n";
            }

            // print columns
            $i = 0;
            foreach($columns as $column_name){

                $value = $row[$column_name];

                // edit & delete links
                if($column_name == $this->identity_name && $i == ($column_count - 1))
                    $html .= "    <td nowrap>" . str_replace('[identity_id]', $value, $links) . "</td>\n";

                // input fields
                elseif(array_key_exists($column_name, $this->grid_input_control)){
                    if(mb_strlen($error) > 0 && $_posted == 1) // repopulate from previous post when validation error is displayed
                        $value = $_POST[$column_name . '-' . $row[$this->identity_name]];
                    $html .= '    <td>' . $this->get_input_control($column_name . '-' . $row[$this->identity_name], $value,  $this->grid_input_control[$column_name], 'grid') . "</td>\n";
                }

                // output
                elseif(array_key_exists($column_name, $this->grid_output_control))
                    $html .= '    <td>' . $this->get_output_control($column_name . '-' . $row[$this->identity_name], $value, $this->grid_output_control[$column_name], 'grid') . "</td>\n";

                // anything else
                else
                    $html .= '    <td>' . $this->get_output_control($column_name . '-' . $row[$this->identity_name], $value, '--text', 'grid') . "</td>\n";

                $i++; // column index
            }

            $html .= "</tr>\n";

            // repeat header
            if($this->grid_repeat_header_at > 0)
                if($j % $this->grid_repeat_header_at == 0 && $j < $count && $j > 0)
                    $html .= $head;
                
            // row counter    
            $j++;
        }

        $html .= "</table>\n";

        // buttons & pagination, close form
        $html .= $pagination_button_bar;
        $html .= "</form>\n";
        $html .= "</div><!-- close #lm -->\n";
		$html .= $this->delete_js(0, 'grid');

        return $html;

    }

    
    function delete_js($identity_id = 0, $_called_from = ''){

        // purpose: extra form & js for deleing records and going back
        // returns: html

        $uri_path = $this->get_uri_path();
        $qs = $this->get_qs(); 
        $back_link = $uri_path . $qs;

        // append id for row highlighting
        if($identity_id > 0)
            $back_link .= "&$this->identity_name=$identity_id";

        // escape for js
        $delete_confirm = str_replace("'", "\'", $this->delete_confirm);
        $update_grid_confirm = str_replace("'", "\'", $this->update_grid_confirm);

        return "
        <form action='$uri_path$qs' method='post' name='delete_js'>
        <input type='hidden' name='action' value='delete' >
        <input type='hidden' name='$this->identity_name' value='$identity_id' >
        <input type='hidden' name='_csrf' value='$_SESSION[_csrf]' >
        <input type='hidden' name='_called_from' value='$_called_from' >
        </form>

        <script type='text/javascript'>
		
		function _delete(id){

            if(!confirm('$delete_confirm'))
                return false;

            if(id)
                document.delete_js.elements['$this->identity_name'].value = id;
            
            document.delete_js.submit();
            return false;

        }    

        function _back(){

            window.location.href = '$back_link';

        }    

        function _toggle(){
        
            var form_cnt = document.forms.length;
            var i;

            for (i = 0; i < form_cnt; i++)
                for (j = 0; j < document.forms[i].length; j++)
                    if(document.forms[i].elements[j].name == '_delete[]')
                        document.forms[i].elements[j].checked = !document.forms[i].elements[j].checked;

            return false;

        }

        function _update_grid(){

            var form_cnt = document.forms.length;
            var i;
            var delete_cnt = 0;
            var msg = '$update_grid_confirm';

            for (i = 0; i < form_cnt; i++)
                for (j = 0; j < document.forms[i].length; j++)
                    if(document.forms[i].elements[j].name == '_delete[]')
                        if(document.forms[i].elements[j].checked)
                            delete_cnt++;

            if(delete_cnt == 0)
                return true;

            msg = msg.replace('[count]', delete_cnt);
            if(!confirm(msg))
                return false;

            return true;

        }
        </script>
        ";
    }
    

    function html_image_input($field_name, $file_name){

        // purpose: if image exists, display image and delete checkbox. also display file input
        // returns: html

        $html = '';
        $class = $this->get_class_name($field_name);

        if(mb_strlen($file_name) > 0){
        
            if(mb_strlen($this->thumb_path))
                $html .= "<a href='$this->upload_path/$file_name' target='_blank'><img src='$this->thumb_path/$file_name' alt='' $this->image_style ></a>";
            else
                $html .= "<a href='$this->upload_path/$file_name' target='_blank'><img src='$this->upload_path/$file_name' alt='' $this->image_style ></a>";

            $html .= " <label><input type='checkbox' name='{$field_name}-delete' value='1' >$this->text_delete_image</label><br>";
        }

        $html .= "<input type='file' name='$field_name' class='$class' size='$this->form_text_input_size'>";

        return $html;

    }


    function html_document_input($field_name, $file_name){

        // purpose: if document exists, display link and delete checkbox. also display file input
        // returns: html

        $class = $this->get_class_name($field_name);

        if(mb_strlen($file_name) > 0)
            $html .= "<a href='$this->upload_path/$file_name' target='_blank'>$file_name</a> <label><input type='checkbox' name='{$field_name}-delete' value='1'>$this->text_delete_document</label><br >";

        $html .= "<input type='file' name='$field_name' class='$class' size='$this->form_text_input_size'>";

        return $html;

    }


    function html_image_output($file_name){

        // purpose: if image exists, display image depending on settings and if thumbnail exists
        // returns: html

        if(mb_strlen($file_name) == 0)
            return;

        if($this->grid_show_images == false)
            return "<a href='$this->upload_path/$file_name' target='_blank'>" . $this->clean_out($file_name, $this->grid_ellipse_at) . "</a>";
        elseif(mb_strlen($this->thumb_path))
            return "<a href='$this->upload_path/$file_name' target='_blank'><img src='$this->thumb_path/$file_name' alt='' $this->image_style ></a>";
        else
            return "<a href='$this->upload_path/$file_name' target='_blank'><img src='$this->upload_path/$file_name' alt='' $this->image_style ></a>";


    }


    function html_document_output($file_name){

        // purpose: if exists, display document link
        // returns: html

        if(mb_strlen($file_name) > 0)
            $html = "<a href='$this->upload_path/$file_name' target='_blank'>" . $this->clean_out($file_name, $this->grid_ellipse_at) . "</a>";

        return $html;

    }


    function html_html_output($value){

        // purpose: when html is displayed in grid remove the tags and just display the plain text content
        // returns: html

        if(mb_strlen($value) == 0)
            return;

        $html = strip_tags($value);
        $html = str_replace('&nbsp;', ' ', $html);

        if($this->grid_ellipse_at > 0)
            if(mb_strlen($html) > $this->grid_ellipse_at)
                $html = mb_substr($html, 0, $this->grid_ellipse_at) . "...";
        $html = str_replace('<', '&lt;', $html);
        $html = str_replace('>', '&gt;', $html);

        return $this->clean_out($html);

    }

	
	function html_radio($field_name, $value, $sql){    

        // purpose: render html radio input, note sql query should return 2 columns
        // returns: html

        static $prev_sql = '';
        static $result;
        $class = $this->get_class_name($field_name);

        // if no sql is provided render 1 = yes/ 0 = no
        if($sql == '')
            $sql = "select 1 as val, 'Yes' as opt union select 0, 'No'";

        // simple optimization
        if($prev_sql != $sql)
            $result = $this->query($sql, array(), 'html_radio()');

        $prev_sql = $sql;

        $control = '';    
        foreach($result as $row){

            $val = current($row);
            $opt = next($row);

            $checked = "";
            if($val == $value)
                $checked = "checked='checked'";
            
            $control .= "<label><input type='radio' name='$field_name' class='$class' value='" . $this->clean_out($val) . "' $checked >" . $this->clean_out($opt) . "</label> ";
        }
        
        return $control;

    }


    function html_select($field_name, $value, $sql, $sql_param = array(), $js_or_css = '', $first_option_blank = true, $multiple = 0){

        // purpose: render html select dropdown from sql query
        // returns: html
        // sample query: select 0 as val, 'no' as opt union select 1, 'yes'

        static $prev_sql = '';
        static $result;
        $out = '';
        $class = $this->get_class_name($field_name);

        // if no sql is provided render 1 = yes, 0 = no
        if($sql == '')
            $sql = "select 1 as val, 'Yes' as opt union select 0, 'No'";
	
        // simple optimization
        if($prev_sql != $sql)
            $result = $this->query($sql, $sql_param, 'html_select()');

        $prev_sql = $sql;

        if($multiple == 0){

            if($first_option_blank)
                $out .= "<option value=''>&nbsp;</option>";

            foreach($result as $row){

                $val = current($row);
                $opt = next($row);

                $selected = '';
                if($val == $value)
                    $selected = "selected='selected'";

                $out .= "<option value='" . $this->clean_out($val) . "' $selected>" . $this->clean_out($opt) . "</option>";
            }
            $out = "<select name='$field_name' class='$class' $js_or_css>$out</select>";
            return $out;
        }
            
        // render multiple select
        $out  = "";
        if($first_option_blank)
            $out .= "<option value=''>&nbsp;</option>";

        $value  = "$this->delim$value$this->delim";
        foreach($result as $row){

            $val = current($row);
            $opt = next($row);

            $selected = '';    
            if(mb_strstr($value, "$this->delim$val$this->delim"))
                $selected = "selected='selected'";
        
            $out .= "<option value='" . $this->clean_out($val) . "' $selected>" . $this->clean_out($opt) . "</option>";
        }    

        $out = "<select name='{$field_name}[]' class='$class' multiple='multiple' size='$multiple' $js_or_css>$out</select>";
        return $out;

    }


    function html_checkbox($field_name, $value, $sql, $sql_param = array()){

        // purpose: render checkbox inputs
        // returns: html

        $class = $this->get_class_name($field_name);

        // make hidden field name. an additional hidden field is require to detect presence of checkbox. 
        $i = mb_strrpos($field_name, '-');
        if($i > 0)
            $field_name_hidden = mb_substr($field_name, 0, $i) . '-checkbox' . mb_substr($field_name, $i); // grid format field_name-checkbox-identity_id
        else
            $field_name_hidden = $field_name . '-checkbox';


        // if no sql is provided just return a single checkbox for 1 = yes
        $checked = '';
        if($sql == ''){
            
            if(intval($value) > 0)
                $checked = "checked='checked'";
            
            return "<label><input type='checkbox' name='$field_name' class='$class' value='1' $checked ></label><input type='hidden' name='$field_name_hidden' value=''>";
        }

        static $prev_sql = '';
        static $result;

        // simple optimization
        if($prev_sql != $sql)
            $result = $this->query($sql, $sql_param, 'html_radio()');

        $prev_sql = $sql;

        $value  = "$this->delim$value$this->delim";
        foreach($result as $row){

            $val = current($row);
            $opt = next($row);

            $checked = '';    
            if(mb_strstr($value, "$this->delim$val$this->delim"))
                $checked = "checked='checked'";

            $html .= "<label><input type='checkbox' name='{$field_name}[]' class='$class' value='" . $this->clean_out($val) . "' $checked >" . $this->clean_out($opt) . "</label>&nbsp;";
        }
        $html .= "<input type='hidden' name='$field_name_hidden' value='0'>";

        return $html;

    }

   
    function cast_value($val, $column_name = '', $posted_from = 'form'){
        
        // purpose: cast data going into the database. set blanks null and format dates
        // returns: string
        // $column_name is not used right now but might be needed as a hack to cast by column name for databases like sqlite
        // missing types seem to always be numbers

        if(is_array($val))
            $val = implode($this->delim, $val);

        $val = trim($val);

        if($posted_from == 'grid')
            $command = @$this->grid_input_control[$column_name];
        else
            $command = @$this->form_input_control[$column_name];

        // get command only, no sql, no '--'
        $cmd = trim(mb_substr($command, mb_strrpos($command, '--') + 2));

        if(isset($this->cast_user_function[$column_name]))
            $val = call_user_func($this->cast_user_function[$column_name], $val);
        elseif($cmd == 'date')
            $val = $this->date_in($val);
        elseif($cmd == 'datetime')
            $val = $this->date_in($val, true);
        elseif($cmd == 'number' && mb_strlen($this->restricted_numeric_input) > 0)
            $val = preg_replace($this->restricted_numeric_input, '', $val);
        
        if(mb_strlen($val) == 0)
            $val = null;

        return $val;

    }


	function cast_id($str){

		// purpose: similar to intval() but supports mysql bigint and does not overflow

		if(preg_match('/-?[0-9]{1,30}/', $str))
			return $str;
		else
			return 0;

	}

    
    function clean_file_name($file_name){

        // purpose: make uniform file_names to lowercase, no dots, no spaces

        $file_name = mb_strtolower($file_name);
        $ext = pathinfo($file_name, PATHINFO_EXTENSION);
        $prefix = mb_substr($file_name, 0, (mb_strlen($ext) + 1) * -1); 
        $prefix = str_replace(' ', '_', $prefix);
        $prefix = preg_replace('([^0-9a-z_\-])', '', $prefix);
        return "$prefix.$ext";

    }


    function clean_out($str, $ellipse_at = 0){

        // purpose: escape html output w/ optional ellipsing

        if(is_array($str))
            $str = implode($this->delim, $str);
        elseif($ellipse_at > 0)
            if(mb_strlen($str) > $ellipse_at)
                $str = mb_substr($str, 0, $ellipse_at, $this->charset) . "...";

        // remove illegal characters
        $str = mb_convert_encoding($str, $this->charset, mb_detect_encoding($str));

        return htmlspecialchars($str, ENT_QUOTES, $this->charset);
    }


    function date_in($str, $use_time = false){

        // purpose: convert local format to database format

        if($str == '')
            return;

        // remove spaces between slash or dash delimiters. allow user to be a little sloppy.
        $str = preg_replace('/([0-9]+)\s?([\/\-]){1}\s?([0-9]+)\s?([\/\-]){1}\s?([0-9]+)/', '\1\2\3\4\5', $str);

        // strtotime requires dash delimiter when using d/m/y   
        if(preg_match('/[dj].*[mn]/', $this->date_out))
            $str = str_replace('/', '-', $str);

        $ts = strtotime($str);
        if($ts === false) 
            return;
        
        if($use_time)    
            return date($this->datetime_in, $ts);
        else
            return date($this->date_in, $ts);

    }


    function date_out($str, $use_time = false){

        // purpose: convert database format to local format

        if(mb_strlen($str) < 8)
            return; 
        
        if($use_time)
            return date($this->datetime_out, strtotime($str));
        else
            return date($this->date_out, strtotime($str));

    }


    function format_title($field_name, $friendly_name = ''){
        
        // purpose: change field name to friendly. example: first_name becomes "First Name" or EntryFee1 becomes "Entry Fee 1"
        // returns: html escaped string

        if(mb_strlen($friendly_name) > 0)
            return $this->clean_out($friendly_name);

        $friendly_name = $field_name;
        $friendly_name = preg_replace('/([a-z]{1})([A-Z]{1})/', '\1 \2', $friendly_name);
        $friendly_name = preg_replace('/([a-z]{1})([0-9]+)/i', '\1 \2 ', $friendly_name);
        $friendly_name = str_replace('_', ' ', $friendly_name);
		$friendly_name = mb_convert_case($friendly_name, MB_CASE_TITLE, $this->charset);

        return $this->clean_out($friendly_name);

    }


    function get_action(){

        // purpose: get the action/command on what function to call
        // submit buttons can be named with the action i.e. "__update" or "__delete";
        // look at the names/key posted to see if there's an action requested

        static $action;

        if(isset($action))
            return $action;

        $post_get = array_merge($_POST, $_GET);

        if(mb_strlen(@$post_get['action']) > 0)
            return $post_get['action'];

        foreach($post_get as $key => $val)
            if(mb_substr($key, 0, 2) == '__')
                return mb_substr($key, 2);

    }


    function get_input_control($column_name, $value, $command, $called_from, &$validate = array()){

        // purpose: render html input based "command", if command is then try to call a user function
        // returns: html 
        
        // parse $command into $sql and $cmd, remove delimiter
		$pos = mb_strrpos($command, '--');
		$cmd = trim(mb_substr($command, $pos + 2));
		$sql = mb_substr($command, 0, $pos);

        // default
        if(mb_strlen($cmd) == 0)
            $cmd = 'text';

        // set input size    
        if($called_from == 'grid')
            $size = $this->grid_text_input_size;
        else
            $size = $this->form_text_input_size;

        $class = $this->get_class_name($column_name); 

        $validate_error = ''; // error next to input
        $validate_tip = '';   // tip next to input
        $validate_placeholder = ''; // text in placeholder - text inputs only
        $validate_placeholder_alternative = ''; // placeholder setting disabled - move text next to input 

        // display tip or error next to input, not both
        if(@$validate[$column_name][4] === false)
            $validate_error = "<span class='lm_validate_error'>" . $this->clean_out($validate[$column_name][1]) . "</span>";
        elseif(@$validate[$column_name][2] != '')
            $validate_tip = "<span class='lm_validate_tip'>" . $this->clean_out($validate[$column_name][2]) . "</span>";

        // always try to get a placeholder for the text inputs
        if($this->validate_tip_in_placeholder)
            $validate_placeholder = $this->clean_out(@$validate[$column_name][2]); // placeholders for text 
        elseif($validate_error == '')
			$validate_placeholder_alternative = "<span class='lm_validate_tip'>" . $this->clean_out($validate[$column_name][2]) . "</span>";
    
		$max_length = '';
		if(intval(@$this->text_input_max_length[$column_name]) > 0)
			$max_length = "maxlength='" . $this->text_input_max_length[$column_name] . "'";
		elseif($this->text_input_max_length_default > 0)
			$max_length = "maxlength='" . $this->text_input_max_length_default . "'";

        if($cmd == 'text')
            return "<input type='text' name='$column_name' class='$class' value='" . $this->clean_out($value) . "' size='$size' $max_length placeholder='$validate_placeholder'>$validate_error $validate_placeholder_alternative";
        elseif($cmd == 'password')
            return "<input type='password' name='$column_name' class='$class' value='" . $this->clean_out($value) . "' size='$size' $max_length placeholder='$validate_placeholder'>$validate_error $validate_placeholder_alternative";
        elseif($cmd == 'number')
            return "<input type='text' name='$column_name' class='$class' value='" . $this->clean_out($value) . "' size='18' $max_length placeholder='$validate_placeholder'>$validate_error $validate_placeholder_alternative";
        elseif($cmd == 'date')
            return "<input type='text' name='$column_name' class='$class' value='" . $this->date_out($value) . "' size='18' $max_length placeholder='$validate_placeholder'>$validate_error $validate_placeholder_alternative";
        elseif($cmd == 'datetime')
            return "<input type='text' name='$column_name' class='$class' value='" . $this->date_out($value, true) . "' size='18' $max_length placeholder='$validate_placeholder'>$validate_error $validate_placeholder_alternative";
        elseif($cmd == 'textarea')
            return "<textarea name='$column_name' class='$class' cols='60' rows='6' placeholder='$validate_placeholder'>" . $this->clean_out($value) . "</textarea>$validate_error $validate_placeholder_alternative";
        elseif($cmd == 'readonly_datetime')
            return $this->date_out($value, true);
        elseif($cmd == 'readonly_date')
            return $this->date_out($value);
        elseif($cmd == 'readonly')
            return $this->clean_out($value);
        elseif($cmd == 'image')
            return $this->html_image_input($column_name, $value) . $validate_tip . $validate_error;
        elseif($cmd == 'document')
            return $this->html_document_input($column_name, $value) . $validate_tip . $validate_error;
        elseif($cmd == 'select')
            return $this->html_select($column_name, $value, $sql, array(), '', $this->select_first_option_blank, 0) . $validate_tip . $validate_error;
        elseif($cmd == 'selectmultiple')
            return $this->html_select($column_name, $value, $sql, array(), '', $this->select_first_option_blank, 6) . $validate_tip . $validate_error;
        elseif($cmd == 'radio')
            return $this->html_radio($column_name, $value, $sql, array()) . $validate_tip . $validate_error;
        elseif($cmd == 'checkbox')
            return $this->html_checkbox($column_name, $value, $sql, array()) . $validate_tip . $validate_error;
		elseif(is_callable($cmd))
            return call_user_func($cmd, $column_name, $value, $command, $called_from, $validate_placeholder) . $validate_tip . $validate_error;
        else
            $this->display_error("Input command or user function not found: $cmd. Be sure to prefix control type with 2 dashes --", 'get_input_control()');

    }


    function get_output_control($column_name, $value, $command, $called_from){

        // purpose: render html output based "command", if command is then try to call a user function
        // returns: html 

        // get command only, no '--'
        $cmd = trim(mb_substr($command, mb_strrpos($command, '--') + 2));

        // default
        if(mb_strlen($cmd) == 0)
            $cmd = 'text';

        if($cmd == 'text')
            return $this->clean_out($value, $this->grid_ellipse_at); 
        elseif($cmd == 'date')
            return $this->date_out($value); 
        elseif($cmd == 'datetime')
            return $this->date_out($value, true); 
        elseif($cmd == 'email')
            return "<a href='mailto:$value'>$value</a>";
        elseif($cmd == 'document')
            return $this->html_document_output($value);
        elseif($cmd == 'image')
            return $this->html_image_output($value);
        elseif($cmd == 'html')
            return $this->html_html_output($value);
		elseif(is_callable($cmd))
            return call_user_func($cmd, $column_name, $value, $command, $called_from);
        else
            $this->display_error("Output command or user function not found: $cmd. Be sure to prefix control type with 2 dashes --", 'get_output_control()');

    }

    
    function get_columns($context = ''){

        // purpose: make array of column names from table name or custom sql 
        // populates: grid_output_control or form_input_control
        // context options: form, grid, or blank/none
        // returns: number indexed array of column names

        $table = $this->table;
        $sql = '';
        $sql_param = array();

        if($context == 'grid'){
            $sql = $this->grid_sql;
            $sql_param = $this->grid_sql_param;
            if($sql == '')
                $sql = "select *, `$this->identity_name` from `$table`";
        }
        elseif($context == 'form'){
            $sql = $this->form_sql;
            $sql_param = $this->form_sql_param;
        }

        if($sql == '')
            $sql = "select * from `$table`";

        // simplify query for faster execution
        $sql = rtrim($sql, "\r\n\t; ");                         // remove last semicolon
        $sql = preg_replace('/order\s+by\s+.+$/i', '', $sql);   // remove order
        $sql = preg_replace('/limit\s+[0-9,\s]+$/i', '', $sql); // remove limit
        $sql .= ' limit 0 ';                                    // add limit

        $sth = $this->dbh->prepare($sql);
        if(!$sth->execute($sql_param)){
            $arr = $sth->errorInfo();
            $error = $arr[2];
            $this->display_error("$error\nsql: $sql\narr_sql_param: " . print_r($sql_param, true), 'get_columns');
            return $columns;
        }

        $i = 0;
        $columns = array();
        while($column = $sth->getColumnMeta($i++))
            array_push($columns, $column['name']);

        // quit now if there's nothing else to do
        if(!$this->auto_populate_controls)
            return $columns;

        // populate form_input_control
        $i = 0;
        while($column = $sth->getColumnMeta($i++)){
            
            $type = mb_strtolower(@$column['native_type']);
            $column_name = $column['name'];
            if(array_key_exists($column_name, $this->form_input_control) || $column_name == $this->identity_name)
                continue;

            if($type == 'date')
                $this->form_input_control[$column_name] = '--date';
            elseif($type == 'datetime' || $type == 'timestamp')
                $this->form_input_control[$column_name] = '--datetime';
            elseif($type == 'blob')
                $this->form_input_control[$column_name] = '--textarea';
            elseif(mb_strstr($type, 'short') || mb_strstr($type, 'int') || mb_strstr($type, 'long') || mb_strstr($type, 'float') || mb_strstr($type, 'double') || mb_strstr($type, 'decimal'))
                $this->form_input_control[$column_name] = '--number';

        }

        // populate grid_output_control with --date, --datetime
        $i = 0;
        while($context == 'grid' && $column = $sth->getColumnMeta($i++)){
            
            $type = mb_strtolower(@$column['native_type']);
            $column_name = $column['name'];
            if(array_key_exists($column_name, $this->grid_output_control) || $column_name == $this->identity_name)
                continue;

            if($type == 'date')
                $this->grid_output_control[$column_name] = '--date';
            elseif($type == 'datetime' || $type == 'timestamp')
                $this->grid_output_control[$column_name] = '--datetime';

        }

        // populate default values
        $sql = "select column_name, column_default from information_schema.columns where column_default is not null and table_name = :table and table_schema = database()";
        $sql_param = array(':table' => $table);
        $result = $this->query($sql, $sql_param, 'get_columns() - populate form_default_value');
        foreach($result as $row){
            
            if($context == 'form')
                if(!array_key_exists($row['column_name'], $this->form_default_value))
                    $this->form_default_value[$row['column_name']] = $row['column_default'];

        }

        return $columns;

    }

    
    function get_pagination($count, $limit, $_offset, $_pagination_off){

        // purpose: pagination for grid

        static $id = 0;    // for unique active page input id
        $get = $this->get_qs('_order_by,_desc,_search');
        $active_page = floor($_offset / $limit) + 1; 
        $total_page = ceil($count / $limit);
        $uri_path = $this->get_uri_path();

        if($count <= 0)
            return;

        $use_paging_link = '';
        if($_pagination_off == 1)
            $use_paging_link = "<a href='{$uri_path}_pagination_off=0&$get' rel='nofollow'>$this->pagination_text_use_paging</a>";

        if($_pagination_off == 1 || $count <= $limit) 
            return number_format($count) . " $this->pagination_text_records $use_paging_link";

        // simple text input for page number on giant datasets. use drop-down for smaller datasets.
        if($count > 100000){
            $input = "<input type='text' size='7' id='active_page_$id' value='$active_page' ><input type='button' value='$this->pagination_text_go' onclick='window.location.href=\"{$uri_path}_offset=\" + ((document.getElementById(\"active_page_$id\").value * $limit) - $limit) + \"&$get\"'>";
        }
        else
        {
            $input = "<select onchange='window.location.href=\"{$uri_path}_offset=\" + this.options[this.selectedIndex].value + \"&$get\"'>";
            for($i = 0, $p = 1; $i < $count; $i += $limit, $p++){
                $sel = '';
                if($p == $active_page)
                    $sel .= "selected='selected'";

                $input .= "<option value='$i' $sel>$p</option>";
            }
            $input .= "</select>";
        }        

        $pagination = "$this->pagination_text_page: $input $this->pagination_text_of $total_page &nbsp; ";
        
        if($_offset == 0)
            $pagination .= " $this->pagination_text_back ";
        else
            $pagination .= " <a href='{$uri_path}_offset=" . ($_offset - $limit) . "&$get'>$this->pagination_text_back</a> ";

        if($active_page >= $total_page)
            $pagination  .= " $this->pagination_text_next ";
        else
            $pagination  .= " <a href='{$uri_path}_offset=" . ($_offset + $limit) . "&$get'>$this->pagination_text_next</a> ";

        $pagination .= " &nbsp; " . number_format($count) . " Total Records <a href='{$uri_path}_pagination_off=1&$get' rel='nofollow'>$this->pagination_text_show_all</a> ";

        $id++;
        return $pagination;

    }

    
    function get_qs($query_string_list = '_order_by,_desc,_offset,_search,_pagination_off'){

        // purpose: render querysting. user selections for order, search, and page are carry from page to page. 
        // this maintains search state while paging, updating, etc...

        // append users additions
        if(mb_strlen($this->query_string_list) > 0)
            $query_string_list .= ',' . $this->query_string_list;

        $get = '';
        $arr = explode(',', trim($query_string_list, ','));
        foreach($arr as $var)
            if(mb_strlen(@$_REQUEST[$var]) > 0)
                $get .= "&$var=" . urlencode($_REQUEST[$var]);

        return ltrim($get, '&');

    }

    
    function get_upload($columns, $table_name, $identity_name, $identity_id, $context = 'form', $field_index = ''){

        // purpose: used in insert and update to handle files
        // returns: true on success, false on error

        $upload_width = $this->upload_width;
        $upload_height = $this->upload_height;
        $upload_crop = $this->upload_crop;
        $thumb_width = $this->thumb_width;
        $thumb_height = $this->thumb_height;
        $thumb_crop = $this->thumb_crop;

        if($context == 'grid')
            $input_control = $this->grid_input_control;
        else
            $input_control = $this->form_input_control;

        foreach($columns as $column_name){

            $control = @$input_control[$column_name];

            // uploads only
            if(!$this->is_upload($input_control, $column_name))
                continue;

            // inputs are named differently on grids
            $input_name = $column_name;
            if($context != 'form')
                if(mb_strlen($field_index) > 0)
                    $input_name = "$column_name-0"; // inserting
                else
                    $input_name = "$column_name-$identity_id"; // updating 

            if(!file_exists($this->upload_path) && mb_strlen($this->upload_path) > 0){
                mkdir($this->upload_path, 0755);
                usleep(500);
            }

            if(!file_exists($this->thumb_path) && mb_strlen($this->thumb_path) > 0){
                mkdir($this->thumb_path, 0755);
                usleep(500);
            }

            // process file deletion requested by checkbox with field_name + "-delete"
            if(@$_POST[$input_name . '-delete'] == 1)
                $this->upload_delete($table_name, $identity_name, $identity_id, $column_name, $input_control);

            // see if a new file was uploaded
            $file_name = $this->upload_file($input_name, $notice, $field_index);

            // reloop - no new file uploaded
            if(mb_strlen($file_name) == 0)
                continue;    
                
            // delete previous existing file
            $this->upload_delete($table_name, $identity_name, $identity_id, $column_name, $input_control);

            // copy upload to thumbnail path
            if(mb_strlen($this->thumb_path) > 0 && $control == '--image')
                if(!copy("$this->upload_path/$file_name", "$this->thumb_path/$file_name")){
                    $this->display_error("Error: Unable to copy uploaded to thumb_path. Make sure path: $this->thumb_path exists and is writeable. Try chmod 0755 (or 0777 if you must) on the destination path.", 'get_upload()');
                    return false;
                }

            // resize or crop main image
            if($control == '--image')
                if($upload_crop)
                    $this->image_crop("$this->upload_path/$file_name", $upload_width, $upload_height);
                else
                    $this->image_resize("$this->upload_path/$file_name", $upload_width, $upload_height);

            // thumbs - resize or crop 
            if($control == '--image' && mb_strlen($this->thumb_path) > 0)
                if($thumb_crop)
                    $this->image_crop("$this->thumb_path/$file_name", $thumb_width, $thumb_height);
                else
                    $this->image_resize("$this->thumb_path/$file_name", $thumb_width, $thumb_height);

            // update file name in table 
            $sql_param = array();
            $sql = "update `$table_name` set `$column_name` = :file_name where `$identity_name` = :identity_id";
            $sql_param[':file_name'] = $file_name;
            $sql_param[':identity_id'] = $identity_id;
            if($this->query($sql, $sql_param, 'get_upload()') === false)
                return false;

        }

        if(mb_strlen($notice) > 0){
            $this->display_error($notice, 'get_upload()');
            return false;
        }

        return true;

    }


    function upload_file($input_name, &$notice, $field_index = ''){

        // purpose: upload file and return file name as a string
        // returns: file name on success, errors sent back by reference to $notice

         // make regex pattern for extension validation from allow list. it should end up looking like this: /(.\.mp3)|(.\.pdf)$/i
        $pattern = '';
        $arr = preg_split('/\s+/', $this->upload_allow_list);
        foreach($arr as $val)
            $pattern .= '(.' . preg_quote($val) . ')|';

        $pattern = '/' . rtrim($pattern, '|') . '$/i'; 

        // get file info
        if(mb_strlen($field_index) > 0){
            $size = intval($_FILES[$input_name]['size'][$field_index]);
            $tmp_name = $_FILES[$input_name]['tmp_name'][$field_index];
            $file_name = $this->clean_file_name($_FILES[$input_name]['name'][$field_index]);
        }
        else{
            $size = intval($_FILES[$input_name]['size']);
            $tmp_name = $_FILES[$input_name]['tmp_name'];
            $file_name = $this->clean_file_name($_FILES[$input_name]['name']);
        }

        // nothing to do
        if($size <= 0 || mb_strlen($file_name) == 0)
            return;
        
        if(mb_strlen($file_name) > 500){
            $notice .= "File name is too long.\n";
            return;
        }

        if(!preg_match($pattern, $file_name)){
            $notice .= "Invalid file type. Only the following documents and media types are allowed: $this->upload_allow_list\n";
            return;
        }

        $file_name = $this->upload_rename_if_exists($this->upload_path, $file_name);

        if(!move_uploaded_file($tmp_name, "$this->upload_path/$file_name")){
            $notice .= "Error: Unable to move uploaded file. Make sure path: $this->upload_path exists and is writeable. Try chmod 0755 (or 0777 is you must) on the destination path.\n";
            return;
        }

        return $file_name;

    }

    
    function upload_delete($table_name, $identity_name, $identity_id, $field_name = '*', $input_control = array()){

        // purpose: delete uploaded files, called from get_upload()
        // returns: true on success or false on error

        $identity_id = $this->cast_id($identity_id);

        if($field_name != '*')
            $field_name = "`$field_name`";


        // get 1 row of data
        $sql_param = array(':identity_id' => $identity_id);
        $sql = "select $field_name from `$table_name` where `$identity_name` = :identity_id";
        $result = $this->query($sql, $sql_param, 'upload_delete()');

        if(!is_array($result))
            $result = array();

        if(count($result) == 0){
            $this->display_error('Unable to locate record.', 'upload_delete()');
            return false;
        }

        // loop thru fields
        $row = $result[0];
        foreach($row as $column_name => $val){

            if(mb_strlen($val) == 0)
                continue;

            // uploads only
            if(!$this->is_upload($input_control, $column_name))
                continue;

            // delete files
            if(mb_strlen($this->upload_path) > 0)
                @unlink("$this->upload_path/$val");

            if(mb_strlen($this->thumb_path) > 0)
                @unlink("$this->thumb_path/$val");
                    
            // empty field now that file is deleted
            $sql = "update `$table_name` set `$column_name` = null where `$identity_name` = :identity_id;";
            $result = $this->query($sql, $sql_param, 'upload_delete()');                
            if($result === false)
                return false;
                
        }
    
        return true;
    
    }


    function upload_rename_if_exists($path, $file_name){

        // purpose: return a new file name if file exists; foo.pdf returns foo_2.pdf then foo_3.pdf, etc...
        
        while(file_exists("$path/$file_name")){

            $number = 2;

            $ext = pathinfo($file_name, PATHINFO_EXTENSION);
            $prefix = mb_substr($file_name, 0, mb_strlen($file_name) - mb_strlen($ext) - 1);

            // extract and increment number at the end of the prefix
            preg_match('/_([0-9]+)$/', $prefix, $matches);
            if(count($matches) == 2){
                $prefix = mb_substr($prefix, 0, mb_strlen($prefix) - mb_strlen($matches[0]));
                $number = $matches[1];
                $number++;
            }

            $file_name = "{$prefix}_{$number}.{$ext}";

        }    

        return $file_name;

    }
    

    function image_resize($file_name, $max_width, $max_height, $output_to_browser = false){

        // purpose: resize image but keep orignal aspect ratio.  if output_to_browser = false, then file is altered and saved. 
        // returns: nothing 

        $ext = mb_strtolower(pathinfo($file_name, PATHINFO_EXTENSION));

        if(!($ext == 'jpg' || $ext == 'jpeg' || $ext == 'gif' || $ext == 'png'))
            return;

        list($orig_width, $orig_height) = getimagesize($file_name);

        // invalid image or nothing to do
        if($orig_width == 0 || $orig_height == 0 || $max_width == 0 || $max_height == 0)
            return;            

        // image is already smaller than maximum size
        if($orig_width < $max_width && $orig_height < $max_height)
            return;            

        $width = $orig_width;
        $height = $orig_height;

        // taller
        if($height > $max_height){
            $width = ($max_height / $height) * $width;
            $height = $max_height;
        }

        // wider
        if($width > $max_width){
            $height = ($max_width / $width) * $height;
            $width = $max_width;
        }

        $image_p = @imagecreatetruecolor($width, $height);

        if($ext == 'gif')
            $image = @imagecreatefromgif($file_name);
        elseif($ext == 'png')
            $image = @imagecreatefrompng($file_name);
        else
            $image = @imagecreatefromjpeg($file_name);

        @imagecopyresampled($image_p, $image, 0, 0, 0, 0, $width, $height, $orig_width, $orig_height);

        if($output_to_browser){

            if($ext == 'gif'){
                header('Content-type: image/gif');
                @imagegif($image_p);
            }
            elseif($ext == 'png'){
                header('Content-type: image/png');
                @imagepng($image_p);
            }
            else{
                header('Content-type: image/jpeg');
                @imagejpeg($image_p, '', $this->image_quality); 
            }
        
        }
        else{

            if($ext == 'gif')
                @imagegif($image_p, $file_name);
            elseif($ext == 'png')
                @imagepng($image_p, $file_name);
            else
                @imagejpeg($image_p, $file_name, $this->image_quality); 

        }

    }


    function image_crop($file_name, $desired_width, $desired_height, $output_to_browser = false){

        // purpose: crop image changes aspect ratio to match the requested dimensions. if output_to_browser = false, then file is altered and saved. 
        // returns: nothing

        $ext = mb_strtolower(pathinfo($file_name, PATHINFO_EXTENSION));

        if(!($ext == 'jpg' || $ext == 'jpeg' || $ext == 'gif' || $ext == 'png'))
            return;

        // get new dimensions
        list($width, $height) = getimagesize($file_name);

        // nothing to do
        if($height == 0 || $width == 0 || $desired_height == 0 || $desired_width == 0)
            return;

        // nothing to do
        if($height == $desired_height && $width == $desired_width && $output_to_browser == false)
            return;

        if($desired_width / $desired_height > $width / $height){
            $new_width = $desired_width;
            $new_height = $height * ($desired_width / $width);
        }
        else{
            $new_width = $width * ($desired_height / $height);
            $new_height = $desired_height;
        }

        // resize
        $image_p = @imagecreatetruecolor($new_width, $new_height);
        $image_f = @imagecreatetruecolor($desired_width, $desired_height);

        if($ext == 'png')
            $image = @imagecreatefrompng($file_name);    
        elseif($ext == 'gif')
            $image = @imagecreatefromgif($file_name);    
        else
            $image = @imagecreatefromjpeg($file_name);

        @imagecopyresampled($image_p, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);

        // top center cropping
        $x = ($new_width - $desired_width) / 2;
        $y = ($new_height - $desired_height) / 5;

        @imagecopyresampled($image_f, $image_p, 0, 0, $x, $y, $desired_width, $desired_height, $desired_width,  $desired_height);

        // save or output
        if($output_to_browser){

            if($ext == 'gif'){
                header('Content-type: image/gif');
                @imagegif($image_f);
            }
            elseif($ext == 'png'){
                header('Content-type: image/png');
                @imagepng($image_f);
            }
            else{
                header('Content-type: image/jpeg');
                @imagejpeg($image_f, '', $this->image_quality); 
            }
        
        }
        else{

            if($ext == 'gif')
                @imagegif($image_f, $file_name);
            elseif($ext == 'png')
                @imagepng($image_f, $file_name);
            else
                imagejpeg($image_f, $file_name, $this->image_quality); 

        }

    }

    
    function display_error($error, $source_function){
        
        // purpose: display errors to user.

        $msg = nl2br($this->clean_out("Error: $error\nSent From: $source_function"));
        echo "<div class='lm_error'>$msg</div>" ;

    }


    function query($sql, $sql_param = array(), $source_function = '', $display_error = true){

        // purpose: wrapper for pdo db call
        // returns: returns array of results, an empty array for no results, or false on error

        if(!$this->set_names){
            $sth = $this->dbh->prepare("set names $this->charset_mysql");
            $sth->execute();
            $this->set_names = true;
        }

        $sth = $this->dbh->prepare($sql);

        if(!$sth->execute($sql_param)){
            $arr = $sth->errorInfo();
            $error = $arr[2];
            if($display_error)
                $this->display_error("$error\nsql: $sql\narr_sql_param: " . print_r($sql_param, true), $source_function);

            return false;
        }

        if(preg_match('/^insert/i', ltrim($sql)))
            return $this->cast_id($this->dbh->lastInsertId());

        if($sth->columnCount() == 0)
            return array();    
        else    
            return $sth->fetchAll(PDO::FETCH_ASSOC);

    }


    function safe_np($named_parameter){

        // purpose: remove illegal characters from pdo named parameter (w/o colon).
        // returns: string

        return preg_replace('/[^a-zA-Z0-9_]/', '', $named_parameter);
            
    }
    

    function redirect($url, $automatic = true){

        // purpose: redirect user to url
        // returns: html redirect
        // if $automatic is false user has to click "continue" to proceed. 

        if($automatic === false){
            echo("<center><a href='$url'>Continue</a></center>");    
            return;
        }

        $port = '';    
        $host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST']); // host without port number
        $protocol = 'http://';
        if(@$_SERVER['HTTPS'] != '' && @$_SERVER['HTTPS'] != 'off')
            $protocol = 'https://';
        if(!($_SERVER['SERVER_PORT'] == '80' || $_SERVER['SERVER_PORT'] == '443'))
            $port = ':' . $_SERVER['SERVER_PORT'];
        if(!isset($_SESSION))
            session_write_close();
        
        header("Location: $protocol$host$port$url");
        die;
    
    }


    function get_uri_path(){

        // purpose: get request uri without the querystring but with query delimiter. user can also specify a URI.
        // returns: uri path string

        static $uri_path = false;
        
        // user defined uri
        if(mb_strlen($this->uri_path) > 0){
                
            if(mb_strstr($this->uri_path, '?'))    
                $uri_path = $this->uri_path . '&'; // if user specifies URI, it may already have a ?
            else
                $uri_path = $this->uri_path . '?';
        }

        if($uri_path)
            return $uri_path;

        $arr = parse_url($_SERVER['REQUEST_URI']);
        $uri_path = $arr['path'];

        if($uri_path == '')
            $uri_path = '/';

        $uri_path = $uri_path . '?';

        return $uri_path;

    }


    function get_class_name($input_name){

        // purpose: generate class name from form input name; remove '-id' or '[]' suffix 
        // returns: string    

        return 'lm_' . preg_replace('/(-[0-9]+)|(\[\])$/', '', $input_name);

    }


    function is_upload(&$input_control, &$column_name){

        // purpose: see if column is an upload type
        // returns: boolean

        if(@$input_control[$column_name] == '--image' || @$input_control[$column_name] == '--document')
            return true;

        return false;

    }

    
    function export(&$result, $columns){

        // purpose: send database result in CSV format to browser. 

        if(mb_strlen($this->export_csv_file_name) > 0)
            $file_name = $this->export_csv_file_name;
        elseif(mb_strlen($this->table) > 0)
            $file_name = $this->clean_file_name($this->table . '.csv');
        else
            $file_name = 'download.csv';

        // output buffering required
        $arr = ob_get_status();
        $level = intval($arr['level']);

        if($level <= 0){
            $error = "ob_start() or ob_start('ob_gzhandler') must be called at the beginning of the script to use CSV Export.";
            $this->display_error($error, 'export()');
            return;
        }

        // erase any existing buffers
        while($level >= 1 ){
            ob_end_clean();
            $level--;
        }

        if(!ob_start('ob_gzhandler'))
            ob_start();

        header("Cache-Control: maxage=1");
        header("Pragma: public");
        header("Content-Type: application/csv");
        header("Content-Disposition: attachment; filename=$file_name");

        // remove last column if last column is the identity that holds the [edit] and [delete] links
        if(end($columns) == $this->identity_name)
             array_pop($columns);
           
        // header row    
        foreach($columns as $key => $val)
            echo $this->export_escape($val);

        echo "\n";

        // loop thru data
        foreach($result as $row){

            foreach($columns as $val)
                echo $this->export_escape($row[$val]);

            echo "\n";
            
        }

        ob_end_flush();
        die();

    }

    
    function export_escape($str){

        // purpose: escape for export()
        // returns: csv quoted and escaped string
        
        return '"' . str_replace('"', '\"', $str) . '",'; 

    }


    function validate(&$validate){

        // purpose: run validation
        // return false if any input fails validation
        // alters the $validate array adding bool flag corresponding to column name

        // $validate is an array of arrays
        // [0] string  $regexp_or_user_function
        // [1] string  $error_message
        // [2] string  $tip_placeholder
        // [3] boolean $optional_input
        // [4] boolean - holds result from validation process - false means input failed validation 

        $columns = $this->get_columns('form');
        $all_valid = true;

        foreach($columns as $column_name){
            
            $regexp_or_user_func = @$validate[$column_name][0];
            
            if($regexp_or_user_func == '')
                continue;

            $val = @$_POST[$column_name];
            if(is_array($val))
                $val = implode($this->delim, $val);
            $val = trim($val);

            // don't validate empty optional parameters
            if(@$validate[$column_name][3] === true && $val == '')
                continue;

            if($regexp_or_user_func == 'email')
                $is_valid = filter_var($val, FILTER_VALIDATE_EMAIL);
            elseif(mb_substr($regexp_or_user_func, 0, 1) != '/')
                $is_valid = call_user_func($regexp_or_user_func);
            else
                $is_valid = preg_match($regexp_or_user_func, $val);

            $validate[$column_name][4] = (bool)$is_valid;
            
            // add error msg if missing for some reason
            if(@$validate[$column_name][1] == '')
                $validate[$column_name][1] == 'Missing or Invalid Input';

            if(!$is_valid)
                $all_valid = false;
        }

        return $all_valid;

    }

}

AnonSec - 2021 | Recode By D7net