'', // The listing URL - just the file and nothing else. 'add' => '?action=add', // The add page URL 'edit' => '?action=edit', // The edit page URL 'delete'=> '?action=delte', // The delete page URL 'js_folder' => 'crud/', // The Javascript folder. This folder is inside the js/ folder. So if this is crud/, that means the js files are in /js/crud/ 'css_folder'=> 'crud/', // The folder that holds CSS files. 'image_folder'=> 'crud/', // The folder in which the images are kept - Will be dynamially set in the consturctor. ); public $listing_query = ''; // The query use to create the listing page. Use setListingQuery() to set this query. That will make sure all the extra parts(sorting, paging) etc. stays intact. public $listing_fields = array(); // An array of list of fields that should be shown in the listing page - use setListingFields() to set this value. public $form_fields = array(); // A list of fields to be shown in the add/edit pages. Use setFormFields() to set this. public $search_fields = array(); // The fields that should allow searching. Use setSearchFields() to edit this. public $current_page_data; // An associate array that holds all the data of the current page. This is for the listing action. public $pager; // The pager for the listing page. Holds the object of the Pager class. public $folder = array( // Some important folders. 'uploads' => 'user_uploads', // The folder in which the uploaded stuff are stored. ); public $allow = array( // Some config settings - mostly boolean stuff 'sorting' => true, // Show the sorting buttons? 'bulk_operations' => true, // Allow bulk operations like bulk/activate/inactivate delete using checkboxs 'searching' => true, // Show the search form. 'status_change' => true, // Allow the status changes. If false, the single click status toggle will be disable, as will the bulk status change thingy. 'add' => true, // Allow the user to add new rows. 'delete' => true, // Lets the user delete stuff. 'edit' => true, // Editing existing row is allowed. ); public $save_states = array('search','search_in','sp_page','sp_items_per_page','sortasc','sortdesc'); private $data_type_field_type_map = array( // What kind of data type maps to what kind of html field. 'virtual' => 'text', 'varchar' => 'text', 'text' => 'textarea', 'mediumtext' => 'textarea', 'int' => 'text', 'bigint' => 'text', 'float' => 'text', 'datetime' => 'datetime', 'date' => 'date', 'enum' => 'select' ); public $success = ''; // A success message - if any. public $error = ''; // Error message holder. public $top_code = ''; // Prints this after showTop() and before printAction() public $bottom_code = ''; // Prints this after printAction() and before showEnd() ///////////////////////////////////////// Configuration Function //////////////////////////////////// function __construct($table, $title='', $primary_key='id') { global $config, $template; $this->table = $table; if($title) $this->title = $title; else $this->title = format($table); // If the title ends with 's' or 'es', set that as the plural. if(preg_match('/e?s$/', $this->title)) { $this->title_plural = $this->title; $this->title = preg_replace('/e?s$/', '', $this->title); } else { $this->title_plural = $this->title . 's'; // :TODO: Get a single to plural convert from the net. } //Set some member variables if(!empty($_REQUEST['action'])) $this->action = $_REQUEST['action']; $this->urls['image_folder'] = joinPath($config['site_url'], 'images/', $this->urls['image_folder']); $this->setPrimaryKey($primary_key); $this->setUrl(); $this->guess(); } /// Guesses all the fields based on the the Database structure. function guess() { $all_fields_info = $this->execQuery("SHOW FIELDS IN {$this->table}", "all"); foreach($all_fields_info as $field_info) { extract($field_info); if(!$Field) continue; $field_type = false; $value_type = false; $validation = array(); $data = array(); if($Key == 'PRI') $this->setPrimaryKey($Field); else { $data_type = preg_replace("/^([a-z]+).*$/", "$1", $Type); switch($data_type) { case 'enum': $vals = preg_replace('/.*\((.+)\)/', "$1", $Type); $vals = str_replace("'", "", $vals); $all_vals = explode(',', $vals); foreach($all_vals as $v) { $data[$v] = format($v); } if($Field == 'status') { $this->setStatusField($Field); $field_type = 'checkbox'; $value_type = 'status'; $data = array_shift(array_keys($data)); // First element in the enum list is the default value. } break; case 'varchar': $length = preg_replace('/.*\((.+)\).*/', "$1", $Type); if($length == 255) $field_type = 'textarea'; $validation['length<'] = $length; } $this->addField($Field, format($Field), $data_type, $validation, $data, $field_type, $value_type); } } // Some pre render stuff. $this->setListingFields(); $this->setFormFields(); $this->setHeaders(); } /// Sets the given field name as the primary key of the current table. function setPrimaryKey($field) { $this->primary_key = $field; } /// Sets the status field for this table function setStatusField($field_name) { $this->status_field = $field_name; } /** * Add a field to the field list - if a field with the same name is present in the list, overwrite it. * Arguments: $field - the name of the field. Eg: url * $name - The field title. Eg: URL * $type - Datatype for that field . Eg: varchar * $validation - an array that specifies all the necessary validations. Eg: array('empty','url'); * $data - some preset data. Useful for setting hidden variables and Dropdown boxes. */ function addField($field, $name=false, $type='varchar', $validation=array(), $data=array(), $field_type=false, $value_type=false) { if($name === false) $name = format($field); if(!$field_type or !$value_type or !$validation) { $info = $this->_guessFieldTypes($field, $name, $type, $validation); if(!$field_type) $field_type = $info['field_type']; if(!$value_type) $value_type = $info['value_type']; if(!$validation) $validation = $info['validation']; } $field_info = array( 'field' => $field, 'name' => $name, 'type' => $type, 'field_type' => $field_type, 'value_type' => $value_type, 'validation' => $validation, 'data' => $data, ); $this->fields[$field] = $field_info; } /** * Add a forign field's data as a dropdown list. * Arguments: $field - the name of the field. Eg: url * $name - the title of the field. 'URL' * $table - the table wth the date that should be used n the dropdown. * $where - the conditions for the data. */ function addListDataField($field, $table, $name=false, $where='') { if(!empty($where)) $where = " WHERE $where"; $this->addField($field, $name, 'enum', array(), $this->execQuery("SELECT id,name FROM `{$table}` $where", "byid")); } /** * Set the rules to validate this field. * Arguments: $field - the name of the field to which these rules must be attached to. * $validation_rules - An array of rules that specifies the validation for that field. This will be passed into the check() for both PHP and JS. * Example: $admin->setValidation('name', array('must'=>true, 'length<'=>100)); * $admin->setValidation('email', array('must'=>true, 'email'=>true)); */ function setValidation($field, $validation_rules) { if(isset($this->fields[$field])) $this->fields[$field]['validation'] = $validation_rules; } /// This function does the job of guessing what the field and value type is based on the field name, data type, validations etc. function _guessFieldTypes($field, $name, $data_type, $validation) { $field_type = false; $value_type = false; if(!$validation) $validation = array(); $field_type = $this->data_type_field_type_map[$data_type]; $value_type = $field_type; // Special Field handles. if($data_type == 'enum' and strpos($field,'status') !== false) { // Status Checkbox. $field_type = 'checkbox'; } elseif($data_type == 'varchar' and $field == 'name') { $validation['must'] = true; } elseif($data_type == 'varchar' and (preg_match('/\burl\b/', $field) or preg_match('/\blink\b/', $field))) { $value_type = 'url'; } elseif($data_type == 'varchar' and ( (strpos($field,'image') !== false) or (strpos($field,'file') !== false) or (strpos($field,'path') !== false))) { $value_type = 'file'; $field_type = 'file'; if(strpos($field,'image') !== false) { $validation['extension'] = array('png','jpg','jpeg','gif','bmp'); } } elseif($data_type == 'datetime' and ($field == 'added_on' or $field == 'edited_on')) { $field_type = 'hidden'; $value_type = 'now'; } return array('field_type'=>$field_type, 'validation'=>$validation, 'value_type'=>$value_type); } /** * Add a field that can only be seen when the data is being listed(list action). This field don't have to be in the database * Example: $admin->addListingField('User Posts','"View All Post of this User"'); */ function addListingField($title, $data) { if($this->action == 'list') { $this->addField(unformat($title), $title, 'virtual', array(), array('html'=>$data)); } $this->setListingFields(); } /** * Specify all the fields that could be searched in. * Example: $admin->setSearchFields('name','url'); */ function setSearchFields() { $given_search_fields = $this->_getArguments(func_get_args()); $search_fields = array(); // If fields are not specified, include all varchar fields if(!$given_search_fields) $given_search_fields = array_keys($this->fields); foreach($given_search_fields as $name) { if(isset($this->fields[$name]['type'])) if($this->fields[$name]['type'] == 'varchar' or $this->fields[$name]['type'] == 'link') $search_fields[$name] = $this->fields[$name]['name']; } $this->search_fields = $search_fields; } /** * The given arguments are set as the fields that should shown in the listing page. * Example: $admin->setListingFields("username", "name", "url"); */ function setListingFields() { $listing_fields = $this->_getArguments(func_get_args()); // If fields are not specified, include everything. if(!$listing_fields) { foreach($this->fields as $name=>$f) $listing_fields[] = $name; } $this->listing_fields = $listing_fields; $this->setSearchFields($listing_fields); } /** * The field provided as the arguments will be shown in the add/edit forms.. * Example: $admin->setFormFields('username','password','email','url','added_on'); */ function setFormFields() { $form_fields = $this->_getArguments(func_get_args()); // If fields are not specified, include everything. if(!$form_fields) { foreach($this->fields as $name=>$f) $form_fields[] = $name; } $this->form_fields = $form_fields; } /** * Set the URL of the page. This function also finds and sets the add/edit/delete urls based on the given URL. If an empty URL is given, the URL of the current page is taken. * Example: $admin->setUrl('http://example.com/admin/users.php'); */ function setUrl($url = '') { if(!$url) { // Get the current URL. if(isset($_SERVER['REQUEST_URI'])) { $url = $_SERVER['REQUEST_URI']; } else { $url = $_SERVER["SCRIPT_NAME"]; if($_SERVER["QUERY_STRING"]) $url .= '?' . $_SERVER["QUERY_STRING"]; // To make sure this works in IIS too. } // Remove some unwanted stuff in the URL $items_to_remove = array('id','select_row[]','action','search','search_in', 'sp_page','sp_items_per_page', 'sortasc', 'sortdesc', 'error', 'success'); $remove_dict = array(); foreach($items_to_remove as $item) $remove_dict[$item] = null; $url = getLink($url, $remove_dict); /* We are going to all this trouble for this. If the crud have multi-views - like 'downer.php?show=to_download' and 'downer.php?show=all', the 'show' param should be part of the $this->url. The form's action is $this->url. That way the data will be preserved. At the same time, we don't want stuff like id, action etc to be part of the url. */ } $this->urls['main'] = $url; $this->urls['add'] = getLink($url, array('action'=>'add')); $this->urls['edit'] = getLink($url, array('action'=>'edit')); $this->urls['delete'] = getLink($url, array('action'=>'delete')); return $url; } ////////////////////////////////////////// Action Functions ////////////////////////////////////////////// /** * This function deletes all the IDs pvodided as the argument. * Arguments: $ids_to_delete - an array of IDs that must be deleted. * Example: $admin->delete(array(5,4,3,2); */ function delete($ids_to_delete) { $to_delete_count = count($ids_to_delete); if(!$to_delete_count) return; global $QUERY; $query = "DELETE FROM {$this->table} WHERE {$this->primary_key} IN ('" . implode("','", $ids_to_delete) . "')"; $deleted_rows = $this->execQuery($query, 'exec'); if($deleted_rows == 1) $this->success = "Row deleted."; elseif($deleted_rows > 1 and $to_delete_count == $deleted_rows) $this->success = "$deleted_rows rows deleted."; elseif($to_delete_count != $deleted_rows) { if($to_delete_count == 1) $this->error = "Failed to delete the row."; else $this->error = "Failed to delete all the specified rows. $deleted_rows/$to_delete_count rows deleted."; } } /** * Adds a new row to the database - using the data provided as the argument. * Argument: $field_data - and associate array with all the necessary data. * Example: $admin->add(array('username'=>'binnyva', 'name'=>'Binny')); */ function add($field_data) { global $sql; // Some fields require special handling... $field_data = $this->preSaveChanges($field_data); if($field_data) { $this->success = 'Added a new ' . $this->title; if(!empty($field_data['name'])) $this->success .= " called '$field_data[name]'"; return $sql->insert($this->table, $field_data); } return false; } /** * Edits the row with the ID given in the first argument and sets the value as the assoc array given as the second argument. * Example: $admin->edit(5, array('username'=>'binnyva', 'name'=>'Binny')); */ function edit($primary_key_value, $field_data) { global $sql, $QUERY; $field_data = $this->preSaveChanges($field_data); if($field_data) { $this->success = 'Updated the ' . $this->title; if(!empty($field_data['name'])) $this->success .= " called '$field_data[name]'"; $sql->update($this->table, $field_data, "`{$this->primary_key}`=$primary_key_value"); return true; } return false; } /** * Activate the row with the given ID * Example: $admin->activate(35, 'status'); */ function activate($primary_key_value, $field_name='status') { if(!isset($this->fields[$field_name])) return false; // Field name comes thru get - make sure its a real field. return $this->execQuery("UPDATE `{$this->table}` SET `$field_name`='1' WHERE `{$this->primary_key}`='$primary_key_value'", 'exec'); } /// Disable the row with the given ID function deactivate($primary_key_value, $field_name='status') { if(!isset($this->fields[$field_name])) return false; $this->execQuery("UPDATE `{$this->table}` SET `$field_name`='0' WHERE `{$this->primary_key}`='$primary_key_value'", 'exec'); } /// Change the status of the row with the given value. If it's enabled, disable it - and if it's disabled, enable it. function toggleStatus($primary_key_value, $field_name='status') { if(!isset($this->fields[$field_name])) return false; $this->execQuery("UPDATE `{$this->table}` SET `$field_name`=if(`$field_name`='1','0','1') WHERE `{$this->primary_key}`='$primary_key_value'", 'exec'); } ////////////////////////////////////////// Internal Helper Functions ///////////////////////////////////// /** * Some fields require special handling before its inserted into the DB. Eg - file fields should be uploaded, Date field should be converted, etc. * Also, calls the validate function. */ function preSaveChanges($field_data) { if(!$this->validateForm()) return false; // Remove invalid fields(stuff not in the DB) $save_data = array(); foreach($this->fields as $field_name => $field_info) { if(!isset($field_data[$field_name]) and $field_info['field_type'] != 'file') { // Make sure that the field shows up in the submit list.(file type don't show in the $_POST array) unset($field_data[$field_name]); continue; } $value = i($field_data, $field_name); // Changing the value depending on the type. switch($field_info['type']) { case 'datetime': if($field_name == 'added_on') { if($this->action == 'add_save') $value = date('Y-m-d H:i:s'); // Automatically stamp the added date/time in this field. } elseif($field_name == 'edited_on') { if($this->action == 'edit_save') $value = date('Y-m-d H:i:s'); } else { $value = date('Y-m-d H:i:s', strtotime($value)); } break; case 'date': $value = date('Y-m-d', strtotime($value)); break; // File uploads. case 'file': $value = ''; if(!empty($_FILES[$field_name]['name'])) { global $config; $valid_extension = ''; if(isset($field_info['validation']['extension'])) $valid_extension = implode(',', $field_info['validation']['extension']); list($filename, $result) = upload($field_name, joinPath($config['site_folder'], $this->folder['uploads']), $valid_extension); if($result) return $this->validationError($field_name, $result); else $value = $filename; } break; // Passwords case 'password': $value = ''; if(!empty($field_data[$field_name])) $value = empty($field_data[$field_name]); break; } // If the field is a File or password, make sure there is user entry before saving it. if($field_info['field_type'] == 'file' or $field_info['field_type'] == 'password') { if($value) $save_data[$field_name] = $value; } else { $save_data[$field_name] = $value; } } return $save_data; } /** * Validate the form submit based of the validation options specified in the field array. * Return: true - if there are no errors and false if there are errors. */ function validateForm() { $conditions = array(); foreach($this->fields as $field) { if(isset($field['validation'])) { $validation_rules = $field['validation']; foreach($validation_rules as $rule=>$value) { $conditions[] = array( 'name' => $field['field'], 'is' => $this->_convertValidationRule($rule), 'value' => $value, ); } } } $result = check($conditions, 4); if($result) { $this->validation_errors = $result; return false; } return true; } /** * Convert the positive rules for validation in crud to the negative rule expected in check(). * For eg, the mandatory field are marked with a 'must' but in the check(), it must be 'empty'. This function takes care of the convertion between the +/- rules. */ function _convertValidationRule($rule) { $rule_map = array( 'must' => 'empty', 'length<' => 'length>', 'length>' => 'length<', ); return $rule_map[$rule]; } /** * This function caches all the validation errors and show them all at the end. */ function validationError($field_name, $error) { if(isset($this->validation_errors[$field_name])) $this->validation_errors[$field_name][] = $error; else $this->validation_errors[$field_name] = array($error); return false; } /// Process the data of the current page and convert it to a format that is usable in the Listing template. function makeListingDisplayData() { if(!$this->current_page_data) return; global $config; $total_rows = count($this->current_page_data); for($i=0; $i<$total_rows; $i++) { $row = $this->current_page_data[$i]; foreach($this->listing_fields as $field_name) { $f = $this->fields[$field_name]; $value = ''; if($f['type'] != 'virtual' and isset($row[$field_name])) $value = $row[$field_name]; $new_value = ''; switch($f['type']) { // Enum - or the listing. case 'enum': if($f['data'] and isset($f['data'][$value])) $new_value = $f['data'][$value]; else $new_value = $value; break; case 'datetime': if($value != '0000-00-00 00:00:00') $new_value = date(phpDateFormat($config['time_format']), strtotime($value)); break; case 'date': if($value != '0000-00-00 00:00:00') $new_value = date(phpDateFormat($config['date_format']), strtotime($value)); break; case 'virtual': //Not actually a DB column. if($f['data']) { $new_value = eval("return " . $f['data']['html'] . ';'); } break; case 'varchar': default: $new_value = $value; } switch($f['field_type']) { case 'select': if($f['data'] and isset($f['data'][$value])) $new_value = $f['data'][$value]; else $new_value = $value; break; } switch($f['value_type']) { case 'url': $url = $value; if(!empty($f['data']['url'])) $url = eval('return ' . $f['data']['url'] . ';'); if(!empty($f['data']['text'])) $value = eval('return ' . $f['data']['text'] . ';'); $new_value = "$value"; break; case 'function': $new_value = $value; if(!empty($f['data']['function'])) { $new_value = call_user_func($f['data']['function'], $value); } } $this->current_page_data[$i][$field_name] = $new_value; } } } /** * Use this function to set the Query that should be used in the listing page. * Example: $admin->setListingQuery("SELECT * FROM User INNER JOIN People on User.id=People.user_id"); */ function setListingQuery($listing_query = '', $recreate_query = false) { if($this->listing_query and !$recreate_query) return $this->listing_query; global $QUERY; if(!$listing_query) { $listing_query = "SELECT * FROM {$this->table}"; } // Sort by specified fields $sort_query = ''; if($this->allow['sorting']) { if(isset($QUERY['sortasc']) and $QUERY['sortasc']) { $sort_query = " `$QUERY[sortasc]` ASC"; $sort_link = "sortasc=$QUERY[sortasc]&"; } elseif(isset($QUERY['sortdesc']) and $QUERY['sortdesc']) { $sort_query = " `$QUERY[sortdesc]` DESC"; $sort_link = "sortdesc=$QUERY[sortdesc]&"; } } //If user wants to search $search_query = ''; if(!empty($_REQUEST['search']) and $this->allow['searching']) { $search_query = " `$QUERY[search_in]` LIKE '%$QUERY[search]%'"; $search_link = "search=$QUERY[search]&search_in=$QUERY[search_in]&"; } $listing_query = $this->_addSqlFragment("WHERE", $listing_query, $search_query); $listing_query = $this->_addSqlFragment("ORDER BY", $listing_query, $sort_query); $this->listing_query = $listing_query; return $this->listing_query; } /////////////////////////////////////////////////// Output functions ///////////////////////////////////////////////////// /** * Sets the JS/CSS includes necessary for this page. */ function setHeaders() { global $config; $done = array(); $this->_addResource('crud.css'); if($this->action == 'edit' or $this->action == 'add') { $this->_addResource('form_functions.js'); $this->_addResource('../library/validation.js'); } elseif($this->action == 'add_save' or $this->action == 'edit_save') { $this->_addResource('list_functions.js'); $this->_addResource('form_functions.js'); $this->_addResource('../library/validation.js'); } else { $this->_addResource('list_functions.js'); } foreach($this->form_fields as $field_name) { $field_info = $this->fields[$field_name]; extract($field_info); if($type == 'datetime' and !isset($done[$type]) and ($this->action == 'edit' or $this->action == 'add' or $this->action == 'add_save' or $this->action == 'edit_save')) { $this->_addResource(joinPath($config['site_url'], 'js', $this->urls['js_folder'], "jscalendar/calendar-blue.css"), "css", true); $this->_addResource("jscalendar/calendar.js", "js"); $this->_addResource("jscalendar/calendar-en.js", "js"); $this->_addResource("jscalendar/calendar-setup.js", "js"); } $done[$type] = true; // To make sure that the includes are not inserted twice. } } /// Prints the listing table and content. Creates the content - and then includes the template file. function printListing() { global $QUERY, $PARAM; $this->setListingQuery(); $this->pager = new SqlPager($this->listing_query, $this->items_per_page); // Create the URL for the pager $save_params = array(); // This states will be saved when going thru pages. foreach($this->save_states as $state_name) { if(!empty($QUERY[$state_name]) and empty($save_params[$state_name])) $save_params[$state_name] = $QUERY[$state_name]; } $this->pager->page_link = getLink($this->urls['main'], $save_params); $this->current_page_data = $this->pager->getPage(); $this->makeListingDisplayData(); require('templates/Crud/listing.php'); } /// Shows the data editing form - creates and caches the data. Then include the form template file. function printForm() { global $QUERY; if($this->action == 'edit') $this->current_page_data = $this->execQuery("SELECT * FROM `{$this->table}` WHERE `{$this->primary_key}`=$QUERY[id]", "assoc"); require('templates/Crud/form.php'); } /// This function decides which action should be shown. function printAction($action = '') { global $QUERY, $sql; if(!$action and !empty($_REQUEST['action'])) $action = $_REQUEST['action']; // Fixes a bug that happens when user presses Enter in the sort input box. if($action == 'Delete Selected' and empty($_REQUEST['select_row'])) $action = 'Sort'; $this->action = $action; switch($action) { case 'edit': case 'add': $this->printForm(); break; case 'add_save': if($this->add($_POST) and $_POST['submit'] != 'Save and Continue Editing') { $this->printListing(); } else { // Validation errors. $this->current_page_data = $_POST; $this->action = 'add'; $this->printForm(); } break; case 'edit_save': if($this->edit($_REQUEST['row_id'], $_POST) and $_POST['submit'] != 'Save and Continue Editing') { $this->printListing(); } else { $this->action = 'edit'; $this->printForm(); } break; case 'sort': $sort_field = ''; foreach($this->listing_fields as $field_name) if($this->fields[$field_name]['value_type'] == 'sort') $sort_field = $field_name; if($sort_field) { foreach($QUERY['sort_row_id'] as $i=>$row_id) { $sql->update($this->table, array($sort_field=>$QUERY['sort_order'][$i]), "{$this->primary_key}=$row_id"); } } $this->printListing(); break; case 'activate': $status_field_name = i($QUERY, 'field_name', 'status'); foreach($QUERY['select_row'] as $row_id) { $this->activate($row_id, $status_field_name); } $this->printListing(); break; case 'deactivate': $status_field_name = i($QUERY, 'field_name', 'status'); foreach($QUERY['select_row'] as $row_id) { $this->deactivate($row_id, $status_field_name); } $this->printListing(); break; case 'toggle_status': $status_field_name = i($QUERY, 'field_name', 'status'); foreach($QUERY['select_row'] as $row_id) { $this->toggleStatus($row_id, $status_field_name); } $this->printListing(); break; case 'delete': $this->delete($_REQUEST['select_row']); // break left out intentionally - we want to list the contents after deleting a row. default: $this->printListing(); } } /// Shows everything - not often called. function render() { showTop($this->title); print $this->top_code; $this->printAction(); print $this->bottom_code; showEnd(); } ////////////////////////////////////////////// Library Stuff ////////////////////////////// private function execQuery($query, $type='all') { global $sql; return $sql->query($query, $type); } private function _getArguments($id_list) { $arguments = $id_list; if(count($arguments) == 1 and is_array($arguments[0])) { //If the first argument is the list(array) of IDs $arguments = $arguments[0]; } return $arguments; } // :TODO: :UGLY: This duplicates a lot of functionality in MVC::addResource() private function _addResource($file, $type='', $use_exact_path = false) { global $template, $config; if(!$type) $type = array_pop(explode(".",$file)); if(preg_match('#https?\://#', $file)) $use_exact_path = true; if(!$use_exact_path) { $file = joinPath($type, $this->urls[$type.'_folder'], $file); if(preg_match('#https?\://#', $file)) $use_exact_path = true; // Starts with 'http://' - so no checks necessary. else { $file = joinPath($config['site_url'], $file); $use_exact_path = true; } } $template->addResource($file, $type, $use_exact_path); } /** * Adds an SQL fragment to an existing query. If we want to add an extra WHERE condition to the query, it will preservere the * existing WHERE conditions and adds an extra clause after that. If there is no WHERE clause, it will add it. */ private function _addSqlFragment($replace_part, $query, $new_string) { if(!$new_string) return $query; //If there is already the said clause in the query, insert the new order by clause into it. $str_pos = strpos($query, $replace_part); if($str_pos) { $query_start = substr($query, 0, $str_pos); $query_end = substr($query, $str_pos + strlen($replace_part) , strlen($query)); if($query_end) { if(strtolower(trim($replace_part)) == 'where') $query_end = " AND " . $query_end; else $query_end = ", ".$query_end; } $query = $query_start . " $replace_part " . $new_string . $query_end; } elseif($new_string) { $query .= " $replace_part " . $new_string; } return $query; } } /* Todo ---- Editor Field Type(or use Editor for all Text data type) Add/Edit/Delete/Status permission checks all over the place. Design. Copy something from somewhere. Maybe implement a themeing sturucture Login, user permissions etc. Logging? */