From 9637b4b3dacc615bf02104db3cd5d134ffd33087 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Mon, 24 Mar 2008 02:15:22 +0000 Subject: [PATCH] r1610: Because ISSO is now in a git repository, we are merely going to add the last revision of ISSO2 to the Bugdar source tree to simplify the release process --- framework/SVN-REVISION | 1 + framework/api.php | 689 +++++++++ framework/date.php | 219 +++ framework/db.php | 512 +++++++ framework/db_mysql.php | 268 ++++ framework/db_mysqli.php | 174 +++ framework/db_postgresql.php | 278 ++++ framework/dev/Doxyfile | 1237 ++++++++++++++++ .../dev/background-position-testcase.html | 31 + framework/dev/changes.txt | 65 + framework/dev/colors.psd | Bin 0 -> 76574 bytes framework/dev/conditionaltest.php | 104 ++ framework/dev/page-background.psd | Bin 0 -> 40137 bytes framework/dev/pgsql_info.txt | 17 + framework/dev/tabs-ends.psd | Bin 0 -> 45702 bytes framework/dev/tabs.psd | Bin 0 -> 51207 bytes framework/dev/tcat.psd | Bin 0 -> 31705 bytes framework/dev/thead.psd | Bin 0 -> 31707 bytes framework/dev/xmltest.php | 22 + framework/dev/xmltest.xml | 29 + framework/functions.php | 701 +++++++++ framework/graph_pie.php | 329 +++++ framework/images/tabs-ends.gif | Bin 0 -> 659 bytes framework/images/tabs.gif | Bin 0 -> 1688 bytes framework/installer.css.php | 104 ++ framework/installer.php | 97 ++ framework/kernel.php | 1255 +++++++++++++++++ framework/mail.php | 377 +++++ framework/pagination.php | 489 +++++++ framework/phpdoc.sh | 35 + framework/printer.css.php | 441 ++++++ framework/printer.php | 931 ++++++++++++ framework/printer_css.php | 635 +++++++++ framework/printer_navigation.php | 307 ++++ framework/template.php | 638 +++++++++ framework/template_fs.php | 219 +++ framework/xml.php | 302 ++++ 37 files changed, 10506 insertions(+) create mode 100644 framework/SVN-REVISION create mode 100755 framework/api.php create mode 100755 framework/date.php create mode 100755 framework/db.php create mode 100755 framework/db_mysql.php create mode 100755 framework/db_mysqli.php create mode 100755 framework/db_postgresql.php create mode 100755 framework/dev/Doxyfile create mode 100755 framework/dev/background-position-testcase.html create mode 100755 framework/dev/changes.txt create mode 100755 framework/dev/colors.psd create mode 100755 framework/dev/conditionaltest.php create mode 100755 framework/dev/page-background.psd create mode 100755 framework/dev/pgsql_info.txt create mode 100755 framework/dev/tabs-ends.psd create mode 100755 framework/dev/tabs.psd create mode 100755 framework/dev/tcat.psd create mode 100755 framework/dev/thead.psd create mode 100755 framework/dev/xmltest.php create mode 100755 framework/dev/xmltest.xml create mode 100755 framework/functions.php create mode 100755 framework/graph_pie.php create mode 100755 framework/images/tabs-ends.gif create mode 100755 framework/images/tabs.gif create mode 100755 framework/installer.css.php create mode 100755 framework/installer.php create mode 100755 framework/kernel.php create mode 100755 framework/mail.php create mode 100755 framework/pagination.php create mode 100755 framework/phpdoc.sh create mode 100755 framework/printer.css.php create mode 100755 framework/printer.php create mode 100755 framework/printer_css.php create mode 100755 framework/printer_navigation.php create mode 100755 framework/template.php create mode 100755 framework/template_fs.php create mode 100755 framework/xml.php diff --git a/framework/SVN-REVISION b/framework/SVN-REVISION new file mode 100644 index 0000000..9ce21c5 --- /dev/null +++ b/framework/SVN-REVISION @@ -0,0 +1 @@ +r872 diff --git a/framework/api.php b/framework/api.php new file mode 100755 index 0000000..28afdad --- /dev/null +++ b/framework/api.php @@ -0,0 +1,689 @@ +apppath + */ + define('F_RELATION_FILE', 0); + + /** + * Relation index for class name + */ + define('F_RELATION_CLASS', 1); + + /** + * Relation index for field-link alternate name + */ + define('F_RELATION_ALTFIELD', 2); +} + +/** +* Abstract API +* +* Abstract class that is used as an API base for most common database interaction +* schemes. Creates a simple structure that holds data and can update, delete, and +* insert. +* +* @author Blue Static +* @copyright Copyright ©2002 - [#]year[#], Blue Static +* @version $Revision$ +* @package ISSO +* +*/ +class API +{ + /** + * Registry object + * @var object + * @access protected + */ + var $registry = null; + + /** + * Fields: used for verification and sanitization + * NAME => array(TYPE, REQUIRED, VERIFY METHOD (:self for self-named method), RELATION => array(FILE, CLASS IN FILE, ALTERNATE FIELD NAME)) + * @var array + * @access protected + */ + var $fields = array(); + + /** + * Values array: sanitized and verified field values + * @var array + * @access public + */ + var $values = array(); + + /** + * Fields that were manually set with set(), not by using set_existing() + * @var array + * @access private + */ + var $setfields = array(); + + /** + * An array of all of the processed relations on an object + * @var array + * @access public + */ + var $relations = array(); + + /** + * WHERE condition + * @var string + * @access private + */ + var $condition = ''; + + /** + * The object table row; a fetched row that represents this instance + * @var array + * @access public + */ + var $objdata = array(); + + /** + * Insert ID from the insert() command + * @var integer + * @access public + */ + var $insertid = 0; + + /** + * Pre- and post-action method stoppers + * @var array + * @access public + */ + var $norunners = array(); + + /** + * The relations to execute on + * @var array + * @access public + */ + var $dorelations = array('fetch'); + + /** + * Error list that has been generated + * @var array + * @access private + */ + var $errors = array(); + + // ################################################################### + /** + * Constructor: cannot instantiate class directly + */ + function __construct(&$registry) + { + if (!is_subclass_of($this, 'API')) + { + trigger_error('Cannot instantiate the API module directly', E_USER_ERROR); + } + + if (!is_object($registry)) + { + trigger_error('The passed registry is not an object', E_USER_ERROR); + } + + $this->registry =& $registry; + } + + // ################################################################### + /** + * (PHP 4) Constructor + */ + function API(&$registry) + { + $this->__construct($registry); + } + + // ################################################################### + /** + * Constructs an error for the error handler to receive + * + * @access protected + * + * @param string Error message + */ + function error($message) + { + $this->errors[] = $message; + + // we want to explicitly specify silence + if (APIError() == 'silent') + { + return; + } + + if (!is_callable(APIError())) + { + trigger_error('No APIError() handler has been set', E_USER_WARNING); + } + + call_user_func(APIError(), $message); + } + + // ################################################################### + /** + * Returns the error list. This is because we don't want people mucking + * with the error system. It will return an empty array if there are + * no errors. + * + * @access public + * + * @return array Array of errors + */ + function check_errors() + { + if (sizeof($this->errors) < 1) + { + return array(); + } + + return $this->errors; + } + + // ################################################################### + /** + * Sets a value, sanitizes it, and verifies it + * + * @access public + * + * @param string Field name + * @param mixed Value + * @param bool Do clean? + * @param bool Do verify? + */ + function set($field, $value, $doclean = true, $doverify = true) + { + if (!isset($this->fields["$field"])) + { + trigger_error('Field `' . $field . '` is not valid', E_USER_WARNING); + return; + } + + $this->values["$field"] = ($doclean ? $this->registry->clean($value, $this->fields["$field"][F_TYPE]) : $value); + + $this->setfields["$field"] = $field; + + if (isset($this->fields["$field"][F_VERIFY]) AND $doverify) + { + if ($this->fields["$field"][F_VERIFY] == ':self') + { + $verify = $this->{"verify_$field"}($field); + } + else + { + $verify = $this->{$this->fields["$field"][F_VERIFY]}($field); + } + + if ($verify !== true) + { + if ($verify === false) + { + $this->error(sprintf(T('Validation of %1$s failed'), $field)); + } + else + { + $this->error($verify); + } + } + } + } + + // ################################################################### + /** + * Sets the condition to use in the WHERE clause; if not passed, then + * it calculates it from the REQ_AUTO field + * + * @access public + * + * @param mixed String with WHERE condition; array of fields to use for WHERE builder + */ + function set_condition($condition = '') + { + if (is_array($condition) AND sizeof($condition) > 0) + { + $this->condition = ''; + + foreach ($condition AS $field) + { + if (!$this->values["$field"]) + { + trigger_error('The specified field `' . $field . '` for the condition could not be found as it is not set', E_USER_WARNING); + continue; + } + + $condbits[] = "$field = " . $this->prepare_field_for_sql($field); + } + $this->condition = implode(' AND ', $condbits); + } + else if ($condition != '') + { + $this->condition = $condition; + } + else + { + foreach ($this->fields AS $name => $options) + { + if ($options[F_REQ] == REQ_AUTO) + { + if (!$this->values["$name"]) + { + trigger_error('Cannot determine condition from the REQ_AUTO field because it is not set', E_USER_WARNING); + continue; + } + + $this->condition = "$name = " . $this->prepare_field_for_sql($name); + } + } + + if ($this->condition == '') + { + trigger_error('No REQ_AUTO fields are present and therefore the condition cannot be created', E_USER_WARNING); + } + } + } + + // ################################################################### + /** + * Sets existing data into $values where it's not already present + * + * @access public + */ + function set_existing() + { + static $run; + if ($run) + { + return; + } + + $this->fetch(); + + foreach ($this->objdata AS $field => $value) + { + if (!isset($this->values["$field"])) + { + $this->values["$field"] = $value; + } + } + + $run = true; + } + + // ################################################################### + /** + * Fetches a record based on the condition + * + * @param bool Populate $this->values[] with data, along with $this->objdata[] ? + * + * @access public + */ + function fetch($populate = false) + { + if ($this->condition == '') + { + trigger_error('Condition is empty: cannot fetch', E_USER_ERROR); + } + + $this->run_action_method('pre_fetch'); + + $result = $this->registry->modules[ISSO_DB_LAYER]->query_first("SELECT * FROM {$this->prefix}{$this->table} WHERE {$this->condition}"); + if (!$result) + { + $this->error(T('No records were returned')); + return; + } + + $this->run_action_method('post_fetch'); + + $this->objdata = $result; + + if ($populate) + { + foreach ($this->objdata AS $key => $value) + { + if (!isset($this->values["$key"])) + { + $this->values["$key"] = $value; + } + } + } + + $this->call_relations('fetch'); + } + + // ################################################################### + /** + * Inserts a record in the database + * + * @access public + */ + function insert() + { + $this->verify(); + + $this->run_action_method('pre_insert'); + + foreach ($this->setfields AS $field) + { + $fields[] = $field; + $values[] = $this->prepare_field_for_sql($field); + } + + $this->registry->modules[ISSO_DB_LAYER]->query("INSERT INTO {$this->prefix}{$this->table} (" . implode(',', $fields) . ") VALUES (" . implode(',', $values) . ")"); + + if (strcasecmp(ISSO_DB_LAYER, 'DB_PostgreSQL') == 0) + { + foreach ($this->fields AS $field => $info) + { + if ($info[F_REQ] == REQ_AUTO) + { + $autofield = $field; + break; + } + } + + $this->insertid = $this->registry->modules[ISSO_DB_LAYER]->insert_id($this->prefix . $this->table, $autofield); + } + else + { + $this->insertid = $this->registry->modules[ISSO_DB_LAYER]->insert_id(); + } + + $this->run_action_method('post_insert'); + } + + // ################################################################### + /** + * Updates a record in the database using the data in $vaues + * + * @access public + */ + function update() + { + if ($this->condition == '') + { + trigger_error('Condition is empty: cannot update', E_USER_ERROR); + } + + $this->run_action_method('pre_update'); + + foreach ($this->setfields AS $field) + { + $updates[] = "$field = " . $this->prepare_field_for_sql($field); + } + $updates = implode(', ', $updates); + + $this->registry->modules[ISSO_DB_LAYER]->query("UPDATE {$this->prefix}{$this->table} SET $updates WHERE {$this->condition}"); + + $this->run_action_method('post_update'); + } + + // ################################################################### + /** + * Deletes a record + * + * @access public + * + * @param bool Run API->set_existing()? + */ + function delete($runset = true) + { + if ($this->condition == '') + { + trigger_error('Condition is empty: cannot delete', E_USER_ERROR); + } + + if ($runset) + { + $this->set_existing(); + } + + $this->run_action_method('pre_delete'); + + $this->registry->modules[ISSO_DB_LAYER]->query("DELETE FROM {$this->prefix}{$this->table} WHERE {$this->condition}"); + + $this->run_action_method('post_delete'); + } + + // ################################################################### + /** + * Verifies that all required fields are set + * + * @access private + */ + function verify() + { + foreach ($this->fields AS $name => $options) + { + if ($options[F_REQ] == REQ_YES) + { + if (!isset($this->values["$name"])) + { + $this->error(sprintf(T('Required field %1$s was not set'), $name)); + } + } + else if ($options[F_REQ] == REQ_SET) + { + $this->{"set_$name"}(); + } + } + } + + // ################################################################### + /** + * Runs a pre- or post-action method for database commands + * + * @access private + * + * @param string Action to run + */ + function run_action_method($method) + { + if (in_array($method, $this->norunners)) + { + return; + } + + $actmethod = (method_exists($this, $method) ? $this->$method() : ''); + } + + // ################################################################### + /** + * Determines if it's safe to run a relation; if so, it will return + * the WHERE SQL clause + * + * @access public + * + * @param string Operation to run + */ + function call_relations($method) + { + if (!is_array($this->dorelations) OR !in_array($method, $this->dorelations)) + { + return; + } + + foreach ($this->fields AS $field => $info) + { + $value = (isset($this->values["$field"]) ? $this->values["$field"] : $this->objdata["$field"]); + if ($value == null OR !is_array($info[F_RELATION])) + { + continue; + } + + if (!file_exists($this->registry->getAppPath() . $info[F_RELATION][F_RELATION_FILE])) + { + trigger_error("Could not load the relation file for field '$field'"); + } + + require_once($this->registry->getAppPath() . $info[F_RELATION][F_RELATION_FILE]); + + $this->relations["$field"] = new $info[F_RELATION][F_RELATION_CLASS]($this->registry); + $this->relations["$field"]->set(($info[F_RELATION][F_RELATION_ALTFIELD] ? $info[F_RELATION][F_RELATION_ALTFIELD] : $field), $value); + $this->relations["$field"]->set_condition(); + $this->relations["$field"]->$method(); + } + } + + // ################################################################### + /** + * Prepares a value for use in a SQL query; it encases and escapes + * strings and string-like values + * + * @access private + * + * @param string Field name + * + * @return string Prepared value entry + */ + function prepare_field_for_sql($name) + { + $type = $this->fields["$name"][F_TYPE]; + + if ($type == TYPE_NOCLEAN OR $type == TYPE_STR OR $type == TYPE_STRUN) + { + return "'" . $this->registry->db->escape_string($this->values["$name"]) . "'"; + } + else if ($type == TYPE_BOOL) + { + return ($this->values["$name"] == true ? "'1'" : "'0'"); + } + else if ($type == TYPE_BIN) + { + return "'" . $this->registry->db->escape_binary($this->values["$name"]) . "'"; + } + else + { + return $this->values["$name"]; + } + } + + // ################################################################### + /** + * Verify field: not a zero value + * + * @access protected + */ + function verify_nozero($field) + { + if ($this->values["$field"] == 0) + { + return sprintf(T('The field "%1$s" cannot be zero'), $field); + } + + return true; + } + + // ################################################################### + /** + * Verify field: not empty + * + * @access protected + */ + function verify_noempty($field) + { + if (empty($this->values["$field"])) + { + return sprintf(T('The field "%1$s" cannot be empty'), $field); + } + + return true; + } +} + +// ################################################################### +/** +* Setter and getter method for the API error reporting system. Passing +* a name will cause the set, no arguments will cause the get. +* +* @access public +* +* @param mixed Method name in callable form +* +* @return mixed Method name in callable form +*/ +function APIError($new = null) +{ + static $caller, $prev; + + if ($new === -1) + { + $caller = $prev; + } + else if ($new !== null) + { + $prev = $caller; + $caller = $new; + } + + return $caller; +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file diff --git a/framework/date.php b/framework/date.php new file mode 100755 index 0000000..8df7b29 --- /dev/null +++ b/framework/date.php @@ -0,0 +1,219 @@ +registry =& $registry; + + $this->servertz = date('Z', TIMENOW) / 3600; + } + + // ################################################################### + /** + * (PHP 4) Constructor + */ + function Date(&$registry) + { + $this->__construct($registry); + } + + // ################################################################### + /** + * Sets the user timezone variable + * + * @access public + * + * @param integer User time zone + */ + function setUserTimezone($tz) + { + $this->usertz = $tz; + } + + // ################################################################### + /** + * Gets the user timezone variable + * + * @access public + * + * @return integer The user timezone + */ + function getUserTimezone() + { + return $this->usertz; + } + + // ################################################################### + /** + * Computes the total offset, taking into account all the various + * options + * + * @access public + */ + function fetch_offset() + { + $this->offsettz = $this->servertz - $this->usertz; + $this->offset = $this->offsettz * 3600; + } + + // ################################################################### + /** + * Formats a UNIX timestamp to a certain date format in the proper time + * zone + * + * @access public + * + * @param string Format of the date (same as PHP's date() function) + * @param integer UNIX timestamp to format + * @param bool Adjust the date to the user's language? + * + * @return string Formatted date + */ + function format($format, $timestamp = TIMENOW, $adjust = true) + { + if ($adjust) + { + $timestamp -= $this->offset; + } + + return date($format, $timestamp); + } + + // ################################################################### + /** + * Fetches an array of timezones for a \n\t\t\t"; + foreach ((array)$this->debuginfo AS $msg) + { + $debug .= "\n\t\t\t"; + } + $debug .= "\n\t\t\n\t"; + + // loaded modules + $modules = $this->show_modules(true); + $debug .= "\n\t
  • \n\t\t\n\t
  • "; + + // template usage + if ($dotemplates) + { + $debug .= "\n\t
  • \n\t\t\n\t
  • "; + } + + $debug .= "\n"; + + $debug = "\n\n\n
    \n\n
    \n" . $this->message('Debug Information', $debug, 1, true, false) . "\n
    $querydebug\n\n\n"; + } + + return $debug; + } +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file diff --git a/framework/mail.php b/framework/mail.php new file mode 100755 index 0000000..17be8a0 --- /dev/null +++ b/framework/mail.php @@ -0,0 +1,377 @@ +registry =& $registry; + } + + // ################################################################### + /** + * (PHP 4) Constructor + */ + function Mail(&$registry) + { + $this->__construct($registry); + } + + // ################################################################### + /** + * Sets the subject + * + * @access public + * + * @param string Subject text + */ + function setSubject($subject) + { + $this->subject = $subject; + } + + // ################################################################### + /** + * Sets the body text (required) + * + * @access public + * + * @param string Body text + */ + function setBodyText($body) + { + $this->bodytext = $body; + } + + // ################################################################### + /** + * Sets the HTML body (optional) + * + * @access public + * + * @param string Body HTML + */ + function setBodyHtml($body) + { + $this->bodyhtml = $body; + } + + // ################################################################### + /** + * Sets the from address + * + * @access public + * + * @param string Sending email address + */ + function setFromAddress($address) + { + $this->from = $address; + } + + // ################################################################### + /** + * Sets the from display name + * + * @access public + * + * @param string From name + */ + function setFromName($name) + { + $this->fromname = $name; + } + + // ################################################################### + /** + * Sets any additional headers + * + * @access public + * + * @param string Additional headers separated by a \n + */ + function setHeaders($headers) + { + $this->headers = $headers; + } + + // ################################################################### + /** + * Sets the character set to send the email in + * + * @access public + * + * @param string Charset + */ + function setCharset($charset) + { + $this->charset = $charset; + } + + // ################################################################### + /** + * Sends an email to the specified address with the specified + * sender, subject, and body. + * + * @access public + * + * @param string Email address to send to + * @param string Name of the recipient + * @param bool Send an HTML multipart (if HTML body specified)? + * + * @return bool Status of the message + */ + function send($address, $name = null, $sendhtml = false) + { + if (empty($address)) + { + trigger_error('You need to specify an email address', E_USER_ERROR); + return false; + } + + // make sure we have a mailer + // TODO - add support for SMTP + if (!@ini_get('sendmail_path')) + { + $this->registry->debug("email: no sendmail -> not sending"); + return false; + } + + // sort out the to addresses + $address = $this->_fetch_first_line($address); + $address = trim($this->registry->unsanitize($address)); + $name = $this->_fetch_first_line($name); + $name = trim($this->registry->unsanitize($name)); + $name = $this->_encodeHeaderValue($name); + $tostring = ($name == null ? $address : "\"$name\" <$address>"); + + // sanitize the from field + $from = $this->_fetch_first_line($this->from); + $from = trim($this->registry->unsanitize($from)); + + // sanitize the from name + $fromname = $this->_fetch_first_line($this->fromname); + $fromname = ($fromname == '' ? $from : trim($this->registry->unsanitize($fromname))); + $fromname = $this->_encodeHeaderValue($this->fromname); + + // sanitize the subject + $subject = $this->_fetch_first_line($this->subject); + $subject = trim($this->registry->unsanitize($subject)); + $subject = $this->_encodeHeaderValue($subject); + + // sanitize the body + $bodytext = $this->registry->modules['functions']->convert_line_breaks($this->bodytext, $this->delim); + $bodytext = trim($this->registry->unsanitize($bodytext, true)); + + // attach additional headers + $headers = $this->registry->modules['functions']->convert_line_breaks($this->headers, $this->delim); + $headers .= ((!preg_match("#{$this->delim}$#", $headers) AND $headers != '') ? "\n" : '') . "From: \"{$fromname}\" <{$from}>" . $this->delim; + $headers .= "Return-Path: {$from}" . $this->delim; + $headers .= "X-Mailer: ISSO Mail Framework \$Revision$" . $this->delim; + $headers .= "MIME-Version: 1.0" . $this->delim; + + // see if we need to use mime/multipart + if ($sendhtml AND !empty($bodyhtml) == true) + { + $boundary = 'ISSO-MULTIPART-' . $this->registry->modules['functions']->rand(10); + $headers .= "Content-Type: multipart/alternative; boundary=\"$boundary\"" . $this->delim; + + $bodyhtml = $this->registry->modules['functions']->convert_line_breaks($this->bodyhtml, $this->delim); + + // first part of the message (plaintext) + $body = "--$boundary" . $this->delim; + $body .= "Content-Type: text/plain; charset=\"" . $this->charset . "\"" . $this->delim; + $body .= "Content-Transfer-Encoding: 8bit" . $this->delim . $this->delim; + $body .= $bodytext . $this->delim; + + // add some space between the parts + $body .= $this->delim . $this->delim . $this->delim; + + // second part (html) + $body .= "--$boundary" . $this->delim; + $body .= "Content-Type: text/html; charset=\"" . $this->charset . "\"" . $this->delim; + $body .= "Content-Transfer-Encoding: 8bit" . $this->delim; + $body .= "Content-Disposition: inline" . $this->delim . $this->delim; + $body .= $bodyhtml . $this->delim; + $body .= "--$boundary--"; + } + else + { + $headers .= "Content-Type: text/plain; charset=\"" . $this->charset . "\"" . $this->delim; + $body = $bodytext; + } + $headers .= "Content-Transfer-Encoding: 8bit" . $this->delim; + + $headers = trim($headers); + + // attempt to send the mail! + if (mail($tostring, $subject, $body, $headers, "-f {$from}")) + { + $this->registry->debug("email: sent to $address"); + } + else + { + $this->registry->debug("email: error sending to $address"); + } + } + + // ################################################################### + /** + * Fetches the first line of a string + * + * @access private + * + * @param string A string + * + * @return string The first line of the string + */ + function _fetch_first_line($string) + { + $string = $this->registry->modules['functions']->convert_line_breaks($string); + $broken = explode("\n", $string); + return $broken[0]; + } + + // ################################################################### + /** + * Encodes a header value (to name, fron name, subject, etc.) according + * to RFC 2047 + * + * @param string The text to encode + * + * @return string Encoded text + */ + function _encodeHeaderValue($text) + { + if (preg_match('#[^a-zA-Z0-9\+\-\*!/]#', $text) == 0) + { + return $text; + } + + // perform this on non-ASCII characters; excluding _ and = because we want them to be encoded as they have + // different meanings in mail messages + $text = preg_replace('#([^a-zA-Z0-9\+\-\*!/])#e', '"=" . strtoupper(dechex(ord("\\1")))', $text); + $text = str_replace('=20', '_' , $text); + + return '=?' . $this->charset . '?q?' . $text . '?='; + } +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file diff --git a/framework/pagination.php b/framework/pagination.php new file mode 100755 index 0000000..a20d215 --- /dev/null +++ b/framework/pagination.php @@ -0,0 +1,489 @@ +registry =& $registry; + } + + // ################################################################### + /** + * (PHP 4) Constructor + */ + function Pagination(&$registry) + { + $this->__construct($registry); + } + + // ################################################################### + /** + * Callback function for the processing of an indivdual page. Needs + * the signature: + * public string callback(string $baseLink, boolean $noLink, integer $pageNumber, Pagination $this) + * + * @access public + * + * @param string Callback function + */ + function setBitProcessor($callback) + { + $this->bitprocessor = $callback; + } + + // ################################################################### + /** + * Callback function for the processing the entire page navigator. Needs + * the signature: + * public string callback(string $baseLink, integer $nextPage, integer $prevPage array $show['first'=>first page, 'last'=>last page, 'prev'=>previous page, 'next'=>next page], string $bits, Pagination $this) + * + * @access public + * + * @param string Callback function + */ + function setNavigatorProcessor($callback) + { + $this->pagenavprocessor = $callback; + } + + // ################################################################### + /** + * Returns the current page number + * + * @access public + * + * @return integer Current page + */ + function getPage() + { + return $this->page; + } + + // ################################################################### + /** + * Returns the current perpage value + * + * @access public + * + * @return integer Current perpage + */ + function getPerPage() + { + return $this->perpage; + } + + // ################################################################### + /** + * Sets total + * + * @access public + * + * @param integer Total number + */ + function setTotal($total) + { + $this->total = $total; + } + + // ################################################################### + /** + * Returns the number of pages to be in the navigator + * + * @access public + * + * @param integer Number of pages + */ + function getPageCount() + { + return $this->pagecount; + } + + // ################################################################### + /** + * Sets pagelinks + * + * @access public + * + * @param integer Number of page links + */ + function setPageLinks($pagelinks) + { + $this->pagelinks = $pagelinks; + } + + // ################################################################### + /** + * Sets pagevar + * + * @access public + * + * @param string Page variable + */ + function setPageVar($pagevar) + { + $this->pagevar = $pagevar; + } + + // ################################################################### + /** + * Sets perpagevar + * + * @access public + * + * @param string Per-page variable + */ + function setPerPageVar($perpagevar) + { + $this->perpagevar = $perpagevar; + } + + // ################################################################### + /** + * Sets maxperpage + * + * @access public + * + * @param integer Maximum number per page + */ + function setMaxPerPage($maxperpage) + { + $this->maxperpage = $maxperpage; + } + + // ################################################################### + /** + * Sets defaultperpage + * + * @access public + * + * @param integer Total number + */ + function setDefaultPerPage($defaultperpage) + { + $this->defaultperpage = $defaultperpage; + } + + + // ################################################################### + /** + * Takes all of the information from the set() functions and then + * prepares all of the data through verification + * + * @access public + */ + function processIncomingData() + { + $this->page = $this->registry->input_clean($this->pagevar, TYPE_INT); + $this->perpage = $this->registry->input_clean($this->perpagevar, TYPE_INT); + $this->pagelinks = $this->registry->clean($this->pagelinks, TYPE_INT); + + if ($this->page <= 0) + { + $this->page = 1; + } + + if ($this->perpage <= 0) + { + $this->perpage = $this->defaultperpage; + } + if ($this->perpage > $this->maxperpage) + { + $this->perpage = $this->maxperpage; + } + + $this->perpage = $this->registry->clean($this->perpage, TYPE_INT); + } + + // ################################################################### + /** + * Takes the variables and splits up the pages + * + * @access public + */ + function splitPages() + { + $this->pagecount = ceil($this->total / $this->perpage); + if ($this->pagelinks == 0) + { + $this->pagelinks = $this->pagecount; + } + } + + // ################################################################### + /** + * Returns the lower limit of the pages + * + * @access public + * + * @param integer Page number + * + * @return integer Lower result limit + */ + function fetchLimit($page = null) + { + if ($page === null) + { + $page = $this->page; + } + + $limit = $page * $this->perpage; + + if ($page < 1) + { + $page = 1; + $limit = 0; + } + else if ($page > $this->pagecount) + { + $page = $this->pagecount - 1; + $limit = $this->total; + } + + if ($limit < 0) + { + return 0; + } + else if ($limit > $this->total) + { + return $this->total; + } + else + { + return $limit; + } + } + + // ################################################################### + /** + * Constructs the page navigator + * + * @access public + * + * @param string Base link path + * + * @return string Generated HTML page navigator + */ + function constructPageNav($baselink) + { + global $bugsys; + + // handle base link + if (strpos($baselink, '?') === false) + { + $baselink .= '?'; + } + else if (!preg_match('#\?$#', $baselink) AND !preg_match('#(&|&)$#', $baselink)) + { + $baselink .= '&'; + } + + // first page number in page nav + $startpage = $this->page - $this->pagelinks; + if ($startpage < 1) + { + $startpage = 1; + } + + // last page number in page nav + $endpage = $this->page + $this->pagelinks; + if ($endpage > $this->pagecount) + { + $endpage = $this->pagecount; + } + + // prev page in page nav + $prevpage = $this->page - 1; + if ($prevpage < 1) + { + $prevpage = 1; + } + + // next page in page nav + $nextpage = $this->page + 1; + if ($nextpage > $this->pagecount) + { + $nextpage = $this->pagecount; + } + + // show the prev page + $show['prev'] = true; + if ($this->page == $startpage) + { + $show['prev'] = false; + } + + // show the next page + $show['next'] = true; + if ($this->page == $endpage) + { + $show['next'] = false; + } + + // show the first page + $show['first'] = false; + if ($startpage > 1) + { + $show['first'] = true; + } + + // show the last page + $show['last'] = false; + if ($endpage < $this->pagecount) + { + $show['last'] = true; + } + + // construct the page bits + $bits = ''; + $call = $this->bitprocessor; + for ($i = $startpage; $i <= $endpage; $i++) + { + if ($i == $this->page) + { + $nolink = true; + } + else + { + $nolink = false; + } + + $bits .= $call($baselink, $nolink, $i, $this); + } + + $call = $this->pagenavprocessor; + return $call($baselink, $nextpage, $prevpage, $show, $bits, $this); + } +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file diff --git a/framework/phpdoc.sh b/framework/phpdoc.sh new file mode 100755 index 0000000..97dd280 --- /dev/null +++ b/framework/phpdoc.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +# This script creates the developer documentation for ISSO with all of the necessary options set to their proper values +# SVN $Id$ + +rm -rf ./apidoc +mkdir apidoc + +################# +## OUTPUT LIST ## +################# +# HTML:frames:default (purple, white) +# HTML:frames:phpdoc.de (grey, tonal) +# HTML:frames:phphtmllib (grey, blue, red) +# HTML:frames:DOM/XX (JS: replace XX with above options) +# HTML:Smarty:PHP (php.net style) +# HTML:Smarty:HandS (light tans) +# PDF:default:default (PDF, ugly) + +################## +## OPTIONS LIST ## +################## +# parse/directory +# target/result +# ignore +# ignore +# do hidden files +# title +# parse private off +# default name/package +# default category +# hilight source code +# output options + +phpdoc -d . -t ./apidoc -i _*.php -i dev/ -dh off -ti "Blue Static ISSO Framework" -pp off -dn ISSO -dc ISSO -s on -o HTML:Smarty:PHP > ./apidoc/log.txt \ No newline at end of file diff --git a/framework/printer.css.php b/framework/printer.css.php new file mode 100755 index 0000000..06ecbc9 --- /dev/null +++ b/framework/printer.css.php @@ -0,0 +1,441 @@ +webpath = dirname($path) . '/'; + + // Gecko browsers need this to display right + header("Content-Type: text/css"); + } +} + +$css = <<webpath}images/tabs.gif); + background-repeat: no-repeat; + background-position: 0% -35px; +} + +/* Blocks of the tab link */ +#tabbar a span +{ + height: 33px; + width: 100%; + + padding: 8px 15px 8px 15px; + + background-image: url({$GLOBALS['isso:callback']->webpath}images/tabs-ends.gif); + background-repeat: no-repeat; + background-position: 100% -35px; +} + +/* The current, active tab */ +#tabbar #focustab, #tabbar #focustab a:hover +{ + color: rgb(64, 64, 64); + + background-position: 0% 0px; + + padding: 8px 0px 9px 0px; +} + +/* Blocks of a focused tab link */ +#tabbar #focustab span +{ + background-position: 100% 0px; + + padding-bottom: 9px; +} + +/* Hovered tabs */ +#tabbar a:hover +{ + background-position: 0% -70px; + + color: rgb(143, 236, 9); +} + +/* Blocks of a hovered tab link */ +#tabbar a:hover span +{ + background-position: 100% -70px; +} + +/* Main content frame */ +#mainbody +{ + width: 80%; + + vertical-align: top; + + padding: 5px 5px 5px 0px; +} + +#wrapper #contentbody #mainbody table +{ + width: 100%; +} + +/* Menu frame */ +#menu +{ + width: 20%; + + padding: 15px; + + vertical-align: top; +} + +/* A menu list of items */ +#menu ul +{ + list-style: none; + + padding: 0px; + margin: 0px; +} + +/* An individual item in the menu list */ +#menu li span +{ + display: block; + + padding: 2px 2px 2px 7px; + margin-top: 1px; + + background-color: rgb(208, 208, 208); +} + +/* Links in the menu list */ +#menu li a +{ + color: rgb(64, 64, 64); + text-decoration: none; + font-size: 10px; + font-weight: bold; +} + +/* A :hover for the row of a menu item */ +#menu li a:hover span +{ + background-color: rgb(158, 236, 95); +} + +/* A header item row */ +#menu li.header span +{ + font-weight: bold; + text-transform: uppercase; + color: rgb(255, 255, 255); + + padding: 3px; + + background-color: rgb(96, 106, 90); +} + +/* A focused item row */ +#menu li.focus span +{ + background-color: rgb(182, 216, 154); + + text-decoration: underline; +} + +CSS; + +if (!$included) +{ + echo $css; +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file diff --git a/framework/printer.php b/framework/printer.php new file mode 100755 index 0000000..d743ff3 --- /dev/null +++ b/framework/printer.php @@ -0,0 +1,931 @@ +) + * @var string + * @access private + */ + var $realm = '[UNDEFINED REALM]'; + + /** + * CSS to place in the page + * @var string + * @access private + */ + var $css = ''; + + /** + * Extra code to place + * @var sring + * @access private + */ + var $code = ''; + + /** + * Page-start hooko + * @var string + * @access private + */ + var $page_start_hook = ':=NO METHOD=:'; + + /** + * Language information array: ('langcode' =>, 'direction' =>, 'charset' =>) + * @var array + * @access private + */ + var $language = array('langcode' => 'en_US', 'direction' => 'ltr', 'charset' => 'utf-8'); + + // ################################################################### + /** + * Constructor + */ + function __construct(&$registry) + { + $this->registry =& $registry; + } + + // ################################################################### + /** + * (PHP 4) Constructor + */ + function Printer(&$registry) + { + $this->__construct($registry); + } + + // ################################################################### + /** + * Sets the realm + * + * @access public + * + * @param string Realm + */ + function setRealm($realm) + { + $this->realm = $realm; + } + + // ################################################################### + /** + * Gets the realm + * + * @access public + * + * @return string Realm + */ + function getRealm() + { + return $this->realm; + } + + // ################################################################### + /** + * Gets the language array information + * + * @access public + * + * @return array Language array + */ + function getLanguageInformation() + { + return $this->language; + } + + // ################################################################### + /** + * Sets the language array information + * + * @access public + * + * @param array Language array + */ + function setLanguageInformation($lang) + { + $this->language = $lang; + } + + // ################################################################### + /** + * Creates a redirect to another page; constructs the header and footer + * (therefore execution stops) + * + * @access public + * + * @param string Location to redirect to + * @param string Redirect message to be shown + * @param array An aray of POST variables to send through on the redirect + */ + function redirect($location, $message = null, $postvars = array()) + { + $timeout = 10 * 200; + + if ($postvars) + { + $js = << + + +JS; + } + else + { + $js = << + + +JS; + } + + if (!defined('ISSO_PRINTER_NO_NAVIGATION')) + { + define('ISSO_PRINTER_NO_NAVIGATION', 1); + } + + $this->page_start(T('Redirect')); + + if ($postvars) + { + $this->form_start($location, null, false, 'postvars'); + + foreach ($postvars AS $key => $value) + { + $this->form_hidden_field($key, $value); + } + + $this->form_end(); + } + + $redir = sprintf(T('Please wait to be redirected. If you are not redirected in a few seconds, click here.'), $location); + $override = false; + if ($message == null) + { + $showmessage = $redir; + } + else + { + $showmessage = '
    ' . $message . '
    '; + $showmessage .= "\n

    " . $redir . "

    "; + $override = true; + } + + $this->page_message(T('Redirect'), $showmessage, $override); + + $this->page_code($js); + + $this->page_end(); + } + + // ################################################################### + /** + * Throws a fatal error; constructs the header and footer + * + * @access public + * + * @param string Error messsage text + */ + function error($message) + { + if (!defined('ISSO_PRINTER_NO_NAVIGATION')) + { + define('ISSO_PRINTER_NO_NAVIGATION', 1); + } + + $this->page_start(T('Error')); + $this->page_message(T('Error'), $message); + $this->page_end(); + + exit; + } + + // ################################################################### + /** + * Outputs the header of the page: doctype, , , , + * <body> and imbeds the style information + * + * @access public + * + * @param string Title of the page + * @param string Class of the page to be applied to the body + * @param string Extra HTML to imbed in the <head> tag + * @param string <body> onLoad action to imbed + */ + function page_start($actiontitle, $pageclass = null, $extra = '', $onload = false) + { + if ($this->registry->debug AND isset($_GET['query'])) + { + ob_start(); + } + + if (defined('ISSO_PRINTER_DONE_HEADER') AND constant('ISSO_PRINTER_DONE_HEADER')) + { + return; + } + + $title = sprintf(T('%1$s - %2$s - %3$s'), $this->registry->getApplication(), $this->realm, $actiontitle); + + echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; + echo "<html xml:lang=\"" . $this->language['langcode'] . "\" lang=\"" . $this->language['langcode'] . "\" dir=\"" . $this->language['direction'] . "\">\n<head>"; + echo "\n\t<title>$title"; + echo "\n\t"; + echo $this->css; + echo $this->code; + echo ($extra ? "\n$extra" : ''); + echo "\n\n\n"; + + if ($this->registry->is_loaded('printer_navigation') AND (!defined('ISSO_PRINTER_NO_NAVIGATION') OR (defined('ISSO_PRINTER_NO_NAVIGATION') AND constant('ISSO_PRINTER_NO_NAVIGATION') != true))) + { + echo $this->registry->modules['printer_navigation']->generate_header_html(); + } + + if (!defined('ISSO_PRINTER_DONE_HEADER')) + { + define('ISSO_PRINTER_DONE_HEADER', 1); + } + } + + // ################################################################### + /** + * Links CSS to the page from a given relative path + * + * @access public + * + * @param string Relative path to the CSS file + */ + function css_link($path) + { + $this->css .= "\n\t"; + } + + // ################################################################### + /** + * Imbeds actual CSS information into the page in "; + } + + // ################################################################### + /** + * Places raw HTML code directly into the documet at the current + * position + * + * @access public + * + * @param string HTML code + */ + function page_code($code) + { + if (defined('ISSO_PRINTER_DONE_HEADER')) + { + echo "\n\n$code\n\n"; + } + else + { + $this->code .= "\n\n$code\n\n"; + } + } + + // ################################################################### + /** + * A block function that produces a table with a message in it. This + * does not print the header and footer. + * + * @access public + * + * @param string The title of the message (appears at the top of the block) + * @param string Content of the message + * @param bool Override the message: control the entire output (no
    )? + */ + function page_message($title, $message, $overridemessage = false) + { + $this->table_start(true, '75%'); + $this->table_head($title, 1); + $this->row_span(($overridemessage ? $message : "
    $message
    "), ':swap:', 'left', 1); + $this->table_end(); + } + + // ################################################################### + /** + * Produces an entire page layout that asks the user whether or not + * they want to perform X action and provides a link to the YES and NO + * action + * + * @access public + * + * @param string Message that asks if they want to do X + * @param string Location to go for YES + * @param string DO action to pass + * @param array Hidden parameters to pass to the next page + */ + function page_confirm($message, $location, $action, $params) + { + if (!defined('ISSO_PRINTER_NO_NAVIGATION')) + { + define('ISSO_PRINTER_NO_NAVIGATION', 1); + } + + $this->page_start(T('Confirm')); + + $this->form_start($location, $action); + foreach ($params AS $key => $value) + { + $this->form_hidden_field($key, $value); + } + + $this->table_start(true, '75%'); + $this->table_head(T('Confirm'), 1); + $this->row_span("
    $message
    ", ':swap:', 'left', 1); + $this->row_submit('', T('Yes'), ''); + $this->table_end(); + + $this->form_end(); + + $this->page_end(); + } + + // ################################################################### + /** + * Closes the HTML document structure an adds the copyright; this also + * stops execution of the page + * + * @access public + */ + function page_end() + { + if ($this->registry->debug AND isset($_GET['query'])) + { + ob_clean(); + ob_end_clean(); + + if (is_array($this->registry->modules[ISSO_DB_LAYER]->history)) + { + foreach ($this->registry->modules[ISSO_DB_LAYER]->history AS $query) + { + echo $this->registry->modules[ISSO_DB_LAYER]->construct_query_debug($query); + } + } + exit; + } + + if ($this->registry->is_loaded('printer_navigation') AND (!defined('ISSO_PRINTER_NO_NAVIGATION') OR (defined('ISSO_PRINTER_NO_NAVIGATION') AND constant('ISSO_PRINTER_NO_NAVIGATION') != true))) + { + echo $this->registry->modules['printer_navigation']->generate_footer_html(); + } + + $copyright = "\n
    \n

    \n\t" . $this->registry->getApplication() . ' ' . $this->registry->getAppVersion() . ", ©2002 - " . date('Y') . " Blue Static\n

    "; + + if (!defined('ISSO_PRINTER_HIDE_SETUP')) + { + echo "\n$copyright"; + echo $this->registry->construct_debug_block(false); + } + + echo "\n\n\n"; + + exit; + } + + // ------------------------------------------------------------------- + + // ################################################################### + /** + * Opens a tag with styling + * + * @access public + * + * @param bool Whether to add a
    before the table + * @param string Value of the width attribute + */ + function table_start($break = true, $width = '90%') + { + if ($break) + { + echo '
    '; + } + + echo "\n
    \n"; + } + + // ################################################################### + /** + * Adds a table row that is sued to head the entire table + * + * @access public + * + * @param string Title string + * @param integer Colspan attribute value + * @param bool Whether to bold the title + */ + function table_head($title, $colspan = 2, $strong = true) + { + echo "\n\t\n\n"; + } + + // ################################################################### + /** + * Creates column headings; useful for a grid-style page. This uses a + * different styling than table_head() and is usually used directly + * after a table header. + * + * @access public + * + * @param array Array of titles to print + */ + function table_column_head($columnarray) + { + if (is_array($columnarray)) + { + $render = "\n"; + + foreach ($columnarray AS $header) + { + $render .= "\t\n"; + } + + $render .= "\n"; + + echo $render; + } + } + + // ################################################################### + /** + * Closes a
    " . (($strong) ? "$title" : $title) . "
    $header
    tag + * + * @access public + */ + function table_end() + { + echo "\n
    \n"; + } + + // ------------------------------------------------------------------- + + // ################################################################### + /** + * Starts a
    tag and adds the DO hidden input field + * + * @access public + * + * @param string Action/name of the file to action to + * @param string Value of the DO parameter; used to do-branch + * @param bool Enctype attribute; used for mime/multi-part + * @param string Name of the form; this only matters for DOM access + * @param string Method to action as; POST or GET (default is POST) + */ + function form_start($action, $do, $enctype = false, $name = 'inputform', $submitmethod = 'post') + { + echo "\n\n\n"; + + if ($do !== null) + { + $this->form_hidden_field('do', $do); + } + } + + // ################################################################### + /** + * Adds a hidden field at the current location + * + * @access public + * + * @param string Name of the field + * @param string Value of the field + */ + function form_hidden_field($name, $value) + { + echo "\n"; + } + + // ################################################################### + /** + * Closes a tag + * + * @access public + */ + function form_end() + { + echo "
    \n\n"; + } + + // ------------------------------------------------------------------- + + // ################################################################### + /** + * Creates a table row that spans an entire row; this is used to divide + * sections, usually + * + * @access public + * + * @param string Text to place in the row + * @param string Class name to style with; by default it alternates between alt1 and alt2 (use :swap: to do that) + * @param string Alignment of the text in the row + * @param integer Colspan attribute + */ + function row_span($text, $class = ':swap:', $align = 'left', $colspan = 2) + { + if ($class === ':swap:') + { + $this->registry->modules['functions']->exec_swap_bg(); + $row_class = $this->registry->modules['functions']->bgcolour; + $is_style_element = false; + } + else + { + if (preg_match('#:style:(.*?)#i', $class)) + { + $is_style_element = true; + $style = str_replace(':style:', '', $class); + } + else + { + $row_class = $class; + $is_style_element = false; + } + } + + echo "\n\n\t$text\n"; + } + + // ################################################################### + /** + * Creates a table row that has more than two columns; this is used in + * conjunction with table_column_head() usually; it takes an array of + * values + * + * @access public + * + * @param array Array of values in form value => alignment key (c for center, l for left, and r for right) + */ + function row_multi_item($row_array) + { + $this->registry->modules['functions']->exec_swap_bg(); + + foreach ($row_array AS $item => $align) + { + $row_data["$align"][] = $item; + } + + echo ""; + + foreach ($row_data AS $align_key => $item_array) + { + if ($align_key == 'c') + { + $align = 'center'; + } + else if ($align_key == 'l') + { + $align = 'left'; + } + else if ($align_key == 'r') + { + $align = 'right'; + } + + foreach ($item_array AS $value) + { + echo "\n\tregistry->modules['functions']->bgcolour}\" align=\"$align\">$value"; + } + } + + echo "\n\n"; + } + + // ################################################################### + /** + * Generic row creation function that has two columns: label and value; + * this is used for many other form functions, but can also be used for + * non-editable fields + * + * @access public + * + * @param string Label text + * @param string HTML or text to place in the value column + * @param string Vertical align (valign attribute) for the row + * @param integer Colspan attribute + * @param string Class to style the row with; default is to alternate + */ + function row_text($label, $value = ' ', $valign = 'top', $colspan = 2, $class = -1) + { + global $IS_SETTINGS; + + if ($class == -1) + { + if (!$IS_SETTINGS) + { + $this->registry->modules['functions']->exec_swap_bg(); + $row_class = $this->registry->modules['functions']->bgcolour; + } + else + { + $row_class = 'alt2'; + } + } + else + { + $row_class = $class; + } + + echo ""; + echo "\n\t$label"; + echo "\n\t$value"; + + echo "\n\n"; + } + + // ################################################################### + /** + * Creates a table row with an text field as the value column + * + * @access public + * + * @param string Label text + * @param string Name of the field + * @param string Value of the field + * @param integer Colspan attribute + * @param integer Size of the field + * @param integer Length attribute; use FALSE for no length to be specified + * @param bool Whether to make this a password field + * @param string Vertical align (valign attribute) + */ + function row_input($label, $name, $value = '', $colspan = 2, $size = 35, $length = false, $password = false, $lalign = 'top') + { + $this->row_text($label, "", $lalign, $colspan); + } + + // ################################################################### + /** + * Creates a table row with a ", 'top', $colspan); + } + + // ################################################################### + /** + * Creates a table row with the tfoot class + * + * @access public + * + * @param string Extra text or HTML to insert into the row + * @param integer Colspan attribute + */ + function row_tfoot($data, $colspan = 2) + { + echo $this->row_span($data, 'tfoot', 'center', $colspan); + } + + // ################################################################### + /** + * Creates a tfoot table row with submit buttons + * + * @access public + * + * @param string Extra HTML to imbed in the row after the buttons + * @param string Submit button text (by default it uses pre-translated "Submit" from :save:) + * @param string Reset button text (default it uses pre-translated "Reset" from :reset:) + * @param integer Colspan attribute + */ + function row_submit($extra = false, $submit = ':save:', $reset = ':reset:', $colspan = 2) + { + if ($submit === ':save:') + { + $submit = " " . T('Submit') . " "; + } + else + { + $submit = " $submit "; + } + + if ($reset === ':reset:') + { + $reset = " " . T('Reset') . " "; + } + else + { + $reset = (($reset) ? " $reset " : ''); + } + + $output = "\n\t\t"; + $output .= ($reset ? "\n\t\t" : ''); + $output .= ($extra ? "\n\t\t$extra" : ''); + $output .= "\n\t"; + $this->row_tfoot($output, $colspan); + } + + // ################################################################### + /** + * Creates an upload row; you need to specify some other paramaters in + * form_start() for this to work + * + * @access public + * + * @param string Label text + * @param string Upload name + * @param integer Colspan attribute + */ + function row_upload($label, $name, $colspan = 2) + { + $this->row_text($label, "", 'top', $colspan); + } + + // ################################################################### + /** + * Adds a name-value pair to an array that is constructed into a + * table row from list_item() items + * + * @access public + * + * @param string Label text + * @param string Name of the $optionlist\n" . (($is_jump) ? "\n" : '') . "\n", $colspan); + } + + // ################################################################### + /** + * Assembles a list of checkboxes from list_item() items + * + * @access public + * + * @param string Label text + * @param string Name of the s + * @param integer Colspan attribute + */ + function row_checkbox($label, $name, $colspan = 2) + { + global $listitem; + + foreach ($listitem AS $value => $box) + { + $optionlist[] = "\n\t $box[name]"; + } + + $listitem = array(); + + $this->row_text($label, "\n" . implode('
    ', $optionlist) . "\n", $colspan); + } + + // ################################################################### + /** + * Creates a row with two radio buttons: yes and now + * + * @access public + * + * @param string Label text + * @param string Name of the BOOL flag + * @param bool TRUE to select the YES by default; FALSE for NO + * @param integer Colspan attribute + */ + function row_yesno($label, $name, $value, $colspan = 2) + { + $this->row_text($label, " " . T('Yes') . " " . T('No'), $colspan); + } +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file diff --git a/framework/printer_css.php b/framework/printer_css.php new file mode 100755 index 0000000..e9de53c --- /dev/null +++ b/framework/printer_css.php @@ -0,0 +1,635 @@ +fetch_modified_link_hook - Required hook that is run when +* preparing revert links for properties +* +* @author Blue Static +* @copyright Copyright ©2002 - [#]year[#], Blue Static +* @version $Revision$ +* @package ISSO +* +*/ +class Printer_CSS +{ + /** + * Framework registry object + * @var object + * @access private + */ + var $registry = null; + + /** + * CSS block list + * @var array + * @access private + */ + var $descriptors = array(); + + /** + * Master data set + * @var array + * @access private + */ + var $masterdata = array(); + + /** + * Cutomized data set + * @var array + * @access private + */ + var $customdata = array(); + + /** + * Valid properties that can be used in CSS + * @var array + * @access private + */ + var $properties = array( + 'background' => 'background', + 'color' => 'color', + 'font-style' => 'font_style', + 'font-size' => 'font_size', + 'font-family' => 'font_family', + 'text-decoration' => 'text_decoration' + ); + + /** + * Hook ran in fetch_modified_link; takes three params: $descriptor, $property, $name + * @var string + * @access private + */ + var $fetch_modified_link_hook = ':=NO METHOD=:'; + + /** + * Module fields + * @var array + * @access private + */ + var $fields = array( + 'fetch_modified_link_hook' => array(REQ_YES, null, false) + ); + + // ################################################################### + /** + * Constructor + */ + function __construct(&$registry) + { + $this->registry =& $registry; + } + + // ################################################################### + /** + * (PHP 4) Constructor + */ + function Printer_CSS(&$registry) + { + $this->__construct($registry); + } + + // ################################################################### + /** + * Sets an ISSO field + * + * @access public + * + * @param string Field name + * @param mixed Value of the field + */ + function set($name, $value) + { + $this->registry->do_set($name, $value, 'printer_css'); + } + + // ################################################################### + /** + * Gets an ISSO field + * + * @access public + * + * @param string Field name + * + * @return mixed Value of the field + */ + function get($fieldname) + { + return $this->registry->do_get($fieldname, 'printer_css'); + } + + // ################################################################### + /** + * Adds a CSS information block to the array for later use + * + * @access public + * + * @param string Block title to display in thead + * @param string CSS class/descriptor/element name + * @param bool Show the link CSS information + */ + function add_block($title, $descriptor, $dolink) + { + if (isset($this->descriptors["$descriptor"])) + { + trigger_error('The descriptor `' . $descriptor . '` already exists', E_USER_WARNING); + return; + } + + $this->descriptors["$descriptor"] = array( + 'title' => $title, + 'descriptor' => $descriptor, + 'dolink' => $dolink + ); + } + + // ################################################################### + /** + * Sets a master data key for a given descriptor and property + * + * @access public + * + * @param string Descriptor + * @param string Property + * @param string Value + */ + function set_master_data($descriptor, $property, $value) + { + $this->masterdata["$descriptor"]["$property"] = $value; + } + + // ################################################################### + /** + * Sets a custom data key for a given descriptor and property + * + * @access public + * + * @param string Descriptor + * @param string Property + * @param string Value + */ + function set_custom_data($descriptor, $property, $value) + { + $this->customdata["$descriptor"]["$property"] = $value; + } + + // ################################################################### + /** + * Generates the HTML needed to output the CSS editing blocks; this is + * done in the form of using ISSO.Printer + * + * @access public + */ + function generate_blocks() + { + $print =& $this->registry->modules['printer']; + + $lang = array( + 'standard_css_attributes' => T('Standard CSS Attributes'), + 'extra_css_attributes' => T('Extra CSS Attributes'), + + 'links_normal' => T('Normal CSS Links'), + 'links_visited' => T('Visited CSS Links'), + 'links_hover' => T('Hover CSS Links'), + + 'background' => T('Background'), + 'font_color' => T('Font Color'), + 'font_style' => T('Font Style'), + 'font_size' => T('Font Size'), + 'font_family' => T('Font Family'), + + 'text_decoration' => T('Text Decoration'), + + 'css_selector' => T('CSS Selector'), + 'save_css' => T('Save CSS') + ); + + foreach ($this->descriptors AS $descriptor) + { + $value = array(); + $status = array(); + + $desc = $descriptor['descriptor']; + + $print->table_start(); + $print->table_head($descriptor['title']); + + foreach ($this->properties AS $prop => $name) + { + $value["$name"] = $this->fetch_value($descriptor['descriptor'], $prop); + $status["$name"] = $this->fetch_modified_status($descriptor['descriptor'], $prop); + } + + $value['extra'] = $this->fetch_value($descriptor['descriptor'], 'extra'); + + $html = " + + + + + +
    +
    + $lang[standard_css_attributes] + + + + + + + + + + + + + + + + + + + + + + +
    " . $this->fetch_modified_link($desc, 'background', $lang['background']) . "
    " . $this->fetch_modified_link($desc, 'color', $lang['font_color']) . "
    " . $this->fetch_modified_link($desc, 'font-style', $lang['font_style']) . "
    " . $this->fetch_modified_link($desc, 'font-size', $lang['font_size']) . "
    " . $this->fetch_modified_link($desc, 'font-family', $lang['font_family']) . "
    +
    +
    +
    + " . $this->fetch_modified_link($desc, 'extra', $lang['extra_css_attributes']) . " + +
    +
    "; + if ($descriptor['dolink']) + { + foreach (array('a:link' => 'a_link', 'a:visited' => 'a_visited', 'a:hover' => 'a_hover') AS $sel => $selname) + { + foreach (array('background' => 'background', 'color' => 'color', 'text-decoration' => 'text_decoration') AS $prop => $name) + { + $value["{$selname}_{$name}"] = $this->fetch_value($descriptor['descriptor'] . ' ' . $sel, $prop); + $status["{$selname}_{$name}"] = $this->fetch_modified_status($descriptor['descriptor'] . ' ' . $sel, $prop); + } + } + + $html .= " + + + + + + + + + +
    +
    + $lang[links_normal] + + + + + + + + + + + + + + +
    " . $this->fetch_modified_link($desc . ' a:link', 'background', $lang['background']) . "
    " . $this->fetch_modified_link($desc . ' a:link', 'color', $lang['font_color']) . "
    " . $this->fetch_modified_link($desc . ' a:link', 'text-decoration', $lang['text_decoration']) . "
    +
    +
    +
    + $lang[links_visited] + + + + + + + + + + + + + + +
    " . $this->fetch_modified_link($desc . ' a:visited', 'background', $lang['background']) . "
    " . $this->fetch_modified_link($desc . ' a:visited', 'color', $lang['font_color']) . "
    " . $this->fetch_modified_link($desc . ' a:visited', 'text-decoration', $lang['text_decoration']) . "
    +
    +
    +
    + $lang[links_hover] + + + + + + + + + + + + + + +
    " . $this->fetch_modified_link($desc . ' a:hover', 'background', $lang['background']) . "
    " . $this->fetch_modified_link($desc . ' a:hover', 'color', $lang['font_color']) . "
    " . $this->fetch_modified_link($desc . ' a:hover', 'text-decoration', $lang['text_decoration']) . "
    +
    +
    "; + } + + $print->row_span($html, 'alt2', 'left', 1); + + $print->row_span(' +
    ' . $lang['css_selector'] . ': ' . $descriptor['descriptor'] . '
    + ', 'tfoot', 'right', 1); + + $print->table_end(); + } + } + + // ################################################################### + /** + * Returns the value of a given descriptor and property by comparing + * the mater set and custom set then returning the right one + * + * @access private + * + * @param string Descriptor + * @param string Property + * + * @return string Value of the given property + */ + function fetch_value($descriptor, $property) + { + if (!isset($this->customdata["$descriptor"]["$property"])) + { + return $this->masterdata["$descriptor"]["$property"]; + } + else + { + return $this->customdata["$descriptor"]["$property"]; + } + } + + // ################################################################### + /** + * Returns the state modified state (false for untouched and true + * for modified) from the descriptor-property value between the master + * set and the custom set of data + * + * @access private + * + * @param string Descriptor + * @param string Property + * + * @return bool Modified from the master value? + */ + function fetch_modified_status($descriptor, $property) + { + if ($this->masterdata["$descriptor"]["$property"] != $this->customdata["$descriptor"]["$property"] AND isset($this->customdata["$descriptor"]["$property"])) + { + return true; + } + + return false; + } + + // ################################################################### + /** + * Fetches a link that shows a revert link for a given property + * that uses AJAX to revert when clicked + * + * @access private + * + * @param string Descriptor + * @param string Property + * @param string Nominalized text + * + * @return string Output HTML + */ + function fetch_modified_link($descriptor, $property, $name) + { + $status = $this->fetch_modified_status($descriptor, $property); + + if ($status) + { + if (is_callable($this->fetch_modified_link_hook)) + { + return call_user_func($this->fetch_modified_link_hook, $descriptor, $property, $name); + } + else + { + trigger_error('Printer_CSS::fetch_modified_link() needs to have the fetch_modified_link_hook( $descriptor , $property , $name ) defined', E_USER_ERROR); + } + } + else + { + return $name; + } + } + + // ################################################################### + /** + * Generates an array of queries that should be run on your database to + * update CSS changes. All of the queries have sprintf() markers that + * need to be evaluated: + * + * %1$s - Database table + * %2$s - styleid field + * %3$s - descriptor field + * %4$s - property field + * %5$s - value field + * %6$d - Styleid value + * + * @access public + * + * @param array Array of user-inputted information to be transformed into queries + * + * @return array Queries that need to be evaluated then ran + */ + function generate_change_query($data) + { + $queries[0] = '--- RESERVED FOR LATER USE ---'; + + $deletes = array(); + + foreach ($this->descriptors AS $descriptor => $opts) + { + $dolink = $opts['dolink']; + + if ($dolink) + { + $loops = array('', ' a:link', ' a:visited', ' a:hover'); + } + else + { + $loops = array(''); + } + + foreach ($loops AS $sel) + { + foreach ($data["$descriptor$sel"] AS $prop => $value) + { + // the given value matches the master -- no change + if ($this->masterdata["$descriptor$sel"]["$prop"] == $value) + { + continue; + } + // the given matches the custom -- no change + else if (isset($this->customdata["$descriptor$sel"]["$prop"]) AND $this->customdata["$descriptor$sel"]["$prop"] == $value) + { + continue; + } + // no matching, it's new + else + { + $value = str_replace('%', '%%', $value); + $deletes[] = "%3\$s = '" . $this->escape($descriptor . $sel) . "' AND %4\$s = '" . $this->escape($prop) . "'"; + $queries[] = "INSERT INTO %1\$s (%2\$s, %3\$s, %4\$s, %5\$s) VALUES (%6\$d, '" . $this->escape($descriptor . $sel) . "', '" . $this->escape($prop) . "', '" . $this->escape($value) . "')"; + } + } + } + } + + if (sizeof($deletes) < 1) + { + $queries[0] = '##'; + } + else + { + $queries[0] = "DELETE FROM %1\$s WHERE styleid = %6\$d AND ((" . implode(') OR (', $deletes) . "))"; + } + + return $queries; + } + + // ################################################################### + /** + * Wrapper for $this->registry->modules[ISSO_DB_LAYER]->escape_string() + * + * @access private + * + * @param string Unprotected string + * + * @return string Sanitized string + */ + function escape($string) + { + return $this->registry->modules[ISSO_DB_LAYER]->escape_string($string); + } + + // ################################################################### + /** + * Generates a linkable/usable CSS stylehseet content file; this can + * be outputted to the browser + * + * @access public + * + * @return string CSS output + */ + function generate_css_output() + { + $data = array(); + + foreach ($this->descriptors AS $descriptor => $opts) + { + $dolink = $opts['dolink']; + + if ($dolink) + { + $loops = array('', ' a:link', ' a:visited', ' a:hover'); + } + else + { + $loops = array(''); + } + + foreach ($loops AS $sel) + { + foreach ($this->masterdata["$descriptor$sel"] AS $prop => $value) + { + $data["$descriptor$sel"]["$prop"] = $value; + } + if (is_array($this->customdata["$descriptor$sel"])) + { + foreach ($this->customdata["$descriptor$sel"] AS $prop => $value) + { + $data["$descriptor$sel"]["$prop"] = $value; + } + } + } + } + + $output = '/* CSS Style Sheet (generated by ISSO.Printer.CSS $Revision$) */'; + + foreach ($data AS $selector => $props) + { + $output .= "\n\n$selector\n{"; + foreach ($props AS $name => $value) + { + if ($name != 'extra' AND $value != '') + { + $output .= str_replace('"', '"', "\n\t$name: $value;"); + } + } + + if ($props['extra']) + { + $extra = explode("\n", $this->registry->modules['functions']->convert_line_breaks($props['extra'])); + + foreach ($extra AS $prop) + { + $output .= "\n\t$prop"; + } + } + + $output .= "\n}"; + } + + return $output; + } +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file diff --git a/framework/printer_navigation.php b/framework/printer_navigation.php new file mode 100755 index 0000000..e183c9c --- /dev/null +++ b/framework/printer_navigation.php @@ -0,0 +1,307 @@ + null, 'link' => null); + + // ################################################################### + /** + * Constructor + */ + function __construct(&$registry) + { + $this->registry =& $registry; + } + + // ################################################################### + /** + * (PHP 4) Constructor + */ + function Printer_Navigation(&$registry) + { + $this->__construct($registry); + } + + // ################################################################### + /** + * Adds a global link to the array; these cannot be removed once added + * + * @access public + * + * @param string Link text + * @param string HREF of the URL + */ + function add_top_link($text, $href) + { + $this->toplinks["$href"] = $text; + } + + // ################################################################### + /** + * Adds to the structure array. The following is the global structure + * of the array, but you can specify any entry point. When you add onto + * the structure, it will go at the bottom + * array( + * 'unique_key' => array( + * '
    unique_key' => array( + * 'unique_key' => array('text', 'url') + * ) + * ) + * ) + * + * Note that the portion in brackets is called the "scope" + * + * @access public + * + * @param string Scope to add to + * @param string Unique key for the scope + * @param string Parent key to add to + * @param string Text to display (usually localized) + * @param string URL to go to when text is clicked + */ + function add_component($scope, $key, $parent, $text, $url) + { + if ($scope == 'tab') + { + $this->tabs["$key"] = array($text, $url); + } + else if ($scope == 'section') + { + $this->sections["$parent"]["$key"] = $text; + } + else if ($scope == 'link') + { + $this->links["$parent"]["$key"] = array($text, $url); + } + } + + // ################################################################### + /** + * Sets the focus for either a tab or link + * + * @access public + * + * @param string Scope operator + * @param string Unique key in scope + * @param string Parent operator (links only) + */ + function set_focus($scope, $key, $parent) + { + if ($scope == 'tab') + { + if (isset($this->tabs["$key"])) + { + $this->focus["$scope"] = $key; + } + else + { + trigger_error('Invalid key for scope', E_USER_WARNING); + } + } + else if ($scope == 'link') + { + if (isset($this->links["$parent"]["$key"])) + { + $this->focus["$scope"] = $key; + } + else + { + trigger_error('Invalid key for scope', E_USER_WARNING); + } + } + else + { + trigger_error('Printer_Navigation::set_focus() only allows setting of focus for tab and link scopes', E_USER_ERROR); + } + } + + // ################################################################### + /** + * Generates the header HTML that is called in ISSO.Printer->page_start() + * to setup the navigation frame + * + * @access public + * + * @return string Generated HTML content + */ + function generate_header_html() + { + $output = '' . "\n\n"; + + // ------------------------------------------------------------------- + + $output2 = array(); + foreach ($this->toplinks AS $href => $text) + { + $output2[] = '' . $text . ''; + } + + $language = $this->registry->modules['printer']->getLanguageInformation(); + + $output .= "\n" . ''; + + // ------------------------------------------------------------------- + + $output .= "\n\n" . '
    '; + + // ------------------------------------------------------------------- + + $output .= "\n" . '
    '; + foreach ($this->tabs AS $key => $content) + { + $link = "\n\t" . 'focus['tab'] == $key) + { + $link .= ' id="focustab"'; + } + + $link .= '>' . $content[0] . ''; + + $output .= $link; + } + $output .= "\n" . '
    '; + + // ------------------------------------------------------------------- + + $output .= "\n\n" . ''; + $output .= "\n" . ''; + + // ------------------------------------------------------------------- + + $output .= "\n" . ''; + + // ------------------------------------------------------------------- + + $output .= "\n" . ''; + $output .= "\n" . ''; + + $output .= "\n\n" . '
    '; + $output .= "\n\n" . '' . "\n"; + + return $output; + } + + // ################################################################### + /** + * Generates the HTML that is inserted in ISSO.Printer->page_end() that + * closes all of the navigation HTML stuff + * + * @access public + * + * @return string Generated HTML content + */ + function generate_footer_html() + { + $output = ''; + + $output .= "\n" . '' . "\n"; + + $output .= "\n" . '
    '; + + $output .= "\n\n" . '
    '; + + return $output; + } +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file diff --git a/framework/template.php b/framework/template.php new file mode 100755 index 0000000..2253e12 --- /dev/null +++ b/framework/template.php @@ -0,0 +1,638 @@ +pre_parse_hook - Name of the function to execute on +* a template before the parsing occurs +* +* @author Blue Static +* @copyright Copyright ©2002 - [#]year[#], Blue Static +* @version $Revision$ +* @package ISSO +* +*/ +class Template +{ + /** + * Framework registry object + * @var object + * @access private + */ + var $registry = null; + + /** + * Name of the database table templates are in + * @var string + * @access private + */ + var $tablename = ''; + + /** + * Name of the table column template names are in + * @var string + * @access private + */ + var $namecolumn = ''; + + /** + * Name of the table column templates are in + * @var string + * @access private + */ + var $datacolumn = ''; + + /** + * Additional WHERE clauses for the template fetch SQL + * @var string + * @access private + */ + var $extrawhere = ''; + + /** + * The name of the function phrases are fetched with + * @var string + * @access private + */ + var $langcall = 'gettext'; + + /** + * The name of the function phrases are sprintf() parsed with + * @var string + * @access private + */ + var $langconst = 'sprintf'; + + /** + * Array of pre-compiled templates that are stored to decrease server load + * @var array + * @access private + */ + var $cache = array(); + + /** + * A list of the number of times each template has been used + * @var array + * @access private + */ + var $usage = array(); + + /** + * A list of templates that weren't cached, but are still used + * @var array + * @access private + */ + var $uncached = array(); + + /** + * Whether or not the page has been flush()'d already + * @var bool + * @access private + */ + var $doneflush = false; + + /** + * The name of a function that is called before template parsing of phrases and conditionals occurs + * @var string + * @access private + */ + var $pre_parse_hook = ':=NO METHOD=:'; + + // ################################################################### + /** + * Constructor + */ + function __construct(&$registry) + { + $this->registry =& $registry; + } + + // ################################################################### + /** + * (PHP 4) Constructor + */ + function Template(&$registry) + { + $this->__construct($registry); + } + + // ################################################################### + /** + * Initializes the class and all subclasses under a common package name + * + * @access protected + * + * @return string The package name + */ + function init_as_package() + { + return 'template'; + } + + // ################################################################### + /** + * Sets tablename + * + * @access public + * + * @param string Table name + */ + function setTableName($name) + { + $this->tablename = $name; + } + + // ################################################################### + /** + * Sets namecolumn + * + * @access public + * + * @param string Name column name + */ + function setNameColumn($name) + { + $this->namecolumn = $name; + } + + // ################################################################### + /** + * Sets datacolumn + * + * @access public + * + * @param string Data column name + */ + function setDataColumn($name) + { + $this->datacolumn = $name; + } + + // ################################################################### + /** + * Sets extrawhere + * + * @access public + * + * @param string Extra WHERE SQL clause + */ + function setExtraWhere($sql) + { + $this->extrawhere = $sql; + } + + // ################################################################### + /** + * Sets the pre-parse hook + * + * @access public + * + * @param string Method name + */ + function setPreParseHook($name) + { + $this->pre_parse_hook = $name; + } + + // ################################################################### + /** + * Takes an array of template names, loads them, and then stores a + * parsed version for optimum speed. + * + * @access public + * + * @param array List of template names to be cached + */ + function cache($namearray) + { + if (sizeof($this->cache) > 0) + { + trigger_error('You cannot cache templates more than once per initialization', E_USER_WARNING); + } + else + { + $templates = $this->registry->modules[ISSO_DB_LAYER]->query("SELECT * FROM " . $this->tablename . " WHERE " . $this->namecolumn . " IN ('" . implode("', '", $namearray) . "')" . ($this->extrawhere ? ' ' . $this->extrawhere : '')); + while ($template = $this->registry->modules[ISSO_DB_LAYER]->fetch_array($templates)) + { + $this->cache[ $template[ $this->namecolumn ] ] = $this->_parse($template[ $this->datacolumn ]); + $this->usage[ $template[ $this->namecolumn ] ] = 0; + } + } + } + + // ################################################################### + /** + * Loads a template from the cache or the _load function and stores the + * parsed version of it + * + * @access public + * + * @param string The name of the template + * + * @return string A parsed and loaded template + */ + function fetch($name) + { + if (isset($this->cache["$name"])) + { + $template = $this->cache["$name"]; + } + else + { + $this->uncached[] = $name; + $this->registry->debug("Manually loading template `$name`"); + $template = $this->_load($name); + $template = $this->_parse($template); + } + + if (!isset($this->usage["$name"])) + { + $this->usage["$name"] = 0; + } + + $this->usage["$name"]++; + + return $template; + } + + // ################################################################### + /** + * Output a template fully compiled to the browser + * + * @access public + * + * @param string Compiled and ready template + */ + function flush($template) + { + ob_start(); + + if (empty($template)) + { + trigger_error('There was no output to print', E_USER_ERROR); + exit; + } + + if ($this->registry->debug AND isset($_GET['query'])) + { + if (is_array($this->registry->modules[ISSO_DB_LAYER]->history)) + { + foreach ($this->registry->modules[ISSO_DB_LAYER]->history AS $query) + { + echo $this->registry->modules[ISSO_DB_LAYER]->construct_query_debug($query); + } + } + exit; + } + + if ($this->doneflush) + { + trigger_error('A template has already been sent to the output buffer', E_USER_ERROR); + exit; + } + + $template = str_replace('', $this->registry->construct_debug_block(true) . '', $template); + + print($template); + } + + // ################################################################### + /** + * Loads an additional template from the database + * + * @access private + * + * @param string The name of the template + * + * @return string Template data from the database + */ + function _load($name) + { + if ($template = $this->registry->modules[ISSO_DB_LAYER]->query_first("SELECT * FROM " . $this->tablename . " WHERE " . $this->namecolumn . " = '$name'" . ($this->extrawhere ? ' ' . $this->extrawhere : ''))) + { + return $template[ $this->datacolumn ]; + } + else + { + trigger_error("The template '$name' could not be loaded", E_USER_ERROR); + exit; + } + } + + // ################################################################### + /** + * A wrapper for all the parsing functions and compiling functins + * + * @access protected + * + * @param string Unparsed template data + * + * @return string Parsed template data + */ + function _parse($template) + { + $template = str_replace('"', '\"', $template); + + if (function_exists($this->pre_parse_hook)) + { + $template = call_user_func($this->pre_parse_hook, $template); + } + + $template = $this->_parse_phrases($template); + $template = $this->_parse_conditionals($template); + return $template; + } + + // ################################################################### + /** + * Prepares language and locale information inside templates + * + * @access private + * + * @param string Template data to be processed + * + * @return string Language-ready template data + */ + function _parse_phrases($template) + { + $tag_start = ' + $close_of_open = strpos($phrase_bunch, $tag_start_end); + if ($close_of_open === false) + { + break; + } + + // Extract the opening tag so it can be parsed + $init_tag = substr($phrase_bunch, 0, ($close_of_open + strlen($tag_start_end))); + $init_tag = str_replace($tag_start, '', $init_tag); + $init_tag = substr($init_tag, 0, strlen($init_tag) - 1); + + // Get the args out of the tag + $args = preg_split('#([0-9].*?)=#', $init_tag); + foreach ($args AS $arg) + { + if ($arg AND $arg != ' ') + { + $arg = trim($arg); + $arg = substr($arg, 2); + $arg = substr($arg, 0, strlen($arg) - 2); + $arglist[] = $arg; + } + } + + // Just get the phrase name + $phrase_name = preg_replace('#(.*?)#i', '$2', $phrase_bunch); + + // Wrap the parsed data into the build function + $function_wrap = '" . ' . $this->langconst . '("' . /*str_replace(array('\"', "'"), array('"', "\'"),*/ $phrase_name/*)*/ . '", "' . implode('", "', $arglist) . '") . "'; + + // Replace the fully-parsed string back into the template + $template = substr_replace($template, $function_wrap, $location_start, $location_end + strlen($tag_end) - $location_start); + + unset($arglist); + } + + // Process the empty phrase objects -- do this now so we don't have to worry about it when we're parsing later + $template = preg_replace('#\{@\\\"(.*?)\\\"\}#ise', '$this->_phrase_string(\'$1\')', $template); + + return $template; + } + + // ################################################################### + /** + * Turns a localized phrase tag into a function call + * + * @access private + * + * @param string Phrase text + * + * @return string Function call for phrase text + */ + function _phrase_string($text) + { + return '" . ' . $this->langcall . '(\'' . str_replace(array('\\\"', "'"), array('"', "\'"), $text) . '\') . "'; + } + + // ################################################################### + /** + * Parser for in-line template conditionals + * + * @access private + * + * @param string Template data awaiting processing + * + * @return string Parsed template data + */ + function _parse_conditionals($template) + { + // tag data + $tag_start = ' + $relpos = $tag_full['else'] - $tag_full['posi']; + + // calculate the length of the expression and opening tag + $length = strlen($parsed[0]) + strlen($tag_start) + strlen($tag_start_end); + + // relative to the start of iftrue + $elsepos = $relpos - $length; + + $parsed[1] = substr($conditional, 0, $elsepos); + $parsed[2] = substr($conditional, $elsepos + strlen($tag_else)); + } + // no else to handle + else + { + $parsed[1] = $conditional; + $parsed[2] = ''; + } + #var_dump($parsed); + + // final parsed output + $parsed = '" . ((' . stripslashes($parsed[0]) . ') ? "' . $parsed[1] . '" : "' . $parsed[2] . '") . "'; + + // replace the conditional + $template = str_replace($fullspread, $parsed, $template); + + // reset the parser + $offset = $tag_full['posi'] + strlen($tag_start) + strlen($tag_start_end); + $tag_full = array(); + $stack = array(); + $parsed = array(); + unset($fullspread, $conditional, $temp_end, $relpos, $length, $elsepos); + break; + } + } + } + + return $template; + } +} + +// ################################################################### +/** +* Debugging function used to print characters in a string that are +* around a certain position. +* +* @access private +* +* @param string The haystack string +* @param integer Position to print around +*/ +function print_around($str, $pos) +{ + echo '>>> PA >>>>>>>>['; + echo htmlspecialchars($str[ $pos - 5 ]); + echo htmlspecialchars($str[ $pos - 4 ]); + echo htmlspecialchars($str[ $pos - 3 ]); + echo htmlspecialchars($str[ $pos - 2 ]); + echo htmlspecialchars($str[ $pos - 1 ]); + echo '©'; + echo htmlspecialchars($str[ $pos + 0 ]); + echo htmlspecialchars($str[ $pos + 1 ]); + echo htmlspecialchars($str[ $pos + 2 ]); + echo htmlspecialchars($str[ $pos + 3 ]); + echo htmlspecialchars($str[ $pos + 4 ]); + echo htmlspecialchars($str[ $pos + 5 ]); + echo ']<<<<<<<< PA <<<'; +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file diff --git a/framework/template_fs.php b/framework/template_fs.php new file mode 100755 index 0000000..147241c --- /dev/null +++ b/framework/template_fs.php @@ -0,0 +1,219 @@ +load('template', null); + +/** +* File System Template System +* +* This framework merely replaces the template loading functions with +* file-system based ones. It has an optional caching system in which +* template data will remain stored in the database as long as the filesystem +* file is modified. To do this, pass a table name to setDatabaseCache() and make sure +* there's a DB module that has access to a table with this schema: +* +* CREATE TABLE template (filename VARCHAR (250) NOT NULL, template TEXT NOT NULL, timestamp INT NOT NULL); +* +* @author Blue Static +* @copyright Copyright ©2002 - [#]year[#], Blue Static +* @version $Revision$ +* @package ISSO +* +*/ +class Template_FS extends Template +{ + /** + * The path, from the path of the application, where templates are stored + * @var string + * @access private + */ + var $templatedir = ''; + + /** + * The extension all the template files have + * @var string + * @access private + */ + var $extension = 'tpl'; + + /** + * The database table name for the template cache + * @var string + * @access private + */ + var $dbCacheTable = null; + + // ################################################################### + /** + * Constructor + */ + function __construct(&$registry) + { + $this->registry =& $registry; + } + + // ################################################################### + /** + * (PHP 4) Constructor + */ + function Template_FS(&$registry) + { + $this->__construct($registry); + } + + // ################################################################### + /** + * Sets the template directory + * + * @access public + * + * @param string The template directory + */ + function setTemplateDir($path) + { + $this->templatedir = $this->registry->fetch_sourcepath($path); + } + + // ################################################################### + /** + * Sets the file extension + * + * @access public + * + * @param string File extension + */ + function setExtension($ext) + { + $this->extension = $ext; + } + + // ################################################################### + /** + * Sets the name of the table to access for the datbase cache + * + * @param string DB table name + */ + function setDatabaseCache($table) + { + $this->dbCacheTable = $table; + } + + // ################################################################### + /** + * Takes an array of template names, loads them, and then stores a + * parsed version for optimum speed. + * + * @access public + * + * @param array List of template names to be cached + */ + function cache($namearray) + { + if (sizeof($this->cache) > 0) + { + trigger_error('You cannot cache templates more than once per initialization', E_USER_WARNING); + } + else + { + $dbCache = array(); + if ($this->dbCacheTable) + { + $db =& $this->registry->modules[ISSO_DB_LAYER]; + $cache = $db->query("SELECT * FROM {$this->dbCacheTable} WHERE filename IN ('" . implode("', '", $namearray) . "')"); + while ($tpl = $db->fetch_array($cache)) + { + $time = filemtime($this->registry->apppath . $this->templatedir . $tpl['filename'] . '.' . $this->extension); + $template = $tpl['template']; + if ($time > $tpl['timestamp']) + { + $template = $this->_parse($this->_load($tpl['filename'])); + $db->query("UPDATE {$this->dbCacheTable} SET template = '" . $db->escape_string($template) . "', timestamp = " . TIMENOW . " WHERE filename = '" . $tpl['filename'] . "'"); + $tpl['template'] = $template; + } + $dbCache["$tpl[filename]"] = $template; + } + } + foreach ($namearray AS $name) + { + if (isset($dbCache["$name"])) + { + $template = $dbCache["$name"]; + } + else + { + $template = $this->_parse($this->_load($name)); + if ($this->dbCacheTable) + { + $db->query("INSERT INTO {$this->dbCacheTable} (filename, template, timestamp) VALUES ('$name', '" . $db->escape_string($template) . "', " . TIMENOW . ")"); + } + } + $this->cache["$name"] = $template; + $this->usage["$name"] = 0; + } + } + } + + // ################################################################### + /** + * Loads a template from the file system from the specified + * $templatedir with the file extension $extension + * + * @access private + * + * @param string The name of the template call + */ + function _load($name) + { + $path = $this->registry->apppath . $this->templatedir . $name . '.' . $this->extension; + if (is_file($path)) + { + if (($template = @file_get_contents($path)) !== false) + { + return $template; + } + else + { + trigger_error("Could not load the template '$path'", E_USER_ERROR); + exit; + } + } + else + { + trigger_error("Could not load the template '$path'", E_USER_ERROR); + exit; + } + } +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file diff --git a/framework/xml.php b/framework/xml.php new file mode 100755 index 0000000..6a69bce --- /dev/null +++ b/framework/xml.php @@ -0,0 +1,302 @@ + function) + * @var array() + * @access private + */ + var $taghandler = array(); + + /** + * Current CDATA value + * @var string + * @access private + */ + var $cdata = ''; + + /** + * Tag stack of all open nodes + * @var array + * @access private + */ + var $stack = array(); + + /** + * Node list for all open tag attributes + * @var array + * @access private + */ + var $attribs = array(); + + /** + * Resulting parsed array + * @var array + * @access private + */ + var $result = array(); + + // ################################################################### + /** + * Constructor + */ + function __construct(&$registry) + { + $this->registry =& $registry; + } + + // ################################################################### + /** + * (PHP 4) Constructor + */ + function XML(&$registry) + { + $this->__construct($registry); + } + + // ################################################################### + /** + * Parse an XML file + * + * @access public + * + * @param string XML file data + * @param string Parse file as UTF-8 instead of ISSO-8859-1 + * + * @return array Array with all the XML data parsed + */ + function parse($data, $utf8 = true) + { + $this->stack = array(); + $this->attribs = array(); + $this->result = array(); + $this->cdata = ''; + + if ($utf8) + { + $this->parser = xml_parser_create('UTF-8'); + } + else + { + $this->parser = xml_parser_create('ISO-8859-1'); + } + + // create a new parser + xml_set_object($this->parser, $this); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); + xml_set_element_handler($this->parser, 'handle_tag_start', 'handle_tag_end'); + xml_set_character_data_handler($this->parser, 'handle_cdata'); + + $this->attach_node($this->result); + + // parse the data and check for errors + if (!xml_parse($this->parser, $data)) + { + $error['code'] = xml_get_error_code($this->parser); + $error['string'] = xml_error_string($error['code']); + $error['line'] = xml_get_current_line_number($this->parser); + $error['column'] = xml_get_current_column_number($this->parser); + trigger_error("XML Error: $error[string] at line $error[line] colunn $error[column]", E_USER_ERROR); + exit; + } + + // destroy the parser + xml_parser_free($this->parser); + + // done... send the results back + return $this->result; + } + + // ################################################################### + /** + * Process the opening location of an XML tag + * + * @access private + * + * @param integer XML parser + * @param string Tag name + * @param array Tag attributes + */ + function handle_tag_start(&$parser, $name, $attrs) + { + // we need to keep track of indicies to monitor the last key in $this->attribs + static $index; + + // trim attributes + array_walk($attrs, 'trim'); + + // existing node set + if (isset($this->attribs["$name"])) + { + // node set has < 1 child nodes + if (!isset($this->attribs["$name"][0])) + { + $tmp = $this->attribs["$name"]; + unset($this->attribs["$name"]); + $this->attribs["$name"][0] = $tmp; + } + + // create a new child node + $this->attribs["$name"][ $index["$name"] ] = (array)$attrs; + $this->attach_node($this->attribs["$name"][ $index["$name"] ]); + $index["$name"]++; + } + // node set doesn't exist, so create it + else + { + $this->attribs["$name"] = (array)$attrs; + $this->attach_node($this->attribs["$name"]); + $index["$name"] = 1; + } + } + + // ################################################################### + /** + * Process XML CDATA + * + * @access private + * + * @param integer XML parser + * @param string CDATA from tag + */ + function handle_cdata(&$parser, $data) + { + $this->cdata .= $data; + } + + // ################################################################### + /** + * Process the closing of an XML tag + * + * @access private + * + * @param integer XML parser + * @param string Tag name + */ + function handle_tag_end(&$parser, $name) + { + // attach data to the node + if (($this->cdata = trim($this->cdata)) != '') + { + $this->attribs['value'] = $this->cdata; + } + + $this->cdata = ''; + + // remove the node + $this->detach_node(); + } + + // ################################################################### + /** + * Shifts the node tree + * + * @access private + * + * @param array Node to place into the stack + */ + function attach_node(&$node) + { + // create a new node + $this->stack[ sizeof($this->stack) ] =& $node; + + // new attributes to work with + $this->attribs =& $node; + } + + // ################################################################### + /** + * Unshifts the node tree + * + * @access private + */ + function detach_node() + { + // drop the newest node + unset($this->stack[ sizeof($this->stack) - 1 ]); + + // assign the attributes to the next newest node + $this->attribs =& $this->stack[ sizeof($this->stack) - 1 ]; + } + + // ################################################################### + /** + * Unless a node has multiple children, there will not be a numerical + * index for the child node. So this means that if you have variable + * XML structure with some of the same types of nodes having one children + * or multiple children, you'd have different things to parse. If + * you want the node unified (as in, all single-children would be indexed + * numerically, run this function on the node. It works on references. + * + * @access public + * + * @param array The node to int-index + */ + function unify_node(&$node) + { + if (!isset($node[0])) + { + $node = array($node); + } + } +} + +/*=====================================================================*\ +|| ################################################################### +|| # $HeadURL$ +|| # $Id$ +|| ################################################################### +\*=====================================================================*/ +?> \ No newline at end of file -- 2.22.5