+
+
+ {CKEditorFuncNum}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Toolkit/Common.php b/Toolkit/Common.php
new file mode 100644
index 0000000..d3cec0c
--- /dev/null
+++ b/Toolkit/Common.php
@@ -0,0 +1,1397 @@
+
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @release CVS: $Id: Common.php,v 1.68 2010/08/15 19:31:47 jamie Exp $
+ * @link http://demo.gaslightmedia.com
+ */
+
+/**
+ * Common collection of functions used throughout the GLM Toolkit
+ *
+ * @category Toolkit
+ * @package Common
+ * @author Jamie Kahgee
+ * @copyright 2008 Gaslight Media
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @link http://demo.gaslightmedia.com
+ */
+class Toolkit_Common
+{
+ // {{{ properties
+
+ /**
+ * Who to send email to when a problem occurs on a live site
+ *
+ * This property doesn't matter for the development servers b/c the
+ * script will display the error encountered and terminate immediately.
+ * On the live site, a notification will be displayed to the user that
+ * an unexpected error has occured and will inform them to try again later.
+ * It will then send an email to the admin alerting them of the email.
+ *
+ * @var string
+ * @access protected
+ * @see Toolkit_Common::handleError()
+ */
+ protected static $admin = ERROR_EMAIL_ADDRESS;
+
+ /**
+ * Who to set the mail from when emailing errors
+ *
+ * @var string
+ * @access protected
+ * @see Toolkit_Common::handleError()
+ */
+ protected static $from = 'Gaslight Media Toolkit ';
+
+ // }}}
+
+ // {{{ arrayFlatten()
+
+ /**
+ * Recursively reduces multi-dimensional arrays to single-dimensional arrays
+ *
+ * @param array $array The array to flatten.
+ * @param int $preserveKeys How to handle array keys
+ * 0 = Never
+ * 1 = Strings
+ * 2 = Always
+ * @param array &$newArray The new created from the flattening
+ *
+ * @return array Single dimensional array.
+ * @access public
+ */
+ public function arrayFlatten(
+ $array,
+ $preserveKeys = 1,
+ &$newArray = Array()
+ ) {
+ foreach ($array as $key => $child) {
+ if (is_array($child)) {
+ $newArray =& Toolkit_Common::arrayFlatten(
+ $child,
+ $preserveKeys,
+ $newArray
+ );
+ } elseif ((int) $preserveKeys + (int) is_string($key) > 1) {
+ $newArray[$key] = $child;
+ } else {
+ $newArray[] = $child;
+ }
+ }
+ return $newArray;
+ }
+
+ // }}}
+
+ // {{{ cleanArray()
+
+ /**
+ * removes values from an array where the key ends in '_rmv'
+ *
+ * @param array &$values array to clean
+ *
+ * @return array array w/ unneeded elements removed
+ * @access public
+ * @static
+ */
+ public static function cleanArray(&$values)
+ {
+ // Get rid of any defined un-needed elements.
+ // un-needed elements after the form is submitted are defined
+ // by the ending _rmv name.
+ foreach ($values as $k => &$v) {
+ if (preg_match('/^.+_rmv$/', $k)) {
+ unset($values[$k]);
+ }
+ }
+
+ return $values;
+ }
+
+ // }}}
+ // {{{ createEmailBody()
+
+ /**
+ * Convert the form into an acceptable table to include in email
+ *
+ * This function can be called from any form class to generate
+ * a HTML table that we can use in an email. Elements can be dynamically
+ * rendered to meet your needs if you wish.
+ *
+ * If you wish to have any element(s) rendered differently than what the
+ * form already rendered them as, you need to define a public method
+ * named "emailRenderElement" in the calling class that will accept an
+ * elements name and you can create the rendering template in that class.
+ *
+ * Example:
+ * This example will turn groups like radio buttons or checkboxes
+ * from lists like:
+ * [ ] element_one [x] element_two [x] element_three
+ * ( ) element_one (x) element_two ( ) element_three
+ * into lists like:
+ * [ ] element_one ( ) element_one
+ * [x] element_two (x) element_two
+ * [x] element_three ( ) element_three
+ *
+ * public function &emailRenderElement($e)
+ * {
+ * switch ($e) {
+ * case 'element_one' :
+ * case 'element_two' :
+ * case 'element_three' :
+ * $renderer =& $this->defaultRenderer();
+ * $renderer->clearAllTemplates();
+ * $renderer->setGroupTemplate('{content}', $e);
+ * $renderer->setGroupElementTemplate('{element} ', $e);
+ * break;
+ *
+ * default :
+ * $renderer = null;
+ * break;
+ * }
+ *
+ * return $renderer;
+ * }
+ *
+ *
+ * @param array $newLabels Assoc array of element names and new
+ * labels to be used in the email form.
+ * eg [$newLabels['element'] => 'Label']
+ * @param array $excludeList Any element that needs to be removed
+ * from the form when creating the table
+ * eg [$list = array(e1, e2, e3, etc..)]
+ *
+ * @return mixed The table body for the email.
+ */
+ public function createEmailBody(
+ array $newLabels = array(),
+ array $excludeList = array()
+ ) {
+ $this->freeze();
+ // Get the values corresponding to the elements present in the form.
+ $formData = $this->exportValues();
+ // The array keys holds all the names of the elements.
+ $elements = array_keys($formData);
+ // Remove any unwanted elements from our elements array.
+ foreach ($excludeList as $trgt) {
+ unset($elements[array_search($trgt, $elements)]);
+ }
+
+ // Which row we are at.
+ $i = 0;
+ $table = new HTML_Table(array('class' => 'data'));
+ // Auto grow the table, since the forms will by dynamic in size.
+ $table->setAutoGrow(true);
+ // Get the labels and values of the elements.
+ foreach ($elements as $name) {
+ $e =& $this->getElement($name);
+ // Get the default HTML for each element.
+ $html = $e->toHtml();
+ // If any elements need to have their html
+ // changed for an email, this function in the
+ // class should exist and will return a renderer
+ // object of how the element should be rendered.
+ if (method_exists($this, 'emailRenderElement')) {
+ $renderer =& $this->emailRenderElement($name);
+ // make sure we have an actual rendering object
+ // if the element doesn't need to be altered it should
+ // just return null.
+ if (is_object($renderer)) {
+ $e->accept($renderer);
+ $html = $renderer->toHtml($name);
+ // We have to reset the entire forms html
+ // otherwise we'll just keep adding to it everytime
+ // we render a new element.
+ // This is a bit of a hack to make this work (because
+ // the _html element is supposed to be a private
+ // property)
+ $renderer->_html = null;
+ }
+ }
+ // Get the label for the element.
+ $label = array_key_exists($name, $newLabels) ?
+ $newLabels[$name] :
+ $e->getLabel();
+
+ // Make the row and increment the row counter.
+ $table->setCellContents($i, 0, $label);
+ $table->setCellAttributes($i, 0, array('class' => 'label'));
+ $table->setCellAttributes($i, 1, array('class' => 'field'));
+ $table->setCellContents($i++, 1, $html);
+ }
+ return $table->toHtml();;
+ }
+
+ // }}}
+ // {{{ createSQLInsert()
+
+ /**
+ * Generates a properly formatted sql query to insert data into a table
+ *
+ * @param string $table Name of the table to insert into
+ * @param array $columns Array of column names you want to set in the
+ * insert statement and bind names
+ *
+ *
+ * Toolkit_Common::createSQLInsert('member', array('name', 'pos'));
+ * will create the sql statement
+ * INSERT INTO member (name, pos) VALUES(:name, :pos)
+ *
+ *
+ * @return string Formatted SQL string
+ * @access public
+ * @static
+ */
+ public static function createSQLInsert($table, array $columns)
+ {
+ $params = implode(', ', $columns);
+ $bindParams = ':' . implode(', :', $columns);
+
+ return "INSERT INTO $table ($params) VALUES ($bindParams)";
+ }
+
+ // }}}
+ // {{{ createSQLUpdate()
+
+ /**
+ * Generates a properly formatted sql query to update data in a table
+ *
+ *
+ * Toolkit_Common::createSQLUpdate('member',
+ * array('name', 'pos'),
+ * array('id = :id');
+ * will create the sql statement
+ * UPDATE member SET name = :name, pos = :pos WHERE id = :id
+ *
+ *
+ * @param string $table Name of the table to update
+ * @param array $columns Array of columns names you want to update
+ * @param array $constraints Constraints to apply to the table to prevent
+ * running the update on every row in the db
+ *
+ * @return string formatted query string
+ * @access public
+ * @static
+ */
+ public static function createSQLUpdate(
+ $table,
+ array $columns,
+ array $constraints = null
+ ) {
+ $length = count($columns);
+ for ($i = 0; $i < $length; ++$i) {
+ $bindParams .= "{$columns[$i]} = :{$columns[$i]}";
+ if ($i < ($length - 1)) {
+ $bindParams .= ', ';
+ }
+ }
+ $sql = "UPDATE $table SET $bindParams";
+
+ if (!empty($constraints)) {
+ $sql .= ' WHERE ' . implode(' AND ', $constraints);
+ }
+
+ return $sql;
+ }
+
+ // }}}
+ // {{{ createTables()
+
+ /**
+ * Read file from parameter and use the PDO parameter to create process file
+ *
+ * @param PDO $pdo PHP Data Object to use for DB calls
+ * @param string $file full path of file to parse
+ *
+ * @return void
+ * @access public
+ * @static
+ */
+ public static function createTables(PDO $pdo, $file)
+ {
+ $sql = file_get_contents($file);
+ // break multiple queries apart by ';'
+ $tok = strtok($sql, ';');
+ try {
+ // while we have valid tokens, execute the query
+ // and grab the next token
+ while ($tok !== false) {
+ $pdo->query($tok);
+ $tok = strtok(';');
+ }
+ } catch (PDOException $e) {
+ Toolkit_Common::handleError($e);
+ }
+ }
+
+ // }}}
+
+ // {{{ dieGracefully()
+
+ /**
+ * Gracefully exit from the script when an unrecoverable error is created
+ *
+ * @param string $msg Message to display to user
+ * @param mixed $e Error object
+ * @param boolean $moreInfo More debugging info when in development
+ *
+ * @return void
+ * @access public
+ */
+ public function dieGracefully($msg = null, $e = null, $moreInfo = false)
+ {
+ if (is_null($e)) {
+ if (is_null($msg)) {
+ die('There was an error, please try again later!');
+ } else {
+ die($msg);
+ }
+ } else {
+ echo $msg . ' ';
+ echo 'Error Caught. ';
+ echo 'Error: ' . $e->getMessage() . ' ';
+ if ($moreInfo) {
+ echo 'Code:
' . $e->getCode() . '
';
+ echo 'Debug Info:
' . $e->getDebugInfo() . '
';
+ }
+ }
+ }
+
+ // }}}
+
+ // {{{ errorException()
+
+ /**
+ * Stops script on Exception error
+ *
+ * Stops the script when an Exception is raised inside a
+ * try/catch block.
+ *
+ * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+ * We don't show any error info, but let the user know an unexpected
+ * error has occured and then mail the error info the the admin.
+ *
+ * Example usage:
+ *
+ * try {
+ if ($foo != $bar) {
+ throw new Exception ("Foo Doesn't equal Bar");
+ }
+ * } catch (Exception $e) {
+ * return Toolkit_Common::handleError($e);
+ * }
+ *
+ *
+ * @param Exception $e Exception Object
+ * @param Mail $mail What to use to send mail to admin
+ *
+ * @return false
+ * @access public
+ * @static
+ */
+ public static function errorException(Exception $e, Mail $mail = null)
+ {
+ if (!is_null($mail)) {
+ $subject = 'Exception Error for ' . SITENAME;
+ self::mailError($mail, $subject, $e);
+ } else {
+ echo self::getErrorInfo($e);
+ }
+
+ return false;
+ }
+
+ // }}}
+ // {{{ errorHTMLQuickFormError()
+
+ /**
+ * Handles PEAR Errors for our developers
+ *
+ * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+ * We don't show any error info, but let the user know an unexpected
+ * error has occured and then mail the error info the the admin.
+ *
+ * HTML_QuickForm Example usage:
+ *
+ * $e =& $this->getElement('elementName');
+ * if (PEAR::isError($e)) {
+ * return Toolkit_Common::handleError($e);
+ * }
+ *
+ *
+ * @param HTML_QuickForm_Error $e QuickFormError Object
+ * @param Mail $mail What to use to send mail to admin
+ *
+ * @return false
+ * @access public
+ * @since Method available since Release 1.0.1
+ * @static
+ */
+ public static function errorHTMLQuickFormError(
+ HTML_QuickForm_Error $e,
+ Mail $mail = null
+ ) {
+ if (!is_null($mail)) {
+ $subject = 'PEAR Error for ' . SITENAME;
+ self::mailError($mail, $subject);
+ } else {
+ echo self::getErrorInfo($e);
+ }
+
+ return false;
+ }
+
+ // }}}
+ // {{{ errorPDOException()
+
+ /**
+ * Stops script on database error
+ *
+ * Stops the script when a PDOException is raised inside a
+ * try/catch block.
+ *
+ * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+ * We don't show any error info, but let the user know an unexpected
+ * error has occured and then mail the error info the the admin.
+ *
+ * Example usage:
+ *
+ * try {
+ * $sql = "
+ * SELECT *
+ * FROM table_name
+ * WHERE id = :id";
+ *
+ * $stmt = $this->dbh->prepare($sql);
+ * $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+ * return $stmt->execute();
+ * } catch (PDOException $e) {
+ * return Toolkit_Common::handleError($e);
+ * }
+ *
+ *
+ * @param PDOException $e PDO Error Object.
+ * @param Mail $mail Mail object used to send admin email
+ *
+ * @return false
+ * @access public
+ * @since Method available since Release 1.0.1
+ */
+ public function errorPDOException(PDOException $e, Mail $mail = null)
+ {
+ if (!is_null($mail)) {
+ $subject = 'SQL Error for ' . SITENAME;
+ self::mailError($mail, $subject, $e);
+ } else {
+ echo self::getErrorInfo($e);
+ }
+
+ return false;
+ }
+
+ // }}}
+ // {{{ errorPEARError()
+
+ /**
+ * Handles PEAR Errors for our developers
+ *
+ * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+ * We don't show any error info, but let the user know an unexpected
+ * error has occured and then mail the error info the the admin.
+ *
+ * @param PEAR_Error $e PEARError Object
+ * @param Mail $mail Mail object used to send admin email
+ *
+ * @return false
+ * @access public
+ * @since Method available since Release 1.0.1
+ */
+ public function errorPEARError(PEAR_Error $e, Mail $mail = null)
+ {
+ if (!is_null($mail)) {
+ $subject = 'PEAR Error for ' . SITENAME;
+ self::mailError($mail, $subject);
+ } else {
+ echo self::getErrorInfo($e);
+ }
+
+ return false;
+ }
+
+ // }}}
+ // {{{ errorPEARException()
+
+ /**
+ * Handles PEAR Exception for our developers
+ *
+ * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+ * We don't show any error info, but let the user know an unexpected
+ * error has occured and then mail the error info the the admin.
+ *
+ * @param PEAR_Exception $e PEARException Object
+ * @param Mail $mail Mail object used to send admin email
+ *
+ * @return false
+ * @access public
+ * @since Method available since Release 1.0.1
+ */
+ public function errorPEARException(PEAR_Exception $e, Mail $mail = null)
+ {
+ if (!is_null($mail)) {
+ $subject = 'SQL Error for ' . SITENAME;
+ self::mailError($mail, $subject, $e);
+ } else {
+ echo self::getErrorInfo($e);
+ }
+
+ return false;
+ }
+
+ // }}}
+ // {{{ errorRuntimeException()
+
+ /**
+ * Stops script on runtime error
+ *
+ * Stops the script when a runtimeException is raised inside a
+ * try/catch block.
+ *
+ * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+ * We don't show any error info, but let the user know an unexpected
+ * error has occured and then mail the error info the the admin.
+ *
+ * @param RuntimeException $e PDO Error Object.
+ * @param Mail $mail Mail object used to send admin email
+ *
+ * @return false
+ * @access public
+ * @since Method available since Release 1.0.2
+ */
+ public function errorRuntimeException(
+ RuntimeException $e,
+ Mail $mail = null
+ ) {
+ if (!is_null($mail)) {
+ $subject = 'Runtime Exception for ' . SITENAME;
+ self::mailError($mail, $subject, $e);
+ } else {
+ echo self::getErrorInfo($e);
+ }
+
+ return false;
+ }
+
+ // }}}
+ // {{{ errorBadMethodCallException()
+
+ /**
+ * Stops script on bad method call error
+ *
+ * Stops the script when a badMethodCallException is raised inside a
+ * try/catch block.
+ *
+ * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+ * We don't show any error info, but let the user know an unexpected
+ * error has occured and then mail the error info the the admin.
+ *
+ * @param BadMethodCallException $e PDO Error Object.
+ * @param Mail $mail Mail object used to send admin email
+ *
+ * @return false
+ * @access public
+ * @since Method available since Release 1.0.3
+ */
+ public function errorBadMethodCallException(
+ BadMethodCallException $e,
+ Mail $mail = null
+ ) {
+ if (!is_null($mail)) {
+ $subject = 'Bad Method Call Exception for ' . SITENAME;
+ self::mailError($mail, $subject, $e);
+ } else {
+ echo self::getErrorInfo($e);
+ }
+
+ return false;
+ }
+
+ // }}}
+
+ // {{{ filterURI()
+
+ /**
+ * Filters uri's before they are validated
+ *
+ * @param string $uri URI to filter
+ *
+ * @return mixed new uri if missing scheme
+ * @access public
+ * @static
+ */
+ public static function filterURI($uri)
+ {
+ $validProtocol = '/^https?\:\/\/.*/';
+ $invalidProtocol = '/^.*?\:\/\/.*/';
+ if (empty($uri)) {
+ // Empty field, just return
+ return $uri;
+ } elseif (preg_match($validProtocol, $uri)) {
+ // has valid protocol, return unchanged
+ // should pass validation.
+ return $uri;
+ } elseif (!preg_match($invalidProtocol, $uri)) {
+ // missing protocol, prepend default
+ // http:// protocol and return.
+ return "http://$uri";
+ } else {
+ // has invalid protocol, return unchanged
+ // validation should catch this and throw error.
+ return $uri;
+ }
+ }
+
+ // }}}
+ // {{{ filterPhone()
+
+ /**
+ * Filters phone numbers before they are validated
+ *
+ * @param string $phone number to filter
+ *
+ * @return mixed newly formatted phone number
+ * @access public
+ * @static
+ */
+ public static function filterPhone($phone)
+ {
+ // Ditch anything that is not a number
+ $number = preg_replace('/[^0-9]/', '', $phone);
+
+ // Invalid Number, validation will catch error
+ $len = strlen($number);
+ if (($len < 10) || ($len > 11)) {
+ return $phone;
+ }
+
+ // subscriber number
+ $sn = substr($number, -4);
+ // city code
+ $cc = substr($number, -7, 3);
+ // area code
+ $ac = substr($number, -10, 3);
+ if ($len == 11) {
+ // country prefix
+ $cp = $number[0];
+ }
+
+ $filteredNumber = "($ac) $cc-$sn";
+ if (!is_null($cp)) {
+ $filteredNumber = "$cp $filteredNumber";
+ }
+
+ return $filteredNumber;
+ }
+
+ // }}}
+
+ // {{{ getCities()
+
+ /**
+ * Get an array of cities from the database
+ *
+ * @param PDO $dbh Database handler
+ * @param integer $state State id the city is in
+ * @param integer $county County id the city is in
+ * @param integer $region Region id the city is in
+ *
+ * @return array states
+ * @access public
+ * @static
+ */
+ public static function getCities(
+ PDO $dbh,
+ $state = null,
+ $county = null,
+ $region = null
+ ) {
+ $param = array();
+ if (ctype_digit((string)$state)) {
+ $param[] = 'state_id = ' . $dbh->quote($state);
+ }
+ if (ctype_digit((string)$county)) {
+ $param[] = 'county_id = ' . $dbh->quote($county);
+ }
+ if (ctype_digit((string)$region)) {
+ $param[] = 'region_id = ' . $dbh->quote($region);
+ }
+
+ try {
+ $sql = "
+ SELECT *
+ FROM city";
+
+ if (!empty($param)) {
+ $sql .= ' WHERE ' . implode(' AND ', $param);
+ }
+ $sql .= ' ORDER BY city_name';
+
+ $stmt = $dbh->prepare($sql);
+ $stmt->execute();
+
+ $cities = array();
+ while ($row = $stmt->fetch()) {
+ $cities[$row['city_id']] = $row['city_name'];
+ }
+
+ return $cities;
+ } catch (PDOException $e) {
+ return Toolkit_Common::handleError($e);
+ }
+ }
+
+ // }}}
+ // {{{ getErrorInfo()
+
+ /**
+ * extract error info from error object
+ *
+ * @param mixed $obj Error object to get info for
+ *
+ * @return string formatted error information
+ * @access public
+ */
+ public function getErrorInfo($obj)
+ {
+ $state = '' . get_class($obj) . ' error: ';
+
+ $state .= ' $_SERVER[\'QUERY_STRING\']: ' . $_SERVER['QUERY_STRING']
+ . "\n";
+ $state .= ' $_SERVER[\'HTTP_REFERER\']: ' . $_SERVER['HTTP_REFERER']
+ . "\n";
+ $state .= ' $_SERVER[\'REDIRECT_QUERY_STRING\']: '
+ . $_SERVER['REDIRECT_QUERY_STRING'] . "\n";
+ $state .= ' $_SERVER[\'REQUEST_URI\']: ' . $_SERVER['REQUEST_URI']
+ . "\n";
+
+ $state .= ' $_GET:
' . print_r($_GET, true) . '
';
+ $state .= ' $_POST:
' . print_r($_POST, true) . '
';
+ $state .= ' $_SESSION:
' . print_r($_SESSION, true) . '
';
+
+ if (method_exists($obj, 'getMessage')) {
+ $state .= $obj->getMessage();
+ if (method_exists($obj, 'getDebugInfo')) {
+ $state .= '; ' . $obj->getDebugInfo();
+ }
+ }
+ if (method_exists($obj, 'getFile')) {
+ $state .= ' in ' . $obj->getFile() . '';
+ }
+ if (method_exists($obj, 'getLine')) {
+ $state .= ' on line ' . $obj->getLine() . '';
+ }
+ if (method_exists($obj, 'getBacktrace')) {
+ $backtrace = print_r($obj->getBacktrace(), true);
+
+ $state .= "
$backtrace
";
+ }
+
+ return $state;
+ }
+
+ // }}}
+ // {{{ getHierarchicalTreeStructure()
+
+ /**
+ * Create a hierarchical tree stored in an linear array
+ *
+ * Produces a representation of a hierarchical tree structure into a
+ * linear array so you can iterate straight through to get the tree
+ * structure.
+ *
+ * @param PDO $pdo Database handler
+ * @param string $table Name of the source relation
+ * @param string $key Name of the key field
+ * @param string $parent Name of the parent-key field
+ * @param string $order Name of the field to order siblings by
+ * @param integer $start Key value of the row to start at
+ * @param integer $maxDepth Maximum depth to descend to, or zero
+ * for unlimited depth
+ * @param boolean $validParent exclude branches that have null
+ * parent values
+ *
+ * @return array Linear array of tree structure
+ * @access public
+ * @see http://www.postgresql.org/doc/8.3/interactive/tablefunc.html#AEN104085
+ */
+ public function getHierarchicalTreeStructure(
+ PDO $pdo,
+ $table = 'pages',
+ $key = 'id',
+ $parent = 'parent',
+ $order = 'pos',
+ $start = 0,
+ $maxDepth = 0,
+ $validParent = true
+ ) {
+ try {
+ $tree = array();
+
+ $sql = "
+ SELECT *
+ FROM connectby('{$table}', '{$key}', '{$parent}',
+ '{$order}', '{$start}', {$maxDepth})
+ as t(id text, parent text, level int, pos int)";
+ if ($validParent) {
+ $sql .= " WHERE parent is not null";
+ }
+ foreach ($pdo->query($sql) as $row) {
+ $tree[$row['id']] = $row['level'];
+ }
+
+ return $tree;
+ } catch (PDOException $e) {
+ return self::handleError($e);
+ }
+ }
+
+ // }}}
+ // {{{ getScripts()
+
+ /**
+ * Gets all scripts for the page
+ *
+ * adds version number to url for all local (non-app.glm) urls
+ * so we can use .htaccess cachebusting
+ *
+ * combines script by server id, so we can decrease http requests to fetch
+ * the needed scripts
+ *
+ * @param array $scripts The array of js scripts for the page
+ *
+ * @return string HTML markup for scripts
+ * @access public
+ * @static
+ */
+ public static function getScripts(array $scripts)
+ {
+ if (!is_array($scripts) || empty($scripts)) {
+ return false;
+ }
+
+ $uniqueScripts = array_unique($scripts);
+ // Get the main jquery file
+ $jquery = JQUERY_CDN_JS;
+ $key = array_search($jquery, $uniqueScripts);
+ // If it exists, remove it from its current location
+ // and put at front of array.
+ if ($key !== false) {
+ unset($uniqueScripts[$key]);
+ array_unshift($uniqueScripts, $jquery);
+ }
+ $format = '';
+
+ $ret = '';
+ $baseUrlStrLen = strlen(BASE_URL);
+ $appUrlStrLen = strlen(GLM_APP_BASE_URL);
+
+ // Use versioning with local scripts for cachebusting
+ $localPath = BASE_URL . 'v/' . VERSION . '/javascript/';
+ $appPath = GLM_APP_BASE_URL . 'javascript/';
+
+ $localScripts = array();
+ $appScripts = array();
+ foreach ($uniqueScripts as $origScript) {
+ //if ($_ENV['GLM_HOST_ID'] == "PRODUCTION") {
+ // if (@substr_compare($origScript, BASE_URL, 0, $baseUrlStrLen) === 0) {
+ // local site scripts
+ // if (substr($origScript, '-3') == '.js') {
+ // $localScripts[] = substr($origScript, strlen(BASE_URL));
+ // } else {
+ // scripts written in other language than JS (ie: php)
+ // $ret .= sprintf($format, $origScript);
+ // }
+ // } elseif (@substr_compare($origScript, GLM_APP_BASE_URL, 0, $appUrlStrLen) === 0) {
+ // app.glm scripts
+ // $pieces = explode('/', $origScript);
+ // switch (end($pieces)) {
+ // case 'ckeditor.js' :
+ // case 'jquery.tree.js' :
+ // $ret .= sprintf($format, $origScript) . "\n";
+ // break;
+
+ // default :
+ // $appScripts[] = substr($origScript, strlen(GLM_APP_BASE_URL));
+ // break;
+ // }
+ // } else {
+ // // other server scripts ie: (google.com)
+ // $ret .= sprintf($format, $origScript) . "\n";
+ // }
+ //} else {
+ $ret .= sprintf($format, $origScript) . "\n";
+ //}
+ }
+
+ if (!empty($appScripts)) {
+ $appPath = $appPath . implode(',', $appScripts);
+ $ret .= sprintf($format, $appPath) . "\n";
+ }
+
+ if (!empty($localScripts)) {
+ $localPath = $localPath . implode(',', $localScripts);
+ $ret .= sprintf($format, $localPath) . "\n";
+ }
+
+ return $ret;
+ }
+
+ // }}}
+ // {{{ getStates()
+
+
+ /**
+ * Get an array of states from the database
+ *
+ * @param PDO $dbh Database handler
+ * @param boolean $unionStatesOnly If we want to only retrieve
+ * the 50 US states
+ *
+ * @return array states
+ * @access public
+ * @static
+ */
+ public static function getStates(PDO $dbh, $unionStatesOnly = false)
+ {
+ if ($unionStatesOnly) {
+ // Just grab the 50 states of the union
+ $where = "WHERE us_state = :bool";
+ }
+
+ try {
+ $sql = "
+ SELECT *
+ FROM state
+ $where
+ ORDER BY state_name";
+
+ $stmt = $dbh->prepare($sql);
+ if ($unionStatesOnly) {
+ $stmt->bindValue(':bool', 1, PDO::PARAM_BOOL);
+ }
+ $stmt->execute();
+
+ $states = array();
+ while ($row = $stmt->fetch()) {
+ $states[$row['state_id']] = $row['state_name'];
+ }
+
+ return $states;
+ } catch (PDOException $e) {
+ return Toolkit_Common::handleError($e);
+ }
+ }
+
+ // }}}
+ // {{{ getStyleSheets()
+
+ /**
+ * Gets all style sheets for the page
+ *
+ * adds version number to url for all local (non-app.glm) urls
+ * so we can use .htaccess cachebusting
+ *
+ * combines style sheets by server id, so we can decrease http
+ * requests to fetch the needed style sheets
+ *
+ * @return string HTML markup for stylesheets
+ * @access public
+ * @static
+ */
+ public static function getStyleSheets()
+ {
+ if ( !isset($GLOBALS['styleSheets'])
+ || !is_array($GLOBALS['styleSheets'])
+ || empty($GLOBALS['styleSheets'])
+ ) {
+ return false;
+ }
+
+ $uniqueStyleSheets = array_unique($GLOBALS['styleSheets']);
+ $format = '';
+
+ $baseUrlStrLen = strlen(BASE_URL);
+ $appUrlStrLen = strlen(GLM_APP_BASE_URL);
+ $ret = '';
+
+ $localPath = BASE_URL . 'v/' . VERSION . '/css/';
+ $appPath = GLM_APP_BASE_URL . 'css/';
+
+ $localStyleSheets = array();
+ $appStyleSheets = array();
+ foreach ($uniqueStyleSheets as $origStyleSheet) {
+ //if ($_ENV['GLM_HOST_ID'] == "PRODUCTION") {
+ // if (strstr($origStyleSheet, '/themes/')) {
+ // $ret .= sprintf($format, $origStyleSheet) . "\n";
+ // } elseif (@substr_compare($origStyleSheet, BASE_URL, 0, $baseUrlStrLen) === 0) {
+ // // local site style sheets
+ // $localStyleSheets[] = substr($origStyleSheet, strlen(BASE_URL));
+ // } elseif (@substr_compare($origStyleSheet, GLM_APP_BASE_URL, 0, $appUrlStrLen) === 0) {
+ // // app.glm style sheets
+ // $appStyleSheets[] = substr($origStyleSheet, strlen(GLM_APP_BASE_URL));
+ // } else {
+ // // other server style sheets ie: (google.com)
+ // $ret .= sprintf($format, $origStyleSheet) . "\n";
+ // }
+ //} else {
+ $ret .= sprintf($format, $origStyleSheet) . "\n";
+ //}
+ }
+
+ if (!empty($appStyleSheets)) {
+ $appPath = $appPath . implode(',', $appStyleSheets);
+ $ret .= sprintf($format, $appPath) . "\n";
+ }
+ if (!empty($localStyleSheets)) {
+ $localPath = $localPath . implode(',', $localStyleSheets);
+ $ret .= sprintf($format, $localPath) . "\n";
+ }
+
+ return $ret;
+ }
+
+ // }}}
+ // {{{ getTableMetaData()
+
+ /**
+ * Gets the meta data of the calling classes table columns
+ *
+ * The table used when retrieving the meta data is defined
+ * in the class property $tableName. The class or parent class
+ * must also have a $tableMetaData property
+ *
+ * @param PDO $pdo Database Handler
+ * @param string $tableName The name of the table to get the meta data for.
+ * @param array $clauses Only retrieve meta data for certain column types
+ *
+ * @return array metadata for table
+ * @access protected
+ */
+ public function getTableMetaData(
+ PDO $pdo,
+ $tableName,
+ array $clauses = null
+ ) {
+ if (is_array($clauses)) {
+ while ($c = current($clauses)) {
+ $ands .= " data_type = '{$c}'";
+ if (false !== next($clauses)) {
+ $ands .= " OR ";
+ }
+ }
+ $ands = " AND ($ands)";
+ }
+ try {
+ $sql = "
+ SELECT column_name, data_type
+ FROM information_schema.columns
+ WHERE table_name = :tname
+ $ands";
+ $stmt = $pdo->prepare($sql);
+ $stmt->bindParam(':tname', $tableName, PDO::PARAM_STR);
+
+ $stmt->execute();
+
+ $metaData = array();
+ while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+ $metaData[$row['column_name']] = $row['data_type'];
+ }
+
+ return $metaData;
+ } catch (PDOException $e) {
+ return self::handleError($e);
+ }
+ }
+
+ // }}}
+
+ // {{{ handleError()
+
+ /**
+ * Handles various script error
+ *
+ * @param Object $e Error Object
+ * @param boolean $developmentServer If we are on a development server
+ *
+ * @return mixed String, false, void
+ * @access public
+ * @static
+ */
+ public static function handleError($e, $developmentServer = DEVELOPMENT)
+ {
+ $errorType = str_replace('_', '', get_class($e));
+ $errorType = "error$errorType";
+
+ if (method_exists(__CLASS__, $errorType)) {
+ if (!$developmentServer) {
+ // Tell the user we encountered an Error.
+ if (file_exists(BASE . '404.html')) {
+ include_once BASE . "404.html";
+ }
+
+ $mail = Mail::factory('mail');
+ self::$errorType($e, $mail);
+ exit();
+ } else {
+ return self::$errorType($e, $mail);
+ }
+ } else {
+ echo '
The system has encountered an un-recoverable error!
';
+ }
+ }
+
+ // }}}
+
+ // {{{ mailError()
+
+ /**
+ * Mails the error to the site admin
+ *
+ * @param Mail $mail Mail object to use
+ * @param string $subject Subject of email
+ * @param Exception $e exception
+ *
+ * @return void
+ * @access public
+ * @static
+ */
+ public static function mailError(
+ Mail $mail,
+ $subject,
+ Exception $e = null
+ ) {
+ $msg = '
' . self::getErrorInfo($e) . '
';
+
+ $htmlMsg = "$msg";
+ $crlf = "\n";
+ $mimeMail = new Mail_mime($crlf);
+ $mimeMail->setFrom(self::$from);
+ $mimeMail->setSubject($subject);
+ $mimeMail->setHTMLBody($htmlMsg);
+ $mimeMail->setTXTBody($msg);
+
+ $body = $mimeMail->get();
+ $headers = $mimeMail->headers();
+
+ $mail->send(self::$admin, $headers, $body);
+ }
+
+ // }}}
+ // {{{ multiDimArrayLocate()
+
+ /**
+ * Locates an array value in a multi-dimensional array
+ *
+ * @param array $array The Array which holds the value you need.
+ * @param mixed $text The value that you are looking for.
+ *
+ * @return mixed The search result.
+ */
+ public function multiDimArrayLocate($array, $text)
+ {
+ if (!is_array($array)) {
+ return;
+ }
+ $arrayResult = array();
+ foreach ($array as $k => $v) {
+ if (is_array($v)) {
+ $temp[$k] = self::multiDimArrayLocate($v, $text);
+ if ($temp[$k]) {
+ $arrayResult[$k] = $temp[$k];
+ }
+ } else {
+ if ($v == $text) {
+ $arrayResult[$k] = $v;
+ }
+ }
+ }
+ return $arrayResult;
+ }
+
+ // }}}
+
+ // {{{ processQuery()
+ //The type hint of the argument is incorrect for the type
+ //definition of the @param tag with argument $dbh in processQuery()
+ /**
+ * Performs the sql insert statement
+ *
+ * If using a Postgresql driver for the PDO, metadata is used when
+ * binding parameters to the prepared statement.
+ *
+ * @param PDO $dbh Database handler object
+ * @param string $tableName table used in query
+ * @param string $sql sql query string
+ * @param array $values associative array of key/value pairs that will
+ * be used to bind to the sql query string
+ *
+ * @return boolean result of the execute statement on the database
+ * @access public
+ * @static
+ */
+ public static function processQuery(
+ PDO $dbh,
+ $tableName,
+ $sql,
+ array $values = null
+ ) {
+ $dbType = $dbh->getAttribute(PDO::ATTR_DRIVER_NAME);
+ if ($dbType == 'pgsql') {
+ $md = Toolkit_Common::getTableMetaData($dbh, $tableName);
+ }
+ $stmt = $dbh->prepare($sql);
+ foreach ($values as $k => &$v) {
+ if ($dbType == 'pgsql') {
+ $metaData = $md[$k];
+ if ($metaData == 'integer') {
+ $dataType = PDO::PARAM_INT;
+ } elseif ($metaData == 'boolean') {
+ $dataType = PDO::PARAM_BOOL;
+ } else {
+ $dataType = PDO::PARAM_STR;
+ }
+ // for empty values that are not actually a zero (0), we
+ // want to insert null's.
+ // as empty values '', are not considered unique
+ if ( empty($v)
+ && $dataType !== PDO::PARAM_BOOL
+ && $dataType !== PDO::PARAM_INT
+ ) {
+ $v = null;
+ $dataType = PDO::PARAM_NULL;
+ }
+ $stmt->bindParam(":$k", $v, $dataType);
+ } else {
+ $stmt->bindParam(":$k", $v);
+ }
+ }
+
+ return $stmt->execute();
+ }
+
+ // }}}
+ // {{{ prepareQuery()
+
+ /**
+ * Prepares the sql statement
+ *
+ * If using a Postgresql driver for the PDO, metadata is used when
+ * binding parameters to the prepared statement.
+ *
+ * @param PDO &$dbh Database handler object
+ * @param string $tableName table used in query
+ * @param string $sql sql query string
+ * @param array $values associative array of key/value pairs that will
+ * be used to bind to the sql query string
+ *
+ * @return PDOStatement if successfully prepares statement
+ * @access public
+ * @static
+ */
+ public static function prepareQuery(
+ PDO &$dbh,
+ $tableName,
+ $sql,
+ array $values = null
+ ) {
+ $dbType = $dbh->getAttribute(PDO::ATTR_DRIVER_NAME);
+ if ($dbType == 'pgsql') {
+ $md = Toolkit_Common::getTableMetaData($dbh, $tableName);
+ }
+ $stmt = $dbh->prepare($sql);
+ foreach ($values as $k => &$v) {
+ if ($dbType == 'pgsql') {
+ $metaData = $md[$k];
+ if ($metaData == 'integer') {
+ $dataType = PDO::PARAM_INT;
+ } elseif ($metaData == 'boolean') {
+ $dataType = PDO::PARAM_BOOL;
+ } else {
+ $dataType = PDO::PARAM_STR;
+ }
+ // for empty values that are not actually a zero (0), we
+ // want to insert null's.
+ // as empty values '', are not considered unique
+ if ( empty($v)
+ && $dataType !== PDO::PARAM_BOOL
+ && $dataType !== PDO::PARAM_INT
+ ) {
+ $v = null;
+ $dataType = PDO::PARAM_NULL;
+ } elseif ($v === '' && $dataType === PDO::PARAM_INT) {
+ $v = null;
+ $dataType = PDO::PARAM_NULL;
+ }
+
+ $stmt->bindParam(":$k", $v, $dataType);
+ } else {
+ $stmt->bindParam(":$k", $v);
+ }
+ }
+
+ return $stmt;
+ }
+
+ // }}}
+
+ // {{{ recursiveArraySearch()
+
+ /**
+ * Recursive multi-dimensional array search
+ *
+ * @param string $n Needle
+ * @param array $h Haystack
+ *
+ * @return mixed search result - false if not found
+ * @access public
+ * @static
+ */
+ public static function recursiveArraySearch($n, array $h)
+ {
+ foreach ($h as $i => $j) {
+ $curr = $i;
+ if ( $n === $j
+ || (is_array($j) && self::recursiveArraySearch($n, $j) !== false)
+ ) {
+ return $curr;
+ }
+ }
+
+ return false;
+ }
+
+ // }}}
+
+ // {{{ show()
+
+ /**
+ * Renders the calling class to the screen
+ *
+ * @access public
+ * @return string calls the toHTML function of calling class
+ */
+ public function show()
+ {
+ echo $this->toHTML();
+ }
+
+ // }}}
+}
diff --git a/Toolkit/Contacts/Admin/EditContact.php b/Toolkit/Contacts/Admin/EditContact.php
new file mode 100644
index 0000000..9a1ea20
--- /dev/null
+++ b/Toolkit/Contacts/Admin/EditContact.php
@@ -0,0 +1,1040 @@
+
+ * @copyright 2010 Gaslight Media
+ * @license Gaslight Media
+ * @version CVS: $Id: EditContact.php,v 1.3 2010/05/13 20:18:38 matrix Exp $
+ * @link http://pear.php.net/package/Contacts
+ * @see References to other sections (if any)...
+ */
+/**
+ * Error codes for Toolkit_Contacts_ContactUs
+ *
+ * Codes are mapped to textual messaged by errorMessage() method,
+ * if you add a new code be sure to add a new message for it to errorMessage()
+ *
+ * @see Toolkit_Contacts_ContactUs::errorMessage()
+ */
+define('FORM_OK', 1);
+
+/**
+ * Description for define
+ */
+define('FORM_ERROR', -1);
+
+/**
+ * Description for define
+ */
+define('NO_RECORD', -2);
+
+/**
+ * Description for define
+ */
+define('INVALID_DB', -3);
+
+/**
+ * Description for define
+ */
+define('MISSING_CONSTANT', -4);
+
+/**
+ * Description for define
+ */
+define('MISSING_CONTACT_TYPE', -5);
+
+/**
+ * Toolkit_Contacts_Admin_EditContact
+ *
+ * Edit Process class to insert or update a contact
+ * can be used with customer table with little modification
+ *
+ * @category Toolkit
+ * @package Contacts
+ * @author Steve Sutton
+ * @copyright 2010 Steve Sutton
+ * @license Gaslight Media
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Contacts
+ * @see References to other sections (if any)...
+ */
+class Toolkit_Contacts_Admin_EditContact
+ extends Toolkit_FormBuilder implements Toolkit_Form
+{
+ // {{{ properties
+
+ /**
+ * Table in Database which holds the contact data
+ *
+ * @var string
+ * @access public
+ */
+ public $tableName = 'contact';
+
+ /**
+ * Primary Key for the table
+ *
+ * @var string
+ * @access public
+ */
+ public $primaryKey = 'id';
+
+ /**
+ * Sequence for the table
+ *
+ * @var string
+ * @access public
+ */
+ public $sequence = 'contact_id_seq';
+
+ /**
+ * Table meta data
+ *
+ * This is used when inserting/updating data for the records
+ * so the PDO's can use explicit data types for the parameters.
+ *
+ * @var array
+ * @access public
+ */
+ public $tableMetaData = array();
+
+ /**
+ * The interests from the contact db
+ *
+ * @var array
+ * @access protected
+ */
+ protected $inquiries = array();
+
+ /**
+ * What do you want the error msg to be if the form doesn't validate
+ *
+ * @var string
+ * @access protected
+ */
+ protected $errorMsg
+ = '
+ Warning: The form was not sent, please review the errors below.
+
';
+
+ /**
+ * What do you want the success msg to be if the form validates successfully
+ *
+ * @var string
+ * @access protected
+ */
+ protected $successMsg
+ = '
+ The information below has been successfully submitted.
+
';
+
+ /**
+ * Include a captcha on the form or not
+ *
+ * @var boolean
+ * @access protected
+ */
+ protected $useCaptcha = false;
+
+ /**
+ * The default templates to inject into the form renderer
+ *
+ * @var string
+ * @access protected
+ */
+ protected $template;
+
+ /**
+ * The default rules to register for validating
+ *
+ * We have to register these rules, or any others we want, before
+ * we are able to use them in our forms.
+ *
+ * These rules can be removed in subclasses before the rules are configured
+ * if you want to omit any of them from validating input - just remember
+ * to not call them in your configured rules!
+ *
+ * Phone: validates input against US and CA style phone #'s
+ *
+ * $rules[] = array('element' => 'phone',
+ * 'message' => 'ERROR: Invalid Phone Format!',
+ * 'type' => 'phone',
+ * 'format' => null,
+ * 'validation' => $this->validationType,
+ * 'reset' => true,
+ * 'force' => false);
+ *
+ *
+ * Zip: Validates input against US and CA zip codes, if DB check is
+ * set to true, validate zip codes against all the zip codes in the
+ * DB.
+ *
+ * $rules[] = array('element' => 'zip',
+ * 'message' => 'ERROR: Invalid Zip!',
+ * 'type' => 'zip',
+ * 'format' => array('requireDBCheck' => true),
+ * 'validation' => $this->validationType,
+ * 'reset' => true,
+ * 'force' => false);
+ *
+ *
+ * Banwords: Make sure each each doesn't contain a banned word. Checking
+ * against a DB of banned words.
+ *
+ * State: Validate input against US and CA region / province codes. If DB
+ * check is set to true, validate region / province against all the
+ * regions / provinces in the DB.
+ *
+ * $rules[] = array('element' => 'state_id',
+ * 'message' => 'ERROR: Invalid State / Province!',
+ * 'type' => 'state',
+ * 'format' => array('requireDBCheck' => true),
+ * 'validation' => $this->validationType,
+ * 'reset' => true,
+ * 'force' => false);
+ *
+ *
+ * @var array
+ * @access protected
+ * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Zip.php
+ * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Phone.php
+ * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Banwords.php
+ * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/State.php
+ */
+ protected $registeredRules = array('phone', 'zip', 'state');
+
+ // }}}
+ // {{{ __construct()
+
+ /**
+ * Class constructor
+ *
+ * @param object $pdo PHP Data Object
+ * @param string $formName Form's name.
+ * @param string $method (optional)Form's method defaults to 'POST'
+ * @param string $action (optional)Form's action
+ * @param string $target (optional)Form's target defaults to '_self'
+ * @param mixed $attributes (optional)Extra attributes for
';
+ }
+ return true;
+ }
+ return false;
+ }
+
+
+ // }}}
+}
+?>
diff --git a/Toolkit/Contacts/VisitorGuide.php b/Toolkit/Contacts/VisitorGuide.php
new file mode 100755
index 0000000..1f0009d
--- /dev/null
+++ b/Toolkit/Contacts/VisitorGuide.php
@@ -0,0 +1,308 @@
+
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @version CVS: $Id: ContactUs.php,v 1.36 2010/07/14 23:33:15 jamie Exp $
+ * @link http://demo.gaslightmedia.com
+ */
+
+/**
+ * Error codes for Toolkit_Contacts_ContactUs
+ *
+ * Codes are mapped to textual messaged by errorMessage() method,
+ * if you add a new code be sure to add a new message for it to errorMessage()
+ *
+ * @see Toolkit_Contacts_ContactUs::errorMessage()
+ */
+
+/**
+ * GLM Contact Us form
+ *
+ * This form handles rendering and processing the contact us form.
+ * Controls the email functionality of the form, whether the client
+ * has a contact DB to store contact records and how to insert/update
+ * submitted form values.
+ *
+ * @category Toolkit
+ * @package Contacts
+ * @author Jamie Kahgee
+ * @copyright 2009 Jamie Kahgee
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @link http://demo.gaslightmedia.com
+ * @see Toolkit_FormBuilder
+ */
+class Toolkit_Contacts_VisitorGuide
+ extends Toolkit_Contacts_ContactUs
+{
+ /**
+ * Contact type to be inserted into the DB as when the form is submitted
+ *
+ * This property is only valid when the [hasContactDB] property is set
+ * to true.
+ *
+ * N.B.
+ * If you subclass this class out to other forms that are
+ * inserted into the contact db, be sure to make each one of their
+ * contactType properties unique. We don't check for duplicates.
+ *
+ * @var string
+ * @access protected
+ */
+ protected $contactType = '4';
+ /**
+ * Message to display if the form is successfully submitted
+ *
+ * @var string
+ * @access protected
+ */
+ protected $successMsg = '
+
+
+ Thank you for requesting the Visitors guide.
+
';
+ /**
+ * Email subject and
header in email
+ *
+ * It gets set in the constructor if you leave empty here, but you
+ * can set it to something different here to override that if you desire.
+ *
+ * @var string
+ * @access protected
+ */
+ public $subject = "Visitor Guide Form from website";
+
+ /**
+ * Class constructor
+ *
+ * @param object $pdo PHP Data Object
+ * @param string $formName Form's name.
+ * @param string $method (optional)Form's method defaults to 'POST'
+ * @param string $action (optional)Form's action
+ * @param string $target (optional)Form's target defaults to '_self'
+ * @param mixed $attributes (optional)Extra attributes for
tag
+ * @param bool $trackSubmit (optional)Whether to track if the form was
+ * submitted by adding a special hidden field
+ *
+ * @author Jamie Kahgee
+ * @access public
+ * @link http://pear.php.net/package/HTML_QuickForm/docs/latest/HTML_QuickForm/HTML_QuickForm.html
+ * @see HTML_QuickForm
+ */
+ public function __construct(
+ PDO $pdo,
+ $formName,
+ $method = 'post',
+ $action = '',
+ $target = '',
+ $attributes = null,
+ $trackSubmit = false
+ ) {
+ parent::__construct(
+ $pdo,
+ $formName,
+ $method,
+ $action,
+ $target,
+ $attributes,
+ $trackSubmit
+ );
+ $this->email = VISITOR_GUIDE_EMAIL;
+ }
+ // {{{ configureConstants()
+
+ /**
+ * Constant variables for the form
+ *
+ * These values won't get overridden by POST or GET vars
+ *
+ * @return void
+ * @access public
+ */
+ public function configureConstants()
+ {
+ $constants = array(
+ 'user_agent' => $_SERVER['HTTP_USER_AGENT'],
+ 'remote_addr' => $_SERVER['REMOTE_ADDR']
+ );
+ $this->setupConstants($constants);
+ }
+
+ // }}}
+ // {{{ configureElements()
+
+ /**
+ * Form element definitions
+ *
+ * @return void
+ * @access public
+ */
+ public function configureElements()
+ {
+ $e = array();
+ if ($this->hasContactDB) {
+ $this->setInterestFields();
+ // Grouped Elements are defined here.
+ $this->interestsGroups =& $this->getInterestFields();
+ }
+
+ // All Elements are created here. This includes group element definitions.
+ $e[] = array(
+ 'type' => 'hidden',
+ 'req' => false,
+ 'name' => 'user_agent'
+ );
+ $e[] = array(
+ 'type' => 'hidden',
+ 'req' => false,
+ 'name' => 'remote_addr'
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => true,
+ 'name' => 'fname',
+ 'display' => 'First Name'
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => true,
+ 'name' => 'lname',
+ 'display' => 'Last Name'
+ );
+ if ($this->tableName == 'customer') {
+ $add1 = 'add1';
+ $add2 = 'add2';
+ } else {
+ $add1 = 'address';
+ $add2 = 'address2';
+ }
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => true,
+ 'name' => $add1,
+ 'display' => 'Address 1'
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => false,
+ 'name' => $add2,
+ 'display' => 'Address 2'
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => true,
+ 'name' => 'city',
+ 'display' => 'City'
+ );
+ $e[] = array(
+ 'type' => 'select',
+ 'req' => true,
+ 'name' => 'state',
+ 'display' => 'State/Province',
+ 'opts' => $GLOBALS['states']
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => true,
+ 'name' => 'zip',
+ 'display' => 'ZIP/Postal Code'
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => true,
+ 'name' => 'email',
+ 'display' => 'Email'
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => true,
+ 'name' => 'email_rmv',
+ 'display' => 'Verify Email'
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => false,
+ 'name' => 'phone',
+ 'display' => 'Phone'
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => false,
+ 'name' => 'fax',
+ 'display' => 'Fax'
+ );
+ $e[] = array(
+ 'type' => 'textarea',
+ 'req' => false,
+ 'name' => 'comments',
+ 'display' => 'Comments'
+ );
+ if ($this->hasContactDB) {
+ $e[] = array(
+ 'type' => 'advcheckbox',
+ 'req' => false,
+ 'name' => 'mail_ok',
+ 'display' => 'I would like to receive Email Newsletters',
+ 'opts' => 'Yes, ',
+ 'val' => array(0, 1)
+ );
+ if (defined("MEMBERS_DB") && MEMBERS_DB) {
+ $e[] = array(
+ 'type' => 'advcheckbox',
+ 'req' => false,
+ 'name' => 'members',
+ 'display' => 'I would like to receive information via e-mail from '.SITENAME.' Members',
+ 'opts' => 'Yes, ',
+ 'val' => array(0, 1)
+ );
+ }
+ }
+ if (is_array($this->interestsGroups)) {
+ foreach ($this->interestsGroups as $group => $gData) {
+ $this->myGroups[] = $gData;
+ $e[] = array(
+ 'type' => 'group',
+ 'req' => false,
+ 'name' => 'interest['.$group.']',
+ 'group' => $gData,
+ 'label' => $group,
+ 'seperator' => ' ',
+ 'appendName' => true
+ );
+ }
+ }
+ $e[] = array(
+ 'type' => 'CAPTCHA_Image',
+ 'req' => false,
+ 'name' => 'captcha_question',
+ 'display' => 'Verification code',
+ 'opts' => $this->captchaOptions
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => true,
+ 'name' => 'captcha_rmv',
+ 'display' => 'Enter verification code'
+ );
+ $e[] = array(
+ 'type' => 'submit',
+ 'req' => false,
+ 'name' => 'submit_rmv',
+ 'display' => 'Submit Form'
+ );
+
+ $this->setupElements($e);
+ }
+
+ // }}}
+}
diff --git a/Toolkit/Contacts/assets/.keepme b/Toolkit/Contacts/assets/.keepme
new file mode 100644
index 0000000..e69de29
diff --git a/Toolkit/Contacts/config.ini b/Toolkit/Contacts/config.ini
new file mode 100644
index 0000000..850e902
--- /dev/null
+++ b/Toolkit/Contacts/config.ini
@@ -0,0 +1,15 @@
+; Contact Database configuration file
+[conf]
+; Table Name for records
+table = "contact"
+; Primary Key
+primarykey = "id"
+; Sequence
+sequence = "contact_id_seq"
+
+[contact_types]
+1 = "Web Contact"
+;2 = "E-News"
+;3 = "Travel Planner"
+;4 = "Visitor Guide"
+5 = "Meeting Request"
diff --git a/Toolkit/Contacts/database-table-modifiers.sql b/Toolkit/Contacts/database-table-modifiers.sql
new file mode 100644
index 0000000..b539d26
--- /dev/null
+++ b/Toolkit/Contacts/database-table-modifiers.sql
@@ -0,0 +1,46 @@
+CREATE SEQUENCE contact_id_seq
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+ALTER TABLE contact ALTER COLUMN id SET DEFAULT nextval('contact_id_seq'::regclass);
+
+ALTER TABLE ONLY contact
+ ADD CONSTRAINT contact_pkey PRIMARY KEY (id);
+
+CREATE INDEX contact_create_date_indx ON contact USING btree (create_date);
+
+CREATE INDEX contact_email_indx ON contact USING btree (email);
+
+CREATE INDEX contact_fname_indx ON contact USING btree (fname);
+
+CREATE UNIQUE INDEX contact_id_indx ON contact USING btree (id);
+
+CREATE INDEX contact_lname_indx ON contact USING btree (lname);
+
+CREATE UNIQUE INDEX news_response_id_indx ON contact USING btree (id);
+
+CREATE UNIQUE INDEX query_db_id_indx ON contact USING btree (id);
+
+CREATE SEQUENCE contact_inq_id_seq
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+ALTER TABLE contact_inq ALTER COLUMN id SET DEFAULT nextval('contact_inq_id_seq'::regclass);
+
+ALTER TABLE ONLY inq_group
+ ADD CONSTRAINT inq_group_pkey PRIMARY KEY (id);
+
+CREATE SEQUENCE inq_group_id_seq
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+
+ALTER TABLE inq_group ALTER COLUMN id SET DEFAULT nextval('inq_group_id_seq'::regclass);
+
+ALTER TABLE ONLY inq_group
+ ADD CONSTRAINT inq_group_pkey PRIMARY KEY (id);
diff --git a/Toolkit/Contacts/database-tables.sql b/Toolkit/Contacts/database-tables.sql
new file mode 100644
index 0000000..1b71879
--- /dev/null
+++ b/Toolkit/Contacts/database-tables.sql
@@ -0,0 +1,45 @@
+CREATE TABLE contact (
+ id integer NOT NULL,
+ create_date date DEFAULT CURRENT_DATE,
+ fname text,
+ lname text,
+ company text,
+ address text,
+ address2 text,
+ city text,
+ state text,
+ zip text,
+ country text,
+ phone text,
+ fax text,
+ email text,
+ user_agent text,
+ remote_addr text,
+ interest text,
+ mail_ok boolean DEFAULT false,
+ visitorguide boolean DEFAULT false,
+ uidpdf text,
+ visitorguide_download boolean DEFAULT false,
+ email_verified boolean DEFAULT true,
+ contact_type text,
+ discover text,
+ member_ok boolean DEFAULT false,
+ staff boolean DEFAULT false,
+ comments text,
+ password text,
+ verify_password text
+);
+
+CREATE TABLE inq_group (
+ id integer NOT NULL,
+ name text
+);
+
+CREATE TABLE contact_inq (
+ id integer NOT NULL,
+ header text,
+ pos integer,
+ description text,
+ image text,
+ groupid integer
+);
diff --git a/Toolkit/Contacts/templates/brochurePage.html b/Toolkit/Contacts/templates/brochurePage.html
new file mode 100755
index 0000000..0ceb81b
--- /dev/null
+++ b/Toolkit/Contacts/templates/brochurePage.html
@@ -0,0 +1,9 @@
+
+
DOWNLOAD:
+
We will email you a link to the PDF version of the {brochure}.
+
+
+ {pdfForm:h}
+
+
ORDER BY MAIL:
+{contactForm:h}
diff --git a/Toolkit/Contacts/templates/currentTables/Element.tpl b/Toolkit/Contacts/templates/currentTables/Element.tpl
new file mode 100755
index 0000000..595457b
--- /dev/null
+++ b/Toolkit/Contacts/templates/currentTables/Element.tpl
@@ -0,0 +1,14 @@
+
+
+
+ *
+
+
+
+
+
+
{error}
+
+ {element}
+
+
diff --git a/Toolkit/Contacts/templates/currentTables/Form.tpl b/Toolkit/Contacts/templates/currentTables/Form.tpl
new file mode 100755
index 0000000..8875737
--- /dev/null
+++ b/Toolkit/Contacts/templates/currentTables/Form.tpl
@@ -0,0 +1,8 @@
+
+
+ {hidden}
+
+ {content}
+
+
+
\ No newline at end of file
diff --git a/Toolkit/Contacts/templates/currentTables/Group.tpl b/Toolkit/Contacts/templates/currentTables/Group.tpl
new file mode 100755
index 0000000..cdd24cf
--- /dev/null
+++ b/Toolkit/Contacts/templates/currentTables/Group.tpl
@@ -0,0 +1,5 @@
+
+
+ {content}
+
+
diff --git a/Toolkit/Contacts/templates/currentTables/GroupElement.tpl b/Toolkit/Contacts/templates/currentTables/GroupElement.tpl
new file mode 100755
index 0000000..1a4ba27
--- /dev/null
+++ b/Toolkit/Contacts/templates/currentTables/GroupElement.tpl
@@ -0,0 +1,9 @@
+
+
+ {element}
+
+ *
+
+ {label}
+
+
diff --git a/Toolkit/Contacts/templates/currentTables/Header.tpl b/Toolkit/Contacts/templates/currentTables/Header.tpl
new file mode 100755
index 0000000..64ac244
--- /dev/null
+++ b/Toolkit/Contacts/templates/currentTables/Header.tpl
@@ -0,0 +1,5 @@
+
Below is the list of businesses you have added to your Trip Planner. The form below will be sent to each individual business listing that has an email address listed with their business listing. For those businesses with no email address, we have included their phone numbers for you to call directly and request additional information.
+
Request Information by Phone from:
+
These business listings have no current email address on file. To receive additional information please call the phone numbers listed next to each business name.
Thank you for your interest in {client_info[name]:h}'!
+
Sincerely,
+
{client_info[name]:h}
+
{client_info[url]:h}
+
+
+
diff --git a/Toolkit/DataGridBuilder.php b/Toolkit/DataGridBuilder.php
new file mode 100644
index 0000000..83218de
--- /dev/null
+++ b/Toolkit/DataGridBuilder.php
@@ -0,0 +1,285 @@
+
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @link http://demo.gaslightmedia.com
+ */
+
+/**
+ * Create Datagrids for displaying data
+ *
+ * This abstract class handles all the base functionality of creating
+ * handeling all the details associated w/ a regular dataGrid.
+ * 1. Creation
+ * 2. Sorting (via column headers or sortform)
+ * 3. Pagenation
+ *
+ * @category Structures
+ * @package Toolkit_DataGridBuilder
+ * @author Jamie Kahgee
+ * @copyright 2008 Gaslight Media
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @Release CVS: $Id: DataGridBuilder.php,v 1.13 2009/09/16 00:07:44 jamie Exp $
+ * @link http://demo.gaslightmedia.com
+ * @see Toolkit_SortForm, Structures_DataGrid
+ */
+abstract class Toolkit_DataGridBuilder extends Structures_DataGrid
+{
+ // {{{ properties
+
+ /**
+ * Options to pass to DataGrid
+ *
+ * @var array
+ * @access protected
+ */
+ protected $options;
+
+ /**
+ * Rendering options for DataGrid
+ *
+ * @var array
+ * @access protected
+ */
+ protected $rendererOptions = array(
+ 'sortIconASC' => '⇑',
+ 'sortIconDESC' => '⇓',
+ 'evenRowAttributes' => 'class="even"',
+ 'oddRowAttributes' => 'class="odd"',
+ 'sortingResetsPaging' => false,
+ );
+
+ /**
+ * Rendering options for DataGrid paging element
+ *
+ * @var array
+ * @access protected
+ */
+ protected $pagerOptions = array(
+ 'nextImg' => 'Next',
+ 'prevImg' => 'Previous',
+ 'separator' => '',
+ 'spacesBeforeSeparator' => 0,
+ 'spacesAfterSeparator' => 0,
+ 'containerClass' => 'paging',
+ );
+
+ /**
+ * SQL query used to obtain the DataGrid
+ *
+ * @var unknown
+ * @access protected
+ */
+ protected $sql;
+
+ /**
+ * How many records must exist in the Datagrid before the sort form shows up
+ *
+ * @var integer
+ * @access protected
+ */
+ protected $sortableAfter = 10;
+
+ /**
+ * The HTML table id of the DataGrid
+ *
+ * @var string
+ * @access protected
+ */
+ protected $tableId = 'dataGrid';
+
+ /**
+ * The HTML class name of the DataGrid
+ *
+ * @var string
+ * @access protected
+ */
+ protected $tableClass = 'dataGrid';
+
+ /**
+ * The HTML id of the DataGrid sorting form (when present)
+ *
+ * @var string
+ * @access protected
+ */
+ protected $sortFormId = 'gridSorter';
+
+ /**
+ * Message to display to users if no records were found
+ *
+ * @var String
+ * @access Protected
+ * @see Toolkit_DataGridBuilder::setNoRecordMessage()
+ */
+ protected $noRecMessage = 'No Records';
+
+ // }}}
+ // {{{ __construct()
+
+ /**
+ * DataGrid constructor
+ *
+ * Instantiates a DataGrid and sets up when to make the grid sortable
+ *
+ * @param PDO $pdo PDO object used in the datagrid
+ * @param integer $limit The number of records to display per page.
+ * @param integer $page The current page view. In most cases,
+ * this is useless. Note: if you specify
+ * this, the "page"GET variable will be ignored.
+ * @param string $rendererType The type of renderer to use. You may
+ * prefer to use the $type argument of
+ * render, fill or getOutput.
+ *
+ * @return void
+ * @access public
+ */
+ public function __construct(
+ PDO $pdo,
+ $limit = null,
+ $page = null,
+ $rendererType = null
+ ) {
+ parent::__construct($limit, $page, $rendererType);
+
+ $this->options = array('dbc' => $pdo);
+ if (!is_null($limit)) {
+ $this->sortableAfter = $limit;
+ }
+ }
+
+ // }}}
+
+ // {{{ configureColumns()
+
+ /**
+ * configure retrieved columns
+ *
+ * Tells the DataGrid how to render the retrieved columns
+ *
+ * @return void
+ * @access protected
+ */
+ abstract protected function configureColumns();
+
+ // }}}
+
+ // {{{ setNoRecordMessage()
+
+ /**
+ * The message to display if no results were found from the sql query
+ *
+ * @param string $msg No result message.
+ *
+ * @return void
+ * @access public
+ */
+ public function setNoRecordMessage($msg)
+ {
+ $this->noRecMessage = $msg;
+ }
+
+ // }}}
+ // {{{ setQuery()
+
+ /**
+ * Sets the sql query to use in the DataGrid to get the results
+ *
+ * @param string $sql The SQL query
+ *
+ * @return void
+ * @access public
+ */
+ public function setQuery($sql)
+ {
+ $this->sql = $sql;
+ }
+
+ // }}}
+ // {{{ show()
+
+ /**
+ * Displays the DataGrid results
+ *
+ * @return void
+ * @access public
+ */
+ public function show()
+ {
+ echo $this->toHTML();
+ }
+
+ // }}}
+
+ // {{{ toHTML()
+
+ /**
+ * returns a HTML table of the datagrid
+ *
+ * @return string
+ * @access public
+ */
+ public function toHTML()
+ {
+ $this->configureColumns();
+
+ try {
+ $bind = $this->bind($this->sql, $this->options, 'PDO');
+ } catch (PDOException $e) {
+ return Toolkit_Common::handleError($e);
+ }
+
+ if (PEAR::isError($bind)) {
+ return Toolkit_Common::handleError($bind);
+ } elseif (($recCount = $this->getRecordCount()) > 0) {
+ $this->setRendererOptions($this->rendererOptions);
+ $renderer =& $this->getRenderer();
+ // Allows us to turn off the id name for the table,
+ // when we subclass this class out.
+ if ($this->tableId) {
+ $renderer->setTableAttribute('id', $this->tableId);
+ }
+ // Allows us to turn off the class name for the table,
+ // when we subclass this class out.
+ if ($this->tableClass) {
+ $renderer->setTableAttribute('class', $this->tableClass);
+ }
+ $gridBody = $this->getOutput();
+
+ if (PEAR::isError($gridBody)) {
+ return Toolkit_Common::handleError($gridBody);
+ }
+
+ $gridPager = $this->getOutput(
+ DATAGRID_RENDER_PAGER,
+ array('pagerOptions' => $this->pagerOptions)
+ );
+ if (PEAR::isError($gridPager)) {
+ return Toolkit_Common::handleError($gridPager);
+ }
+
+ if ($recCount > $this->sortableAfter) {
+ $form = new Toolkit_SortForm($this->sortFormId);
+ $options = array('directionStyle' => 'radio');
+ $this->fill($form, $options, DATAGRID_RENDER_SORTFORM);
+ // Datagrid never ads a submit button.
+ $form->addElement('submit', null, 'Submit');
+ $gridSorter = $form->toHTML();
+ }
+
+ return $gridPager . $gridSorter . $gridBody . $gridPager;
+ } else {
+ return "
{$this->noRecMessage}
";
+ }
+ }
+
+ // }}}
+}
+?>
diff --git a/Toolkit/Database.php b/Toolkit/Database.php
new file mode 100644
index 0000000..f9deb36
--- /dev/null
+++ b/Toolkit/Database.php
@@ -0,0 +1,262 @@
+
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @version CVS: $Id: Database.php,v 1.8 2010/05/25 14:09:12 jamie Exp $
+ * @link http://demo.gaslightmedia.com
+ */
+
+/**
+ * Database Singleton class
+ *
+ * Used to create a one and only one instance of a databsae PDO object.
+ * Only uses the PHP PDO. Later additions may be worked on the
+ * use master/slave database setups. Hopefully!
+ *
+ * @category Database
+ * @package Database
+ * @author Jamie Kahgee
+ * @copyright 2008 Gaslight Media
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @link http://demo.gaslightmedia.com
+ */
+class Toolkit_Database
+{
+ // {{{ properties
+
+ /**
+ * Master database handler object
+ *
+ * Handles insert/update/delete requests
+ *
+ * @var PDO
+ * @access private
+ */
+ private $_masterDbh;
+
+ /**
+ * Slave database handler object
+ *
+ * Handles read requests
+ *
+ * @var PDO
+ * @access private
+ */
+ private $_slaveDbh;
+
+ /**
+ * Class instance
+ *
+ * @var Toolkit_Database
+ * @access private
+ */
+ private static $_instance;
+
+ // }}}
+ // {{{ __construct()
+
+ /**
+ * Class constructor
+ *
+ * Make the database handler connection for the class
+ *
+ * @param boolean $master Whether Use connection for master of slave
+ *
+ * @access private
+ */
+ private function __construct($master)
+ {
+ try {
+ $dsn = 'pgsql:' . CONN_STR;
+ // Keep the fetch mode set to FETCH_BOTH, we use
+ // associative arrays throughout our apps, but some PEAR lib
+ // apps require numerical indicies to work correctly.
+ // i.e. (DataGrids working w/ PDO's)
+ $driverOptions = array(
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_BOTH,
+ );
+ $this->_masterDbh = new PDO($dsn, null, null, $driverOptions);
+ $this->_masterDbh->setAttribute(
+ PDO::ATTR_ERRMODE,
+ PDO::ERRMODE_EXCEPTION
+ );
+ $this->_setDateStyle($this->_masterDbh);
+ $this->_setSearchPath($this->_masterDbh);
+ } catch (PDOException $e) {
+ Toolkit_Common::handleError($e);
+ }
+ }
+
+ // }}}
+ // {{{ __clone()
+
+ /**
+ * Clone magic method
+ *
+ * Don't allow cloning of instances of this class
+ * which could potentially duplicate system resources
+ * and make extra DB connections
+ *
+ * @return void
+ * @access private
+ */
+ private function __clone()
+ {
+ // Do nothing so we don't create duplicate resources
+ }
+
+ // {{{ _setDateStyle()
+
+ /**
+ * Set the default date style
+ * @param PDO $dbh
+ */
+ private function _setDateStyle(PDO &$dbh)
+ {
+ $dbh->query("SET DATESTYLE TO 'SQL, US'");
+ }
+
+ // }}}
+ // {{{ _setSearchPath()
+
+ /**
+ * Set the search path for schemas
+ * @param PDO $dbh
+ */
+ private function _setSearchPath(PDO &$dbh)
+ {
+ // Add schemas to search path.
+ $sql = "
+ SELECT set_config(
+ 'search_path',
+ current_setting('search_path') || ', ' || :schema,
+ false
+ )";
+
+ $stmt = $dbh->prepare($sql);
+ $stmt->bindValue(':schema', 'toolbox', PDO::PARAM_STR);
+ $stmt->execute();
+
+ $stmt->bindValue(':schema', 'ckimages', PDO::PARAM_STR);
+ $stmt->execute();
+
+ if (defined('RETAIL_SHOP') && RETAIL_SHOP) {
+ $stmt->bindValue(':schema', 'retail_shop', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+
+ if (defined('GLM_BLOCKS') && GLM_BLOCKS) {
+ // define seasonator search path
+ $stmt->bindValue(':schema', 'blocks', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+
+ if (defined('CONTACT_DB') && CONTACT_DB) {
+ // define banner search path
+ $stmt->bindValue(':schema', 'contacts', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+
+ if (defined('BANNERS') && BANNERS) {
+ // define banner search path
+ $stmt->bindValue(':schema', 'banners', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+
+ if (defined('ROTATING_IMAGES') && ROTATING_IMAGES) {
+ // define rotating images search path
+ $stmt->bindValue(':schema', 'rotatingImages', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+
+ if (defined('PHOTO_GALLERY') && PHOTO_GALLERY) {
+ // define phot gallery search path
+ $stmt->bindValue(':schema', 'photos', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+
+ if (defined('POSTCARD_DB') && POSTCARD_DB) {
+ // define phot gallery search path
+ $stmt->bindValue(':schema', 'postcards', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+
+ if (defined('EVENT_DB') && EVENT_DB) {
+ // define members search path
+ $stmt->bindValue(':schema', 'events', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+
+ if (defined('MEMBERS_DB') && MEMBERS_DB) {
+ // define members search path
+ $stmt->bindValue(':schema', 'members', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+
+ if (defined('COUPONS') && COUPONS) {
+ // define coupon search path
+ $stmt->bindValue(':schema', 'coupons', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+
+ if (defined('PRESS_DB') && PRESS_DB) {
+ // define press/archive search path
+ $stmt->bindValue(':schema', 'press', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+ if (defined('EMPLOYMENT') && EMPLOYMENT) {
+ // define press/archive search path
+ $stmt->bindValue(':schema', 'employment', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+ if (defined('VIDEOS') && VIDEOS) {
+ // define videos search path
+ $stmt->bindValue(':schema', 'videos', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+ if (defined('EVENT_MANAGEMENT') && EVENT_MANAGEMENT) {
+ // define event management search path
+ $stmt->bindValue(':schema', 'reservations', PDO::PARAM_STR);
+ $stmt->execute();
+ }
+ }
+
+ // }}}
+
+ // {{{ getInstance()
+
+ /**
+ * Get an instance of this class
+ *
+ * If this object hasn't been instantiated once already
+ * then create the object and return the dbh.
+ * Otherwise just return the already instantiated dbh.
+ *
+ * @param boolean $master Whether Use connection for master or slave
+ *
+ * @return PDO $_instance database handler instance
+ * @access public
+ * @static
+ */
+ public static function getInstance($master = true)
+ {
+ settype($master, 'bool');
+
+ if (!(self::$_instance instanceof self)) {
+ self::$_instance = new self($master);
+ }
+
+ return $master
+ ? self::$_instance->_masterDbh
+ : self::$_instance->_slaveDbh;
+ }
+
+ // }}}
+}
diff --git a/Toolkit/FileServer/AdapterAbstract.php b/Toolkit/FileServer/AdapterAbstract.php
new file mode 100644
index 0000000..956483f
--- /dev/null
+++ b/Toolkit/FileServer/AdapterAbstract.php
@@ -0,0 +1,405 @@
+
+ * @copyright 2010 Jamie Kahgee
+ * @license http://www.gaslightmedia.com/ Gaslightmedia
+ * @version CVS: $Id: AdapterAbstract.php,v 1.6 2010/06/04 11:33:54 jamie Exp $
+ * @link <>
+ * @see References to other sections (if any)...
+ */
+
+
+/**
+ * Validation Secret
+ */
+define('IS_VALIDATE_SECRET', 'Glm0IS1secreT');
+
+/**
+ * URL to file server, this is dynamic based on the server
+ * so can be setup for any sandbox
+ */
+define('IS_SUBMIT_URL', FILE_SERVER_URL . 'submit.phtml');
+
+/**
+ * Adapter class for linking to the file server
+ *
+ * @category Toolkit
+ * @package FileServer
+ * @author Jamie Kahgee
+ * @copyright 2010 Jamie Kahgee
+ * @license http://www.gaslightmedia.com/ Gaslightmedia
+ * @version Release: @package_version@
+ * @link <>
+ * @see References to other sections (if any)...
+ */
+abstract class Toolkit_FileServer_AdapterAbstract
+{
+ // {{{ properties
+
+ /**
+ * ID of owner that was setup in the file server
+ *
+ * This is defined via the file server web interface
+ * @var string
+ * @access protected
+ */
+ protected $ownerID;
+
+ /**
+ * Owners password that was setup for them
+ *
+ * This is defined via the file server web interface
+ * @var string
+ * @access protected
+ */
+ protected $ownerPW;
+
+ /**
+ * XML to send to the file server
+ * @var unknown
+ * @access protected
+ */
+ protected $xml;
+
+ // }}}
+ // {{{ __construct()
+
+ /**
+ * Constructor
+ *
+ * @param string $owner File Server owner
+ * @param string $pword File Server owner password
+ */
+ public function __construct($owner = IS_OWNER_ID, $pword = IS_OWNER_PW)
+ {
+ $this->ownerID = $owner;
+ $this->ownerPW = $pword;
+ }
+
+ // }}}
+
+ // {{{ buildFileServerXML()
+
+ /**
+ * buildFileServerXML
+ *
+ * Create the xml for the FileServerRequest
+ *
+ * @param string $fName File name to upload or delete
+ * @param string $type Upload, Delete or URL
+ *
+ * @return string xml content
+ * @access protected
+ */
+ protected function buildFileServerXML($fName, $type)
+ {
+ $xml = new DOMDocument('1.0');
+ $xml->formatOutput = true;
+
+ $fileServerRequest = $xml->createElement('FileServerRequest');
+ $fileServerRequest->setAttribute('version', '1.0');
+
+ // access request
+ $accessRequest = $xml->createElement('AccessRequest');
+ $owner = $xml->createElement('Owner');
+ $ownerID = $xml->createElement('OwnerID', $this->ownerID);
+ $ownerPW = $xml->createElement('OwnerPW', $this->ownerPW);
+
+ $owner->appendChild($ownerID);
+ $owner->appendChild($ownerPW);
+
+ $accessRequest->appendChild($owner);
+
+ $fileServerRequest->appendChild($accessRequest);
+
+ $file = $this->getFileElement($xml, $type, $fName);
+
+ $fileServerRequest->appendChild($file);
+ $validStr = md5($this->ownerID . $this->ownerPW . IS_VALIDATE_SECRET);
+
+ $validation = $xml->createElement('Validation', $validStr);
+ $fileServerRequest->appendChild($validation);
+ $xml->appendChild($fileServerRequest);
+
+ return $xml->saveXML($xml);
+ }
+
+ // }}}
+
+ // {{{ delete()
+
+ /**
+ * Delete a file from the file server
+ *
+ * @param string $name File name
+ *
+ * @return string file name
+ * @access public
+ * @throws Toolkit_FileServer_Exception
+ */
+ public function delete($name)
+ {
+ // don't do anything if on development server
+ if (defined('DEVELOPMENT') && DEVELOPMENT == true) {
+ return true;
+ }
+ $ch = curl_init();
+
+ $fileData = array(
+ 'request' => $this->buildFileServerXML($name, 'Delete')
+ );
+ $curlOptions = array(
+ CURLOPT_URL => IS_SUBMIT_URL,
+ CURLOPT_HEADER => 0,
+ CURLOPT_RETURNTRANSFER => 1,
+ CURLOPT_POSTFIELDS => $fileData
+ );
+ curl_setopt_array($ch, $curlOptions);
+
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ try {
+ $xmlDoc = new DOMDocument;
+ $response = str_replace('', '', $response);
+ $xmlDoc->loadXML($response);
+ $successCode = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/ReplyStatus/SuccessCode'
+ );
+ $message = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/ReplyStatus/Message'
+ );
+
+ if ($successCode != 0) {
+ throw new RangeException(
+ "Invalid response `$response` - `$message`"
+ );
+ }
+
+ return $message;
+ } catch (RangeException $e) {
+ Toolkit_Logger::logException('Image Server', $e);
+ throw new Toolkit_FileServer_Exception(
+ 'Invalid File Server Response'
+ );
+ }
+ }
+
+ // }}}
+
+ // {{{ getFileElement()
+
+
+ /**
+ * Gets the file element used in the XML sent to the file server
+ *
+ * @param DOMDocument $xml DOM object used to build the xml string
+ * @param string $type Type of manipulation (Upload, URL, Delete)
+ * @param string $fName File name
+ *
+ * @return DOMElement $file File xml element
+ * @access protected
+ */
+ abstract protected function getFileElement(
+ DOMDocument $xml,
+ $type,
+ $fName
+ );
+
+ // }}}
+ // {{{ getErrorMessage()
+
+ /**
+ * Gets an appropriate error message for file upload errors
+ *
+ * @param integer Error code
+ *
+ * @return string Error message
+ * @access protected
+ */
+ protected function getErrorMessage($code)
+ {
+ switch ($code) {
+ case UPLOAD_ERR_INI_SIZE :
+ $message = 'The Uploaded file exceeds the upload max filesize directive in php.ini';
+ break;
+
+ case UPLOAD_ERR_FORM_SIZE :
+ $message = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form';
+ break;
+
+ case UPLOAD_ERR_PARTIAL :
+ $message = 'The uploaded file was only partially uploaded';
+ break;
+
+ case UPLOAD_ERR_NO_FILE :
+ $message = 'No file was uploaded';
+ break;
+
+ case UPLOAD_ERR_NO_TMP_DIR :
+ $message = 'Missing a temporary folder';
+ break;
+
+ case UPLOAD_ERR_CANT_WRITE :
+ $message = 'Failed to write file to disk';
+ break;
+
+ case UPLOAD_ERR_EXTENSION :
+ $message = 'A PHP extension stopped the file upload';
+ break;
+
+ default :
+ $message = 'Unknown error uploading file';
+ break;
+ }
+
+ return $message;
+ }
+
+ // }}}
+
+ // {{{ upload()
+
+ /**
+ * Upload a file to the File Server
+ *
+ * @param string $name $_FILES index key that holds the file data
+ *
+ * @return array Uploaded file data
+ * @access public
+ * @throws Toolkit_FileServer_Exception
+ */
+ public function upload($name)
+ {
+ $fileType = null;
+ if (preg_match('/^http/', $name)) {
+ $fileType = 'URL';
+ } elseif (isset($_FILES[$name]) && is_array($_FILES[$name])) {
+ $fileType = 'Upload';
+ }
+
+ if ($_FILES[$name]['error'] != 0) {
+ $message = $this->getErrorMessage($_FILES[$name]['error']);
+ throw new Toolkit_FileServer_Exception(
+ $message,
+ $_FILES[$name]['error']
+ );
+ }
+
+ if (is_null($fileType)) {
+ return false;
+ } else {
+ $request = $this->buildFileServerXML($name, $fileType);
+
+ $ch = curl_init();
+ if ($fileType == 'URL') {
+ $fileData = array(
+ 'request' => $request
+ );
+ } else {
+ $fileData = array(
+ 'request' => $request,
+ 'file_upload' => "@{$_FILES[$name]['tmp_name']}",
+ 'file_upload_name' => $_FILES[$name]['name']
+ );
+ }
+
+ $curlOptions = array(
+ CURLOPT_URL => IS_SUBMIT_URL,
+ CURLOPT_HEADER => 0,
+ CURLOPT_RETURNTRANSFER => 1,
+ CURLOPT_POSTFIELDS => $fileData
+ );
+ curl_setopt_array($ch, $curlOptions);
+
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ try {
+ $xmlDoc = new DOMDocument();
+ $response = str_replace('', '', $response);
+ $xmlDoc->loadXML($response);
+ $successCode = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/ReplyStatus/SuccessCode'
+ );
+ $message = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/ReplyStatus/Message'
+ );
+ $storedName = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/File/StoredName'
+ );
+ $storedSize = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/File/StoredSize'
+ );
+ $mediaType = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/File/MediaType'
+ );
+
+ if ($successCode != 0) {
+ throw new RangeException(
+ "Invalid response `$response` - `$message`"
+ );
+ }
+
+ $extension = end(explode('.', $storedName));
+ return array(
+ 'name' => $storedName,
+ 'size' => $storedSize,
+ 'type' => $mediaType,
+ 'extension' => $extension,
+ );
+ } catch (RangeException $e) {
+ Toolkit_Logger::logException('Image Server', $e);
+ throw new Toolkit_FileServer_Exception(
+ 'Invalid File Server Response'
+ );
+ }
+ }
+ }
+
+ // }}}
+
+ // {{{ xmlPathContent()
+
+ /**
+ * Extract a XML node value
+ *
+ * @param DOMDocument $dom DOM object holding the XML
+ * @param string $content Node name to extract content from
+ *
+ * @return mixed Node value on success, false if can't find the value
+ * @access protected
+ */
+ protected function xmlPathContent(DOMDocument $dom, $content)
+ {
+ $xPath = new DOMXPath($dom);
+ $nodeList = $xPath->query($content);
+ if ($nodeList->length > 0) {
+ $node = $nodeList->item(0);
+ return $node->nodeValue;
+ }
+
+ return false;
+ }
+
+ // }}}
+}
diff --git a/Toolkit/FileServer/Exception.php b/Toolkit/FileServer/Exception.php
new file mode 100644
index 0000000..8739c0f
--- /dev/null
+++ b/Toolkit/FileServer/Exception.php
@@ -0,0 +1,5 @@
+
diff --git a/Toolkit/FileServer/FileAdapter.php b/Toolkit/FileServer/FileAdapter.php
new file mode 100644
index 0000000..a83ef98
--- /dev/null
+++ b/Toolkit/FileServer/FileAdapter.php
@@ -0,0 +1,101 @@
+
+ * @copyright 2010 Jamie Kahgee
+ * @license http://www.gaslightmedia.com/ Gaslightmedia
+ * @version CVS: $Id: FileAdapter.php,v 1.4 2010/05/25 14:02:28 jamie Exp $
+ * @link <>
+ * @see References to other sections (if any)...
+ */
+
+/**
+ * File Adapter
+ *
+ * Used for linking to the file server for file uploading, storage and deletion
+ *
+ * @category Toolkit
+ * @package FileServer
+ * @author Jamie Kahgee
+ * @copyright 2010 Jamie Kahgee
+ * @license http://www.gaslightmedia.com/ Gaslightmedia
+ * @version Release: @package_version@
+ * @link <>
+ * @see References to other sections (if any)...
+ */
+class Toolkit_FileServer_FileAdapter extends Toolkit_FileServer_AdapterAbstract
+{
+ // {{{ getFileElement()
+
+ /**
+ * Creates the file element for the XML string
+ *
+ * @param DOMDocument $xml XML object used to build the string
+ * @param string $type Type of action to take on the file (Upload, Delete)
+ * @param string $fName file name to delete
+ *
+ * @return DOMElement $file File xml element
+ * @access protected
+ * @throws RuntimeException
+ */
+ protected function getFileElement(DOMDocument $xml, $type, $fName)
+ {
+ $file = $xml->createElement('File');
+
+ switch ($type) {
+ case 'Upload' :
+ $action = $xml->createElement('FileAction', 'Submit');
+ $delivery = $xml->createElement('DeliveryMethod', 'Submit');
+ $fieldName = $xml->createElement('FieldName', 'file_upload');
+
+ $fileName = filter_var($_FILES[$fName]['name'], FILTER_SANITIZE_STRING);
+ $nameParts = explode('.', $fileName);
+ $extension = end($nameParts);
+ $fieldName = $xml->createElement('Extension', end($nameParts));
+
+ $file->appendChild($action);
+ $file->appendChild($delivery);
+ $file->appendChild($fieldName);
+ break;
+
+ case 'URL':
+ $action = $xml->createElement('FileAction', 'Submit');
+ $delivery = $xml->createElement('DeliveryMethod', 'URL');
+ $src = $xml->createElement('Src', $fName);
+
+ $nameParts = explode('.', $fName);
+ $extension = end($nameParts);
+ $fieldName = $xml->createElement('Extension', end($nameParts));
+
+ $file->appendChild($action);
+ $file->appendChild($delivery);
+ $file->appendChild($src);
+ $file->appendChild($fieldName);
+ break;
+
+ case 'Delete' :
+ $action = $xml->createElement('FileAction', 'Delete');
+ $file->appendChild($action);
+
+ $fileName = $xml->createElement('FileName', $fName);
+ $file->appendChild($fileName);
+ break;
+
+ default :
+ throw new RuntimeException("Invalid XML type - `$type`.");
+ break;
+ }
+
+ return $file;
+ }
+
+ // }}}
+}
diff --git a/Toolkit/FileServer/ImageAdapter.php b/Toolkit/FileServer/ImageAdapter.php
new file mode 100644
index 0000000..85b7951
--- /dev/null
+++ b/Toolkit/FileServer/ImageAdapter.php
@@ -0,0 +1,248 @@
+
+ * @copyright 2010 Jamie Kahgee
+ * @license http://www.gaslightmedia.com/ Gaslightmedia
+ * @version CVS: $Id: ImageAdapter.php,v 1.5 2010/06/04 11:34:31 jamie Exp $
+ * @link <>
+ * @see References to other sections (if any)...
+ */
+
+/**
+ * Image Adapter
+ *
+ * Used for linking to the file server for image uploading, storage and deletion
+ *
+ * @category Toolkit
+ * @package FileServer
+ * @author Jamie Kahgee
+ * @copyright 2010 Jamie Kahgee
+ * @license http://www.gaslightmedia.com/ Gaslightmedia
+ * @version Release: @package_version@
+ * @link <>
+ * @see References to other sections (if any)...
+ */
+class Toolkit_FileServer_ImageAdapter extends Toolkit_FileServer_AdapterAbstract
+{
+ // {{{ getAllowedMimeTypes()
+
+ /**
+ * Get the mime types that are allowed to be uploaded
+ *
+ * @return array allowed mime types
+ */
+ public function getAllowedMimeTypes()
+ {
+ return array(
+ 'image/jpe',
+ 'image/jpeg',
+ 'image/jpg',
+ 'image/jfif',
+ 'image/pjpeg',
+ 'image/pjp',
+ 'image/gif',
+ 'image/gif',
+ 'image/png',
+ );
+ }
+
+ // }}}
+ // {{{ getFileElement()
+
+ /**
+ * Creates the file element for the XML string
+ *
+ * @param DOMDocument $xml XML object used to build the string
+ * @param string $type Type of action to take on the file (Upload, Delete)
+ * @param string $fName file name to delete
+ *
+ * @return DOMElement $file File xml element
+ * @access protected
+ * @throws RuntimeException
+ */
+ protected function getFileElement(DOMDocument $xml, $type, $fName)
+ {
+ $file = $xml->createElement('File');
+
+ switch ($type) {
+ case 'Upload' :
+ $action = $xml->createElement('FileAction', 'Submit');
+ $delivery = $xml->createElement('DeliveryMethod', 'Submit');
+ $fieldName = $xml->createElement('FieldName', 'file_upload');
+
+ $file->appendChild($action);
+ $file->appendChild($delivery);
+ $file->appendChild($fieldName);
+ break;
+
+ case 'URL':
+ $action = $xml->createElement('FileAction', 'Submit');
+ $delivery = $xml->createElement('DeliveryMethod', 'URL');
+ $src = $xml->createElement('Src', $fName);
+
+ $file->appendChild($action);
+ $file->appendChild($delivery);
+ $file->appendChild($src);
+ break;
+
+ case 'Delete':
+ $action = $xml->createElement('FileAction', 'Delete');
+ $file->appendChild($action);
+
+ $fileName = $xml->createElement('FileName', $fName);
+ $file->appendChild($fileName);
+ break;
+
+ default :
+ throw new RuntimeException("Invalid XML type - `$type`.");
+ break;
+ }
+
+ return $file;
+ }
+
+ // }}}
+ // {{{ getImageSize()
+
+ /**
+ * getImageSize
+ *
+ * Return image data on an image from image server
+ *
+ * @param string $image Full URI to image
+ * http://is0/userId/imageStyle/imageName
+ * Must be a full URI including an authority.
+ * No relative URIs, the // are mandatory
+ *
+ * @return array Image data 0 => width, 1 => height, 2 => html
+ * @access public
+ * @throws Exception
+ */
+ public function getImageSize($image)
+ {
+ $options = array('allowed_schemes' => array('http','https'));
+ if (!Validate::uri($image, $options)) {
+ throw new Exception('Invalid URI for the image');
+ }
+
+ $ch = curl_init();
+ $curlOptions = array(
+ CURLOPT_URL => "{$image}/info",
+ CURLOPT_HEADER => 0,
+ CURLOPT_RETURNTRANSFER => 1,
+ CURLOPT_POSTFIELDS => $fileData
+ );
+ curl_setopt_array($ch, $curlOptions);
+
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ if (!@simplexml_load_string($response)) {
+ $logger = Toolkit_Logger::getLogger();
+ $logger->warning("$response - $image");
+ }
+
+ $wPattern = '/(.*)<\/width>/';
+ preg_match($wPattern, $response, $matches);
+ $width = $matches[1];
+ $hPattern = '/(.*)<\/height>/';
+ preg_match($hPattern, $response, $matches);
+ $height = $matches[1];
+ $html = "width=\"{$width}\" height=\"{$height}\"";
+ return array($width, $height, $html);
+ }
+
+ // }}}
+ // {{{ uploadImageFile()
+
+ /**
+ * Upload a file to the File Server
+ *
+ * @param string $fname full path to image file
+ *
+ * @return array Uploaded file data
+ * @access public
+ * @throws Toolkit_FileServer_Exception
+ */
+ public function uploadImageFile($fname)
+ {
+ if (!file_exists($fname)) {
+ throw new RuntimeException("could not find file `$fname`");
+ }
+
+ $request = $this->buildFileServerXML(basename($fname), 'Upload');
+
+ $ch = curl_init();
+ $fileData = array(
+ 'request' => $request,
+ 'file_upload' => "@$fname",
+ 'file_upload_name' => basename($fname)
+ );
+
+ $curlOptions = array(
+ CURLOPT_URL => IS_SUBMIT_URL,
+ CURLOPT_HEADER => 0,
+ CURLOPT_RETURNTRANSFER => 1,
+ CURLOPT_POSTFIELDS => $fileData
+ );
+ curl_setopt_array($ch, $curlOptions);
+
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ try {
+ $xmlDoc = new DOMDocument();
+ $response = str_replace('', '', $response);
+ $xmlDoc->loadXML($response);
+ $successCode = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/ReplyStatus/SuccessCode'
+ );
+ $message = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/ReplyStatus/Message'
+ );
+ $storedName = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/File/StoredName'
+ );
+ $storedSize = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/File/StoredSize'
+ );
+ $mediaType = $this->xmlPathContent(
+ $xmlDoc,
+ '/FileServerResponse/File/MediaType'
+ );
+
+ if ($successCode != 0) {
+ throw new RangeException(
+ "Invalid response `$response` - `$message`"
+ );
+ }
+
+ return array(
+ 'name' => $storedName,
+ 'size' => $storedSize,
+ 'type' => $mediaType,
+ );
+ } catch (RangeException $e) {
+ Toolkit_Logger::logException('Image Server', $e);
+ throw new Toolkit_FileServer_Exception(
+ 'Invalid File Server Response'
+ );
+ }
+ }
+
+ // }}}
+}
+?>
diff --git a/Toolkit/FileServer/Mock/ImageAdapter.php b/Toolkit/FileServer/Mock/ImageAdapter.php
new file mode 100644
index 0000000..ed9307d
--- /dev/null
+++ b/Toolkit/FileServer/Mock/ImageAdapter.php
@@ -0,0 +1,125 @@
+
+ * @copyright 2010 Jamie Kahgee
+ * @license http://www.gaslightmedia.com/ Gaslightmedia
+ * @version CVS: $Id: ImageAdapter.php,v 1.1 2010/07/29 16:30:02 jamie Exp $
+ * @link <>
+ * @see References to other sections (if any)...
+ */
+
+/**
+ * Mock Image Adapter
+ *
+ * Mocks uploading and deleting images from the image server
+ * This script prevents unneeded resources from being uploaded
+ * or deleted from the file server
+ *
+ * @category FileServer
+ * @package Toolkit_FileServer
+ * @author Jamie Kahgee
+ * @copyright 2010 Jamie Kahgee
+ * @license http://www.gaslightmedia.com/ Gaslightmedia
+ * @version Release: @package_version@
+ * @link <>
+ * @see References to other sections (if any)...
+ */
+class Toolkit_FileServer_Mock_ImageAdapter
+ extends Toolkit_FileServer_ImageAdapter
+{
+ // {{{ getImageSize()
+
+ /**
+ * getImageSize
+ *
+ * Return image data on an image from image server
+ *
+ * @param string $image Full URI to image
+ * http://is0/userId/imageStyle/imageName
+ * Must be a full URI including an authority.
+ * No relative URIs, the // are mandatory
+ *
+ * @return array Image data 0 => width, 1 => height, 2 => html
+ * @access public
+ * @throws Exception
+ */
+ public function getImageSize($image)
+ {
+ $width = $height = 1;
+ $html = "width=\"{$width}\" height=\"{$height}\"";
+ return array($width, $height, $html);
+ }
+
+ // }}}
+ // {{{ uploadImageFile()
+
+ /**
+ * Upload a file to the File Server
+ *
+ * @param string $fname full path to image file
+ *
+ * @return array Uploaded file data
+ * @access public
+ * @throws Toolkit_FileServer_Exception
+ */
+ public function uploadImageFile($fname)
+ {
+ return array(
+ 'name' => 'is00-mock-image.jpg',
+ 'size' => 100,
+ 'type' => 'image/jpeg',
+ );
+ }
+
+ // }}}
+ // {{{ upload()
+
+ /**
+ * Upload a file to the File Server
+ *
+ * @param string $name $_FILES index key that holds the file data
+ *
+ * @return array Uploaded file data
+ * @access public
+ * @throws Toolkit_FileServer_Exception
+ */
+ public function upload($name)
+ {
+ return array(
+ 'name' => 'is00-mock-image.jpg',
+ 'size' => 100,
+ 'type' => 'image/jpeg',
+ );
+ }
+
+ // }}}
+ // {{{ delete()
+
+ /**
+ * Delete a file from the file server
+ *
+ * @param string $name File name
+ *
+ * @return string file name
+ * @access public
+ * @throws Toolkit_FileServer_Exception
+ */
+ public function delete($name)
+ {
+ return true;
+ }
+
+ // }}}
+}
+?>
diff --git a/Toolkit/FlexyDataGridBuilder.php b/Toolkit/FlexyDataGridBuilder.php
new file mode 100644
index 0000000..793b864
--- /dev/null
+++ b/Toolkit/FlexyDataGridBuilder.php
@@ -0,0 +1,223 @@
+
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @version CVS: $Id: FlexyDataGridBuilder.php,v 1.14 2010/07/25 17:08:39 jamie Exp $
+ * @link <>
+ */
+
+/**
+ * Create Flexy templating Datagrids for displaying data
+ *
+ * This abstract class handles all the base functionality of creating
+ * handeling all the details associated w/ a Flexy template datagrid
+ * 1. Creation
+ * 2. Sorting (via column headers or sortform)
+ * 3. Pagenation
+ *
+ * @category Toolkit
+ * @package FlexyDataGridBuilder
+ * @author Jamie Kahgee
+ * @copyright 2008 Gaslight Media
+ * @license http://www.gaslightmedia.com/Gaslightmedia Gaslightmedia
+ * @link http://pear.php.net/package/Structures_DataGrid/docs
+ * @see Structures_DataGrid_Renderer_Flexy, HTML_Template_Flexy
+ */
+abstract class Toolkit_FlexyDataGridBuilder extends Toolkit_DataGridBuilder
+{
+ // {{{ properties
+
+ /**
+ * Name of the table which we will be dealing with
+ *
+ * @var string
+ * @access protected
+ */
+ protected $tableName;
+
+ /**
+ * The name of the template that renders this datagrid
+ *
+ * @var string
+ * @access protected
+ */
+ protected $template;
+
+ /**
+ * Output an object as $t to the template
+ *
+ * converts array keys to objects usable in a template form.
+ *
+ *
+ * $this->ctrlObj['url'] = BASE_URL;
+ *
+ *
+ *
+ *
+ *
+ *
+ * @var array
+ * @access protected
+ */
+ protected $ctrlObj = array();
+
+ /**
+ * Rendering options for DataGrid
+ *
+ * @var array
+ * @access protected
+ */
+ protected $rendererOptions = array(
+ 'sortIconASC' => '⇑',
+ 'sortIconDESC' => '⇓',
+ 'sortingResetsPaging' => false,
+ );
+
+ /**
+ * The HTML id of the DataGrid
+ *
+ * @var string
+ * @access protected
+ */
+ protected $gridId = 'dataGrid';
+
+ /**
+ * The HTML class of the DataGrid
+ *
+ * @var string
+ * @access protected
+ */
+ protected $gridClass = 'dataGrid';
+
+ // }}}
+ // {{{ __construct()
+
+ /**
+ * Constructor
+ *
+ * Builds the DataGrid class.
+ *
+ * @param PDO $pdo PDO object used in the datagrid
+ * @param integer $limit The number of records to display per page.
+ * @param integer $page The current page view. In most cases,
+ * this is useless. Note: if you specify
+ * this, the "page"GET variable will be ignored.
+ * @param string $rendererType The type of renderer to use. You may
+ * prefer to use the $type argument of
+ * render, fill or getOutput.
+ *
+ * @return void
+ * @access public
+ */
+ public function __construct(
+ PDO $pdo,
+ $limit = null,
+ $page = null,
+ $rendererType = null
+ ) {
+ $this->dbh = $pdo;
+ if (ctype_digit($_GET['setPerPage'])) {
+ $limit = $_GET['setPerPage'];
+ }
+
+ parent::__construct($pdo, $limit, $page, $rendererType);
+
+ // If all records show on one page, then hide sort form.
+ if (!is_null($limit)) {
+ $this->sortableAfter = $limit;
+ }
+ }
+
+ // }}}
+
+ // {{{ show()
+
+ /**
+ * Displays the DataGrid results
+ *
+ * @param Structures_DataGrid_Renderer_Flexy $renderer Rendering engine used to render DG
+ *
+ * @return void
+ * @access public
+ */
+ public function show(Structures_DataGrid_Renderer_Flexy $renderer)
+ {
+ echo $this->toHtml($renderer);
+ }
+
+ // }}}
+
+ // {{{ toHtml()
+
+ /**
+ * Returns the DataGrid
+ *
+ * @param Structures_DataGrid_Renderer_Flexy $renderer Rendering engine used to render DG
+ * @param string $template Template to use
+ *
+ * @return string html templated datagrid results
+ * @access public
+ */
+ public function toHtml(
+ Structures_DataGrid_Renderer_Flexy $renderer,
+ $template = null
+ ) {
+ $this->configureColumns();
+
+ try {
+ $bind = $this->bind($this->sql, $this->options, 'PDO');
+ } catch (PDOException $e) {
+ return Toolkit_Common::handleError($e);
+ }
+
+ if (PEAR::isError($bind)) {
+ return Toolkit_Common::handleError($bind);
+ }
+
+ if (method_exists($this, 'setControlObject')) {
+ $this->setControlObject();
+ foreach ($this->ctrlObj as $k => $v) {
+ $renderer->$k = $v;
+ }
+ }
+
+ $renderer->setOptions($this->rendererOptions);
+ $renderer->setOptions(array('pagerOptions' => $this->pagerOptions));
+
+ $this->attachRenderer($renderer);
+ $tEngine = $renderer->getContainer();
+
+ $pathToTemplate = $tEngine->options['templateDir'][0];
+
+ if ( !is_null($template)
+ && file_exists("$pathToTemplate/$template")
+ ) {
+ $tEngine->compile($template);
+ } elseif (isset($this->template)
+ && file_exists($pathToTemplate . $this->template)
+ ) {
+ $tEngine->compile($this->template);
+ } else {
+ throw new RuntimeException('Template not available');
+ }
+
+ // Get the entire datagrid body.
+ $gridBody = $this->getOutput();
+ if (PEAR::isError($gridBody)) {
+ return Tolkit_Common::handleError($gridBody);
+ }
+
+ return $gridSorter . $gridBody;
+ }
+
+ // }}}
+}
+?>
diff --git a/Toolkit/Form.php b/Toolkit/Form.php
new file mode 100644
index 0000000..c14a16b
--- /dev/null
+++ b/Toolkit/Form.php
@@ -0,0 +1,143 @@
+
+ * @copyright 2009 Jamie Kahgee
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @version CVS: $Id: Form.php,v 1.6 2009/08/31 17:43:48 jamie Exp $
+ * @link http://demo.gaslightmedia.com/
+ */
+
+/**
+ * Minimum form object method definitions
+ *
+ * Each form must at least define some elements and setup the rules for
+ * that form. They must also create a method which handless extracting
+ * the form object into an html string suitable to be displayed onto a
+ * page.
+ *
+ * @category Toolkit
+ * @package Form
+ * @author Jamie Kahgee
+ * @copyright 2009 Jamie Kahgee
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @link http://demo.gaslightmedia.com/
+ */
+interface Toolkit_Form
+{
+ // {{{ configureElements()
+
+ /**
+ * Defines all elements to be used in a form
+ *
+ * Creates an array of elements and passes them off to the setupElements
+ * function wich adds each element to the form object.
+ *
+ *
+ * public function configureElements()
+ * {
+ * $e = array();
+ *
+ * $e[] = array(
+ * 'type' => 'text',
+ * 'req' => false
+ * 'name' => 'text_field',
+ * 'display' => 'Text Label',
+ * );
+ * $e[] = array(
+ * 'type' => 'checkbox',
+ * 'req' => false
+ * 'name' => 'checkbox_field',
+ * 'display' => 'Checkbox Label',
+ * );
+ *
+ * // ... More Element Definitions
+ *
+ * $this->setupElements($e);
+ * }
+ *
+ *
+ * @access public
+ * @return void
+ */
+ //public function configureElements();
+
+ // }}}
+ // {{{ configureForm()
+
+ /**
+ * Calls all the configuration methods to configure a form for use
+ *
+ * @access public
+ * @return void
+ */
+ //public function configureForm();
+
+ // }}}
+ // {{{ configureRules()
+
+ /**
+ * Defines all element rules to be used for validation in the form
+ *
+ * At the bare minimum, this function needs to be called to setup the
+ * form rules, even if no extra rules are defined. Because it still
+ * creates all the required rules that are defined w/ each element.
+ *
+ *
+ * public function configureRules()
+ * {
+ * $r = array();
+ *
+ * $r[] = array(
+ * 'element' => 'text_field',
+ * 'message' => 'ERROR: 10 characters max!',
+ * 'type' => 'maxlength',
+ * 'format' => 10,
+ * 'validation' => $this->validationType,
+ * 'reset' => true,
+ * 'force' => false,
+ * );
+ * $r[] = array(
+ * 'element' => 'text_field',
+ * 'message' => 'ERROR: Numric characters only!',
+ * 'type' => 'numeric',
+ * 'format' => null,
+ * 'validation' => $this->validationType,
+ * 'reset' => true,
+ * 'force' => false,
+ * );
+ *
+ * // ... More Rule Definitions
+ *
+ * $this->setupRules($r);
+ * }
+ *
+ *
+ * @access public
+ * @return void
+ */
+ public function configureRules();
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Get an html string that contains the form
+ *
+ * Check if the form needs to be validated (ie. it was submitted)
+ * Check if submitted data needs to be processed
+ *
+ * @access public
+ * @return string an html string that contains the entire form
+ */
+ public function toHtml();
+
+ // }}}
+}
diff --git a/Toolkit/FormBuilder.php b/Toolkit/FormBuilder.php
new file mode 100644
index 0000000..2265a77
--- /dev/null
+++ b/Toolkit/FormBuilder.php
@@ -0,0 +1,1263 @@
+
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @version CVS: $Id: FormBuilder.php,v 1.48 2010/08/05 14:19:47 jamie Exp $
+ * @link http://demo.gaslightmedia.com
+ */
+
+/**
+ * Allow the inclusion of the Element Grid plugin for QuickForms
+ */
+require_once 'HTML/QuickForm/ElementGrid.php';
+
+/**
+ * Handle QuickForm CAPTCHA's
+ */
+require_once 'HTML/QuickForm/CAPTCHA/Image.php';
+
+/**
+ * Require PEAR QuickForm class
+ */
+require_once 'HTML/QuickForm.php';
+
+require_once 'HTML/QuickForm/Rule/Zip.php';
+require_once 'HTML/QuickForm/Rule/Phone.php';
+require_once 'HTML/QuickForm/Rule/Banwords.php';
+require_once 'HTML/QuickForm/Rule/State.php';
+
+/**
+ * Base functionality for creating HTML_Quickforms
+ *
+ * @category Toolkit
+ * @package FormBuilder
+ * @author Jamie Kahgee
+ * @copyright 2008 Gaslight Media
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @link http://demo.gaslightmedia.com
+ */
+abstract class Toolkit_FormBuilder extends HTML_QuickForm
+{
+ // {{{ properties
+
+ /**
+ * Holds if the form has been validated
+ *
+ * once the form is validated, store the result of the validation
+ * in this variable. That way, if multiple validations are attempted
+ * we don't have to reprocess all the data
+ *
+ * @var boolean
+ * @access protected
+ */
+ protected $validated = false;
+
+ /**
+ * Global form captcha question element
+ *
+ * @var object
+ * @access protected
+ */
+ protected $captchaQuestion;
+
+ /**
+ * Global form captcha answer from user
+ *
+ * @var object
+ * @access protected
+ */
+ protected $captchaAnswer;
+
+ /**
+ * Form submitted data used when emailing form results
+ *
+ * @var array
+ * @access protected
+ */
+ protected $formData = array();
+
+ /**
+ * Automatically create validation rules for any date elements
+ *
+ * This will only work if the date element is in m-d-Y format
+ *
+ * @var boolean
+ * @access protected
+ */
+ protected $autoValidateDateElements = true;
+
+ /**
+ * How do we want to validate the form
+ *
+ * Possible options are [server, client]
+ *
+ * @var string
+ * @access protected
+ */
+ protected $validationType = 'server';
+
+ /**
+ * Some special forms dont utlize this stylesheet
+ * Allow classes to override this setting so it doesn't
+ * get included
+ *
+ * @var boolean
+ * @access protected
+ */
+ protected $includeContactStyleSheet = true;
+
+ /**
+ * What do you want the error msg to be if the form doesn't validate
+ *
+ * @var string
+ * @access protected
+ */
+ protected $errorMsg
+ = '
+ Warning: The form was not sent, please review the errors below.
+
';
+
+ /**
+ * What do you want the success msg to be if the form validates successfully
+ *
+ * @var string
+ * @access protected
+ */
+ protected $successMsg
+ = '
+ The information below has been successfully submitted.
+
';
+
+ /**
+ * Whether uploaded files should be processed too (if present)
+ *
+ * @var string
+ * @access protected
+ */
+ protected $mergeFiles = true;
+
+ /**
+ * Include a captcha on the form or not
+ *
+ * @var boolean
+ * @access protected
+ */
+ protected $useCaptcha;
+
+ /**
+ * The default templates to inject into the form renderer
+ *
+ * @var string
+ * @access protected
+ */
+ protected $template;
+
+ /**
+ * The default rules to register for validating
+ *
+ * We have to register these rules, or any others we want, before
+ * we are able to use them in our forms.
+ *
+ * These rules can be removed in subclasses before the rules are configured
+ * if you want to omit any of them from validating input - just remember
+ * to not call them in your configured rules!
+ *
+ * Phone: validates input against US and CA style phone #'s
+ *
+ * $rules[] = array('element' => 'phone',
+ * 'message' => 'ERROR: Invalid Phone Format!',
+ * 'type' => 'phone',
+ * 'format' => null,
+ * 'validation' => $this->validationType,
+ * 'reset' => true,
+ * 'force' => false);
+ *
+ *
+ * Zip: Validates input against US and CA zip codes, if DB check is
+ * set to true, validate zip codes against all the zip codes in the
+ * DB.
+ *
+ * $rules[] = array('element' => 'zip',
+ * 'message' => 'ERROR: Invalid Zip!',
+ * 'type' => 'zip',
+ * 'format' => array('requireDBCheck' => true),
+ * 'validation' => $this->validationType,
+ * 'reset' => true,
+ * 'force' => false);
+ *
+ *
+ * Banwords: Make sure each each doesn't contain a banned word. Checking
+ * against a DB of banned words.
+ *
+ * State: Validate input against US and CA region / province codes. If DB
+ * check is set to true, validate region / province against all the
+ * regions / provinces in the DB.
+ *
+ * $rules[] = array('element' => 'state_id',
+ * 'message' => 'ERROR: Invalid State / Province!',
+ * 'type' => 'state',
+ * 'format' => array('requireDBCheck' => true),
+ * 'validation' => $this->validationType,
+ * 'reset' => true,
+ * 'force' => false);
+ *
+ *
+ * @var array
+ * @access protected
+ * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Zip.php
+ * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Phone.php
+ * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Banwords.php
+ * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/State.php
+ */
+ protected $registeredRules = array('phone', 'zip', 'state');
+
+ // }}}
+ // {{{ __construct()
+
+ /**
+ * Class constructor
+ *
+ * @param string $formName Form's name.
+ * @param string $method (optional)Form's method defaults to 'POST'
+ * @param string $action (optional)Form's action
+ * @param string $target (optional)Form's target defaults to '_self'
+ * @param mixed $attributes (optional)Extra attributes for
tag
+ * @param bool $trackSubmit (optional)Whether to track if the form was
+ * submitted by adding a special hidden field
+ *
+ * @access public
+ * @link http://pear.php.net/package/HTML_QuickForm/docs/latest/HTML_QuickForm/HTML_QuickForm.html
+ * @see HTML_QuickForm
+ * @todo Remove assigning the dbh the global dbh and setup a PDO
+ * to be passed in from a parameter - this will allow for easier
+ * PHPUnit testing
+ */
+ public function __construct(
+ $formName,
+ $method = 'post',
+ $action = '',
+ $target = '',
+ $attributes = null,
+ $trackSubmit = false
+ ) {
+ // T_VARIABLE error when passing this server
+ // var in on the constructors params.
+ $action = empty($action) ? $_SERVER['REQUEST_URI'] : $action;
+ parent::__construct(
+ $formName,
+ $method,
+ $action,
+ $target,
+ $attributes,
+ $trackSubmit
+ );
+ $this->template = BASE . 'Toolkit/Forms/templates/tables/';
+ $this->dbh = Toolkit_Database::getInstance();
+
+ if ($this->includeContactStyleSheet) {
+ $GLOBALS['styleSheets'][] = BASE_URL . 'css/contactform.css';
+ }
+ }
+
+ // }}}
+
+ // {{{ addCharLimit()
+
+ /**
+ * adds a maxlength character limitation to an element
+ *
+ * @param string $element The name of the element to add
+ * the char limit to
+ * @param integer $limit The number of characters allowed
+ *
+ * @return void
+ * @access public
+ */
+ public function addCharLimit($element, $limit)
+ {
+ $this->addRule(
+ $element,
+ "ERROR: $limit characters max!",
+ 'maxlength',
+ $limit,
+ 'server'
+ );
+ // Add a maxlength attribute to the field on the form
+ // which will help prevent users from sending over 100 chars
+ // to the server in the first place.
+ if ($this->getElementType($element) == 'text') {
+ $e =& $this->getElement($element);
+ $e->setMaxLength($limit);
+ }
+ }
+
+ // }}}
+ // {{{ apiVersion()
+
+ /**
+ * Returns the current FormBuilder API version
+ *
+ * @since 1.10
+ * @access public
+ * @return float
+ */
+ public function version()
+ {
+ return 1.1;
+ }
+
+ // }}}
+
+ // {{{ checkDateInput()
+
+ /**
+ * Checks the validation of a m-d-Y date
+ *
+ * This function will only be called if the autoValidateDateElements
+ * property is set to true.
+ *
+ * @param array $value Date element from form.
+ *
+ * @return boolean
+ * @access public
+ * @see Toolkit_FormBuilder->autoValidateDateElements
+ */
+ public function checkDateInput($value)
+ {
+ if ( empty($value['m'])
+ || empty($value['d'])
+ || empty($value['Y'])
+ ) {
+ return false;
+ }
+ return checkdate($value['m'], $value['d'], $value['Y']);
+ }
+
+ // }}}
+ // {{{ cleanElements()
+
+ /**
+ * Removes elements from form
+ *
+ * Loops through elements and if any names end in '_rmv' they are removed
+ * from the form object.
+ *
+ * Dynamically finds all elements on a form and removes any that
+ * end in '_rmv'. This can be really usefull if you want to display
+ * from results after the form was successfully submitted and validated,
+ * but don't need to display any extra form elements initially displayed
+ * on the form (like email confirmation fields, or static instruction
+ * elements).
+ *
+ * @param array $elements Form elements.
+ *
+ * @return void
+ * @access protected
+ */
+ protected function cleanElements($elements)
+ {
+ if (is_array($elements)) {
+ foreach ($elements as $e) {
+ if ($e['type'] == 'group') {
+ $this->cleanGroup($e['name']);
+ } elseif (preg_match('/^.+_rmv$/', $e['name'])) {
+ $this->removeElement($e['name']);
+ }
+ }
+ }
+ }
+
+ // }}}
+ // {{{ cleanForm()
+
+ /**
+ * Removes elements from form
+ *
+ * Dynamically finds all elements on a form and removes any that
+ * end in '_rmv'. This can be really usefull if you want to display
+ * from results after the form was successfully submitted and validated,
+ * but don't need to display any extra form elements initially displayed
+ * on the form (like email confirmation fields, or static instruction
+ * elements).
+ *
+ * @return void
+ * @access protected
+ */
+ protected function cleanForm()
+ {
+ $formArray = $this->toArray();
+ if (array_key_exists('sections', $formArray)) {
+ foreach ($formArray['sections'] as $k) {
+ if (preg_match('/^.+_rmv$/', $k['name'])) {
+ $this->removeElement($k['name']);
+ }
+ $this->cleanElements($k['elements']);
+ }
+ } else {
+ $this->cleanElements($formArray['elements']);
+ }
+ }
+
+ // }}}
+ // {{{ cleanGroup()
+
+ /**
+ * Removes any elements from a group that have names that end in '_rmv'
+ *
+ * @param string $name The name of the group element
+ *
+ * @return void
+ * @access protected
+ */
+ protected function cleanGroup($name)
+ {
+ $e =& $this->getElement($name);
+ $g =& $e->getElements();
+ $i = 0;
+ while ($name = $e->getElementName($i++)) {
+ if (preg_match('/^.+_rmv/', $name)) {
+ unset($g[($i - 1)]);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ createGridElement()
+
+ /**
+ * Creates a grid element for the form
+ *
+ * Written so this can be overridden easily in subclasses later if needed!
+ *
+ * @param string $elementType QuickForm ElementGrid element definition
+ *
+ * @return mixed ElementGrid element.
+ * @access protected
+ */
+ protected function &createGridElement($elementType)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array($this, 'createElement'), $args);
+
+ }
+
+ // }}}
+
+ // {{{ getErrorMessage()
+
+ public function getErrorMessage()
+ {
+ return $this->errorMsg;
+ }
+
+ // }}}
+ // {{{ prepElement()
+
+ /**
+ * Make sure all the element array indexes are set
+ *
+ * @param array &$e Element to prep
+ *
+ * @return void
+ * @access protected
+ */
+ protected function prepElement(&$e)
+ {
+ if (!isset($e['opts'])) {
+ $e['opts'] = '';
+ }
+ if (!isset($e['att'])) {
+ $e['att'] = '';
+ }
+ if (!isset($e['val'])) {
+ $e['val'] = '';
+ }
+ if (!isset($e['display'])) {
+ $e['display'] = '';
+ }
+ if (!isset($e['label'])) {
+ $e['label'] = '';
+ }
+ if (!isset($e['noCharLimit'])) {
+ $e['noCharLimit'] = false;
+ }
+ }
+
+ // }}}
+
+ // {{{ registerRules()
+
+ /**
+ * Registers custom form rules you can use when validating
+ *
+ * If the registeredRule token is an array, any QF rule can be
+ * registered. This is useful if you want to register rules
+ * from outside classes
+ * e.g. (Validate, Validate_US, Validate_CA, etc...).
+ *
+ * This will set up a rule called 'checkEmail' which uses the
+ * Validate::email() function.
+ * you can still pass in parameters to this rule if you pass them in
+ * via the 'format' option when you are defining your rules.
+ *
+ * class exampleClass
+ * {
+ * $registeredRules = array(
+ * array(
+ * 'checkEmail',
+ * 'callback',
+ * 'email',
+ * 'Validate'
+ * )
+ * );
+ *
+ * // ... Rest of your class code
+ *
+ * public function configureRules()
+ * {
+ * $r = array();
+ *
+ * $r[] = array(
+ * 'element' => 'process_email',
+ * 'message' => 'ERROR: Invalid email format!',
+ * 'type' => 'checkEmail',
+ * 'format' => array('use_rfc822' => true),
+ * 'validation' => $this->validationType,
+ * 'reset' => false,
+ * 'force' => false
+ * );
+ *
+ * $this->setupRules($r);
+ * }
+ *
+ * // ... Rest of your class code
+ * }
+ *
+ *
+ * If the registeredRule is a string, the corresponding rule in the
+ * glmPEAR/HTML/QuickForm/Rule/ directory will be registered with
+ * the form.
+ *
+ * This will set up the 'phone' rule already defined in the
+ * glmPEAR/HTML/QuickForm/Rule directory which validates both
+ * US and Canadian phone numbers
+ *
+ * class exampleClass
+ * {
+ * $registeredRules = array('phone');
+ *
+ * // ... Rest of your class code
+ *
+ * public function configureRules()
+ * {
+ * $r = array();
+ *
+ * $r[] = array(
+ * 'element' => 'phone',
+ * 'message' => 'ERROR: Invalid number (xxx) xxx-xxxx!',
+ * 'type' => 'phone',
+ * 'format' => null,
+ * 'validation' => $this->validationType,
+ * 'reset' => false,
+ * 'force' => false
+ * );
+ *
+ * $this->setupRules($r);
+ * }
+ *
+ * // ... Rest of your class code
+ * }
+ *
+ *
+ * @return void
+ * @access protected
+ */
+ protected function registerRules()
+ {
+ if (is_array($this->registeredRules)) {
+ foreach ($this->registeredRules as $r) {
+ if (is_array($r)) {
+ call_user_func_array(array(&$this, 'registerRule'), $r);
+ } else {
+ // Don't nedd to register rules more than once
+ if (!$this->isRuleRegistered($r)) {
+ $this->registerRule($r, null, "HTML_QuickForm_Rule_$r");
+ }
+ }
+ }
+ }
+ }
+
+ // }}}
+
+ // {{{ setAutoValidateDateElements()
+
+ /**
+ * Set if we need to auto validate the Date Elements
+ *
+ * @param boolean $validate true/false to auto validate date elements
+ *
+ * @return void
+ * @access public
+ */
+ public function setAutoValidateDateElements($validate)
+ {
+ $this->autoValidateDateElements = $validate;
+ }
+
+ // }}}
+ // {{{ setFormData()
+
+ /**
+ * Sets the submitted values from the form
+ *
+ * Set these values into an internal variable so they will be accessible
+ * anywhere we need them in the form.
+ *
+ * @param array $exclude (optional) Any element names you don't want
+ * included. Since this is primarily used in
+ * emailing, this is helpful to exclude any data
+ * we don't want before the array is generated.
+ *
+ * @return void
+ * @access protected
+ */
+ protected function setFormData(array $exclude = array())
+ {
+ $values = $this->getSubmitValues();
+ foreach ($values as $k => $v) {
+ if ($this->elementExists($k)) {
+ $e =& $this->getElement($k);
+ if (!in_array($e->getName(), $exclude)) {
+ switch ($e->getType()) {
+ case 'date' :
+ list($m, $d, $y) = array_values($e->getValue());
+ // If all the date fields are empty, then don't add
+ // the output to the formData.
+ if (!(empty($m[0]) && empty($d[0]) && empty($y[0]))) {
+ $this->formData[$e->getName()]['label']
+ = $e->getLabel();
+
+ $oldDate = $e->getValue();
+ $newDate = Toolkit_Common::arrayFlatten(
+ $oldDate,
+ 0,
+ $newDate
+ );
+
+ $this->formData[$e->getName()]['element']
+ = implode(' / ', $newDate);
+ unset($oldDate, $newDate);
+ }
+ break;
+
+ case 'group':
+ $e->freeze();
+ $this->formData[$e->getName()]['label'] = $e->getLabel();
+ $this->formData[$e->getName()]['element'] = $e->toHtml();
+ break;
+
+ case 'select' :
+ $this->formData[$e->getName()]['label'] = $e->getLabel();
+
+ $values = $e->getValue();
+ foreach ($values as $v) {
+ $this->formData[$e->getName()]['element'] .= $v;
+ }
+ break;
+
+ default :
+ $this->formData[$e->getName()]['label'] = $e->getLabel();
+ $this->formData[$e->getName()]['element'] = $e->getValue();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // }}}
+ // {{{ setupConstants()
+
+ /**
+ * Sets the form constant values
+ *
+ * @param array $constants Associative array of form constant values.
+ *
+ * @return void
+ * @access protected
+ */
+ protected function setupConstants($constants)
+ {
+ $this->setConstants($constants);
+ }
+
+ // }}}
+ // {{{ setupDefaults()
+
+ /**
+ * Sets the form default values
+ *
+ * @param array $defaults Associative array of form default values.
+ *
+ * @return void
+ * @access protected
+ */
+ protected function setupDefaults($defaults)
+ {
+ $this->setDefaults($defaults);
+ }
+
+ // }}}
+ // {{{ setupElements()
+
+ /**
+ * Sets up all the elements to the form
+ *
+ * Takes a multi-dimensional array of form elements and uses them
+ * to set up the form objects elements
+ *
+ * @param array $elements Multi-Dimensional array of form elements.
+ *
+ * @return void
+ * @access protected
+ */
+ protected function setupElements($elements)
+ {
+ if (!is_array($elements)) {
+ return;
+ }
+ foreach ($elements as &$e) {
+ $this->prepElement($e);
+ switch ($e['type']) {
+ case 'group' :
+ if (is_array($e['group']) && !empty($e['group'])) {
+ // Special rendering for grouped elements.
+ unset($field);
+ foreach ($e['group'] as $g) {
+ $this->prepElement($g);
+ $field[] =& HTML_QuickForm::createElement(
+ $g['type'],
+ $g['name'],
+ $g['display'],
+ $g['opts'],
+ $g['att'],
+ $g['val']
+ );
+ }
+ $source =& $this->addGroup(
+ $field,
+ $e['name'],
+ $e['label'],
+ $e['seperator'],
+ $e['appendName']
+ );
+ }
+ break;
+
+ case 'elementGrid' :
+ $source =& $this->addElement(
+ $e['type'],
+ $e['name'],
+ $e['display'],
+ $e['opts'],
+ $e['att'],
+ $e['val']
+ );
+ unset($columnNames);
+
+ // Loop through the rows (r) and columns (c)
+ // to add each of the elements to our grid.
+ foreach ($e['group'] as $k => $r) {
+ unset($set, $rowTitle);
+ foreach ($r as $c) {
+ $columnNames[$c['display']] = $c['display'];
+
+ $set[] =& $this->createGridElement(
+ $c['type'],
+ $c['name'],
+ null,
+ $c['opts'],
+ $c['att']
+ );
+ }
+ $rowTitle = is_int($k) ? ' ' : $k;
+ $source->addRow($set, $rowTitle);
+ }
+
+ $source->setColumnNames($columnNames);
+ break;
+
+ default :
+ // Render all elements except groups
+ try {
+ $source =& $this->addElement(
+ $e['type'],
+ $e['name'],
+ $e['display'],
+ $e['opts'],
+ $e['att'],
+ $e['val']
+ );
+
+ if (PEAR::isError($source)) {
+ var_dump($source);
+ throw new Exception ('PEAR QuickForm Element Error');
+ }
+ } catch (HTML_QuickForm_Error $e) {
+ Toolkit_Common::dieGracefully(null, $e);
+ } catch (Exception $e) {
+ Toolkit_Common::handleError($e);
+ }
+
+ if ($e['type'] == 'advmultiselect') {
+ $source->setLabel($e['labels']);
+ }
+ if ($e['name'] == 'categories') {
+ $res = $source->loadArray($this->categories);
+ if (PEAR::isError($res)) {
+ Toolkit_Common::dieGracefully(null, $res);
+ }
+ }
+ if ($e['type'] == 'header') {
+ $this->formHeaders[$e['display']] = $e;
+ }
+
+ if ($e['name'] == 'captcha_rmv') {
+ $this->captchaAnswer =& $source;
+ }
+
+ if ($e['name'] == 'captcha_question') {
+ $this->captchaQuestion =& $source;
+ }
+ break;
+ }
+ }
+ $this->formElements = $elements;
+ }
+
+ // }}}
+ // {{{ setupFilters()
+
+ /**
+ * Sets any filters needed for the form elements when submitting
+ *
+ * @param array $filters Element filters.
+ *
+ * @return void
+ * @access protected
+ */
+ protected function setupFilters($filters)
+ {
+ foreach ($filters as $f) {
+ $res = $this->applyFilter($f['element'], $f['filter']);
+
+ if (PEAR::isError($res)) {
+ Toolkit_Common::handleError($res);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ setupRules()
+
+ /**
+ * Apply rules to the form
+ *
+ * 100 & 1000 char length limitations are automatically assigned to
+ * text/textarea elements to help reduce load limitations on the server.
+ * -request per Chuck in a conference call on (5/22/2009 @ 12:15pm)
+ *
+ * Applies rules that are defined in child classes to the form elements
+ * group rules can be kind of tricky, since you can't apply a rule
+ * directly to an element inside of a rule you have to define
+ * the rule inside a nest of array's and then add a group rule.
+ * the array will contain all the elements inside the group you wish
+ * to apply rules to.
+ *
+ * You can assign as many rules as you would like to individual elements,
+ * and you aren't required to make the array associative, although it is
+ * easier to see whats going on.
+ *
+ * see: http://pear.activeventure.com/package/package.html.html-quickform.html-quickform.addgrouprule.html
+ * for another example.
+ *
+ * // Define the rules for each element in the group.
+ * $num_rule = array(
+ * 'ERROR: Must contain a valid decimal number!',
+ * 'numeric'
+ * );
+ * // Collect together the rules for each element.
+ * $lat_rules = array('lat' => array($num_rule));
+ * $lon_rules = array('lon' => array($num_rule));
+ * $r[] = array(
+ * 'element' => 'latitude',
+ * 'message' => 'ERROR:',
+ * 'type' => 'group',
+ * 'format' => $lat_rules,
+ * 'validation' => $this->validationType,
+ * 'reset' => false,
+ * 'force' => false
+ * );
+ *
+ *
+ * To make a group required but not require every element in the group
+ * you can use the addGroupRule function again
+ * for example: say you have a group of checkboxes and you only only
+ * require 1 be checked. a simple group rule such as the following
+ * will handle this.
+ * N.B. notice the extra "howMany" index.
+ *
+ * $r[] = array(
+ * 'element' => 'element_name',
+ * 'message' => 'ERROR: Error to display!',
+ * 'type' => 'required',
+ * 'format' => null,
+ * 'howMany' => 1,
+ * 'validation'=> $this->validationType,
+ * 'reset' => true,
+ * 'force' => false,
+ * );
+ *
+ *
+ * @param array $rules Multi-Dimensional array of rules for form elements.
+ *
+ * @return void
+ * @access protected
+ */
+ protected function setupRules(array $rules = null)
+ {
+ $this->registerRules();
+ $preRegisteredRules = $this->getRegisteredRules();
+ if (is_array($this->formElements)) {
+ foreach ($this->formElements as $e) {
+ // Put length limitations on text and textarea fields
+ if ($e['type'] == 'text' && !$e['noCharLimit']) {
+ $this->addCharLimit($e['name'], 100);
+ } elseif ($e['type'] == 'textarea' && !$e['noCharLimit']) {
+ $this->addCharLimit($e['name'], 1000);
+ } elseif ($e['type'] == 'group') {
+ // We need to apply these same limitations to the
+ // text and textarea fields inside of groups
+ $r = array();
+ if (is_array($e['group'])) {
+ foreach ($e['group'] as $i) {
+ // Loop through group elements and if they are
+ // text/textarea fields then put the field into
+ // a rule array that we will assign to the group
+ if ($i['type'] == 'text' && !$i['noCharLimit']) {
+ $r[$i['name']][] = array(
+ 'ERROR: 100 characters max!',
+ 'maxlength'
+ );
+ } elseif ($i['type'] == 'textarea' && !$i['noCharLimit']) {
+ $r[$i['name']][] = array(
+ 'ERROR: 1000 characters max!',
+ 'maxlength'
+ );
+ }
+ }
+ }
+ if (!empty($r)) {
+ $this->addGroupRule($e['name'], $r);
+ }
+ }
+ if ($this->validationType == 'client') {
+ $label = $e['display'];
+ }
+ if ($e['req']) {
+ if ($e['type'] == 'group') {
+ foreach ($e['group'] as $ge) {
+ if ($ge['req']) {
+ $rule[$ge['name']][] = array(
+ 'ERROR: You must complete this field!',
+ 'required',
+ null,
+ $this->validationType);
+ }
+ }
+ $this->addGroupRule($e['name'], $rule);
+ unset($rule);
+ } elseif ($e['type'] == 'date') {
+ if (!empty($e['error'])) {
+ // Custom error message for the date element.
+ $error = $e['error'];
+ } else {
+ // Default error message for the date element.
+ $error = 'ERROR: You must enter a date!';
+ }
+ $this->addGroupRule(
+ $e['name'],
+ $error,
+ 'required',
+ 3,
+ $this->validationType
+ );
+ if ($this->autoValidateDateElements) {
+ $this->addRule(
+ $e['name'],
+ 'ERROR: Date is invalid!',
+ 'callback',
+ array(&$this, 'checkDateInput')
+ );
+ }
+ } else {
+ $this->addRule(
+ $e['name'],
+ "$label ERROR: You must complete this field!",
+ 'required',
+ null,
+ $this->validationType
+ );
+ }
+ }
+ }
+ }
+ if (is_array($rules)) {
+ foreach ($rules as $r) {
+ if (!is_array($r['element'])) {
+ $group = ($this->getElementType($r['element']) == 'group');
+ }
+ if ($group) {
+ $this->addGroupRule(
+ $r['element'],
+ $r['message'],
+ $r['type'],
+ $r['format'],
+ $r['howMany'],
+ $r['validation'],
+ $r['reset']
+ );
+ } elseif (in_array($r['type'], $preRegisteredRules)) {
+ $this->addRule(
+ $r['element'],
+ $r['message'],
+ $r['type'],
+ $r['format'],
+ $r['validation'],
+ $r['reset'],
+ $r['force']
+ );
+ }
+ }
+ }
+ }
+
+ // }}}
+ // {{{ setupRenderers()
+
+ /**
+ * Sets up renderers for form objects
+ *
+ * Uses the default form renderer to allow templates to be injected
+ * into the form object before displaying on a page. This allows
+ * changing the face of the form w/ out any backend adjustment.
+ *
+ * Files that can be created for templates are
+ *
+ * Examples:
+ * # Element.tpl
+ *
+ *
+ *
+ *
+ * *
+ *
+ *
+ *
+ *
+ *
+ *
{error}
+ *
+ * {element}
+ *
+ *
+ *
+ *
+ * # Form.tpl
+ *
+ *
+ *
+ *
+ * {content}
+ *
+ *
+ *
+ *
+ *
+ * # GroupElement.tpl
+ *
+ *
+ *
+ * {element}
+ *
+ * *
+ *
+ * {label}
+ *
+ *
+ *
+ *
+ * # Group.tpl
+ *
+ *
+ *
+ * {content}
+ *
+ *
+ *
+ *
+ * # Header.tpl
+ *
+ *
+ *
+ * {header}
+ *
+ *
+ *
+ *
+ * # RequiredNote.tpl
+ *
+ * * Denotes required field
+ *
+ *
+ * @param array $groups any groups that need to be rendered
+ * via the groupElementTemplate and groupTemplate
+ *
+ * @return void
+ * @access protected
+ */
+ protected function setupRenderers(array $groups = array())
+ {
+ $renderer =& $this->defaultRenderer();
+ if (is_dir($this->template)) {
+ if ($dh = opendir($this->template)) {
+ while (($file = readdir($dh)) !== false) {
+ // Ignore current dir and parent dir.
+ if ($file != '..' && $file != '.' && $file != 'CVS') {
+ $baseName = reset(explode('.', $file));
+ // Ignores swp files.
+ if (!empty($baseName)) {
+ $method = "set{$baseName}Template";
+ $template = file_get_contents($this->template . $file);
+ if ( $method != 'setGroupTemplate'
+ && $method != 'setGroupElementTemplate'
+ ) {
+ if (method_exists($renderer, $method)) {
+ $renderer->$method($template);
+ }
+ } else {
+ // apply the templates to any defined groups
+ foreach ($groups as $k) {
+ $renderer->$method($template, $k);
+ }
+ }
+ }
+ }
+ }
+ closedir($dh);
+ }
+ }
+
+ // Set the default how the captcha's should look
+ // can be overridden in subclasses
+ if ($this->useCaptcha) {
+ $required = '*';
+ $error = '
',
+ 'captcha_rmv'
+ );
+ }
+ }
+
+ // }}}
+ // {{{ useCaptcha()
+
+ /**
+ * Set if we need to use a captcha in this form or not
+ *
+ * @param boolean $useCaptcha true/false to use a captcha with the form
+ *
+ * @return void
+ * @access public
+ */
+ public function useCaptcha($useCaptcha)
+ {
+ $this->useClueTip();
+ $this->useCaptcha = $useCaptcha;
+ }
+
+ // }}}
+ // {{{ useClueTip()
+
+ /**
+ * Set if we need to use the JS cluetip
+ *
+ * @param boolean $useCaptcha include cluetip resources or not
+ *
+ * @return void
+ * @access public
+ */
+ public function useClueTip()
+ {
+ $GLOBALS['styleSheets'][]
+ = GLM_APP_BASE_URL . 'libjs/cluetip/jquery.cluetip.css';
+ $GLOBALS['bottomScripts'][]
+ = GLM_APP_BASE_URL . 'libjs/cluetip/jquery.cluetip.js';
+ $GLOBALS['bottomScripts'][]
+ = GLM_APP_BASE_URL . 'libjs/cluetip/lib/jquery.hoverIntent.js';
+ $GLOBALS['bottomScripts'][]
+ = GLM_APP_BASE_URL . 'libjs/cluetip/lib/jquery.bgiframe.min.js';
+ $GLOBALS['bottomScripts'][]
+ = GLM_APP_BASE_URL . 'libjs/contactform.js';
+ }
+
+ // }}}
+ // {{{ show()
+
+ /**
+ * Shows the form
+ *
+ * @return void
+ * @access public
+ */
+ public function show()
+ {
+ Toolkit_Common::show();
+ }
+
+ // }}}
+ // {{{ validate()
+
+ public function validate()
+ {
+ if (!$this->validated) {
+ $this->validated = parent::validate();
+ }
+
+ return $this->validated;
+ }
+
+ // }}}
+}
diff --git a/Toolkit/Forms/Rules/Date.php b/Toolkit/Forms/Rules/Date.php
new file mode 100644
index 0000000..e5a53c8
--- /dev/null
+++ b/Toolkit/Forms/Rules/Date.php
@@ -0,0 +1,93 @@
+
+ * @copyright 2012 Gaslight Media
+ * @license Gaslight Media
+ * @version SVN: $id$
+ * @link <>
+ */
+
+/**
+ * Toolkit_Forms_Rules_Date
+ *
+ * Description of Toolkit_Forms_Rules_Date
+ *
+ * @category Toolkit
+ * @package Forms/Rules
+ * @author Jamie Kahgee
+ * @copyright 2012 Gaslight Media
+ * @license Gaslight Media
+ * @release Release: $id$
+ * @link <>
+ */
+class Toolkit_Forms_Rules_Date extends HTML_QuickForm_Rule
+{
+ /**
+ * Validate if a string is a valid date
+ *
+ * If element is part of a group, then you can pass in the group name
+ * under the options parameter, that way you can find the date values
+ *
+ * You must pass in at least the format of the date in an array.
+ * array('format' => '%m-%d-%Y') is the default
+ *
+ * @param string $value the date array to validate
+ * @param array $options options used to dictate validation of the date
+ *
+ * @return bool if the string could correctly be validated as a date.
+ * @access public
+ * @see Validate::date()
+ */
+ function validate($value, array $options)
+ {
+ $month = !isset($options['group'])
+ ? $value['m']
+ : $value[$options['group']]['m'];
+ $day = !isset($options['group'])
+ ? $value['d']
+ : $value[$options['group']]['d'];
+ $year = !isset($options['group'])
+ ? $value['Y']
+ : $value[$options['group']]['Y'];
+
+ if (isset($options['allowEmpty'])
+ && $options['allowEmpty']
+ && empty($month)
+ && empty($day)
+ && empty($year)
+ ) {
+ return true;
+ }
+
+ unset($options['group'], $options['allowEmpty']);
+
+ $day = str_pad($day, 2, '0', STR_PAD_LEFT);
+ $month = str_pad($month, 2, '0', STR_PAD_LEFT);
+
+ return Validate::date("$month-$day-$year", $options);
+ }
+
+ /**
+ * gets validation script
+ *
+ * @return array
+ */
+ function getValidationScript()
+ {
+ return array('', true);
+ }
+}
+
+HTML_QuickForm::registerRule(
+ 'Date',
+ 'rule',
+ 'Toolkit_Forms_Rules_Date',
+ BASE . 'Toolkit/Forms/Rules/Date.php'
+);
+?>
diff --git a/Toolkit/Forms/Rules/Image.php b/Toolkit/Forms/Rules/Image.php
new file mode 100644
index 0000000..86eec64
--- /dev/null
+++ b/Toolkit/Forms/Rules/Image.php
@@ -0,0 +1,209 @@
+
+ * @copyright 2012 Gaslight Media
+ * @license Gaslight Media
+ * @version SVN: $id$
+ * @link <>
+ */
+
+/**
+ * Toolkit_Forms_Rules_Image
+ *
+ * Description of Toolkit_Forms_Rules_Image
+ *
+ * @category Toolkit
+ * @package Forms/Rules
+ * @author Jamie Kahgee
+ * @copyright 2012 Gaslight Media
+ * @license Gaslight Media
+ * @release Release: $id$
+ * @link <>
+ */
+class Toolkit_Forms_Rules_Image extends HTML_QuickForm_Rule
+{
+ // {{{ validate()
+
+ /**
+ * Handle image uploading rules
+ *
+ * The options array is required and has specific key values
+ * that must be present.
+ *
+ * form : (HTML_Quickform) form object - so we can insert
+ * the new image name back back into the forms
+ * submit values.
+ * fieldName : (string) name of the upload field - this is
+ * generally the same name as the field we're
+ * calling the rule on.
+ * imageField : (string) name of the field that stores the
+ * filename of the image that was uploaded
+ * is : (Toolkit_FileServer_ImageAdapter)
+ * image server instance
+ * deleteExistingImage : (boolean) Whether to delete any old image from
+ * the image server before uploading the new image
+ * injectImage : (array)
+ * tgtElement : (string) Name of the field that will hold the preview
+ * of the image - This is usually a static element
+ * path : (optional) Path to the image - defaults to FORM_THUMB
+ * imgTag : (optional) Format of the image tag - defaults to
+ * where %s is the path + image
+ *
+ *
+ * Full Example:
+ *
+ * $r[] = array(
+ * 'element' => 'file_rmv',
+ * 'message' => 'ERROR: Error uploading image!',
+ * 'type' => 'Image',
+ * 'format' => array(
+ * 'form' => $this,
+ * 'fieldName' => 'file_rmv',
+ * 'imageField' => 'image',
+ * 'is' => new Toolkit_FileServer_ImageAdapter(),
+ * 'deleteExistingImage' => true,
+ * 'injectImage' => array(
+ * 'tgtElement' => 'current_image_rmv',
+ * 'imgTag' => '',
+ * 'path' => FORM_THUMB
+ * )
+ * ),
+ * 'validation' => $this->validationType,
+ * 'reset' => false,
+ * 'force' => false
+ * );
+ *
+ * @param string $value the $_FILES['xxxx'] array
+ * @param array $options upload options
+ *
+ * @return bool if the image was successfully uploaded or not
+ * @access public
+ */
+ function validate($value, array $options)
+ {
+ // No image uploaded - return true
+ // this can be a valid state, if the field is required - then a
+ // required rule should be used and will catch before this rule
+ // is even applied.
+ if (empty($value['tmp_name'])) {
+ return true;
+ }
+
+ if (!isset($options['fieldName'])) {
+ throw new RuntimeException(
+ 'Missing `fieldName` key in options'
+ );
+ }
+ if ( isset($options['is'])
+ && ($options['is'] instanceof Toolkit_FileServer_ImageAdapter)
+ ) {
+ $is = $options['is'];
+ } else {
+ throw new RuntimeException(
+ 'Missing `is` key in options for Image Server instance'
+ );
+ }
+
+ if ( isset($options['form'])
+ && ($options['form'] instanceof HTML_QuickForm)
+ ) {
+ $form =& $options['form'];
+ } else {
+ throw new RuntimeException(
+ 'Missing `form` key in options'
+ );
+ }
+
+ if (isset($options['imageField'])) {
+ $imageField = $options['imageField'];
+ } else {
+ throw new RuntimeException(
+ 'Missing `imageField` key in options'
+ );
+ }
+
+ if ( isset($options['injectImage'])
+ && !is_array($options['injectImage'])
+ ) {
+ if (!is_array($options['injectImage'])) {
+ throw new RuntimeException(
+ '`injectImage` must be an array of options'
+ );
+ }
+ } elseif ( isset($options['injectImage'])
+ && is_array($options['injectImage'])
+ && !isset($options['injectImage']['tgtElement'])
+ ) {
+ throw new RuntimeException(
+ 'The `injectImage` array needs a `tgtElement` index to inject the new image into'
+ );
+ }
+
+ if ( isset($options['deleteExistingImage'])
+ && $options['deleteExistingImage']
+ ) {
+ $existingImage =& $form->getSubmitValue($imageField);
+ if (!empty($existingImage)) {
+ $is->delete($existingImage);
+ }
+ }
+
+ try {
+ $uploadedImage = $is->upload($options['fieldName']);
+ } catch (RangeException $e) {
+ Toolkit_Logger::logException('Image Server', $e);
+ return false;
+ }
+
+ if (isset($options['injectImage'])) {
+ $imgTag = isset($options['injectImage']['imgTag'])
+ ? $options['injectImage']['imgTag']
+ : '';
+
+ $path = isset($options['injectImage']['path'])
+ ? $options['injectImage']['path']
+ : FORM_THUMB;
+
+ $htmlImg = sprintf($imgTag, $path . $uploadedImage['name']);
+ $tgt = $options['injectImage']['tgtElement'];
+ $currImg =& $form->getElement($tgt);
+ $currImg->setValue($htmlImg);
+ }
+
+ $fileName =& $form->getElement($imageField);
+ $fileName->setValue($uploadedImage['name']);
+ $form->_submitValues[$imageField] = $uploadedImage['name'];
+
+ return true;
+ }
+
+ // }}}
+
+ // {{{ getValidationScript()
+
+ /**
+ * gets validation script
+ *
+ * @return array
+ */
+ function getValidationScript()
+ {
+ return array('', true);
+ }
+
+ // }}}
+}
+
+HTML_QuickForm::registerRule(
+ 'Image',
+ 'rule',
+ 'Toolkit_Forms_Rules_Image',
+ BASE . 'Toolkit/Forms/Rules/Image.php'
+);
+?>
diff --git a/Toolkit/Forms/Rules/ShortUrl.php b/Toolkit/Forms/Rules/ShortUrl.php
new file mode 100644
index 0000000..823f14c
--- /dev/null
+++ b/Toolkit/Forms/Rules/ShortUrl.php
@@ -0,0 +1,81 @@
+
+ * @copyright 2012 Gaslight Media
+ * @license Gaslight Media
+ * @version SVN: $id$
+ * @link <>
+ */
+
+/**
+ * Toolkit_Forms_Rules_ShortUrl
+ *
+ * Description of Toolkit_Forms_Rules_ShortUrl
+ *
+ * @category Toolkit
+ * @package Forms/Rules
+ * @author Jamie Kahgee
+ * @copyright 2012 Gaslight Media
+ * @license Gaslight Media
+ * @release Release: $id$
+ * @link <>
+ */
+class Toolkit_Forms_Rules_ShortUrl extends HTML_QuickForm_Rule
+{
+ // {{{ validate()
+
+ /**
+ * Validate if a string is a valid short url
+ *
+ * Short URLs can only contain alpha numeric characters plus
+ * dash and underscore. They must also not be the same name as a file
+ * on the server.
+ *
+ * @param string $value short url to validate
+ *
+ * @return bool if the string could correctly be validated as a short url
+ * @access public
+ */
+ public function validate($value)
+ {
+ $hasValidCharacters = !preg_match('/[^-a-zA-Z0-9_]/', $value);
+ // Short URLs that are the same as an existing file ie: (index.php)
+ // cause .htaccess to throw errors
+ $fileDoesNotExist = !file_exists(BASE . $value);
+ // Short URLs that are the same as an existing file basename
+ // ie: (index) cause .htaccess to throw errors
+ list($path, $suffix) = explode('.', $value);
+ $files = array_filter(glob(BASE . "$path*"), 'is_file');
+ $baseNameDoesNotExist = ($files !== false && empty($files));
+ return $hasValidCharacters && $fileDoesNotExist && $baseNameDoesNotExist;
+ }
+
+ // }}}
+ // {{{ getValidationScript()
+
+ /**
+ * gets validation script
+ *
+ * @return array
+ */
+ public function getValidationScript()
+ {
+ return array('', true);
+ }
+
+ // }}}
+}
+
+HTML_QuickForm::registerRule(
+ 'ShortUrl',
+ 'rule',
+ 'Toolkit_Forms_Rules_ShortUrl',
+ BASE . 'Toolkit/Forms/Rules/ShortUrl.php'
+);
+?>
diff --git a/Toolkit/Forms/Rules/Url.php b/Toolkit/Forms/Rules/Url.php
new file mode 100644
index 0000000..4216c24
--- /dev/null
+++ b/Toolkit/Forms/Rules/Url.php
@@ -0,0 +1,80 @@
+
+ * @copyright 2012 Gaslight Media
+ * @license Gaslight Media
+ * @version SVN: $id$
+ * @link <>
+ */
+
+/**
+ * Toolkit_Forms_Rules_Url
+ *
+ * Description of Toolkit_Forms_Rules_Url
+ *
+ * @category Toolkit
+ * @package Forms/Rules
+ * @author Jamie Kahgee
+ * @copyright 2012 Gaslight Media
+ * @license Gaslight Media
+ * @release Release: $id$
+ * @link <>
+ */
+class Toolkit_Forms_Rules_Url extends HTML_QuickForm_Rule
+{
+ // {{{ validate()
+
+ /**
+ * Validate if a string is a valid url
+ *
+ * @param string $value url to validate
+ * @param array $options set to null
+ *
+ * @return bool if the string could correctly be validated as a url
+ * @access public
+ */
+ public function validate($value, array $options = null)
+ {
+ $validateFilter = FILTER_VALIDATE_URL;
+
+ if (is_null($options)) {
+ return (bool) filter_var($value, $validateFilter);
+ }
+
+ $lambdaFunction = create_function('$a, $b', 'return $a | $b;');
+ $flags = array_reduce($options, $lambdaFunction, 0);
+
+ return (bool) filter_var($value, $validateFilter, $flags);
+
+ }
+
+ // }}}
+ // {{{ getValidationScript()
+
+ /**
+ * gets validation script
+ *
+ * @return array
+ */
+ public function getValidationScript()
+ {
+ return array('', true);
+ }
+
+ // }}}
+}
+
+HTML_QuickForm::registerRule(
+ 'Url',
+ 'rule',
+ 'Toolkit_Forms_Rules_Url',
+ BASE . 'Toolkit/Forms/Rules/Url.php'
+);
+?>
diff --git a/Toolkit/Forms/templates/tables/Element.tpl b/Toolkit/Forms/templates/tables/Element.tpl
new file mode 100644
index 0000000..595457b
--- /dev/null
+++ b/Toolkit/Forms/templates/tables/Element.tpl
@@ -0,0 +1,14 @@
+
+
+
+ *
+
+
+
+
+
+
{error}
+
+ {element}
+
+
diff --git a/Toolkit/Forms/templates/tables/Form.tpl b/Toolkit/Forms/templates/tables/Form.tpl
new file mode 100644
index 0000000..8ed6c2f
--- /dev/null
+++ b/Toolkit/Forms/templates/tables/Form.tpl
@@ -0,0 +1,11 @@
+
+
+
+ {hidden}
+
+ {requiredNote}
+
+ {content}
+
+
+
\ No newline at end of file
diff --git a/Toolkit/Forms/templates/tables/Group.tpl b/Toolkit/Forms/templates/tables/Group.tpl
new file mode 100644
index 0000000..cdd24cf
--- /dev/null
+++ b/Toolkit/Forms/templates/tables/Group.tpl
@@ -0,0 +1,5 @@
+
+
+ {content}
+
+
diff --git a/Toolkit/Forms/templates/tables/GroupElement.tpl b/Toolkit/Forms/templates/tables/GroupElement.tpl
new file mode 100644
index 0000000..1a4ba27
--- /dev/null
+++ b/Toolkit/Forms/templates/tables/GroupElement.tpl
@@ -0,0 +1,9 @@
+
+
+ {element}
+
+ *
+
+ {label}
+
+
diff --git a/Toolkit/Forms/templates/tables/Header.tpl b/Toolkit/Forms/templates/tables/Header.tpl
new file mode 100644
index 0000000..64ac244
--- /dev/null
+++ b/Toolkit/Forms/templates/tables/Header.tpl
@@ -0,0 +1,5 @@
+
+
+ {header}
+
+
diff --git a/Toolkit/Forms/templates/tables/RequiredNote.tpl b/Toolkit/Forms/templates/tables/RequiredNote.tpl
new file mode 100644
index 0000000..8d61480
--- /dev/null
+++ b/Toolkit/Forms/templates/tables/RequiredNote.tpl
@@ -0,0 +1 @@
+
5ic7XC*2PRyZs2nd
zFr20p1+lU)V@d&oIV;d=u;g)IXjMd`r#xT)16@A=B@r;AymUVG$HIOE`jdju0GFlbHGWiZtM^#V-*nnT+dfBWfMM8o3l
z
z%7C$)-M5ycAbh@|o@)JZJh4xCEK|96hAZ~bVtKUXMg%Adw_ni^i`nl6iU7rdvIE?5
zBba-DO^wn>2Vl)aYVIN6%I+*5JgTRyKX(85J9w=KZ0rDAWoRwVJ!ftO+36w+0===P
zKO?5LW^lY~|5Tz@S1?kT+rdzH@hgjsO;%$l^fH
ztZPl^T)J=a+9N=&r~nUsizrQ(zzSw@O;lB^Go}+~Zalv8)Xm?2{$}*7`S9gCX6yrX
z0rg_7+^OkG6AhU?{ppxh3#3QU3Ts0=dikMX(nhQ%0QCY*0GdNXSJuY6Vr}pOLoT&z
zHS*XMFi?Ss7^@*b6M*InKzW%vVg1wBAJGImmhOXRmw_)Yfnm1i%xz3@V9;jut)sAR
zq>d|rq1Co{4=!0?00V=0fL#d;CtxXvtEmA2`_J8hls*Gi_W)ZGF2-s=#`fa@mqR68o;h=7kh>BfzyOxsTT=JZj*I{R002ovPDHLk
FV1kG^r+NSY
literal 0
HcmV?d00001
diff --git a/Toolkit/PHPImageEditor/images/edit.gif b/Toolkit/PHPImageEditor/images/edit.gif
new file mode 100644
index 0000000000000000000000000000000000000000..1a5a0afb0a0caa2a61167dff9d54a75b96d3e55c
GIT binary patch
literal 417
zcmV;S0bc$`Nk%w1VGaNh0M!5h=a~ocwK4SBr1A6i@#DSmsuKMD{qpki@$~ui)X47c
z@cR1t^!E1a>gn(E`^U$}?(p~B-r@NC|LpGc?(XjJ@bK*H?Cb08^YrxW?eFjJ@$m2N
z?(OUM)O_pg?eOpL%dmj*^6})@$=uo3=j7V;`~T(M*Y@1g;l!HVnkV+$m)O3v`uqIw
z@bBp5bv>gnk5@$m8S^7HfZ@$&TT?CSXV`1||&@9*#T_xJDc
z^Zot(A^8LV00000EC2ui01f~V000KQK*MiHEE0E&8wPW*Icq@39b#5NV>2k
zBC~b@b~P=nNfWAe-b%_i6tS^-1y(h@EsB~1TqDA_h@fkxG$bHgvj}VxE1JLgr!*!^
ILUxTc0Q$^Q5C8xG
literal 0
HcmV?d00001
diff --git a/Toolkit/PHPImageEditor/images/slider_pointer.gif b/Toolkit/PHPImageEditor/images/slider_pointer.gif
new file mode 100644
index 0000000000000000000000000000000000000000..8e8fdcdb809dbf36e09e3dedf327e9c5112d0eee
GIT binary patch
literal 347
zcmZ?wbhEHb^>y
zUAlDX>({UE-@jkFbm^BbUv})+@!`XVty{PL`SWM`^ywQmZ20)`V}5@A)2B~&?b`L}
z)2Cm*e%-rw@7uR;j~_qYzJ2?{hYx#ud)wRF*REZA=gytBwzhfm<~@1xgKxe+fVL(q-I>7#P{ttYv3pP-KeT63Zm6tXi_WL|n;{!5RSRqLCf|
literal 0
HcmV?d00001
diff --git a/Toolkit/PHPImageEditor/images/slider_track.gif b/Toolkit/PHPImageEditor/images/slider_track.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e1dc8e0d89ab40cbaee10ea10b16e86e55697ba6
GIT binary patch
literal 176
zcmV;h08jr%Nk%w1VUYm`0FeU#=j!UPu(0y-^8f$;A^8LW00093EC2ui0FeO)0007k
zjE||y?GK}zwAzca-n{$20pduO=82~2%C_zc$MUQO^o{5G&iDQg3<`%o8}W!tDrbVG
z^9hYgr_`$Tip^@b+^)A8_=?BmviXd@4%Y0p`wfrF=k&V$j?e4&{CS`c7$`VMSZH{N
en5ekOi1^nC87VnQS!sERdH0yf+3A^A0029RMO?W6
literal 0
HcmV?d00001
diff --git a/Toolkit/PHPImageEditor/images/tab_body.png b/Toolkit/PHPImageEditor/images/tab_body.png
new file mode 100644
index 0000000000000000000000000000000000000000..6e164fe392b12f25ca90a4472f43c321f2c44ce9
GIT binary patch
literal 198
zcmeAS@N?(olHy`uVBq!ia0vp^oIq^E!2~23{n9jnlw^r(L`iUdT1k0gQ7VIDN`6wR
zf@f}GdTLN=VoGJ<$y6Jlq6|+L$B>F!cP?7;H8}7vA25CNfBqw09dm9`+4S=t8J8O{
zabD_sqro*Pu{|6H_V+Po~-c6fkP)6WDOn)q$eaESlC*!xZt+q
wuGJEfFB90`Hn()y$p`RXI;=709|MO0gH{nUM-GQF56}z-Pgg&ebxsLQ0M@`T2><{9
literal 0
HcmV?d00001
diff --git a/Toolkit/PHPImageEditor/includes/constants.php b/Toolkit/PHPImageEditor/includes/constants.php
new file mode 100644
index 0000000..0a7a255
--- /dev/null
+++ b/Toolkit/PHPImageEditor/includes/constants.php
@@ -0,0 +1,49 @@
+
+ * @copyright 2012 Gaslight Media
+ * @license Gaslight Media
+ * @version SVN: $id$
+ * @link <>
+ */
+ /*
+ Copyright 2008, 2009, 2010 Patrik Hultgren
+
+ YOUR PROJECT MUST ALSO BE OPEN SOURCE IN ORDER TO USE PHP IMAGE EDITOR.
+ OR ELSE YOU NEED TO BUY THE COMMERCIAL VERSION AT:
+ http://www.shareit.com/product.html?productid=300296445&backlink=http%3A%2F%2Fwww.phpimageeditor.se%2F
+
+ This file is part of PHP Image Editor.
+
+ PHP Image Editor is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ PHP Image Editor is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PHP Image Editor. If not, see .
+ */
+
+
+ define("PHP_VERSION_MINIMUM", "5");
+ define("GD_VERSION_MINIMUM", "2.0.28");
+ define("IMAGE_ORIGINAL_PATH", "editimagesoriginal/");
+ define("IMAGE_WORK_WITH_PATH", "editimagesworkwith/");
+ define("IMAGE_PNG_PATH", "editimagespng/");
+ define("MENU_RESIZE", "0");
+ define("MENU_ROTATE", "1");
+ define("MENU_CROP", "2");
+ define("MENU_EFFECTS", "3");
+?>
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/includes/functions.php b/Toolkit/PHPImageEditor/includes/functions.php
new file mode 100644
index 0000000..5fa5bbb
--- /dev/null
+++ b/Toolkit/PHPImageEditor/includes/functions.php
@@ -0,0 +1,50 @@
+.
+ */
+
+
+ function PIE_GetTexts($filePath)
+ {
+ $texts = array();
+ $lines = file($filePath);
+
+ foreach($lines as $line_num => $line)
+ {
+ if (substr_count($line, "#") == 0)
+ {
+ $keyAndText = explode("=", trim($line));
+ $texts[$keyAndText[0]] = $keyAndText[1];
+ }
+ }
+
+ return $texts;
+ }
+
+ function PIE_Echo($text)
+ {
+ echo $text;
+ //echo utf8_encode($text);
+ }
+?>
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/index.php b/Toolkit/PHPImageEditor/index.php
new file mode 100644
index 0000000..99b3f7f
--- /dev/null
+++ b/Toolkit/PHPImageEditor/index.php
@@ -0,0 +1,250 @@
+.
+ */
+
+
+
+ header("Cache-Control: no-store");
+ header('content-type: text/html; charset: utf-8');
+ include 'includes/constants.php';
+ include 'config.php';
+ include 'includes/functions.php';
+ include 'classes/phpimageeditor.php';
+ global $objPHPImageEditor;
+ $objPHPImageEditor = new PHPImageEditor();
+?>
+isAjaxPost) { ?>
+
+
+
+ PHP Image Editor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
';
+
+ $this->paragraphLinks[] = sprintf(
+ $linksFormat,
+ $paragraph['id'],
+ $paragraph['title']
+ );
+ $key = $flip ? !$key : $key;
+ }
+ }
+
+ $this->cache->save($html, "paragraphs-$id", 'Toolbox');
+ return $html;
+ }
+ }
+
+ // }}}
+
+ // {{{ getStaticPageContent()
+
+ /**
+ * Description for getStaticPageCOntent()
+ *
+ * @param unknown $pageId PageID
+ *
+ * @return string
+ * @access protected
+ */
+ protected function getStaticPageContent($pageId)
+ {
+ $html = '';
+
+ if (file_exists(BASE . "static/$pageId.phtml")) {
+ ob_start();
+ include BASE . "static/$pageId.phtml";
+ $html = ob_get_contents();
+ ob_end_clean();
+ }
+
+ return $html;
+ }
+
+ // }}}
+}
+?>
diff --git a/Toolkit/Template/PageControllerAbstract.php b/Toolkit/Template/PageControllerAbstract.php
new file mode 100644
index 0000000..dd06879
--- /dev/null
+++ b/Toolkit/Template/PageControllerAbstract.php
@@ -0,0 +1,115 @@
+
+ * @copyright 2009 Gaslight Media
+ * @license Gaslight Media
+ * @version CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link <>
+ */
+
+/**
+ * Toolkit_Template_PageControllerAbstract
+ *
+ * Description of Toolkit_Template_PageControllerAbstract
+ *
+ * @category Toolkit
+ * @package Template
+ * @author Jamie Kahgee
+ * @copyright 2012 Gaslight Media
+ * @license Gaslight Media
+ * @release Release: $id$
+ * @link <>
+ */
+abstract class Toolkit_Template_PageControllerAbstract
+ extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+ /**
+ * Description for getPageGatewayFactory()
+ *
+ * @access protected
+ * @return unknown
+ */
+ abstract protected function getPageGatewayFactory();
+
+ /**
+ * Description for getParagraphGatewayFactory()
+ *
+ * @access protected
+ * @return unknown
+ */
+ abstract protected function getParagraphGatewayFactory();
+
+ /**
+ * Description for indexAction()
+ *
+ * @return void
+ * @access public
+ */
+ public function indexAction()
+ {
+ // Initiate HTML_Template_Flexy.
+ $template = new HTML_Template_Flexy($this->registry->flexyOptions);
+ $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+ new Toolkit_Toolbox_PageGatewayPublish(
+ $this->registry->dbh
+ )
+ );
+ $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+ $this->getPageGatewayFactory()
+ );
+ // Page object used for merging with the flexy template object.
+ // now using the page class from toolkit
+ $glmPage = new Toolkit_Page(
+ new Toolkit_Template_Page(),
+ $breadCrumbsFactory,
+ $this->getPageGatewayFactory(),
+ $this->getParagraphGatewayFactory(),
+ new Toolkit_Template_Navigation_Factory(),
+ $keywordReplacement,
+ $this->registry->catid
+ );
+ $glmPage->fetchPage();
+ /*
+ if (isset($_GET['member_id']) && is_numeric($_GET['member_id'])) {
+ // get member name and put in title
+ $sql = "
+ SELECT member_name
+ FROM member
+ WHERE member_id = {$_GET['member_id']}";
+ if ($memData = $toolbox->DB->db_auto_get_data($sql)) {
+ $member_name = htmlentities(strip_tags($memData[0]['member_name']));
+ $glmPage->title = $member_name.' - '.$glmPage->title;
+ }
+ }
+
+ if ($catid == HOME_ID && !$_POST) {
+ $sql = "
+ SELECT *
+ FROM bus_category
+ WHERE featured = true";
+ $dbh = Toolkit_Database::getInstance();
+ foreach ($dbh->query($sql) as $row) {
+ $row['link'] = $toolbox->get_seo_url($row['id']);
+ $glmPage->features[] = $row;
+ }
+ }
+ $glmPage->hasHeadlines = !empty($glmPage->features);
+ */
+
+ $glmPage->topScripts = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+ $glmPage->bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+ $glmPage->styles = Toolkit_Common::getStyleSheets();
+
+ // Compile the template.html from the templates directory.
+ $template->compile('template.html');
+ // Merge compiled template with the $glmPage object.
+ $template->outputObject($glmPage);
+ }
+}
+?>
diff --git a/Toolkit/Toolbox/BreadCrumbsAbstract.php b/Toolkit/Toolbox/BreadCrumbsAbstract.php
new file mode 100644
index 0000000..c32fb66
--- /dev/null
+++ b/Toolkit/Toolbox/BreadCrumbsAbstract.php
@@ -0,0 +1,119 @@
+dbh = $dbh;
+
+ if (!ctype_digit((string)$id)) {
+ throw new InvalidArgumentException(
+ "\$id must be an integer `$id` given"
+ );
+ }
+
+ $this->id = $id;
+ }
+
+ // }}}
+ // {{{ getPage()
+
+ protected function getPage($id)
+ {
+ try {
+ $sql = "
+ SELECT *
+ FROM pages
+ WHERE id = :id";
+
+ $stmt = $this->dbh->prepare($sql);
+ $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+ $stmt->execute();
+
+ return $stmt->fetch(PDO::FETCH_ASSOC);
+ } catch (PDOException $e) {
+ Toolkit_Logger::logException('DB Error', $e);
+ throw new Toolkit_Toolbox_Exception(
+ "Could not fetch parent for page `$id`"
+ );
+ }
+ }
+
+ // }}}
+ // {{{ __toString()
+
+ public function __toString()
+ {
+ return '
' . $this->getPath() . '
';
+ }
+
+ // }}}
+ // {{{ getId()
+
+ /**
+ * @return the $id
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ // }}}
+ // {{{ getPageUri()
+
+ abstract protected function getPageUri(array $page);
+
+ // }}}
+ // {{{ getPath()
+
+ /**
+ * @return the $path
+ */
+ public function getPath()
+ {
+ if ($this->id == HOME_ID) {
+ return;
+ }
+
+ $id = $this->id;
+ $stack = array();
+ do {
+ $page = $this->getPage($id);
+
+ $navigationName = $this->id == $id
+ ? $page['navigation_name']
+ : $this->getPageUri($page);
+
+ $stack[] = $navigationName;
+ $id = $page['parent'];
+ } while ($id != 0);
+
+ $reverse = array_reverse($stack);
+ $this->path = implode(' > ', $reverse);
+
+ return $this->path;
+ }
+
+ // }}}
+ // {{{ setId()
+
+ /**
+ * @param $id the $id to set
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ // }}}
+}
+?>
diff --git a/Toolkit/Toolbox/Database/addIncludeCoupons.sql b/Toolkit/Toolbox/Database/addIncludeCoupons.sql
new file mode 100644
index 0000000..7dbf908
--- /dev/null
+++ b/Toolkit/Toolbox/Database/addIncludeCoupons.sql
@@ -0,0 +1,19 @@
+--
+-- update the pages tables with new field for include_coupons
+-- and update the rule for history
+--
+
+ALTER TABLE toolbox.pages ADD include_coupons BOOLEAN;
+ALTER TABLE toolbox.pages ALTER include_coupons SET DEFAULT false;
+UPDATE toolbox.pages SET include_coupons = false;
+
+ALTER TABLE toolbox.pages_draft ADD include_coupons BOOLEAN;
+ALTER TABLE toolbox.pages_draft ALTER include_coupons SET DEFAULT false;
+UPDATE toolbox.pages_draft SET include_coupons = false;
+
+ALTER TABLE toolbox.pages_history ADD include_coupons BOOLEAN;
+ALTER TABLE toolbox.pages_history ALTER include_coupons SET DEFAULT false;
+UPDATE toolbox.pages_history SET include_coupons = false;
+
+
+\i ./procedures/pages_update.sql
diff --git a/Toolkit/Toolbox/Database/addIncludeMembers.sql b/Toolkit/Toolbox/Database/addIncludeMembers.sql
new file mode 100644
index 0000000..44fba29
--- /dev/null
+++ b/Toolkit/Toolbox/Database/addIncludeMembers.sql
@@ -0,0 +1,19 @@
+--
+-- update the pages tables with new field for include_members
+-- and update the rule for history
+--
+
+ALTER TABLE toolbox.pages ADD include_members BOOLEAN;
+ALTER TABLE toolbox.pages ALTER include_members SET DEFAULT false;
+UPDATE toolbox.pages SET include_members = false;
+
+ALTER TABLE toolbox.pages_draft ADD include_members BOOLEAN;
+ALTER TABLE toolbox.pages_draft ALTER include_members SET DEFAULT false;
+UPDATE toolbox.pages_draft SET include_members = false;
+
+ALTER TABLE toolbox.pages_history ADD include_members BOOLEAN;
+ALTER TABLE toolbox.pages_history ALTER include_members SET DEFAULT false;
+UPDATE toolbox.pages_history SET include_member = false;
+
+
+\i ./procedures/pages_update.sql
diff --git a/Toolkit/Toolbox/Database/application.sql b/Toolkit/Toolbox/Database/application.sql
new file mode 100644
index 0000000..02859ae
--- /dev/null
+++ b/Toolkit/Toolbox/Database/application.sql
@@ -0,0 +1,33 @@
+CREATE SCHEMA toolbox;
+GRANT ALL ON SCHEMA toolbox TO nobody;
+
+--
+-- Tables
+--
+\i ./tables/pages.sql
+\i ./tables/pages_history.sql
+\i ./tables/pages_draft.sql
+\i ./tables/paragraphs.sql
+\i ./tables/paragraphs_history.sql
+\i ./tables/paragraphs_draft.sql
+\i ./tables/files.sql
+\i ./tables/files_history.sql
+\i ./tables/files_draft.sql
+
+--
+-- Procedures
+--
+\i ./procedures/pages_update.sql
+\i ./procedures/pages_pos.sql
+\i ./procedures/paragraphs_update.sql
+\i ./procedures/paragraphs_pos.sql
+\i ./procedures/paragraphs_draft_pos.sql
+\i ./procedures/check_paragraph_pos_consistency.sql
+-- \i ./procedures/check_pages_pos_consistency.sql
+\i ./procedures/files_update.sql
+\i ./procedures/files_pos.sql
+\i ./procedures/delete_subtree.sql
+
+--
+-- Modules
+--
diff --git a/Toolkit/Toolbox/Database/data/applicationData.sql b/Toolkit/Toolbox/Database/data/applicationData.sql
new file mode 100644
index 0000000..b3db82f
--- /dev/null
+++ b/Toolkit/Toolbox/Database/data/applicationData.sql
@@ -0,0 +1,32 @@
+INSERT INTO toolbox.pages(active, template, keyword, navigation_name, paragraph_links) VALUES (TRUE, 1, 'home', 'Home', TRUE);
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 0, 2, 'Parent');
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 2, 1, 'Child');
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 2, 2, 'Child Sibling');
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 3, 1, 'Grand Child');
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 3, 2, 'Grand Child Sibling');
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 0, 3, 'Sibling');
+
+INSERT INTO toolbox.paragraphs(active, title, description, image, caption, page, pos) VALUES(TRUE, 'Lorem Ipsum Delor', '
Dictumst quis adipiscing etiam odio et augue, ut, cum cras adipiscing dolor cursus eros in integer in enim, aliquet in, amet tempor porta scelerisque, a eros sociis nec, porta in, lacus lacus, integer phasellus vut sit? Massa turpis porttitor. In, enim pulvinar? Ac et, ac nascetur porttitor magnis natoque duis! Nisi, ac et, aliquet? Cras tempor dignissim! Adipiscing cursus vel, integer dis penatibus vut! Et? Nec lundium lacus nunc nunc tempor a pid cum turpis magnis urna est eu rhoncus porttitor mauris ridiculus eu! Dolor pid porta ac dis lorem phasellus, tincidunt eros montes auctor adipiscing? Sed adipiscing risus scelerisque scelerisque ridiculus tortor, ut aliquam. Aenean enim et. Lectus enim dapibus scelerisque eros elementum nec, sagittis arcu. Purus augue ac.
+
Enim tincidunt pid, augue est magna velit lacus in tincidunt cras sed tempor! Sed. Dis adipiscing odio, urna mus! Natoque arcu elit. Dapibus natoque a ridiculus scelerisque nisi cras. Phasellus ultricies natoque augue sagittis mauris adipiscing porta pulvinar integer! Lacus adipiscing sit porta ultricies mus natoque amet porta etiam, mauris. Egestas! Et urna! Platea risus magnis porta magnis in! Integer turpis cursus enim ac, ac? Phasellus enim. Cras enim! Lectus pulvinar, ac porta elementum nec, vut elementum pulvinar dignissim elementum porta placerat porttitor a lundium, turpis velit et mattis augue hac tempor adipiscing, magnis, lectus, pulvinar diam. Scelerisque scelerisque. Nunc sagittis ut proin, tincidunt ultrices velit parturient, sit vel enim. Porta? Ac non! Magna tincidunt arcu augue aenean eros.
+
Placerat et placerat massa aliquam aliquet scelerisque, urna lorem! Adipiscing rhoncus, pid natoque turpis, auctor. Placerat sit, non sed, mattis augue dis placerat amet elementum vel habitasse, placerat. Etiam cum magnis arcu nisi a proin mus sit tortor montes! Integer aliquam, pellentesque sed sit. Dis mattis magnis! Enim non tincidunt cursus urna urna magna etiam? Facilisis ultricies dapibus porttitor, turpis ac et placerat urna, urna turpis vel, porta purus ac non! Nunc lorem? Tincidunt quis sagittis ridiculus sit, turpis? A ut ac rhoncus. Aenean tristique a tincidunt. Ac in odio dis et nascetur, est proin. Nisi, aliquam elementum, sed. Nunc sociis. Ut enim, ultricies nascetur a magna porta duis ut tincidunt! Aenean, augue cursus tristique ridiculus, integer eros dolor.
Sit, et odio, risus proin adipiscing mattis! Est! Non lorem, et scelerisque sociis! Ac? Porttitor in, pid auctor, etiam hac sagittis dignissim scelerisque elementum? Nisi. Auctor in, lacus, enim vut, ac eros magna cursus nisi et! Vut tortor natoque rhoncus augue est tincidunt tortor in sed elementum, tristique purus. Nec nascetur porta adipiscing cum. Diam porttitor lorem hac parturient cras, pulvinar integer! Vel ac, rhoncus ridiculus ac magna est integer, ut ultrices! Pulvinar hac lundium pid pulvinar dapibus et rhoncus! Montes et pulvinar vut. Sit porta arcu turpis! Dictumst, proin pulvinar et turpis tristique odio aliquam, est odio hac dis? Elit elementum integer adipiscing turpis tincidunt massa? Habitasse dignissim lectus porta, nunc, aliquam est? Integer enim? Lectus, dignissim natoque, turpis.
Tortor adipiscing ut magna cras elit elit elit, dis! Eros pid scelerisque, aenean sagittis odio tristique elementum sed sit, porta ridiculus! Eros lundium integer? Integer, pulvinar sociis mid! Enim, magna velit mattis et ultrices egestas sed egestas non pulvinar etiam! Urna porta et pulvinar lorem sed aliquam, ridiculus. Dolor aenean integer enim, amet porta enim penatibus, proin a ut tincidunt, turpis magnis lectus quis enim aliquet, mauris in vel quis! Facilisis dolor. Urna eu, amet quis, mattis sed! Phasellus lorem! Non platea. Sit elit sed proin mus, mauris mauris dignissim, tincidunt et augue placerat tristique elementum purus lundium a cursus platea pid, ultricies platea? Elit sed eu diam, elementum sit rhoncus massa, lorem hac, urna natoque sit a tincidunt vel.
Porta, augue facilisis, arcu massa enim massa phasellus nisi elementum, aliquet sit phasellus sed nunc turpis, augue cum, cum lundium, ridiculus, vut sociis auctor! Tincidunt pulvinar, tempor eu mid amet? Odio, lacus ultrices, nunc purus, cursus in diam mattis et penatibus. Dolor. Elementum pid tincidunt magna penatibus risus, odio aliquet. Dapibus amet eros enim diam? Elit, urna elit porttitor platea. Nascetur proin odio habitasse aenean magnis? Facilisis nascetur? A elementum, lorem diam, phasellus phasellus montes magna proin? Habitasse risus, vel porta mauris tortor velit, egestas tortor augue. Vut sed placerat aliquet enim diam porttitor dignissim, montes, mattis enim! Egestas ultrices? Nec porta lundium dapibus! Dictumst turpis augue integer, proin nec lectus dictumst! Platea urna, rhoncus hac ac adipiscing a augue.
Dictumst quis adipiscing etiam odio et augue, ut, cum cras adipiscing dolor cursus eros in integer in enim, aliquet in, amet tempor porta scelerisque, a eros sociis nec, porta in, lacus lacus, integer phasellus vut sit? Massa turpis porttitor. In, enim pulvinar? Ac et, ac nascetur porttitor magnis natoque duis! Nisi, ac et, aliquet? Cras tempor dignissim! Adipiscing cursus vel, integer dis penatibus vut! Et? Nec lundium lacus nunc nunc tempor a pid cum turpis magnis urna est eu rhoncus porttitor mauris ridiculus eu! Dolor pid porta ac dis lorem phasellus, tincidunt eros montes auctor adipiscing? Sed adipiscing risus scelerisque scelerisque ridiculus tortor, ut aliquam. Aenean enim et. Lectus enim dapibus scelerisque eros elementum nec, sagittis arcu. Purus augue ac.
Sit, et odio, risus proin adipiscing mattis! Est! Non lorem, et scelerisque sociis! Ac? Porttitor in, pid auctor, etiam hac sagittis dignissim scelerisque elementum? Nisi. Auctor in, lacus, enim vut, ac eros magna cursus nisi et! Vut tortor natoque rhoncus augue est tincidunt tortor in sed elementum, tristique purus. Nec nascetur porta adipiscing cum. Diam porttitor lorem hac parturient cras, pulvinar integer! Vel ac, rhoncus ridiculus ac magna est integer, ut ultrices! Pulvinar hac lundium pid pulvinar dapibus et rhoncus! Montes et pulvinar vut. Sit porta arcu turpis! Dictumst, proin pulvinar et turpis tristique odio aliquam, est odio hac dis? Elit elementum integer adipiscing turpis tincidunt massa? Habitasse dignissim lectus porta, nunc, aliquam est? Integer enim? Lectus, dignissim natoque, turpis.
Tortor adipiscing ut magna cras elit elit elit, dis! Eros pid scelerisque, aenean sagittis odio tristique elementum sed sit, porta ridiculus! Eros lundium integer? Integer, pulvinar sociis mid! Enim, magna velit mattis et ultrices egestas sed egestas non pulvinar etiam! Urna porta et pulvinar lorem sed aliquam, ridiculus. Dolor aenean integer enim, amet porta enim penatibus, proin a ut tincidunt, turpis magnis lectus quis enim aliquet, mauris in vel quis! Facilisis dolor. Urna eu, amet quis, mattis sed! Phasellus lorem! Non platea. Sit elit sed proin mus, mauris mauris dignissim, tincidunt et augue placerat tristique elementum purus lundium a cursus platea pid, ultricies platea? Elit sed eu diam, elementum sit rhoncus massa, lorem hac, urna natoque sit a tincidunt vel.
', '', '', TRUE, 2, 1);
+INSERT INTO toolbox.paragraphs(active, title, description, page, pos) VALUES(TRUE, 'Child Title', 'Child Description', 3, 1);
+INSERT INTO toolbox.paragraphs(active, title, description, page, pos) VALUES(TRUE, 'Sibling Title', 'Sibling Description', 7, 1);
+INSERT INTO toolbox.paragraphs(active, title, page, pos) VALUES(TRUE, 'Sibling Paragraph A', 7, 1);
+INSERT INTO toolbox.paragraphs(active, title, page, pos) VALUES(TRUE, 'Sibling Paragraph B', 7, 1);
+INSERT INTO toolbox.paragraphs(active, title, page, pos) VALUES(TRUE, 'p Paragraph', 4, 1);
+INSERT INTO toolbox.paragraphs(active, title, page, pos) VALUES(TRUE, 'p Paragraph', 5, 1);
+INSERT INTO toolbox.paragraphs(active, title, page, pos) VALUES(TRUE, 'p Paragraph', 6, 1);
+
+
+INSERT INTO members.city(state_id, city_name) VALUES (34, 'Cary');
+INSERT INTO members.city(state_id, city_name) VALUES (23, 'Petoskey');
+
+INSERT INTO members.member(member_name, member_login, member_passwd, street, lat, lon, phone, process_email, url, city_id, state_id, zip, member_contact_email, mailing_address, primary_contact_fname, primary_contact_lname, active, mailing_city, mailing_state_id, mailing_zip, new_member) values('1111 AAA Test', 'uname', 'pword', '120 E. Lake St.', 45.374893, -84.958404, '(231) 487-0692', 'jamie@gaslightmedia.com', 'http://www.gaslightmedia.com', 3, 23, 49770, 'jamie@gaslightmedia.com', '139 Dove Cottage Ln.', 'Jamie', 'Kahgee', true, 'Cary', 34, 27519, false);
+
+INSERT INTO coupons.coupons(title, url, sdate, edate, expiration, category, active, member) VALUES ('Coupon Test', 'http://www.google.com', '2010-04-22', '2010-10-22', '2010-10-22', 'Accommodations', true, 1);
diff --git a/Toolkit/Toolbox/Database/procedures/check_pages_pos_consistency.sql b/Toolkit/Toolbox/Database/procedures/check_pages_pos_consistency.sql
new file mode 100644
index 0000000..085ff72
--- /dev/null
+++ b/Toolkit/Toolbox/Database/procedures/check_pages_pos_consistency.sql
@@ -0,0 +1,38 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS check_pages_pos_consistency ON toolbox.pages;
+
+--
+-- Function and trigger checking for duplicate positions when a paragraph
+-- has been inserted or updated.
+--
+-- This trigger only happens after an insert or update and is initially
+-- deferred which allows us to wrap our movements in a transaction and
+-- examine the results when we are done
+--
+-- This is a final check to help ensure data integrity
+--
+CREATE OR REPLACE FUNCTION toolbox.check_pages_pos_consistency() RETURNS TRIGGER AS $check_pages_pos_consistency$
+ DECLARE
+ duplicates INTEGER;
+ BEGIN
+
+ SELECT
+ INTO duplicates count(*)
+ FROM toolbox.pages
+ WHERE parent = NEW.parent
+ AND pos = NEW.pos;
+
+ IF (duplicates > 1) THEN
+ RAISE EXCEPTION 'Duplicate page positions!';
+ END IF;
+
+ RETURN NULL;
+ END;
+$check_pages_pos_consistency$ LANGUAGE plpgsql;
+
+CREATE CONSTRAINT TRIGGER trigger_pages_pos_check_consistency
+AFTER INSERT OR UPDATE ON toolbox.pages
+ DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE toolbox.check_pages_pos_consistency();
diff --git a/Toolkit/Toolbox/Database/procedures/check_paragraph_pos_consistency.sql b/Toolkit/Toolbox/Database/procedures/check_paragraph_pos_consistency.sql
new file mode 100644
index 0000000..a6b46ae
--- /dev/null
+++ b/Toolkit/Toolbox/Database/procedures/check_paragraph_pos_consistency.sql
@@ -0,0 +1,38 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS check_paragraphs_pos_consistency ON toolbox.paragraphs;
+
+--
+-- Function and trigger checking for duplicate positions when a paragraph
+-- has been inserted or updated.
+--
+-- This trigger only happens after an insert or update and is initially
+-- deferred which allows us to wrap our movements in a transaction and
+-- examine the results when we are done
+--
+-- This is a final check to help ensure data integrity
+--
+CREATE OR REPLACE FUNCTION toolbox.check_paragraphs_pos_consistency() RETURNS TRIGGER AS $check_paragraphs_pos_consistency$
+ DECLARE
+ duplicates INTEGER;
+ BEGIN
+
+ SELECT
+ INTO duplicates count(*)
+ FROM toolbox.paragraphs
+ WHERE page = NEW.page
+ AND pos = NEW.pos;
+
+ IF (duplicates > 1) THEN
+ RAISE EXCEPTION 'Duplicate page positions!';
+ END IF;
+
+ RETURN NULL;
+ END;
+$check_paragraphs_pos_consistency$ LANGUAGE plpgsql;
+
+CREATE CONSTRAINT TRIGGER trigger_paragraphs_pos_check_consistency
+AFTER INSERT OR UPDATE ON toolbox.paragraphs
+ DEFERRABLE INITIALLY DEFERRED
+ FOR EACH ROW EXECUTE PROCEDURE toolbox.check_paragraphs_pos_consistency();
diff --git a/Toolkit/Toolbox/Database/procedures/delete_subtree.sql b/Toolkit/Toolbox/Database/procedures/delete_subtree.sql
new file mode 100644
index 0000000..55b1781
--- /dev/null
+++ b/Toolkit/Toolbox/Database/procedures/delete_subtree.sql
@@ -0,0 +1,67 @@
+DROP TABLE IF EXISTS WorkingTable;
+CREATE LOCAL TEMPORARY TABLE WorkingTable
+(id INTEGER NOT NULL)
+ON COMMIT DELETE ROWS;
+
+CREATE OR REPLACE FUNCTION toolbox.delete_subtree (IN dead_guy INTEGER) RETURNS void AS $$
+ DECLARE
+ old_pos INTEGER; -- current position of the target node
+ old_par INTEGER; -- current parent of the target node
+ BEGIN
+ -- defer consraint that forbids (parent = id)
+ SET CONSTRAINTS ALL DEFERRED;
+
+ -- set the old_pos and old_par variable
+ SELECT pos
+ INTO old_pos
+ FROM toolbox.pages
+ WHERE id = dead_guy;
+
+ SELECT parent
+ INTO old_par
+ FROM toolbox.pages
+ WHERE id = dead_guy;
+
+ -- mark root of subtree and immediate subordinates
+ UPDATE toolbox.pages
+ SET id = CASE WHEN id = dead_guy
+ THEN -99999 ELSE id END,
+ parent = CASE WHEN parent = dead_guy
+ THEN -99999 ELSE parent END
+ WHERE dead_guy IN (id, parent);
+
+ WHILE EXISTS -- mark leaf nodes
+ (SELECT *
+ FROM toolbox.pages
+ WHERE parent = -99999
+ AND id > -99999)
+ LOOP -- get list of next level subordinates
+
+ DELETE FROM WorkingTable;
+ INSERT INTO WorkingTable
+ SELECT id FROM toolbox.pages WHERE parent = -99999;
+
+ -- delete old markers so we don't keep an infinite loop
+ DELETE FROM toolbox.pages
+ WHERE parent = -99999;
+
+ -- mark next level of subordinates
+ UPDATE toolbox.pages
+ SET parent = -99999
+ WHERE parent IN (SELECT id FROM WorkingTable);
+
+ END LOOP;
+
+ -- delete all marked nodes
+ DELETE FROM toolbox.pages
+ WHERE id = -99999;
+
+ -- reset all the positions at the target nodes level
+ UPDATE toolbox.pages
+ SET pos = pos - 1
+ WHERE parent = old_par
+ AND pos > old_pos;
+
+ SET CONSTRAINTS ALL IMMEDIATE;
+ END;
+$$ LANGUAGE plpgsql;
diff --git a/Toolkit/Toolbox/Database/procedures/files_pos.sql b/Toolkit/Toolbox/Database/procedures/files_pos.sql
new file mode 100644
index 0000000..d0a27a6
--- /dev/null
+++ b/Toolkit/Toolbox/Database/procedures/files_pos.sql
@@ -0,0 +1,28 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS files_pos ON toolbox.files;
+
+--
+-- Function and trigger setting a page pos when inserted
+--
+CREATE OR REPLACE FUNCTION toolbox.files_pos() RETURNS TRIGGER AS $files_pos$
+ DECLARE
+ new_pos INTEGER;
+ BEGIN
+
+ SELECT
+ INTO new_pos COALESCE(MAX(pos) + 1, 1)
+ FROM toolbox.files
+ WHERE paragraph = NEW.paragraph;
+
+ NEW.pos := new_pos;
+
+ RETURN NEW;
+
+ END;
+$files_pos$ LANGUAGE plpgsql;
+
+CREATE TRIGGER files_pos
+BEFORE INSERT ON toolbox.files
+ FOR EACH ROW EXECUTE PROCEDURE toolbox.files_pos();
diff --git a/Toolkit/Toolbox/Database/procedures/files_update.sql b/Toolkit/Toolbox/Database/procedures/files_update.sql
new file mode 100644
index 0000000..d018224
--- /dev/null
+++ b/Toolkit/Toolbox/Database/procedures/files_update.sql
@@ -0,0 +1,20 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS files_update ON toolbox.files;
+
+--
+-- Function and trigger copying a file revision
+-- everytime it is updated.
+--
+CREATE OR REPLACE FUNCTION toolbox.files_update() RETURNS TRIGGER AS $files_update$
+ BEGIN
+
+ INSERT INTO toolbox.files_history SELECT NEW.*;
+ RETURN NEW;
+ END;
+$files_update$ LANGUAGE plpgsql;
+
+CREATE TRIGGER files_update
+AFTER INSERT OR UPDATE ON toolbox.files
+ FOR EACH ROW EXECUTE PROCEDURE toolbox.files_update();
diff --git a/Toolkit/Toolbox/Database/procedures/pages_pos.sql b/Toolkit/Toolbox/Database/procedures/pages_pos.sql
new file mode 100644
index 0000000..9deffe1
--- /dev/null
+++ b/Toolkit/Toolbox/Database/procedures/pages_pos.sql
@@ -0,0 +1,28 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS pages_pos ON toolbox.pages;
+
+--
+-- Function and trigger setting a page pos when inserted
+--
+CREATE OR REPLACE FUNCTION toolbox.pages_pos() RETURNS TRIGGER AS $pages_pos$
+ DECLARE
+ new_pos INTEGER;
+ BEGIN
+
+ SELECT
+ INTO new_pos COALESCE(MAX(pos) + 1, 1)
+ FROM toolbox.pages
+ WHERE parent = NEW.parent;
+
+ NEW.pos := new_pos;
+
+ RETURN NEW;
+
+ END;
+$pages_pos$ LANGUAGE plpgsql;
+
+CREATE TRIGGER pages_pos
+BEFORE INSERT ON toolbox.pages
+ FOR EACH ROW EXECUTE PROCEDURE toolbox.pages_pos();
diff --git a/Toolkit/Toolbox/Database/procedures/pages_update.sql b/Toolkit/Toolbox/Database/procedures/pages_update.sql
new file mode 100644
index 0000000..df5fba3
--- /dev/null
+++ b/Toolkit/Toolbox/Database/procedures/pages_update.sql
@@ -0,0 +1,30 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS pages_update ON toolbox.pages;
+
+--
+-- Function and trigger copying a page revision
+-- everytime it is updated.
+--
+CREATE OR REPLACE FUNCTION toolbox.pages_update() RETURNS TRIGGER AS $pages_update$
+ BEGIN
+
+ INSERT INTO toolbox.pages_history
+ (id,active,mobile_active,headline,headline_intro,include_member_map,
+ keyword,meta_description,meta_title,navigation_name,
+ paragraph_links,parent,pos,search_form,region,short_url,
+ template,include_members,include_coupons)
+ SELECT
+ NEW.id,NEW.active,NEW.mobile_active,NEW.headline,NEW.headline_intro,NEW.include_member_map,
+ NEW.keyword,NEW.meta_description,NEW.meta_title,NEW.navigation_name,
+ NEW.paragraph_links,NEW.parent,NEW.pos,NEW.search_form,NEW.region,
+ NEW.short_url,NEW.template,NEW.include_members,NEW.include_coupons;
+ RETURN NEW;
+
+ END;
+$pages_update$ LANGUAGE plpgsql;
+
+CREATE TRIGGER pages_update
+AFTER INSERT OR UPDATE ON toolbox.pages
+ FOR EACH ROW EXECUTE PROCEDURE toolbox.pages_update();
diff --git a/Toolkit/Toolbox/Database/procedures/paragraphs_draft_pos.sql b/Toolkit/Toolbox/Database/procedures/paragraphs_draft_pos.sql
new file mode 100644
index 0000000..3eb789d
--- /dev/null
+++ b/Toolkit/Toolbox/Database/procedures/paragraphs_draft_pos.sql
@@ -0,0 +1,28 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS paragraphs_draft_pos ON toolbox.paragraphs_draft;
+
+--
+-- Function and trigger setting a page pos when inserted
+--
+CREATE OR REPLACE FUNCTION toolbox.paragraphs_draft_pos() RETURNS TRIGGER AS $paragraphs_draft_pos$
+ DECLARE
+ new_pos INTEGER;
+ BEGIN
+
+ SELECT
+ INTO new_pos COALESCE(MAX(pos) + 1, 1)
+ FROM toolbox.paragraphs_draft
+ WHERE page = NEW.page;
+
+ NEW.pos := new_pos;
+
+ RETURN NEW;
+
+ END;
+$paragraphs_draft_pos$ LANGUAGE plpgsql;
+
+CREATE TRIGGER paragraphs_draft_pos
+BEFORE INSERT ON toolbox.paragraphs_draft
+ FOR EACH ROW EXECUTE PROCEDURE toolbox.paragraphs_draft_pos();
diff --git a/Toolkit/Toolbox/Database/procedures/paragraphs_pos.sql b/Toolkit/Toolbox/Database/procedures/paragraphs_pos.sql
new file mode 100644
index 0000000..9bfe0ee
--- /dev/null
+++ b/Toolkit/Toolbox/Database/procedures/paragraphs_pos.sql
@@ -0,0 +1,48 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS paragraphs_pos ON toolbox.paragraphs;
+
+--
+-- Function and trigger setting a page pos when inserted
+--
+CREATE OR REPLACE FUNCTION toolbox.paragraphs_pos() RETURNS TRIGGER AS $paragraphs_pos$
+ DECLARE
+ new_pos INTEGER;
+ BEGIN
+
+ SELECT
+ INTO new_pos COALESCE(MAX(pos) + 1, 1)
+ FROM toolbox.paragraphs
+ WHERE page = NEW.page;
+
+ -- Always use the new position when doing an insert
+ IF (TG_OP = 'INSERT') THEN
+ NEW.pos := new_pos;
+ ELSIF (TG_OP = 'UPDATE') THEN
+ -- Only update the paragraph position
+ -- on an update if we are changing the
+ -- page the paragraph is assigned to
+ IF (OLD.page != NEW.page) THEN
+ NEW.pos := new_pos;
+
+ -- update the old page paragraph positions so
+ -- we aren't left with any gaps in the sequence
+ UPDATE toolbox.paragraphs
+ SET pos = pos - 1
+ WHERE pos > OLD.pos
+ AND page = OLD.page
+ AND EXISTS (SELECT * -- ensure the old page still exists
+ FROM toolbox.pages
+ WHERE id = OLD.page);
+ END IF;
+ END IF;
+
+ RETURN NEW;
+
+ END;
+$paragraphs_pos$ LANGUAGE plpgsql;
+
+CREATE TRIGGER paragraphs_pos
+BEFORE INSERT OR UPDATE ON toolbox.paragraphs
+ FOR EACH ROW EXECUTE PROCEDURE toolbox.paragraphs_pos();
diff --git a/Toolkit/Toolbox/Database/procedures/paragraphs_update.sql b/Toolkit/Toolbox/Database/procedures/paragraphs_update.sql
new file mode 100644
index 0000000..dd553bc
--- /dev/null
+++ b/Toolkit/Toolbox/Database/procedures/paragraphs_update.sql
@@ -0,0 +1,21 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS paragraphs_update ON toolbox.paragraphs;
+
+--
+-- Function and trigger copying a paragraph revision
+-- everytime it is updated.
+--
+CREATE OR REPLACE FUNCTION toolbox.paragraphs_update() RETURNS TRIGGER AS $paragraphs_update$
+ BEGIN
+
+ INSERT INTO toolbox.paragraphs_history SELECT NEW.*;
+ RETURN NEW;
+
+ END;
+$paragraphs_update$ LANGUAGE plpgsql;
+
+CREATE TRIGGER paragraphs_update
+AFTER INSERT OR UPDATE ON toolbox.paragraphs
+ FOR EACH ROW EXECUTE PROCEDURE toolbox.paragraphs_update();
diff --git a/Toolkit/Toolbox/Database/removeApplication.sql b/Toolkit/Toolbox/Database/removeApplication.sql
new file mode 100644
index 0000000..1a393a9
--- /dev/null
+++ b/Toolkit/Toolbox/Database/removeApplication.sql
@@ -0,0 +1,8 @@
+--
+-- This will drop everything in the toolbox schema.
+-- Nothing better be in here except toolbox related objects
+-- or it will be dropped
+--
+-- The force is strong w/ this one, use it wisely.
+--
+DROP SCHEMA IF EXISTS toolbox CASCADE;
diff --git a/Toolkit/Toolbox/Database/tables/files.sql b/Toolkit/Toolbox/Database/tables/files.sql
new file mode 100644
index 0000000..b6490d2
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/files.sql
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS toolbox.files CASCADE;
+
+CREATE TABLE toolbox.files
+(id SERIAL,
+ filename TEXT,
+ bytes INTEGER NOT NULL DEFAULT 0,
+ "type" TEXT NOT NULL,
+ urltext TEXT,
+ paragraph INTEGER NOT NULL
+ REFERENCES toolbox.paragraphs(id)
+ ON UPDATE CASCADE
+ ON DELETE CASCADE,
+ pos INTEGER NOT NULL DEFAULT 1,
+ PRIMARY KEY (id));
+
+GRANT ALL ON toolbox.files_id_seq TO nobody;
+GRANT ALL ON toolbox.files TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/files_draft.sql b/Toolkit/Toolbox/Database/tables/files_draft.sql
new file mode 100644
index 0000000..8e9c7a1
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/files_draft.sql
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS toolbox.files_draft CASCADE;
+
+CREATE TABLE toolbox.files_draft
+(LIKE toolbox.files
+ INCLUDING DEFAULTS,
+ FOREIGN KEY (paragraph) REFERENCES toolbox.paragraphs_draft(id)
+ ON UPDATE CASCADE
+ ON DELETE CASCADE,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON toolbox.files_draft TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/files_history.sql b/Toolkit/Toolbox/Database/tables/files_history.sql
new file mode 100644
index 0000000..47b1665
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/files_history.sql
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS toolbox.files_history CASCADE;
+
+CREATE TABLE toolbox.files_history
+(LIKE toolbox.files
+ INCLUDING DEFAULTS,
+ revision SERIAL NOT NULL
+);
+
+GRANT ALL ON toolbox.files_history_revision_seq TO nobody;
+GRANT ALL ON toolbox.files_history TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/pages.sql b/Toolkit/Toolbox/Database/tables/pages.sql
new file mode 100644
index 0000000..551f06d
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/pages.sql
@@ -0,0 +1,36 @@
+DROP TABLE IF EXISTS toolbox.pages CASCADE;
+
+CREATE TABLE toolbox.pages
+(id SERIAL,
+ active BOOLEAN NOT NULL
+ DEFAULT FALSE,
+ mobile_active BOOLEAN NOT NULL
+ DEFAULT FALSE,
+ headline BOOLEAN NOT NULL
+ DEFAULT FALSE,
+ headline_intro TEXT,
+ include_member_map BOOLEAN NOT NULL
+ DEFAULT FALSE,
+ keyword TEXT,
+ meta_description TEXT,
+ meta_title TEXT,
+ navigation_name TEXT,
+ paragraph_links BOOLEAN NOT NULL
+ DEFAULT FALSE,
+ parent INTEGER NOT NULL -- 0 is top level
+ DEFAULT 0,
+ pos INTEGER NOT NULL
+ DEFAULT 1,
+ search_form BOOLEAN NOT NULL
+ DEFAULT FALSE,
+ region INTEGER,
+ short_url TEXT,
+ template INTEGER NOT NULL,
+ include_members BOOLEAN DEFAULT FALSE,
+ include_coupons BOOLEAN DEFAULT FALSE,
+ PRIMARY KEY (id),
+ CHECK (id <> parent), -- Page cannot be own parent,
+ CHECK (pos > 0)); -- Page position cannot fall below zero
+
+GRANT ALL ON toolbox.pages_id_seq TO nobody;
+GRANT ALL ON toolbox.pages TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/pages_draft.sql b/Toolkit/Toolbox/Database/tables/pages_draft.sql
new file mode 100644
index 0000000..070b744
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/pages_draft.sql
@@ -0,0 +1,14 @@
+DROP TABLE IF EXISTS toolbox.pages_draft CASCADE;
+
+CREATE TABLE toolbox.pages_draft
+(LIKE toolbox.pages
+ INCLUDING DEFAULTS,
+ published_page INTEGER
+ DEFAULT NULL
+ REFERENCES toolbox.pages (id)
+ ON UPDATE CASCADE
+ ON DELETE CASCADE,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON toolbox.pages_draft TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/pages_history.sql b/Toolkit/Toolbox/Database/tables/pages_history.sql
new file mode 100644
index 0000000..e1d84d1
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/pages_history.sql
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS toolbox.pages_history CASCADE;
+
+CREATE TABLE toolbox.pages_history
+(LIKE toolbox.pages
+ INCLUDING DEFAULTS,
+ revision_timestamp TIMESTAMP NOT NULL
+ DEFAULT CURRENT_TIMESTAMP,
+ revision SERIAL NOT NULL
+);
+
+GRANT ALL ON toolbox.pages_history_revision_seq TO nobody;
+GRANT ALL ON toolbox.pages_history TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/pages_mobile.sql b/Toolkit/Toolbox/Database/tables/pages_mobile.sql
new file mode 100644
index 0000000..e6d816f
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/pages_mobile.sql
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS toolbox.pages_mobile CASCADE;
+
+CREATE TABLE toolbox.pages_mobile
+(id SERIAL,
+ page INTEGER NOT NULL
+ REFERENCES toolbox.pages(id)
+ ON DELETE CASCADE,
+ mobile_active BOOLEAN NET NULL
+ DEFAULT FALSE,
+PRIMARY KEY (id));
+
+GRANT ALL ON toolbox.pages_mobile TO nobody;
+GRANT ALL ON toolbox.pages_mobile_id_seq TO nobody;
+
+CREATE UNIQUE INDEX page_mobile_page_indx ON toolbox.pages_mobile(page);
diff --git a/Toolkit/Toolbox/Database/tables/pages_preview.sql b/Toolkit/Toolbox/Database/tables/pages_preview.sql
new file mode 100644
index 0000000..3f4e5ae
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/pages_preview.sql
@@ -0,0 +1,7 @@
+CREATE TEMPORARY TABLE toolbox.pages_preview
+(LIKE toolbox.pages
+ INCLUDING DEFAULTS,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON toolbox.pages_preview TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/paragraphs.sql b/Toolkit/Toolbox/Database/tables/paragraphs.sql
new file mode 100644
index 0000000..26aa4ee
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/paragraphs.sql
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS toolbox.paragraphs CASCADE;
+
+CREATE TABLE toolbox.paragraphs
+(id SERIAL,
+ active BOOLEAN NOT NULL
+ DEFAULT TRUE,
+ back_to_top BOOLEAN NOT NULL
+ DEFAULT FALSE,
+ caption TEXT,
+ description TEXT,
+ image TEXT,
+ page INTEGER NOT NULL
+ REFERENCES toolbox.pages (id)
+ ON UPDATE CASCADE
+ ON DELETE CASCADE,
+ pos INTEGER NOT NULL
+ DEFAULT 1,
+ title TEXT,
+ PRIMARY KEY (id),
+ CHECK (pos > 0)); -- paragraph position cannot fall below zero
+
+GRANT ALL ON toolbox.paragraphs_id_seq TO nobody;
+GRANT ALL ON toolbox.paragraphs TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/paragraphs_draft.sql b/Toolkit/Toolbox/Database/tables/paragraphs_draft.sql
new file mode 100644
index 0000000..85c3e33
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/paragraphs_draft.sql
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS toolbox.paragraphs_draft CASCADE;
+
+CREATE TABLE toolbox.paragraphs_draft
+(LIKE toolbox.paragraphs
+ INCLUDING DEFAULTS,
+ FOREIGN KEY (page) REFERENCES toolbox.pages_draft(id)
+ ON UPDATE CASCADE
+ ON DELETE CASCADE,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON toolbox.paragraphs_draft TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/paragraphs_history.sql b/Toolkit/Toolbox/Database/tables/paragraphs_history.sql
new file mode 100644
index 0000000..6515b02
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/paragraphs_history.sql
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS toolbox.paragraphs_history CASCADE;
+
+CREATE TABLE toolbox.paragraphs_history
+(LIKE toolbox.paragraphs
+ INCLUDING DEFAULTS,
+ revision SERIAL NOT NULL
+);
+
+GRANT ALL ON toolbox.paragraphs_history_revision_seq TO nobody;
+GRANT ALL ON toolbox.paragraphs_history TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/paragraphs_preview.sql b/Toolkit/Toolbox/Database/tables/paragraphs_preview.sql
new file mode 100644
index 0000000..b669e3f
--- /dev/null
+++ b/Toolkit/Toolbox/Database/tables/paragraphs_preview.sql
@@ -0,0 +1,6 @@
+CREATE TEMPORARY TABLE toolbox.paragraphs_preview
+(LIKE toolbox.paragraphs
+ INCLUDING DEFAULTS
+);
+
+GRANT ALL ON toolbox.paragraphs_preview TO nobody;
diff --git a/Toolkit/Toolbox/DraftPagesTree.php b/Toolkit/Toolbox/DraftPagesTree.php
new file mode 100644
index 0000000..3b02d27
--- /dev/null
+++ b/Toolkit/Toolbox/DraftPagesTree.php
@@ -0,0 +1,42 @@
+rootNodeStart : $this->subTreeStart;
+
+ if (is_array($leaf) && !empty($leaf)) {
+ foreach ($tree as $parent => $children) {
+ foreach ($children as $branch) {
+ $html .= sprintf($this->leafStart, $branch['id'], null);
+ $previewUrl = BASE_URL . "index.php?rt=Draft&catid={$branch['id']}";
+
+ $html .= "{$branch['navigation_name']}";
+
+ $html .= '
+ To include all members, turn this feature on and
+ don\'t select categories or regions below.
+ To restrict to specific categories, select desired
+ categories under Category Filter.
+ To restrict to specific regions, select desired
+ regions under Region Filter.
+
+ To include all coupons, turn this feature on and
+ don\'t select categories below.
+ To restrict to specific categories, select desired
+ categories under Categories Filter.
+
+
'
+ );
+ // Note that we call to populate this select list at the
+ // bottom of this function after the element is made
+ // so we load attributes (classes) into each option as needed.
+ $e[] = array(
+ 'type' => 'select',
+ 'req' => false,
+ 'name' => 'coupon_categories',
+ 'display' => 'Category Filter (none = all)',
+ 'opts' => $this->_getCouponCategories($dbh),
+ 'att' => array(
+ 'multiple' => 'multiple',
+ 'size' => 4,
+ 'title' => '-- Select to Add --',
+ 'id' => 'CouponCategories'
+ )
+ );
+ }
+
+ if (defined('HOME_HEADLINES') && HOME_HEADLINES) {
+ $e[] = array(
+ 'type' => 'html',
+ 'req' => false,
+ 'name' => '',
+ );
+ $e[] = array(
+ 'type' => 'html',
+ 'req' => false,
+ 'name' => '',
+ );
+ $e[] = array(
+ 'type' => 'header',
+ 'req' => false,
+ 'name' => 'homePageHeadlinesHdr',
+ 'display' => 'Home Page Headlines'
+ );
+ $e[] = array(
+ 'type' => 'advcheckbox',
+ 'req' => false,
+ 'name' => 'headline',
+ 'display' => 'Headline',
+ 'opts' => 'Include this page in the Home Page Headlines',
+ 'val' => array(0, 1)
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => false,
+ 'name' => 'headline_intro',
+ 'display' => 'Intro',
+ 'opts' => array('size' => 55),
+ 'noCharLimit' => true
+ );
+ }
+ $e[] = array(
+ 'type' => 'html',
+ 'req' => false,
+ 'name' => '',
+ );
+ if ($_SERVER['PHP_AUTH_USER'] == 'MediaAdmin' || DEVELOPMENT) {
+ $e[] = array(
+ 'type' => 'html',
+ 'req' => false,
+ 'name' => '',
+ );
+ $e[] = array(
+ 'type' => 'header',
+ 'req' => false,
+ 'name' => 'metaHdr',
+ 'display' => 'Metadata Information'
+ );
+ $e[] = array(
+ 'type' => 'text',
+ 'req' => false,
+ 'name' => 'meta_title',
+ 'display' => 'Title Tag',
+ 'opts' => array('size' => 35)
+ );
+ $e[] = array(
+ 'type' => 'textarea',
+ 'req' => false,
+ 'name' => 'meta_description',
+ 'display' => 'Description',
+ 'opts' => array(
+ 'cols' => 40,
+ 'rows' => 5
+ ),
+ );
+ $e[] = array(
+ 'type' => 'html',
+ 'req' => false,
+ 'name' => '',
+ );
+ } else {
+ $e[] = array(
+ 'type' => 'hidden',
+ 'req' => false,
+ 'name' => 'meta_title'
+ );
+ $e[] = array(
+ 'type' => 'hidden',
+ 'req' => false,
+ 'name' => 'meta_description'
+ );
+ }
+ $e[] = array(
+ 'type' => 'html',
+ 'req' => false,
+ 'name' => '',
+ );
+ $e[] = array(
+ 'type' => 'header',
+ 'req' => false,
+ 'name' => 'templatesHdr',
+ 'display' => 'Page Layout'
+ );
+ $e[] = array(
+ 'type' => 'group',
+ 'req' => false,
+ 'name' => 'page_layout',
+ 'group' => $templates,
+ 'seperator' => '',
+ 'appendName' => false
+ );
+ $e[] = array(
+ 'type' => 'html',
+ 'req' => false,
+ 'name' => '',
+ );
+ if (isset($_GET['id']) && ctype_digit($_GET['id'])) {
+ $e[] = array(
+ 'type' => 'html',
+ 'req' => false,
+ 'name' => '',
+ );
+ $e[] = array(
+ 'type' => 'header',
+ 'req' => false,
+ 'name' => 'templatesHdr',
+ 'display' => 'Page Information'
+ );
+ $e[] = array(
+ 'type' => 'static',
+ 'req' => false,
+ 'name' => 'page_id',
+ 'display' => 'Page ID',
+ );
+ $e[] = array(
+ 'type' => 'static',
+ 'req' => false,
+ 'name' => 'active_alt',
+ 'display' => 'State',
+ );
+ $e[] = array(
+ 'type' => 'static',
+ 'req' => false,
+ 'name' => 'revised',
+ 'display' => 'Revised',
+ );
+ $e[] = array(
+ 'type' => 'static',
+ 'req' => false,
+ 'name' => 'created',
+ 'display' => 'Created Date',
+ );
+ $e[] = array(
+ 'type' => 'static',
+ 'req' => false,
+ 'name' => 'last_modified',
+ 'display' => 'Last Modified Date',
+ );
+ $e[] = array(
+ 'type' => 'html',
+ 'req' => false,
+ 'name' => '',
+ );
+ }
+ $e[] = array(
+ 'type' => 'html',
+ 'req' => false,
+ 'name' => '',
+ );
+
+ // If we are editing a page, show three submit buttons
+ // otherwise, just show one insert button.
+ $e[] = array(
+ 'type' => 'group',
+ 'req' => false,
+ 'name' => 'submit_buttons',
+ 'group' => $submitBtns,
+ 'label' => '',
+ 'seperator' => '',
+ 'appendName' => false,
+ );
+
+ $this->setupElements($e);
+
+ // Do the same for the pages
+ if ($_GET['rt'] == 'EditPage' && $showParentPage) {
+ $this->loadParentPages($dbh);
+ }
+
+ // Load the member categories after the elements have been created
+ // so we can get more control how the options are rendered
+ // ie (adding classes to them)
+ if (defined('MEMBERS_DB') && MEMBERS_DB) {
+ $this->loadMemberCategories($dbh);
+ if ($hasRegions) {
+ $this->loadMemberRegions($dbh);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ configureFilters()
+
+ /**
+ * Form filter definitions
+ *
+ * Applies a data filter for the given fields when the form is submitted
+ *
+ * @return void
+ * @access public
+ */
+ public function configureFilters()
+ {
+ $f = array();
+
+ $f[] = array(
+ 'element' => '__ALL__',
+ 'filter' => 'trim'
+ );
+
+ $this->setupFilters($f);
+ }
+
+ // }}}
+ // {{{ configureForm()
+
+ /**
+ * Bundle all form configuration calls into one function call
+ *
+ * @param PDO $dbh Database handler
+ * @param Toolkit_Toolbox_GatewayFactoryAbstract $factory Gateway factory
+ * @param Toolkit_FileServer_ImageApater $is Image Server
+ * @param Config_Container $memRoot (optional) Configuration object
+ * @param Config_Container $cpnRoot (optional) Configuration object
+ *
+ * @return void
+ * @access public
+ */
+ public function configureForm(
+ PDO $dbh,
+ Toolkit_Toolbox_GatewayFactoryAbstract $factory,
+ Toolkit_FileServer_ImageAdapter $is,
+ Config_Container $memRoot = null,
+ Config_Container $cpnRoot = null
+ ) {
+ $this->configureDefaults($factory, $_GET['id']);
+ $this->configureElements($dbh, $memRoot, $cpnRoot, $_GET['id']);
+ $this->configureRules($is);
+ $this->configureFilters();
+ $this->configureConstants($dbh);
+ }
+
+ // }}}
+ // {{{ configureRules()
+
+ /**
+ * Form rule definitions
+ *
+ * Adds validation rules for the given fields
+ *
+ * @param Toolkit_FileServer_ImageAdapter $is Image Server
+ *
+ * @return void
+ * @access public
+ */
+ public function configureRules(Toolkit_FileServer_ImageAdapter $is)
+ {
+ $r = array();
+
+ // Form Rules
+ $r[] = array(
+ 'element' => 'short_url',
+ 'message' => 'ERROR: Short URLs may only contain alpha numeric characters plus - (dash) or _ (underscore)!',
+ 'type' => 'ShortUrl',
+ 'format' => null,
+ 'validation' => $this->validationType,
+ 'reset' => false,
+ 'force' => false
+ );
+ if (!empty($_FILES['file']['tmp_name'])) {
+ $r[] = array(
+ 'element' => 'file',
+ 'message' => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+ 'type' => 'mimetype',
+ 'format' => $is->getAllowedMimeTypes(),
+ 'validation' => 'server',
+ 'reset' => false,
+ 'force' => false
+ );
+ }
+ $r[] = array(
+ 'element' => 'file',
+ 'message' => 'ERROR: Error uploading image!',
+ 'type' => 'Image',
+ 'format' => array(
+ 'form' => $this,
+ 'fieldName' => 'file',
+ 'imageField' => 'image',
+ 'is' => $is,
+ 'deleteExistingImage' => false,
+ 'injectImage' => array('tgtElement' => 'current_image_thumb')
+ ),
+ 'validation' => 'server',
+ 'reset' => false,
+ 'force' => false
+ );
+
+ $this->setupRules($r);
+ }
+
+ // }}}
+
+ // {{{ getCouponCategories()
+
+ /**
+ * Fetches all coupon categories
+ *
+ * @param PDO $dbh Database handler
+ *
+ * @return array coupon categories
+ */
+ private function _getCouponCategories(PDO $dbh)
+ {
+ $sql = "
+ SELECT *
+ FROM coupon_category
+ ORDER BY name";
+ $couponCats = array();
+ foreach ($dbh->query($sql) as $row) {
+ $couponCats[$row['id']] = $row['name'];
+ }
+ return $couponCats;
+ }
+
+ // }}}
+
+ // {{{ loadParentPages()
+
+ /**
+ * Load option elements into the parent select list
+ *
+ * These options are loaded via this seperate function vs inline w/ the
+ * element definition b/c we need a little more control defining
+ * the class names for each option so they will render nice when a user
+ * is looking at the list.
+ *
+ * @param PDO $dbh Database handler
+ *
+ * @return void
+ * @throws PDOException throws exception on sql error
+ * @access public
+ */
+ public function loadParentPages(PDO $dbh)
+ {
+ try {
+ // Get a tree list of categories in linear order with
+ // category keys in the values and their level in the tree
+ // in the value
+ $c = Toolkit_Common::getHierarchicalTreeStructure(
+ $dbh,
+ 'pages',
+ 'id',
+ 'parent',
+ 'pos',
+ 0,
+ $this->maxDepth
+ );
+
+ // unset the home page, this is never an option to have children
+ // underneath it.
+ unset($c[HOME_ID]);
+
+ // If we are editing a page, then we don't want that page
+ // to show up as an option in the select list.
+ if (is_numeric($_GET['id'])) {
+ reset($c);
+ // Get us to the point in the array were this page is located
+ while (key($c) != $_GET['id'] && current($c) !== false) {
+ next($c);
+ }
+ // Make sure we didn't traverse off the end of the array
+ if (current($c) !== false) {
+ // get the starting level we are currently at
+ $sl = current($c);
+ // remove this page (the one we're editing) from the
+ // array and advance the internal array pointer
+ unset($c[key($c)]);
+ // now we need to make sure all sub pages beneath this
+ // page are also not being shown
+
+ // while we don't traverse off the end of the array
+ while (current($c) !== false) {
+ // get the current sub level we are at
+ $csl = current($c);
+ // if the current sub-level is the same as the
+ // starting level, that means we have traversed through
+ // all the sub-pages and can break out of the loop
+ if ($csl <= $sl) {
+ break;
+ } else {
+ // we are still in a sub-level page, so unset
+ // this page so it doesn't show, and advance
+ // the internal array pointer
+ unset($c[key($c)]);
+ }
+ }
+ }
+ }
+
+ // Get all the data about each category
+ $sql = "
+ SELECT *
+ FROM pages
+ WHERE id = ?";
+
+ $stmt = $dbh->prepare($sql);
+ // Get the member categories select list element
+ $e =& $this->getElement('parent');
+ foreach ($c as $i => $j) {
+ $stmt->execute(array($i));
+ $row = $stmt->fetch();
+ // the class level is always 1 less than what is reported
+ // from our $c array
+ $x = $j - 1;
+ // Add the option data to the select list.
+ $e->addOption(
+ $row['navigation_name'],
+ $i,
+ array('class' => "level-$x")
+ );
+ }
+ } catch (PDOException $e) {
+ Toolkit_Logger::logException('DB error', $e);
+ throw new Toolkit_Toolbox_Exception(
+ "Error loading parent pages"
+ );
+ }
+ }
+
+ // }}}
+ // {{{ loadMemberCategories()
+
+ /**
+ * Loads member categories into the select list
+ *
+ * Gets an array structure of the member categories in a linear tree order
+ * Then walk through the array and load each category into the select list
+ *
+ * @param PDO $dbh Database handler
+ *
+ * @return void
+ * @access public
+ */
+ public function loadMemberCategories(PDO $dbh)
+ {
+ try {
+ // Get a tree list of categories in linear order with
+ // category keys in the values and their level in the tree
+ // in the value
+ $c = Toolkit_Common::getHierarchicalTreeStructure(
+ $dbh,
+ 'category',
+ 'category_id',
+ 'parent_id'
+ );
+
+ // Get all the data about each category
+ $sql = "
+ SELECT *
+ FROM category
+ WHERE category_id = ?";
+
+ $stmt = $dbh->prepare($sql);
+ // Get the member categories select list element
+ $e =& $this->getElement('member_categories');
+ if (is_array($c)) {
+ foreach ($c as $i => $j) {
+ $stmt->execute(array($i));
+ $row = $stmt->fetch();
+ // the class level is always 1 less than what is reported
+ // from our $c array
+ $x = $j - 1;
+ // Add the option data to the select list.
+ $e->addOption($row['name'], $i, array('class' => "level-$x"));
+ }
+ }
+ } catch (PDOException $e) {
+ Toolkit_Logger::logException('DB error', $e);
+ throw new Toolkit_Toolbox_Exception(
+ "Error loading member categories"
+ );
+ }
+ }
+
+ // }}}
+ // {{{ loadMemberRegions()
+
+ /**
+ * Loads member Cities into the select list
+ *
+ * Gets an array structure of the member Cities in a sorted order by name
+ * Then walk through the array and load each category into the select list
+ *
+ * @param PDO $dbh Database handler
+ *
+ * @return void
+ * @access public
+ */
+ public function loadMemberRegions(PDO $dbh)
+ {
+ try {
+ // Get all the data about each category
+ $sql = "
+ SELECT *
+ FROM region
+ ORDER BY region_name";
+
+ $stmt = $dbh->query($sql);
+ // Get the member categories select list element
+ $e =& $this->getElement('member_regions');
+ while ($row = $stmt->fetch()) {
+ // Add the option data to the select list.
+ $e->addOption($row['region_name'], $row['region_id']);
+ }
+ } catch (PDOException $e) {
+ Toolkit_Logger::logException('DB error', $e);
+ throw new Toolkit_Toolbox_Exception(
+ "Error loading member regions"
+ );
+ }
+ }
+
+ // }}}
+
+ // {{{ setMaxDepth()
+
+ /**
+ * Sets the max depth level that the parent page select list will show
+ *
+ * @param integer $md New max depth
+ *
+ * @return void
+ * @access public
+ */
+ public function setMaxDepth($md)
+ {
+ $this->maxDepth = $md;
+ }
+
+ // }}}
+ // {{{ setupRenderers()
+
+ /**
+ * Custom rendering templates for special fields on the form
+ *
+ * @return void
+ * @access protected
+ */
+ protected function setupRenderers()
+ {
+ parent::setupRenderers();
+ $renderer =& $this->defaultRenderer();
+
+ $required = '';
+ $required .= ' * ';
+ $required .= '';
+
+ $error = '';
+ $error .= '