* v1.0.4 */ /* Description ----------- A simple database management system which stores data in files in a namespace in the dokuwiki folder There is a new folder under dokuwiki named database (held as $dbName) This folder contains - a new "system" folder called "Create Table" - one folder per user table , e.g. "01-Countries" and "02-people" For any session, the record is assigned an id, which is the row number in the file User data is held in the folders with names of the form nn-Name , e.g. 01-Countries When a user creates a table, a new folder is created, with the next sequence number assigned, e.g. 02-People Each folder holds two files and data within the files is pipe(|)-separated: definition.txt holds the column definitions (see below) data.txt holds the values in the columns There are two types of reference fields, where the field holds a pointer rather than a value. To implement this, there are two special types of column, over and above string , number etc. The concept of LOOKUP is that within one set of data (held in the language file) are held such things as gender If the column has type 101, this relates to the first row in the set, i.e. gender. The field then holds the number 1 or 2 to distinguish the value. The concept of REFERENCE is that a user table is defined with an ID as the first field. This will dynamically add it to the list of reference tables when a column is defined. If, for example, this was a table of countries, and a row with id = 1 and name = France existed, whenever a column was defined as referring to that table, if 1 was in the field, France would be displayed. The above is hidden from the user, since they only ever see the drop-downs showing France etc. The main objects are: Environment Session Table Column File Row Record Table consists of Columns File consists of Rows The data for a Table is held in a File When a row is read in from the file it is read into a Record Admin ----- this involves treating definition.txt as a table with one record per column The elements in the definition of each column are name - a name for the column (not used) label - displayed on the screen mandatory - yes/no unique - yes/no - particularly need for reference tables for both the id and the value e.g. countries type - 1 String 2 Number 3 Date 101 Lookup Gender ) 102 Yes and No ) more can be added in lang.php 103 Lookup Title ) 1001 Reference Countries (for example) - added when table created with unique numeric id as first column visible - yes/no N.B The following are not permitted in v1.0 Removing tables Removing columns The following are not recommended : Deleting or amending records in reference tables V1.0.1 ------ wild-cards in search Remove any HTML errors Correct sorting by reference column V1.0.2 ------ language-enable the following - data types - Referring to other tables - lookups ensure compatibility with IE V1.0.3 ------- 1. allow fields to be hidden , additional column attribute called visible defaults to Yes 2. New link fields new field type INTERNAL_LINK - format [[wiki:aa:bb|Displayed Name]] - display_for_change displays above - display function displays "Displayed Name" as href to link with id=wiki:aa:bb - the string is stored with : instead of | new field type EXTERNAL_LINK - format [[http://....... |Displayed Name]] - display_for_change displays above - display function displays "Displayed Name" as href to link - the string is stored with : instead of | Must sort correctly on links last element 3. optimistic locking - lost updates - check each original field value against current contents - if any not the same there must have been an intermediate update N.B To implement Lost updates it is necessary to store the original valueson the update form and the delete form, these are then passed to the update function New lang entries needed for lost_update external links hidden v1.0.4 ------ Create the "Create table" folder if it is not there already This allows zip to hold only the code for database folder and to use Dokuwiki plugin manager */ /** * Environment Class * * Establishes the location of the files * */ class Environment { var $URL; var $TYPES=array(); // the names of the types read from the lang file var $NUMBER_OF_BASIC_TYPES = 5; // the number of basic types string, number etc var $TYPE_STRING = 1; var $TYPE_NUMBER =2; var $TYPE_DATE = 3; var $TYPE_INTERNAL_LINK = 4; var $TYPE_EXTERNAL_LINK = 5; var $COLUMNS=array(); // holds the definition of the structure, e.g. column attributes var $YES = 1; var $LOOKUP_OFFSET = 100; // the first set of lookups is 101 var $REFERENCE_OFFSET = 1000; // the first reference table is 1001 var $TYPE_LOOKUP = 999; // the column is of type lookup var $DICTIONARY_FOLDER = "Create table"; var $DEBUG_FILE = "debug.txt"; var $WILD_CARD = '%'; // can be used at the start or end of a filter var $COLUMN_SEPARATOR='|'; // used to separate fields when the data is written to the file var $COLON = ':'; // used to separate the elements in the link field var $baseFolder ; var $imagesFolder; var $tableNames= array(); // the names of the tables /** * * Constructor * * @param none * @returns object * */ function Environment () { global $dbName; global $myLang; // Set up the main data types $this->TYPES[1]=getMyLang('type_string'); $this->TYPES[2]=getMyLang('type_number'); $this->TYPES[3]=getMyLang('type_date'); $this->TYPES[4]=getMyLang('type_internal_link'); $this->TYPES[5]=getMyLang('type_external_link'); // The column parameters are set up here - the label comes from lang file $this->COLUMNS['name'][1] ='name'; $this->COLUMNS['name'][2] ='label'; $this->COLUMNS['name'][3] ='mandatory'; $this->COLUMNS['name'][4] ='unique'; $this->COLUMNS['name'][5] ='type'; $this->COLUMNS['name'][6] ='visible'; $this->COLUMNS['mandatory'][1] =1; $this->COLUMNS['mandatory'][2] =1; $this->COLUMNS['mandatory'][3] =1; $this->COLUMNS['mandatory'][4] =1; $this->COLUMNS['mandatory'][5] =1; $this->COLUMNS['mandatory'][6] =1; $this->COLUMNS['unique'][1] =1; $this->COLUMNS['unique'][2] =1; $this->COLUMNS['unique'][3] =2; $this->COLUMNS['unique'][4] =2; $this->COLUMNS['unique'][5] =2; $this->COLUMNS['unique'][6] =2; $this->COLUMNS['type'][1] =1; $this->COLUMNS['type'][2] =1; $this->COLUMNS['type'][3] =102; $this->COLUMNS['type'][4] =102; $this->COLUMNS['type'][5] =999; $this->COLUMNS['type'][6] =102; $this->COLUMNS['visible'][1] =1; $this->COLUMNS['visible'][2] =1; $this->COLUMNS['visible'][3] =1; $this->COLUMNS['visible'][4] =1; $this->COLUMNS['visible'][5] =1; $this->COLUMNS['visible'][6] =1; // Pick up the column label for ($i=1;$i<= count($myLang['column_label']);$i++) $this->COLUMNS['label'][$i] = getMyLang('column_label',$i); // The images oflder $this->imagesFolder=DOKU_BASE.'lib/plugins/'.$dbName.'/images/'; // The base folder $this->baseFolder = DOKU_INC.'data/pages/'.$dbName.'/'; $this->URL = DOKU_URL.'doku.php?idx='.$dbName; // Begin add v1.0.4 // Check if dictionary folder exists $directoryName = $this->baseFolder.$this->DICTIONARY_FOLDER; if (!file_exists($directoryName)) { // echo "creating $this->baseFolder"; mkdir("$this->baseFolder",0777); // echo ("creating $this->baseFolder$this->DICTIONARY_FOLDER"); mkdir("$this->baseFolder$this->DICTIONARY_FOLDER",0777); } // End add v1.0.4 // Get list of table names excluding Dictionary if ($handle = opendir($this->baseFolder)) { while (false !== ($file = readdir($handle))) { if ($file != "." && $file != ".." && $file != $this->DICTIONARY_FOLDER) { $counter = abs(substr($file,0,strpos($file,'-'))); $this->tableNames[$counter] = substr($file,3); } } closedir($handle); } } /** * gets the folder name for a table * * @param number $p_id the id of the table * @returns string the folder name * */ function get_folder($p_id) { $ret = $this->baseFolder; if ($p_id<10 ) $id='0'.$p_id; else $id = $p_id; // form the name of the folder based on - $ret .= $id."-".$this->tableNames[$p_id]."/"; return $ret; } } /** * Session Class * * Holds session-specific information * */ class Session { var $columnTypeList = array(); var $lookupTitles = array(); var $lookupOptions = array(); /** * Constructor * * Establishes the location of the files and any lookups * */ function Session() { global $display; global $environment; global $auditTrail; global $session; global $myLang; // Get list of column types $auditTrail->add_header("Session"); for ($i=1;$i<=$environment->NUMBER_OF_BASIC_TYPES;$i++) { $this->columnTypeList[$i] = $environment->TYPES[$i]; } // Get the list of lookups $auditTrail->add("Lookups"); // the lookups are in myLang$['lookup'] // Set up lookupTitles, lookupOptions and columnTypeList for ($lookupCount=1; $lookupCount<=count($myLang['lookup']);$lookupCount++) { $this->lookupTitles[$lookupCount] = getMyLang('lookup',$lookupCount,1); $this->columnTypeList[$environment->LOOKUP_OFFSET+$lookupCount] = getMyLang('prompt_looking_up').' '.getMyLang('lookup',$lookupCount,1); for ($optionCount=2;$optionCount<=count($myLang['lookup'][$lookupCount]);$optionCount++) { $this->lookupOptions[$lookupCount][$optionCount-1] = getMyLang('lookup',$lookupCount,$optionCount); } } $counter=0; // Get the list of reference tables $auditTrail->add("reference Tables"); foreach ($environment->tableNames as $tableName) { $counter++; $tab = new Table($counter); if ($tab->possibleReference) { $this->columnTypeList[$environment->REFERENCE_OFFSET+$counter] =getMyLang('prompt_referencing').' '.$tableName; $auditTrail->add("reference Table ".$tableName); } } $auditTrail->add_footer("Session"); } /** * Determines whether or not the user has the necessary permissions to perform the action * * @param string $action - the action being taken * @returns boolean true if the user has the necessary permissions * (not convinced that the perms are being used correctly (should be binary mask) * */ function allowed ($action) { global $auditTrail; $INFO = pageinfo(); // picked up from the dokuwiki environment $perm = $INFO['perm']; // the permissions of the current user $auditTrail->add_header('Session->allowed'); // Decide on the level of permission required if ($action== 'insertForm') $requiredLevel = AUTH_CREATE; if ($action== 'insertAction') $requiredLevel = AUTH_CREATE; if ($action== 'updateForm') $requiredLevel = AUTH_EDIT; if ($action== 'updateaction') $requiredLevel = AUTH_EDIT; if ($action== 'createTable') $requiredLevel = AUTH_ADMIN; if ($action== 'createTableForm') $requiredLevel = AUTH_ADMIN; if ($action== 'admin') $requiredLevel = AUTH_ADMIN; if ($action== 'deleteForm') $requiredLevel = AUTH_DELETE; if ($action== 'deleteAction') $requiredLevel = AUTH_DELETE; $auditTrail->add('requiredLevel = '.$requiredLevel); $auditTrail->add('perm = '.$perm); // Decide whether or not to procede $auditTrail->add_footer('Session->allowed'); if ($perm >= $requiredLevel) return true; else return false; } /** * Get the list of data type including lookups and references * * @param string $value the selected value which will be marked as selected * @param string $name e.g. "values" so that the names of the variables are values0 values1 etc * @param string $type if "search" the No Selection is added * @returns array list of options for data type * */ function list_of_types($value, $name, $type) { global $environment; global $myLang; $list = ""; return $list; } } /** * Messages Class * * Record and display the messages * */ class Messages { var $messageList = array(); var $exist = false; function Messages () { $this->exist = false; } /** * add the message to the list * * @param string $p_message the error message * @returns None * */ function add($p_message) { $this->messageList[] = $p_message; $this->exist = true; } /** * display the messages * * @param none * @returns none * */ function fail_soft () { global $auditTrail; /* Uncomment if debugging $auditTrail->display(); */ } /** * display all the messages * * @param None * @returns None * */ function displayAll() { global $display; $display->output(""); foreach($this->messageList as $message) { $display->output(""); } $display->output("
".$message."
"); } } /** * AuditTrail Class * * accumulates and formats audit trail info * */ class AuditTrail { var $messageList = array(); var $indent; /** * Constructor * * @param none * @returns object * */ function AuditTrail () { $this->indent=0; } /** * add a message to the audit trail * * @param string $p_message the message * @returns none * */ function add($p_message) { global $display; $this->messageList[] = $this->tab().$p_message; $display->debug_output($this->tab().$p_message); } /** * add a header to the audit trail * * @param string $p_message the message * @returns none * */ function add_header($p_message) { $this->indent++; $this->add("<--- Start of ". $p_message."-->"); } /** * add a footer to the audit trail * * @param string $p_message the message * @returns none * */ function add_footer($p_message) { $this->add("<--- End of ". $p_message."-->"); $this->indent--; } /** * calculate the indentation for the audit trail * * @param string $p_message the message * @returns none * */ function tab() { $t=''; for ($i=0;$i<$this->indent;$i++) $t .= '>>'; return $t; } /** * display the messages in the audit trail * * @param string $p_message the message * @returns none * */ function display() { echo "
Audit Trail"; foreach($this->messageList as $message) echo "
".$message; echo "
"; } } /** * Column Class * * holds info on a column in a table * */ class Column { var $id; var $name; var $label; var $mandatory; var $unique; var $type; var $visible; var $isVisible; var $allowNoSelection; /** * * Constructor * * @param number $p_id id of the column * @param string $p_name name of the column * @param string $p_label label for the column * @param number $p_mandatory lookup , 1=yes, 2= no * @param string $p_type type of the column, string, number, date, lookup or reference * @param string $p_visible visible , 1=yes, 2= no * @returns object * */ function Column ($p_id, $p_name, $p_label, $p_mandatory, $p_unique, $p_type, $p_visible) { global $display; global $environment; global $auditTrail; $auditTrail->add_header('Creating column '.$p_name); $this->id = $p_id; $this->name = $p_name; $this->label = $p_label; $this->mandatory = $p_mandatory; $this->unique = $p_unique; $this->type = $p_type; // allowNoSelection is the equivalent of optional if ($this->mandatory == $environment->YES) $this->allowNoSelection=false; else $this->allowNoSelection=true; // set up isVisible $this->visible = $p_visible; if ($this->visible == $environment->YES) $this->isVisible = true; else $this->isVisible = false; $auditTrail->add_footer('Creating column '.$p_name); } /** * return the string to display the column for edit * * @param string $value the value to be displayed for edit * @returns string the HTML to be displayed * */ function display_for_change($value='',$type='search') { global $environment; global $session; global $auditTrail; //The output used for the search in the list and for update and create $auditTrail->add_header('Column - display_for_change column = '.$this->name); $name = "values".$this->id; $ret=''; //Convert the form held on the database to the form for display and change $value = $this->fromDatabase($value); // When searching, you can have "No selection" if ($type=='search') $noSelectionAllowed = true; else $noSelectionAllowed = $this->allowNoSelection; // true if not mandatory if ($this->type==$environment->TYPE_LOOKUP) { $auditTrail->add('returning list_of_types'); $ret=$session->list_of_types($value, $name,$type); } elseif ($this->type==$environment->TYPE_STRING || $this->type==$environment->TYPE_NUMBER || $this->type==$environment->TYPE_DATE || $this->type==$environment->TYPE_INTERNAL_LINK || $this->type==$environment->TYPE_EXTERNAL_LINK) { $ret .= ' ' ') $ret .= ' value="'.$value.'"'; $ret.=' />'; } elseif ($this->type>$environment->LOOKUP_OFFSET && $this->type<$environment->REFERENCE_OFFSET) { // It is a lookup $list=$session->lookupOptions[$this->type - $environment->LOOKUP_OFFSET]; $ret .= $this->drop_down($name,$list,$noSelectionAllowed,$value); } elseif ($this->type>$environment->REFERENCE_OFFSET) { //It is a reference table $referenceTable = new Table($this->type - $environment->REFERENCE_OFFSET,"data"); //get all the records $referenceTable->get_all(); // put items in list $recCounter=0; $listOfValues=array(); foreach($referenceTable->recordObjs as $record) { $recCounter++; $auditTrail->add('Reference table = '.$record->values[1]); $listOfPointers[$recCounter] = $record->values[1]; $listOfValues[$recCounter]=$record->values[2]; } $sortedListObj = new SortedArray($listOfValues); $sortedList = $sortedListObj->sortedList; $counter = 0; foreach($sortedList as $ptr) { $counter++; $sortedListOfPointers[$counter] = $listOfPointers[$ptr]; $sortedListOfValues[$counter] = $listOfValues[$ptr]; } $ret .= $this->drop_down($name,$sortedListOfValues,$noSelectionAllowed,$value,$sortedListOfPointers); } $auditTrail->add_footer('column - display_for_change'); return $ret; } /** * return the string to display the original value on an edit form as a hidden field * * @param string $value the value of the field * @returns string the HTML to display * */ function display_original($p_counter, $p_value) { $fieldName = "originals".$p_counter; $ret = ''; return $ret; } /** * prepare the field for inserting into/updating the database * * @param string $value the value of the field * @returns string * */ function toDatabase($value) { global $environment; global $messages; $ret = $value; // default // No | within field unless link $posOfPipe = strpos($value, $environment->COLUMN_SEPARATOR); // Now test if there is a pipe character if ($posOfPipe === false) { // If there is no pipe and this is a link then there is a problem if ($this->type == $environment->TYPE_INTERNAL_LINK || $this->type == $environment->TYPE_EXTERNAL_LINK) { $messages->add($this->label.getMyLang('err_dat_missing_pipe').$value); } } else { // But if there is a pipe and this is not a link then there is a problem if ($this->type != $environment->TYPE_INTERNAL_LINK && $this->type != $environment->TYPE_EXTERNAL_LINK) { $messages->add($this->label.getMyLang('err_dat_extra_pipe').$value); } } // The display format of a link is either aaa:bbb:ccc|linkName or http://www...|linkName } // but the db version replaces the | with : if ($this->type == $environment->TYPE_INTERNAL_LINK) { // convert the | into : $ret = str_replace( $environment->COLUMN_SEPARATOR, $environment->COLON, $value); } if ($this->type == $environment->TYPE_EXTERNAL_LINK) { // convert the | into : $ret = str_replace( $environment->COLUMN_SEPARATOR, $environment->COLON, $value); } return $ret; } /** * convert the field from its form in the database * * @param string $value the value of the field * @returns string * */ function fromDatabase($value) { global $environment; $ret = $value; if ($this->type == $environment->TYPE_INTERNAL_LINK || $this->type == $environment->TYPE_EXTERNAL_LINK) { // convert the last : into | if ($value>'') { $ptr=strrpos ($value,$environment->COLON); // the last colon $ret = substr($value,0,$ptr).$environment->COLUMN_SEPARATOR.substr($value,$ptr+1); } } return $ret; } /** * validate the value being entered for a field * * @param string $value the value of the field * @param object $messages the messages object * @returns none but message added if not valid * */ function validate ($value, $messages) { global $environment; global $myLang; global $conf; // Number if ($this->type == $environment->TYPE_NUMBER || $this->type > $environment->NUMBER_OF_BASIC_TYPES) { if (!is_numeric($value)) { $messages->add($this->label.getMyLang('err_dat_typ_num').$value); } } // Date if ($this->type == $environment->TYPE_DATE) { if ($value>'' ) { $day=substr($value,8,2); $month = substr($value,5,2); $year = substr($value,0,4); if (substr($value,4,1) !='/' ||substr($value,7,1) !='/' ||!checkdate($month,$day,$year)) $messages->add($this->label.getMyLang('err_dat_typ_dat').$value); } } // Internal Link if ($this->type == $environment->TYPE_INTERNAL_LINK) { if (substr($value,0,2) != '[[' || substr($value,strlen($value)-2) != ']]' || strpos($value, $environment->COLUMN_SEPARATOR) === false ) { $messages->add($this->label.getMyLang('err_dat_typ_int').$value); } } // External Link if ($this->type == $environment->TYPE_EXTERNAL_LINK) { if (substr($value,0,2) != '[[' || substr($value,strlen($value)-2) != ']]' || strpos($value, $environment->COLUMN_SEPARATOR) === false ) { $messages->add($this->label.getMyLang('err_dat_typ_ext').$value); } } // Mandatory if ($this->mandatory==$environment->YES) { if ($value=='') { $messages->add($this->label.getMyLang('err_dat_typ_man')); } } } /** * display the value of a field * * @param string $value the value of the field * @returns string * */ function display($value) { //The output used for the display only of values global $environment; global $session; global $conf; // String or number if ($this->type==$environment->TYPE_STRING ||$this->type==$environment->TYPE_NUMBER) $ret = $value; // Date elseif ($this->type==$environment->TYPE_DATE) { /* $dateFormat = $conf['dformat']; $year = substr($value,0,4); $month = substr($value,5,2); $day = substr($value,8,2); $dateAndTime = mktime(0,0,0,$month,$day,$year); $ret = date($dateFormat, $dateAndTime); */ $ret = $value; } // Internal Link elseif ($this->type==$environment->TYPE_INTERNAL_LINK) { $elements=explode($environment->COLON, $value); if ($value>'') { $last=count($elements)-1; $name=$elements[$last]; $ptr=strrpos ($value,$environment->COLON); // the last colon $ret =''.substr($name,0,strlen($name)-2).''; } } // External Link elseif ($this->type==$environment->TYPE_EXTERNAL_LINK) { $elements=explode($environment->COLON, $value); if ($value>'') { $last=count($elements)-1; $name=$elements[$last]; $ptr=strrpos ($value,$environment->COLON); // the last colon $ret =''.substr($name,0,strlen($name)-2).''; } } // Lookup of column types (admin) elseif ($this->type == $environment->TYPE_LOOKUP) { $ret = $session->columnTypeList[$value]; } // Lookup elseif ($this->type>$environment->LOOKUP_OFFSET && $this->type<$environment->REFERENCE_OFFSET) { $list=$session->lookupOptions[$this->type - $environment->LOOKUP_OFFSET]; if ($value=='') $ret=''; else $ret = $list[$value]; } // Reference elseif ($this->type>$environment->REFERENCE_OFFSET) { //Open the reference table $referenceTable = new Table($this->type - $environment->REFERENCE_OFFSET,"data"); //get all the records $referenceTable->get_all(); // put items in list $recCounter=0; foreach($referenceTable->recordObjs as $record) { $recCounter++; if ($value == $record->values[1]) $ret = $record->values[2]; } } return $ret; } /** * construct the drop-down list for a field * * @param string $name the name of the column * @param array $values the list of values of the field * @param boolean $plusZero whether a zero value (No Selection) should be added * @param object $p_value the selected value * @param array $listOfPointers reference tables must have id as first field - this is the list of ids * @returns string the HTML for the list * */ function drop_down($name,$values,$plusZero=false,$p_value='',$listOfPointers='') { global $myLang; $ret = ''; $ret .= "'; $this->IDX .=''; } /** * Displays the form for creating a record * * @param table object $tab the table in which the record will be created * @param messages object $messages the messages object so that any errors are trapped and displayed * @param string array $values the values to be placed in the fields in case there have been errors * @returns none * */ function create_form ($tab, $messages, $values='') { global $environment; global $myLang; // Indent $this->output ('
'); // The table name $this->output ('

'.$tab->name.'

'); if ($tab->isAdmin) $title = getMyLang('prompt_col'); else $title = getMyLang('prompt_rec'); // The title ie. Creating Records $this->output ('

'.getMyLang('prompt_cre').' '.$title.'

'); if ($messages->exist) $messages->displayAll(); // Indent $this->output ('
'); $this->output ($this->form_start()); $this->output (''); $counter=0; foreach($tab->columnObjs as $column) { $counter++; $this->output (''); $this->output (''); $this->output (''); $this->output (''); } $this->output ('
'); $this->output ($column->label); $this->output (''); // If there were validation messages, redisplay values $name = "values".$counter; if (is_array($values)) $this->output($column->display_for_change($values[$counter],'create')); else $this->output($column->display_for_change('','create')); $this->output ('
'); $this->output (''); // This form seems to behave differently from the others $this->output($this->button('insertAction',getMyLang('btn_cre_cre') ,false, $tab->displayId)); $this->output ($this->form_end()); // The Home button $this->output($this->button('home',getMyLang('btn_home') ,true)); // The Back button $this->output($this->button('listall',getMyLang('btn_back') ,true, $tab->displayId)); $this->output (''); $this->output('
'); $this->output ('
'); } /** * Displays the form for editing a record * * @param record object $p_recordObj the record to be edited * @param messages object $messages the messages object so that any errors are trapped and displayed * @param string array $values the values to be placed in the fields * @returns none * */ function edit_form($p_recordObj,$messages,$values='') { // $p_recordObj has the current db values, $values has the values from the last editing (if they exist) global $environment; global $myLang; $tableObj=$p_recordObj->tableObj; // Indent $this->output ('
'); // The table name $this->output ('

'.$tableObj->name.'

'); if ($tab->isAdmin) $title = getMyLang('prompt_col'); else $title = getMyLang('prompt_rec'); // The title ie. Creating Records $this->output ('

'.getMyLang('prompt_edt').' '.$title.'

'); if ($messages->exist) $messages->displayAll(); $columnObjs = $tableObj->columnObjs; // Indent $this->output ('
'); // Start the form $this->output ($this->form_start()); // Start of table $this->output( ""); for($counter=1;$counteroutput(''); $this->output(''); $this->output (''); $this->output(''); } $this->output('
'); $this->output($columnObjs[$counter]->label); $this->output (''); if (is_array($values)) $this->output($column->display_for_change($values[$counter],'update')); else $this->output($column->display_for_change($p_recordObj->values[$counter],'update')); $this->output('
'); // Now for the original db values as hidden fields for($counter=1;$counteroutput($column->display_original($counter, $p_recordObj->values[$counter])); } // Update button $this->output($this->button('updateAction',getMyLang('btn_edt_upd'),false, $tableObj->displayId,$p_recordObj->id)); $this->output($this->form_end()); // The Home button $this->output($this->button('home',getMyLang('btn_home') ,true)); // The Back button $this->output($this->button('listall',getMyLang('btn_back') ,true, $tableObj->displayId)); $this->output('
'); $this->output('
'); } /** * Displays the form for deleting a record * * @param record object $p_recordObj the record to be deleted * @param messages object $messages the messages object so that any errors are trapped and displayed * @returns none * */ function delete_form($p_recordObj,$messages) { global $environment; global $myLang; $tableObj=$p_recordObj->tableObj; // Indent $this->output ('
'); // The table name $this->output ('

'.$tableObj->name.'

'); if ($tableObj->isAdmin) $title = getMyLang('prompt_col'); else $title = getMyLang('prompt_rec'); // The title ie. Deleting ..... $this->output ('

'.getMyLang('prompt_del_rec').'

'); if ($messages->exist) $messages->displayAll(); $columnObjs = $tableObj->columnObjs; // Indent $this->output ('
'); $this->output ($this->form_start()); $this->output( ""); // Display the contents for($counter=1;$counteroutput(''); $this->output(''); $this->output(''); $this->output(''); } // Now for the original db values as hidden fields for($counter=1;$counteroutput($column->display_original($counter, $p_recordObj->values[$counter])); } // Display the buttons $this->output('
'); $this->output($columnObjs[$counter]->label); $this->output(''); $this->output($columnObjs[$counter]->display($p_recordObj->values[$counter])); $this->output('
'); $this->output($this->button('deleteAction',getMyLang('btn_del_cnf'),false, $tableObj->displayId,$p_recordObj->id)); $this->output ($this->form_end()); // The Home button $this->output($this->button('home',getMyLang('btn_home') ,true)); // The Back button $this->output($this->button('listall',getMyLang('btn_back') ,true, $tableObj->displayId)); $this->output('
'); $this->output ('
'); } /** * Displays all the records in a table for sorting, searching etc * * @param table object $tab the table whose contents are to be displayed * @param array $list the contents to be displayed * @param array $sortedList the sorted list of record IDs * @param number $sortedColumnId the column on which it has been sorted * @returns none * */ function list_all($tab, $list,$sortedList='',$sortedColumnId=0) { global $environment; global $myLang; $numberOfCells = count($tab->columnObjs)+3; $this->output ('
'); // The title $this->output ('

'.$tab->name.'

'); // The Home button $this->output($this->button('home',getMyLang('btn_home') ,true)); //The create button $prompt = getMyLang('btn_lst_cre_new_rec'); if ($tab->isAdmin) $prompt=getMyLang('btn_lst_cre_new_col'); $this->output($this->button('insertForm',$prompt,true, $tab->displayId)); //The Admin button if (!$tab->isAdmin) { $this->output($this->button('admin',getMyLang('btn_adm_tab_str'),true, $tab->displayId)); } else { $this->output($this->button('listall',getMyLang('btn_lst_tab_cnt') ,true, $tab->id)); } //************************************************************************************ $this->output ($this->form_start()); $this->output (''); $colCounter=0; $this->output (''); $this->output (''); // The No sort radio button $this->output (''); foreach($tab->columnObjs as $column) { $colCounter++; if ($column->isVisible) { $this->output ("'); } } $this->output (''); //The filter cells $this->output (''); // The Search row of filters and the Search button $colCounter=0; $this->output (''); foreach($tab->columnObjs as $column) { $colCounter++; if ($column->isVisible) { $this->output (''); } } $this->output (''); $this->output (''); //************************************************************************************** $this->output (''); // the rest of the main table $recCounter = 0; $columnCount=count($tab->columnObjs); //The row begins for ($recCounter=1;$recCounter<=count($tab->recordObjs);$recCounter++) { $recPointer= $sortedList[$recCounter]; $record=$tab->recordObjs[$recPointer]; //Check against the filter list $okToDisplay = false; if (in_array($recCounter,$list,true)) $okToDisplay = true; if ($okToDisplay) { $this->output (""); // Each column in the row $this->output (''); // The columns displayed for ($fieldCounter=1;$fieldCounter<=$columnCount;$fieldCounter++) { $column = $tab->columnObjs[$fieldCounter]; if ($column->isVisible) { $value = $record->values[$fieldCounter]; $this->output (''); } } $this->output (""); } } $this->output (''); $this->output ('
'); $this->output (getMyLang('rad_lst_no_srt')); $toGo = 'output ($toGo); $this->output ('"); // The Sort radio button $this->output ($column->label); $toGo = 'output ($toGo); $this->output ('
'); $this->output($this->iconButton('list','search.png', getMyLang('btn_lst_srch_srt'), $tab->displayId)); $this->output (''); $this->output ($column->display_for_change('','search')); $this->output ('
'); // The Edit button $this->output($this->iconButton('updateForm','edit.png',getMyLang('btn_lst_edt'), $tab->displayId,$recPointer)); if (!$tab->isAdmin) { // The Delete button $this->output($this->iconButton('deleteForm','delete.png',getMyLang('btn_lst_del'), $tab->displayId,$recPointer)); } $this->output (''); $this->output ($tab->columnObjs[$fieldCounter]->display($value)); $this->output ('
'); $this->output ($this->form_end()); //************************************************************************************ $this->output ('
'); } /** * Displays the form for creating a new table * * @param none * @returns none * */ function createTableForm() { global $environment; global $messages; global $myLang; $this->output ('

'.getMyLang('ttl_cre_tab').'

'); if ($messages->exist) $messages->displayAll(); $this->output (''); $this->output ($this->form_start()); $this->output(''); $this->output(''); $this->output(''); $this->output(''); $this->output('
'); $this->output(getMyLang('prompt_ent_tab_name')); $this->output(''); $this->output(''); $this->output('
'); $this->output($this->button('createTable',getMyLang('btn_cre_new_tab'),false)); $this->output($this->form_end()); // The Home button $this->output($this->button('home',getMyLang('btn_home') ,true)); } /** * Constructs the HTML form start tag * * @param none * @returns string the HTML form end tag * */ function form_start() { global $environment; $ret = '
'; return $ret; } /** * Displays a button if permitted * * @param string $action the action taken * @param string $name the name of the button * @param boolean $withTopAndTail true if the and
are to be included * @param number $table the ID of the table * @param number $record the ID of the record * @returns string The HTML required * */ function button ($action, $name, $withTopAndTail=false, $table='', $record='') { $ret=''; global $session; global $auditTrail; $auditTrail->add_header('button action = '.$action.' name = '.$name); if ($session->allowed($action)) { if ($withTopAndTail) $ret .= $this->form_start(); $ret .= $this->IDX; $ret .=''; if ($table!='') $ret .= ''; if ($record!='') $ret .= ''; $ret .='form_end(); } $auditTrail->add_footer('button action = '.$action.' name = '.$name); return $ret; } /** * Displays a button icon if permitted * * @param string $action the action taken * @param string $image the image to be displayed * @param string $title the title for the hint * @param number $table the ID of the table * @param number $record the ID of the record * @returns string The HTML required * */ function iconButton ($action, $image, $title, $table='', $record='') { $ret=''; global $session; global $auditTrail; global $environment; $auditTrail->add_header('button action = '.$action.' name = '.$name); if ($session->allowed($action)) { $ret .= $this->IDX; $ret .= ''; } $auditTrail->add_footer('iconButton action = '.$action.' name = '.$name); return $ret; } /** * Constructs the HTML form end tag * * @param none * @returns string the HTML form end tag * */ function form_end() { $ret =''; return $ret; } /** * Outputs/renders to the screen * * @param string $p_string the string to be output * @param number $p_indent = -1 : reduce indent * = 0 ; leave indent * = +1 : increase indent * @returns none * */ function output ($p_string, $p_indent=0) { if ($p_indent==0) { if (substr($p_string,0,3)=='currentSteps--; } $INCREMENT = 2; // for the indent of the HTML ptln( $p_string, $this->currentSteps*$INCREMENT); if ($p_indent==0) { if (substr($p_string,0,2)=='currentSteps++; } } /** * Outputs to the debug file if the debug flag is set to true * * @param string $p_string the string to be output * @returns none * */ function debug_output ($p_string) { global $environment; if ($this->debug) { $file = new File($environment->DEBUG_FILE,"debug"); $file->write_record($p_string); } } /** * the default screen * * @param none * @returns none * */ function wiki () { } } /** * TestObj Class * * Used specifically for the sort * */ class TestObj { var $id; var $name; /** * * Constructor * * @param number $id the ID of the element * @param string $name the name of the element * @returns object * */ function TestObj($id,$name) { $this->id = $id; $this->name = $name; } /** * This is the static comparing function: * * @param string $a the string to be compared * @param string $b the string to be compared * @returns boolean 0 if =, 1 if a>b, else -1 * */ function cmp_obj($a, $b) { $al = strtolower($a->name); $bl = strtolower($b->name); if ($al == $bl) { return 0; } return ($al > $bl) ? +1 : -1; } } /** * SortedArray Class * * Holds the sorted array specifically for sorting * */ class SortedArray { var $sortedList = array(); /** * * Constructor * * @param array $list the array to be sorted * @returns object * */ function SortedArray($list) { $recCounter=0; foreach($list as $entry) { $recCounter++; $toBeSorted[]=new TestObj($recCounter,$entry); } usort ($toBeSorted,array("TestObj","cmp_obj")); $counter=0; foreach($toBeSorted as $testObj) { $counter++; $this->sortedList[$counter] = $testObj->id; } } } /** * Main program which takes the appropriate action * * @param none * @returns none * */ function action_db() { global $environment; global $display; global $session; global $IDX; global $dbName; global $messages; global $auditTrail; global $myLang; // Set up by action.php global $action; global $tableId; global $recordId; $display = new Display(); $auditTrail = new AuditTrail(); $INFO = pageinfo(); $auditTrail->add_header("action_db"); $auditTrail->add('action = '.$action.' table = '.$tableId.' record = '.$recordId); $messages = new Messages(); // Check whether the debug flag is set if (isset($_REQUEST['debug'])) { if ($_REQUEST['debug']=='yes') { $file=new File($environment->DEBUG_FILE,"debug"); $file->create_file(); $display->debug=true; } elseif ($_REQUEST['debug']=='no') { $file=new File($environment->DEBUG_FILE,"debug"); $file->delete_file(); $display->debug=false; } } $session = new Session(); // Check if it has come from the wiki directly if ($action =='') { // It has come directly from the wiki $action = "listall"; $command = substr($IDX,strlen($dbName)+1); $tableId = substr($command,0,2); // Pick up the Create Table option if ($IDX==$dbName.':'.$environment->DICTIONARY_FOLDER) $action='createTableForm'; else { // make sure all is OK $tab=new Table($tableId); // if there was a problem then report it if ($messages->exist) $action="messages"; } } $auditTrail->add("action=".$action); // Check whether the user can carry out this action if (!$session->allowed($action)) { $action= "message"; $message=getMyLang('msg_wrn_priv'); $messages->add($message); $action = "messages"; } $auditTrail->add("action=".$action); // Now handle the various actions // action = messages if ($action=="messages") $messages->displayAll(); // action = home if ($action == "home") { $display->wiki(); } // action = createTable if ($action == "createTable") { $tableName = ''; if (isset($_REQUEST["tableName"])) $tableName = $_REQUEST["tableName"]; if ($tableName > '') { $tab = new Table($tableName); } else { $messages->add(getMyLang('msg_wrn_tab_nam') ); } if ($messages->exist) { $action = "createTableForm"; } else { $tableId = - $tab->id; $messages->add(getMyLang('msg_tab_cre')); $action = "insertForm"; } } // action = admin if ($action=="admin") { $auditTrail->add("action=".$action." tableId is ".$tableId."will be negative"); $tableId = - $tableId; $action="listall"; } // action = insertAction if ($action == "insertAction") { global $display; $tab=new Table ($tableId); for ($counter=1;$counter<=count($tab->columnObjs);$counter++) { $test="values".$counter; if (isset($_REQUEST[$test])) { $values[$counter]= $_REQUEST[$test]; $column = $tab->columnObjs[$counter]; $column->validate($values[$counter], $messages); $values[$counter] = $column->toDatabase($values[$counter]); } } $display->debug_output ( "val=".$values[1]." and ".$values[2]); if ($messages->exist) { $action = "insertForm"; } else { $tab->create($values); if ($messages->exist) $action="insertForm"; else $action="listall"; } } // action = updateAction if ($action == "updateAction") { global $display; $tab=new Table ($tableId); $done = false; $display->debug_output ("main:update action: table= ".$tab->name); for ($counter=1;$counter<=count($tab->columnObjs);$counter++) { $column = $tab->columnObjs[$counter]; // Get the changed values from the form $test="values".$counter; if (isset($_REQUEST[$test])) { $values[$counter]= $_REQUEST[$test]; $column->validate($values[$counter], $messages); $values[$counter] = $column->toDatabase($values[$counter]); } // Now to get the originals $test="originals".$counter; if (isset($_REQUEST[$test])) { $originals[$counter]= $_REQUEST[$test]; } } // The array values now has the values after being changed // $originals has the original values stored as hidden fields on the form if ($messages->exist) { // Problems with the validation so try again $action = "updateForm"; } else { if (!$tab->isUnique($values,$recordId)) { $action = "updateForm"; } else { if ($tab->hasChanged($recordId, $originals)) { $values = ''; // to force the refresh of the values in the screen $action = "updateForm"; } else { $tab->update($recordId, $values); if ($messages->exist) { // Problems with the update so try again $action = "updateForm"; } else { $action="listall"; } } } } } // action = deleteAction if ($action == "deleteAction") { global $display; $tab=new Table ($tableId); $display->debug_output ("main:delete action: table= ".$tab->name); for ($counter=1;$counter<=count($tab->columnObjs);$counter++) { $column = $tab->columnObjs[$counter]; // Now to get the originals $test="originals".$counter; if (isset($_REQUEST[$test])) { $originals[$counter] = $_REQUEST[$test]; } } if ($tab->hasChanged($recordId, $originals)) { $action = "deleteForm"; } else { $tab->delete($recordId); if ($messages->exist) // Problems with the delete so try again $action = "deleteForm"; else $action = "listall"; } } // action = list or listall if ($action == "list" || $action == "listall") { $auditTrail->add("action=".$action." tableId is ".$tableId); $tab=new Table ($tableId); $tab->get_all(); $sortedColumnId=0; $filters=array(); $sortedList = array(); // **************** Any Sorting ? ************************************************ if (isset($_REQUEST["sort"]) && $_REQUEST["sort"]>'0') { $sortedColumnId = $_REQUEST["sort"]; // Prepare the sort array in $list // If this is a link column then we should sort on the name of the link, i.e. the last element if ($tab->columnObjs[$sortedColumnId]->type == $environment->TYPE_EXTERNAL_LINK ||$tab->columnObjs[$sortedColumnId]->type == $environment->TYPE_INTERNAL_LINK) { $counter=0; foreach($tab->recordObjs as $recordObj) { $counter++; $value = $recordObj->values[$sortedColumnId]; $elements=explode($environment->COLON, $value); $name=$elements[count($elements)-1]; $list[$counter] = $name; } } // If this is a reference column then sort on the second column in the reference table elseif ($tab->columnObjs[$sortedColumnId]->type >= $environment->REFERENCE_OFFSET) { $refTableID = $tab->columnObjs[$sortedColumnId]->type - $environment->REFERENCE_OFFSET; $refTable = new ReferenceTable($refTableID); $baseList = $refTable->get_list(); $counter = 0; foreach($tab->recordObjs as $recordObj) { $counter++; $list[$counter] = $baseList[$recordObj->values[$sortedColumnId]]; } // Any other type just sort on that column } else { $counter=0; foreach($tab->recordObjs as $recordObj) { $counter++; $list[$counter] = $recordObj->values[$sortedColumnId]; } } // now do the sorting - the results are in sortedList as pointers $sortedListObj = new SortedArray($list); $sortedList = $sortedListObj->sortedList; } else { // No Sorting required just put consecutive integers in $sortedList $counter=0; foreach ($tab->recordObjs as $recordObj) { $counter++; $sortedList[$counter] = $counter; } } //check if any filters have been applied but not if it's listall if ($action != "listall") { for ($counter=1;$counter<=count($tab->columnObjs);$counter++) { $test="values".$counter; if (isset($_REQUEST[$test])) $filters[$counter]= $_REQUEST[$test]; else $filters[$counter]=''; } } $list=$tab->filter($filters,$sortedList); $display->list_all($tab, $list, $sortedList, $sortedColumnId); } // action = createTableForm if ($action == 'createTableForm' ) { $display->createTableForm(); } // action = insertForm if ($action == "insertForm") { $auditTrail->add("Calling new Table with id = ".$tableId); $tab=new Table ($tableId); $display->create_form($tab,$messages,$values); } // action = updateForm if ($action == "updateForm") { $tab=new Table ($tableId); // If messages are empty then this must be the first time so store the originals if ($tab->possibleReference) { $messages->add(getMyLang('msg_ref_tab_wng_1')); $messages->add(getMyLang('msg_ref_tab_wng_2')); $messages->add(getMyLang('msg_ref_tab_wng_3')); } $tab->get_all(); $rec=$tab->get($recordId); $display->edit_form($rec,$messages,$values); } // action = deleteForm if ($action == "deleteForm") { $tab=new Table ($tableId); if ($tab->possibleReference) { $messages->add(getMyLang('msg_ref_tab_wng_1')); $messages->add(getMyLang('msg_ref_tab_wng_2')); $messages->add(getMyLang('msg_ref_tab_wng_3')); } $tab->get_all(); $rec=$tab->get($recordId); $display->delete_form($rec, $messages); } $auditTrail->add_footer("action_db"); } /** * Get the translated element in $lang * * @param $id1 first id of the lang element * @param $id2 second id of the lang element * @param $id3 third id of the lang element * @returns string translation * */ function getMyLang($id1,$id2='',$id3='') { global $myLang; if ($id3>'') $value = $myLang[$id1][$id2][$id3]; elseif ($id2>'') $value = $myLang[$id1][$id2]; else $value = $myLang[$id1]; return utf8_encode($value); } ?>