Photos application files added
authorLaury GvR <laury@gaslightmedia.com>
Mon, 21 Jul 2014 13:12:07 +0000 (09:12 -0400)
committerLaury GvR <laury@gaslightmedia.com>
Mon, 21 Jul 2014 13:12:07 +0000 (09:12 -0400)
Photos application files added

55 files changed:
Toolkit/Photos/Admin/FileUploader.php [new file with mode: 0644]
Toolkit/Photos/Admin/PageTree.php [new file with mode: 0644]
Toolkit/Photos/Auth.php [new file with mode: 0644]
Toolkit/Photos/AuthContainer.php [new file with mode: 0644]
Toolkit/Photos/Controllers/IndexController.php [new file with mode: 0644]
Toolkit/Photos/Database/addSlideShow.sql [new file with mode: 0644]
Toolkit/Photos/Database/application.sql [new file with mode: 0644]
Toolkit/Photos/Database/removeApplication.sql [new file with mode: 0644]
Toolkit/Photos/Database/resetSequences.sql [new file with mode: 0644]
Toolkit/Photos/Database/tables/photo.sql [new file with mode: 0755]
Toolkit/Photos/Database/tables/photo2category.sql [new file with mode: 0644]
Toolkit/Photos/Database/tables/photo_category.sql [new file with mode: 0755]
Toolkit/Photos/Database/tables/photo_category_bus.sql [new file with mode: 0755]
Toolkit/Photos/Database/tables/photo_default.sql [new file with mode: 0755]
Toolkit/Photos/Database/updatePhotos.sql [new file with mode: 0644]
Toolkit/Photos/Database/upgradeApp.sql [new file with mode: 0644]
Toolkit/Photos/Display.php [new file with mode: 0644]
Toolkit/Photos/Exception.php [new file with mode: 0644]
Toolkit/Photos/Factory.php [new file with mode: 0644]
Toolkit/Photos/Gallery.php [new file with mode: 0644]
Toolkit/Photos/GalleryCategoryFilter.php [new file with mode: 0644]
Toolkit/Photos/Models/Category.php [new file with mode: 0644]
Toolkit/Photos/Models/MediaMapper.php [new file with mode: 0644]
Toolkit/Photos/Models/Page2Category.php [new file with mode: 0644]
Toolkit/Photos/Models/Photo.php [new file with mode: 0644]
Toolkit/Photos/Models/Photo2Category.php [new file with mode: 0644]
Toolkit/Photos/Models/User.php [new file with mode: 0644]
Toolkit/Photos/Photo.php [new file with mode: 0644]
Toolkit/Photos/SlideShowCategoryFilter.php [new file with mode: 0644]
Toolkit/Photos/application.ini [new file with mode: 0644]
Toolkit/Photos/assets/.keepme [new file with mode: 0644]
Toolkit/Photos/css/gallery.css [new file with mode: 0755]
Toolkit/Photos/css/style.css [new file with mode: 0644]
Toolkit/Photos/css/thickbox.css [new file with mode: 0755]
Toolkit/Photos/js/jquery.columnview.js [new file with mode: 0755]
Toolkit/Photos/js/photoGallery.js [new file with mode: 0644]
Toolkit/Photos/login.php [new file with mode: 0644]
Toolkit/Photos/photoProxy.php [new file with mode: 0644]
Toolkit/Photos/photoSearch.php [new file with mode: 0644]
Toolkit/Photos/setupNewPhotos.php [new file with mode: 0644]
Toolkit/Photos/templates/Admin/editCategory.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/editPhoto.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/editUser.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/emailUser.tpl [new file with mode: 0644]
Toolkit/Photos/templates/Admin/index.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/listCategories.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/listPhotos.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/listUsers.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/nav.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/searchForm.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/userNav.html [new file with mode: 0644]
Toolkit/Photos/templates/photoCats.html [new file with mode: 0755]
Toolkit/Photos/templates/photoGalleryWrapper.html [new file with mode: 0644]
Toolkit/Photos/templates/photos.html [new file with mode: 0755]
admin/photos.php [new file with mode: 0644]

diff --git a/Toolkit/Photos/Admin/FileUploader.php b/Toolkit/Photos/Admin/FileUploader.php
new file mode 100644 (file)
index 0000000..b81c390
--- /dev/null
@@ -0,0 +1,458 @@
+<?php
+/**
+ * FileUploader.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Photos/Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+define('YAHOO_YUI_BASE', MEDIA_APP_BASE_URL . 'yui/build/');
+define('PHOTOS_UPLOAD_PATH', BASE . 'admin/Photos/uploads/');
+
+/**
+ * Toolkit_Photos_Admin_FileUploader
+ *
+ * Description of Toolkit_Photos_Admin_FileUploader
+ *
+ * @category  Toolkit
+ * @package   Photos/Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2014 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Photos_Admin_FileUploader
+    extends Toolkit_FormBuilder
+{
+    // {{{ Class Properties
+    /**
+     * Description of $debug
+     * @var boolean
+     */
+    public $debug = false;
+
+    /**
+     * Message for successful upload
+     * @var string
+     */
+    protected $successMsg
+        = '<div id="form-success-top">
+               You have successfully uploaded the files to your photo album.
+           </div>';
+    // }}}
+
+    //    {{{    configureDefaults()
+
+    /**
+     * Initializes default form values
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $defaults = array('catid' => $_REQUEST['catid']);
+
+        $this->setupDefaults($defaults);
+    }
+
+    //    }}}
+    //    {{{    configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        $categories = $this->getPhotoCategories();
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'albumHdr_rmv',
+            'display' => '
+            <p style="width: 700px; padding-left: 5px;">
+                <strong>USE:</strong>
+            Hold down the Ctrl key to select or unselect more than one photo
+            at a time. Hold down the Shift key to select a range.</p>
+            <div style="position: absolute; top: 320px;width: 700px; border: 1px dashed #9FD8EF;padding: 20px; margin: 0 0 20px 0;">
+            This feature (Multiple Uploads) is only supported in certain modern browsers:
+            <ul>
+                <li>Internet Explorer 10</li>
+                <li>FireFox 8 and up</li>
+                <li>Safari 5 and up</li>
+                <li>Google Chrome 17 and up</li>
+            </ul>
+            </div>'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'catid'
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'name'    => 'files[]',
+            'display' => 'Files to Upload',
+            'opts'    => array(
+                'id'       => 'myuploader',
+                'multiple' => 'multiple',
+                'accept'   => 'image/*'
+            )
+        );
+
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit Form'
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+    //    {{{    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()
+
+    /**
+     * Helper function, configures the entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    //  }}}
+    //    {{{    configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+        $this->setupRules($r);
+    }
+
+    //    }}}
+
+    // {{{ getFileData()
+    /**
+     * Get file data
+     *
+     * @param string $fileName Filename
+     * @param string $path     File path
+     *
+     * @return array|boolean
+     * @access public
+     */
+    function getFileData($fileName, $path)
+    {
+        $filename = $path . '/' . $fileName;
+        if (is_file($filename) && is_readable($filename)) {
+            return array(
+                'name' => $fileName,
+                'tmp_name' => $filename,
+                'size' => filesize($filename),
+                'type' => mime_content_type($filename)
+            );
+        } else {
+            return false;
+        }
+    }
+    // }}}
+    // {{{ getPhotoCategorieso()
+
+    /**
+     * Get photo categories
+     *
+     * @return array
+     * @access public
+     */
+    function getPhotoCategories()
+    {
+        try {
+            $sql = "
+              SELECT id, category
+                FROM photo_category
+            ORDER BY category";
+            $stmt = $this->dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $categories[$row['id']] = $row['category'];
+            }
+            return $categories;
+        } catch(PDOExecption $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }// }}}
+    // {{{ getCategoryMaxPos()
+    /**
+     * get max pos plus one
+     *
+     * @param mixed $id photo category id
+     *
+     * @access public
+     * @return string
+     */
+    function getCategoryMaxPos($id)
+    {
+        try {
+            $sql = "
+            SELECT COALESCE(max(pos) + 1, 1)
+              FROM photo2category
+             WHERE category = :catid";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(":catid", $id, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }// }}}
+
+    //    {{{    processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return array     Result of Insert / Update function
+     * @access public
+     */
+    public function processData($values)
+    {
+        if (   is_array($values['files'])
+            && !empty($values['files'])) {
+            $this->processFilesArray($values);
+        } else {
+            $this->processFolder($values);
+        }
+        return true;
+    }
+
+    //    }}}
+
+    /**
+     * Process files array
+     *
+     * @param array $values Values array
+     *
+     * @return void
+     * @access public
+     */
+    public function processFilesArray($values)
+    {
+        $images = array();
+        $photos = array();
+        $acceptedFiles = array(
+            'image/jpeg',
+            'image/gif',
+            'image/png'
+        );
+        $is = new Toolkit_FileServer_ImageAdapter(IS_OWNER_ID, IS_OWNER_PW);
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            Toolkit_Database::getInstance()
+        );
+        $pos = $this->getCategoryMaxPos($values['catid']);
+        if (is_array($values['files']['name'])) {
+            for ($i = 0; $i < count($values['files']['name']); ++$i) {
+                // check that it's an image
+                // jpg jpeg gif or png
+                if (   !$values['files']['error'][$i]
+                    && in_array($values['files']['type'][$i], $acceptedFiles)
+                ) {
+                    $images[] = array(
+                        'name'     => $values['files']['name'][$i],
+                        'tmp_name' => $values['files']['tmp_name'][$i],
+                        'size'     => $values['files']['size'][$i]
+                    );
+                }
+            }
+        }
+        if (!empty($images)) {
+            foreach ($images as $img) {
+                $imgData = $is->uploadImageFile($img['tmp_name']);
+                $photo = Toolkit_Photos_Models_Photo::createByValues(
+                    array(
+                        'image' => $imgData['name'],
+                        'catid' => $values['catid']
+                    )
+                );
+                $photoId = $mediaMapper->savePhoto($photo);
+                $photo2Category
+                    = Toolkit_Photos_Models_Photo2Category::createByValues(
+                        array(
+                            'photo'    => $photoId,
+                            'category' => $values['catid'],
+                            'pos'      => $pos
+                        )
+                    );
+                $mediaMapper->savePhoto2Category($photo2Category);
+                ++$pos;
+            }
+        }
+    }
+
+    /**
+     * Process folder
+     *
+     * @param array $values Values array
+     *
+     * @throws Exception
+     * @return void
+     * @access public
+     */
+    public function processFolder($values)
+    {
+        $is  = new Toolkit_Image_Server();
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            Toolkit_Database::getInstance()
+        );
+        try {
+            if (!is_dir($_SESSION['uploadFolder'])) {
+                throw new Exception('No Folder exists');
+            }
+        } catch (Exception $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+        $d   = dir($_SESSION['uploadFolder']);
+        $pos = $this->getCategoryMaxPos($_REQUEST['catid']);
+        $i = 0;
+        while (false !== ($entry = $d->read())) {
+            if (!in_array($entry, array('.','..','.svn','CVS'))) {
+                // before we can send the image to the image class we need
+                // to inject the data for the images into the $_FILES array
+                $_FILES['img' . $i] = $this->getFileData($entry, $_SESSION['uploadFolder']);
+                // send to image server and insert values
+                $image_name = $is->imageUpload('img' . $i, $_SESSION['uploadFolder']);
+                $photo = Toolkit_Photos_Models_Photo::createByValues(
+                    array(
+                        'image' => $image_name,
+                        'catid' => $_REQUEST['catid']
+                    )
+                );
+                $photoId = $mediaMapper->savePhoto($photo);
+                $photo2Category
+                    = Toolkit_Photos_Models_Photo2Category::createByValues(
+                        array(
+                            'photo'    => $photoId,
+                            'category' => $_REQUEST['catid'],
+                            'pos'      => $pos
+                        )
+                    );
+                $mediaMapper->savePhoto2Category($photo2Category);
+                ++$pos;
+                unlink($_SESSION['uploadFolder'] . '/'. $entry);
+                ++$i;
+            }
+        }
+        $d->close();
+        if (is_dir($_SESSION['uploadFolder'])) {
+            rmdir($_SESSION['uploadFolder']);
+        }
+    }
+
+    //    {{{    setupRenderers()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        parent::setupRenderers();
+        $renderer =& $this->defaultRenderer();
+        $required = '<!-- BEGIN required --><span class="req"> * </span><!-- END required -->';
+        $error    = '<!-- BEGIN error --><div class="req"> {error} </div><!-- END error -->';
+    }
+
+    //  @codeCoverageIgnoreEnd
+    //    }}}
+
+    //    {{{    toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        $this->setupRenderers();
+        if ($this->validate()) {
+            $this->cleanForm();
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                HTTP_Session2::unregister('uploadFolder');
+                header("Location: photos.php?ac=editCategory&id=" . $_REQUEST['catid']);
+                exit;
+            }
+
+        } elseif ($this->isSubmitted()) {
+            $output  = $this->errorMsg;
+            $output .= parent::toHTML();
+            $output = '<pre>'.print_r($this, true).'</pre>';
+        } else {
+            $output = parent::toHTML();
+        }
+        return $output;
+    }
+
+    //    }}}
+}
+?>
diff --git a/Toolkit/Photos/Admin/PageTree.php b/Toolkit/Photos/Admin/PageTree.php
new file mode 100644 (file)
index 0000000..9e52bfa
--- /dev/null
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * PageTree.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_PageTree
+ *
+ * Display the toolbox page as ul lil list for jQuery-Column viewer
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Admin_PageTree
+{
+    private $_dbh;
+    private $_rootNodeStart = "<ul class=\"menu\" id=\"demo1\">\n";
+    private $_leafStartExpanded = "\n\t<li class=\"expanded\" %s>\n";
+    private $_leafStartLeaf = "\n\t<li class=\"leaf\" %s>\n";
+    private $_subTreeStart = "\n<ul class=\"menu\">\n";
+    private $_treeEnd = "\n</ul>\n";
+    private $_leafEnd = "\n\t</li>\n";
+    private $_tree;
+
+    public function __construct(PDO $dbh)
+    {
+        $this->_dbh = $dbh;
+    }
+
+    /**
+     * creates and executes the sql query for getting the pages
+     *
+     * @return array | null
+     */
+    private function _findAll()
+    {
+        try {
+            $members
+                = (filter_var(MEMBERS_CATEGORY, FILTER_VALIDATE_INT))
+                ? "WHERE id NOT IN (" . MEMBERS_CATEGORY . ")
+                     AND parent NOT IN (" . MEMBERS_CATEGORY . ")"
+                : '';
+            $sql = "
+                SELECT id,parent,navigation_name
+                  FROM pages
+                $members
+                 ORDER by parent, pos";
+
+            return $this->_dbh
+                ->query($sql)
+                ->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Get all pages for the tree
+     *
+     * @return array
+     */
+    private function _fetchPages()
+    {
+        $pages = $this->_findAll();
+        if (is_array($pages)) {
+            $threads = array();
+            foreach ($pages as $page) {
+                $page['children'] = array();
+                $threads[] = $page;
+            }
+
+            $children = array();
+            while (list($key, $value) = each ($threads)) {
+                $children[$value['parent']][$value['id']] = $value;
+            }
+
+            $this->_tree = $children;
+        } else {
+            $this->_tree = array();
+        }
+    }
+
+    /**
+     * Create html of the pages tree for jqueyr.columnview
+     *
+     * @return string
+     */
+    public function toHtml()
+    {
+        $this->_fetchPages();
+        if (is_array($this->_tree)) {
+            $html = $this->createTree($this->_tree, reset($this->_tree));
+        }
+        return $html;
+    }
+
+    /**
+     * Creates the tree structure for the pages jquery column view
+     *
+     * @param array $tree  Array for tree
+     * @param type  $leaf  Array for leaf
+     * @param type  $level tree level
+     *
+     * @return string
+     */
+    protected function createTree(array $tree, $leaf, $level = 0)
+    {
+        $html = !$level ? $this->_rootNodeStart : $this->_subTreeStart;
+        if (is_array($leaf) && !empty($leaf)) {
+            while (list($parent, $branch) = each($leaf)) {
+                $pageName = htmlspecialchars($branch['navigation_name']);
+                if ($tree[$parent]) {
+                    $html .= sprintf($this->_leafStartExpanded, null);
+                    $html .= "<a href=\"#\" data-page=\"{$branch['id']}\" data-name=\"{$pageName}\">{$branch['navigation_name']} </a> ";
+                    $html .= $this->createTree($tree, $tree[$parent], $level + 1);
+                } else {
+                    $html .= sprintf($this->_leafStartLeaf, null);
+                    $html .= "<a href=\"#\" data-page=\"{$branch['id']}\" data-name=\"{$pageName}\">{$branch['navigation_name']} </a> ";
+                    $html .= $this->_leafEnd;
+                }
+            }
+        }
+        $html .= $this->_treeEnd;
+        if ($level) {
+            $html .= $this->_leafEnd;
+        }
+        return $html;
+    }
+}
diff --git a/Toolkit/Photos/Auth.php b/Toolkit/Photos/Auth.php
new file mode 100644 (file)
index 0000000..8b5e31e
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Member Authentication
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Auth.php,v 1.22 2010/08/10 18:08:44 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members_Auth-LoginForm, Toolkit_Members_Auth-PasswordForm
+ */
+
+require_once 'Auth.php';
+
+/**
+ * Methods for the memberdb authentication system
+ *
+ * Handles Cookie and session generation, id challenges and security for
+ * the memberdb application
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members_Auth-LoginForm, Toolkit_Members_Auth-PasswordForm
+ */
+class Toolkit_Photos_Auth extends Auth
+{
+    //    {{{    properties
+
+    /**
+     * Maximum idle time
+     *
+     * If more seconds pass before a new page request, then the user
+     * will have to re-authenticate back into the application.
+     * 1800 = 30 min
+     * 3600 = 1 hr
+     *
+     * @var    integer
+     * @access protected
+     */
+    protected $idleTime = 3600;
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Constructor
+     *
+     * Sets up the storage driver
+     *
+     * @param Config_Container              $c             Configuration object
+     * @param Toolkit_Members_AuthContainer $storageDriver storage driver
+     * @param string                        $loginFunction (optional)Name of the function
+     *                                                     that creates the login form
+     * @param boolean                       $showLogin     (optional)Should the login form
+     *                                                     be displayed if neccessary?
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(
+        Auth_Container $storageDriver,
+        $loginFunction = '',
+        $showLogin = false
+    ) {
+        parent::Auth($storageDriver, '', $loginFunction, $showLogin);
+    }
+
+    //    }}}
+
+    //    {{{    setIdle()
+
+    /**
+     * Set the maximum idle time
+     *
+     * @param integer $time time in seconds
+     * @param boolean $add  (optional)add time to current maximum idle time or not
+     *
+     * @return void
+     * @access public
+     */
+    public function setIdle($time = null, $add = false)
+    {
+        $time = is_null($time) ? $this->idleTime : $time;
+        parent::setIdle($time, $add);
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Photos/AuthContainer.php b/Toolkit/Photos/AuthContainer.php
new file mode 100644 (file)
index 0000000..a3eed86
--- /dev/null
@@ -0,0 +1,193 @@
+<?php
+
+/**
+ * AuthContainer.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_AuthContainer
+ *
+ * Description of AuthContainer
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_AuthContainer
+    extends Auth_Container
+{
+    //    {{{    properties
+
+    /**
+     * Database handler
+     * @var    PDO
+     * @access private
+     */
+    private $_dbh;
+
+    /**
+     * Addition options for the storage container
+     * @var array
+     * @access private
+     */
+    private $_options = array();
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $dbh, array $options = null)
+    {
+        $this->_dbh = $dbh;
+        $this->_setDefaults();
+        if (is_array($options)) {
+            $this->_parseOptions($options);
+        }
+    }
+
+    //    }}}
+    //    {{{    _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    private function _setDefaults()
+    {
+        $this->_options['sessionName'] = 'glmMedia';
+        $this->_options['table']       = 'contact';
+        $this->_options['usernamecol'] = 'email';
+        $this->_options['passwordcol'] = 'media_pass';
+        $this->_options['db_fields']   = array('id', 'fname', 'lname');
+        $this->_options['cryptType']   = 'none';
+        $this->_options['db_where']    = ' AND media = true AND approved = true'
+            . ' AND (expire IS NULL OR expire > current_date)';
+    }
+
+    //    }}}
+    //    {{{    _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @param array $array options for class
+     *
+     * @access private
+     * @return void
+     */
+    private function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->_options[$key])) {
+                $this->_options[$key] = $value;
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{    fetchData()
+
+    /**
+     * Get the user information from the database
+     *
+     * @param string $username username to authenticate
+     * @param string $password password to authenticate against username
+     *
+     * @return boolean If the user was authenticated or not
+     * @access public
+     * @throws Toolkit_Members_Exception upon error querying DB for user
+     */
+    public function fetchData($username, $password)
+    {
+        if (   is_string($this->_options['db_fields'])
+            && strstr($this->_options['db_fields'], '*')
+        ) {
+            $sqlFrom = '*';
+        } else {
+            $sqlFrom  = $this->_options['usernamecol'];
+
+            if (strlen($fields = $this->_getDBFields()) > 0) {
+                $sqlFrom .= ", $fields";
+            }
+
+        }
+
+        $pword = ($this->_options['cryptType'] == 'md5') ? 'MD5(:pword)' : ':pword';
+
+        $sql = "
+            SELECT $sqlFrom
+              FROM {$this->_options['table']}
+             WHERE {$this->_options['usernamecol']} = :uname
+               AND {$this->_options['passwordcol']} = $pword";
+
+        if ($this->_options['db_where']) {
+            $sql .= $this->_options['db_where'];
+        }
+
+        try {
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':uname', $username, PDO::PARAM_STR);
+            $stmt->bindParam(':pword', $password, PDO::PARAM_STR);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+            if ($row !== FALSE) {
+                foreach ($row as $key => $value) {
+                    $this->_auth_obj->setAuthData($key, $value);
+                }
+                return true;
+            }
+
+            return false;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Members_Exception(
+                "Error validating user `$username` - `$password`"
+            );
+        }
+    }
+
+    //    }}}
+    //    {{{    _getDBFields()
+
+    /**
+     * Get extra db fields to fetch and set in the auth data
+     *
+     * @return array comma separated string of extra db fields for a SQL query
+     * @access private
+     */
+    private function _getDBFields()
+    {
+        if (isset($this->_options['db_fields'])) {
+            if (is_array($this->_options['db_fields'])) {
+                return implode(', ', $this->_options['db_fields']);
+            }
+        }
+    }
+
+    //    }}}
+}
+
diff --git a/Toolkit/Photos/Controllers/IndexController.php b/Toolkit/Photos/Controllers/IndexController.php
new file mode 100644 (file)
index 0000000..1a6f3e8
--- /dev/null
@@ -0,0 +1,514 @@
+<?php
+
+/**
+ * IndexController.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_IndexController
+ *
+ * Description of IndexController
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Controllers_IndexController
+    extends Toolkit_BaseControllerAbstract
+    implements Toolkit_IController
+{
+
+    const LIST_CATEGORY_TPL = 'Admin/listCategories.html';
+    const LIST_PHOTOS_TPL   = 'Admin/listPhotos.html';
+    const LIST_USERS_TPL    = 'Admin/listUsers.html';
+    const EDIT_CATEGORY_TPL = 'Admin/editCategory.html';
+    const EDIT_PHOTO_TPL    = 'Admin/editPhoto.html';
+    const EDIT_USER_TPL     = 'Admin/editUser.html';
+    const ADMIN_PAGE_TPL    = 'Admin/index.html';
+
+    public function indexAction()
+    {
+        $pageUrl = $this->registry->page;
+        $flexyOptions = $this->registry->config->flexyOptions->toArray();
+        $tpl = new HTML_Template_Flexy($flexyOptions);
+        $tpl->compile(self::LIST_CATEGORY_TPL);
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $page = new stdClass();
+        $page->imgPath = PHOTO_SMALL_URL;
+
+        // list template
+        $page->editUrl       = $pageUrl . '?ac=editCategory&id=';
+        $page->deleteUrl     = $pageUrl . '?ac=deleteCategory&id=';
+        $page->categories    = $mediaMapper->fetchAllCategories();
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function ajaxPhotoSearchAction()
+    {
+        $jsonData = array();
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $photoSearch = filter_var($_REQUEST['photoName'], FILTER_SANITIZE_STRING);
+        $photos      = $mediaMapper->fetchPhotosByName(true);
+        if ($photos) {
+            foreach ($photos as $photo) {
+                $jsonData[] = $photo->getTitle();
+            }
+        }
+        return json_encode($jsonData);
+    }
+
+    public function multipleUploadAction()
+    {
+        define('PHOTOS_UPLOAD_PATH', BASE . 'uploads/photos/');
+
+        HTTP_Session2::useCookies(false);
+        HTTP_Session2::start('photoUploader');
+        $form = new Toolkit_Photos_Admin_FileUploader('file_upload_form');
+        $form->debug = YUI_UPLOADER_DEBUG;
+        $form->configureForm();
+        $formOut = $form->toHtml();
+        return $this->_createPage($formOut);
+    }
+
+    private function _createPage($html)
+    {
+        $GLOBALS['topScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jquery/jquery-1.7.2.min.js';
+        $GLOBALS['topScripts'][]
+            = MEDIA_BASE_URL . 'admin/Photos/checkBrowserSupport.js';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'Toolkit/Photos/css/style.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'css/contactform.css';
+        $GLOBALS['topScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+        $mediaMapper      = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $tpl              = new HTML_Template_Flexy(
+            $this->registry->config->flexyOptions->toArray()
+        );
+        $page = new stdClass();
+        $ac    = filter_var($_REQUEST['ac'], FILTER_SANITIZE_STRING);
+        $catId = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+        if ($ac && ($ac == 'editCategory') && $catId) {
+            $page->categoryId = $catId;
+        }
+
+        $page->categorySearchUrl
+            = MEDIA_BASE_URL . 'admin/photos.php';
+        $this->_setNav($page);
+        $page->topScripts    = $this->_getTopScripts();
+        $page->bottomScripts = $this->_getBottomScripts();
+        $page->styles        = $this->_getCss();
+        $page->categories    = $mediaMapper->fetchAllCategories();
+        $page->AppName       = $this->registry->config->application->name;
+        $page->content       = $html;
+        $tpl->compile(self::ADMIN_PAGE_TPL);
+        return $tpl->bufferedOutputObject($page);
+    }
+
+    private function _setNav(&$page)
+    {
+        $page->userAdminUrl = MEDIA_BASE_URL . 'admin/photos.php';
+        $pageUrl = $this->registry->page;
+        $ac      = filter_var($_REQUEST['ac'], FILTER_SANITIZE_STRING);
+
+        // navigation urls
+        if ($ac == 'editCategory') {
+            $catId = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+            if ($catId) {
+                $page->addPhotoUrl
+                    = $pageUrl . '?ac=editPhoto&catid=' . $catId;
+                $page->addMultipleUrl
+                    = $pageUrl . '?ac=multipleUpload&catid=' . $catId;
+            }
+        }
+        if ($ac == 'multipleUpload') {
+            $catId = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+            if ($catId) {
+                $page->addPhotoUrl
+                    = $pageUrl . '?ac=editPhoto&catid=' . $catId;
+                $page->addMultipleUrl
+                    = $pageUrl . '?ac=multipleUpload&catid=' . $catId;
+            }
+        }
+        $page->addCategoryUrl = $pageUrl . '?ac=editCategory';
+        $page->listCategoryUrl = $pageUrl;
+        if ($this->registry->config->settings->mediaExclusive) {
+            $page->listUsersUrl    = $pageUrl . '?ac=listUsers';
+        }
+    }
+
+    public function searchPhotosAction()
+    {
+       $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $flexyOptions
+            = $this->registry->config->flexyOptions->toArray();
+        $tpl
+            = new HTML_Template_Flexy($flexyOptions);
+        $page
+            = new stdClass();
+        $page->imgPath = PHOTO_SMALL_URL;
+        $page->photos = $mediaMapper->fetchPhotosByName();
+        $page->editUrl = $this->registry->page
+            . '?ac=editPhoto';
+        $this->_setNav($page);
+        $tpl->compile(self::LIST_PHOTOS_TPL);
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function listUsersAction()
+    {
+        $flexyOptions = $this->registry->config->flexyOptions->toArray();
+        $tpl = new HTML_Template_Flexy($flexyOptions);
+        $tpl->compile(self::LIST_USERS_TPL);
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $page = new stdClass();
+        $this->_setNav($page);
+        $page->editUrl = $this->registry->page
+            . '?ac=editUser&catid=' . $catId
+            . '&id=';
+        $status = filter_var($_REQUEST['userStatus'], FILTER_SANITIZE_STRING);
+        $filters = array();
+        switch ($status) {
+        case 'approved':
+            $page->statusText = 'Approved';
+            $filters[] = "approved = true";
+            $filters[] = "(expire > current_date OR expire IS NULL)";
+            break;
+        case 'denied':
+            $page->statusText = 'Denied';
+            $filters[] = "denied = true";
+            break;
+        case 'expired':
+            $page->statusText = 'Expired';
+            $filters[] = "expire is not null";
+            $filters[] = "expire < current_date";
+            break;
+        default:
+            $page->statusText = 'Pending';
+            $filters[] = "((approved is null
+                AND denied IS NULL)
+                OR (denied = false AND approved = false))";
+            break;
+        }
+        $page->users = $mediaMapper->fetchUsers($filters);
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function showPagesAction()
+    {
+        $pageTree = new Toolkit_Photos_Admin_PageTree($this->registry->dbh);
+        return $pageTree->toHtml();
+    }
+
+    public function editCategoryAction()
+    {
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        if ($_POST) {
+            $saved = $mediaMapper->saveCategoryWithPost();
+            if ($saved) {
+                header('Location: ' . $this->registry->page);
+                exit;
+            }
+        }
+        $categoryId = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'css/contactform.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_APP_BASE_URL
+            . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Photos/js/jquery.columnview.js';
+        $flexyOptions
+            = $this->registry->config->flexyOptions->toArray();
+        $tpl
+            = new HTML_Template_Flexy($flexyOptions);
+        $page
+            = new stdClass();
+        $page->mediaExclusive
+            = $this->registry->config->settings->mediaExclusive;
+        $page->slideShow
+            = $this->registry->config->settings->slideShowOption;
+        $page->pages2Categories
+            = ($categoryId)
+            ? $mediaMapper->fetchCategoryPages($categoryId)
+            : null;
+        $page->imgPath = PHOTO_SMALL_URL;
+        $page->photos
+            = ($categoryId)
+            ? $mediaMapper->fetchAllPhotosByCatid($categoryId)
+            : null;
+        $page->categoryId = $categoryId;
+        $page->editUrl = $this->registry->page
+            . '?ac=editPhoto&catid=' . $categoryId
+            . '&id=';
+        $this->_setNav($page);
+        if ($categoryId) {
+            $page->category = $mediaMapper->fetchCategoryById($categoryId);
+        }
+        $page->formAction = $this->registry->page . '?ac=editCategory';
+        $tpl->compile(self::EDIT_CATEGORY_TPL);
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function editPhotoAction()
+    {
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $catId   = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+        // grab the category
+        if ($catId) {
+            $photoCategory = $mediaMapper->fetchCategoryById($catId);
+        }
+        $pageUrl = $this->registry->page;
+
+        if ($_POST) {
+            $saved = $mediaMapper->savePhotoWithPost();
+            if ($saved) {
+                header('Location: '
+                    . $this->registry->page.'?ac=editCategory&id='
+                    . $catId);
+                exit;
+            }
+            exit;
+        }
+        $photoId = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'css/contactform.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_APP_BASE_URL . 'libjs/plugins/asmselect/1.0.4a/jquery.asmselect.css';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/plugins/asmselect/1.0.4a/jquery.asmselect.js';
+        $flexyOptions  = $this->registry->config->flexyOptions->toArray();
+        $tpl           = new HTML_Template_Flexy($flexyOptions);
+        $page          = new stdClass();
+        $page->mediaExclusive
+            = $this->registry->config->settings->mediaExclusive;
+        $page->catid   = $catId;
+        $page->photo2Categories
+            = ($photoId)
+            ? $mediaMapper->fetchPhoto2CategoryByPhotoId($photoId)
+            : new ArrayObject();
+        $page->imgPath = PHOTO_SMALL_URL;
+        $page->categoryName= $photoCategory->getCategory();
+        $this->_setNav($page);
+        if ($photoId) {
+            $page->photo = $mediaMapper->fetchPhotoById($photoId);
+        }
+        $page->categories = $mediaMapper->fetchAllCategories();
+        $page->formAction = $this->registry->page
+            . '?ac=editPhoto&catid=' . $catId;
+        $tpl->compile(self::EDIT_PHOTO_TPL);
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function editUserAction()
+    {
+        $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'css/contactform.css';
+        $pageUrl = $this->registry->page;
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        if ($_POST) {
+            // if the user is beign approved
+            if ($_REQUEST['status'] == 'approved') {
+                // expire date needs to be greater or equal to current date
+                $expire = filter_var(
+                    $_REQUEST['expire'],
+                    FILTER_VALIDATE_REGEXP,
+                    array(
+                        'options' => array(
+                            'regexp' => '%[0-9]{2}/[0-9]{2}/[0-9]{4}%'
+                        )
+                    )
+                );
+                $isActiveDate
+                    = (!$expire || strtotime($expire) >= mktime());
+                if ($isActiveDate) {
+                    $this->emailUser('approved');
+                }
+            }
+            // if the user is being denied
+            if ($_REQUEST['status'] == 'denied') {
+                $this->emailUser('denied');
+            }
+
+            $saved = $mediaMapper->saveUserWithPost();
+            if ($saved) {
+                $status = filter_var(
+                    $_REQUEST['userStatus'], FILTER_SANITIZE_STRING
+                );
+                header(
+                    'Location: ' . $this->registry->page.'?ac=listUsers'
+                    . '&userStatus=' . $status
+                );
+                exit;
+            }
+            exit;
+        }
+        $userId
+            = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+        $flexyOptions
+            = $this->registry->config->flexyOptions->toArray();
+        $tpl  = new HTML_Template_Flexy($flexyOptions);
+        $page = new stdClass();
+        $this->_setNav($page);
+        if ($userId) {
+            $page->user = $mediaMapper->fetchUserById($userId);
+        }
+        $page->formAction = $this->registry->page . '?ac=editUser';
+        $tpl->compile(self::EDIT_USER_TPL);
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function emailUser($status)
+    {
+        $userId
+            = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $page          = new stdClass();
+        $page->user    = $mediaMapper->fetchUserById($userId);
+        if (!$page->user instanceof Toolkit_Photos_Models_User) {
+            return false;
+        }
+        if (!$page->user->getEmail()) {
+            return false;
+        }
+        $page->title      = SITENAME . ' Media Gallery';
+        $page->subject    = 'Your Media Request for Photos - Traverse City';
+        $page->email_from = MEDIA_REQUEST_FORM_EMAIL;
+        switch ($status) {
+        case 'approved' :
+            $page->approved = true;
+            break;
+        case 'denied' :
+            $page->denied = true;
+            break;
+        default :
+            return false;
+        }
+        $flexyOptions = $GLOBALS['flexyOptions'];
+        $flexyOptions['templateDir'] = BASE . 'Toolkit/Photos/templates/Admin';
+        $flexyOptions['compileDir'] = BASE . 'Toolkit/Photos/templates/compiled';
+        $tpl = new HTML_Template_Flexy($flexyOptions);
+        $tpl->compile('emailUser.tpl');
+        $htmlMsg = $tpl->bufferedOutputObject($page);
+        $msg = strip_tags($htmlMsg);
+        $mimeMail = new Mail_mime("\n");
+        $mimeMail->setFrom("Traverse City Media Gallery <".MEDIA_REQUEST_FORM_EMAIL.">");
+        $mimeMail->setSubject($page->subject);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        $mail =& Mail::factory('mail');
+        $body = $mimeMail->get();
+        $setHeader['Reply-To'] = "Traverse City Media Gallery <".MEDIA_REQUEST_FORM_EMAIL.">";
+
+        $headers = $mimeMail->headers($setHeader);
+
+        $res = $mail->send($page->user->getEmail(), $headers, $body);
+        if (PEAR::isError($res)) {
+            return Toolkit_Common::handleError($res);
+        } else {
+            return $res;
+        }
+        exit;
+    }
+
+    public function moveCategoriesAction()
+    {
+        $mediaMapper      = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $values = filter_var_array(
+            $_REQUEST,
+            array(
+                'catPhotos' => array(
+                    'filter' => FILTER_VALIDATE_INT,
+                    'flags'  => FILTER_FORCE_ARRAY
+                )
+            )
+        );
+        if ($values['catPhotos']) {
+            $mediaMapper->saveCategoryPositionsByArray($values['catPhotos']);
+        }
+        return true;
+    }
+
+    public function movePhotosAction()
+    {
+        $categoryId  = filter_var($_REQUEST['categoryId'], FILTER_VALIDATE_INT);
+        if (!$categoryId) {
+            return false;
+        }
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $values = filter_var_array(
+            $_REQUEST,
+            array(
+                'categoryId' => FILTER_VALIDATE_INT,
+                'photos' => array(
+                    'filter' => FILTER_VALIDATE_INT,
+                    'flags'  => FILTER_FORCE_ARRAY
+                )
+            )
+        );
+        if ($values['photos']) {
+            $mediaMapper->savePhotoPositionsByCategory(
+                $values['photos'],
+                $values['categoryId']
+            );
+        }
+        return '<pre>'.print_r($photo2categories, true).'</pre>';
+    }
+
+    private function _getCss()
+    {
+        return Toolkit_Common::getStyleSheets();
+    }
+
+    private function _getTopScripts()
+    {
+        return Toolkit_Common::getScripts($GLOBALS['topScripts']);
+    }
+
+    private function _getBottomScripts()
+    {
+        return Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+    }
+
+}
diff --git a/Toolkit/Photos/Database/addSlideShow.sql b/Toolkit/Photos/Database/addSlideShow.sql
new file mode 100644 (file)
index 0000000..3dc1a96
--- /dev/null
@@ -0,0 +1,7 @@
+--
+-- adding slideshow
+--
+
+ALTER TABLE photos.photo_category ADD slideshow BOOLEAN;
+ALTER TABLE photos.photo_category ALTER slideshow SET DEFAULT false;
+UPDATE photos.photo_category SET slideshow = false;
diff --git a/Toolkit/Photos/Database/application.sql b/Toolkit/Photos/Database/application.sql
new file mode 100644 (file)
index 0000000..eb1c72c
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE SCHEMA photos;
+GRANT ALL ON SCHEMA photos TO nobody;
+--
+-- Tables
+--
+\i ./tables/photo_category.sql
+\i ./tables/photo.sql
+\i ./tables/photo_category_bus.sql
+\i ./tables/photo_default.sql
+\i ./tables/photo2category.sql
diff --git a/Toolkit/Photos/Database/removeApplication.sql b/Toolkit/Photos/Database/removeApplication.sql
new file mode 100644 (file)
index 0000000..7428041
--- /dev/null
@@ -0,0 +1,8 @@
+--
+--     This will drop everything in the photos schema.
+--     Nothing better be in here except photos related objects
+--     or it will be dropped
+--
+--     The force is strong w/ this one, use it wisely.
+--
+DROP SCHEMA IF EXISTS photos CASCADE;
diff --git a/Toolkit/Photos/Database/resetSequences.sql b/Toolkit/Photos/Database/resetSequences.sql
new file mode 100644 (file)
index 0000000..4cd9be2
--- /dev/null
@@ -0,0 +1,7 @@
+--
+-- Need to reset sequences for photos tables
+-- try running this file
+--
+SET search_path TO photos;
+SELECT setval('photo_id_seq', (SELECT max(id) FROM photos.photo));
+SELECT setval('photo_category_id_seq', (SELECT max(id) FROM photos.photo_category));
\ No newline at end of file
diff --git a/Toolkit/Photos/Database/tables/photo.sql b/Toolkit/Photos/Database/tables/photo.sql
new file mode 100755 (executable)
index 0000000..7ca56ad
--- /dev/null
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS photos.photo CASCADE;
+
+CREATE TABLE photos.photo
+(id SERIAL,
+ title TEXT,
+ description TEXT,
+ image TEXT,
+ catid INTEGER NOT NULL
+    REFERENCES photos.photo_category (id)
+    ON UPDATE CASCADE
+    ON DELETE CASCADE,
+ pos INTEGER,
+ exclusive BOOLEAN DEFAULT false,
+ download BOOLEAN DEFAULT false,
+ PRIMARY KEY (id));
+
+GRANT ALL on photos.photo_id_seq to nobody;
+GRANT ALL on photos.photo to nobody;
diff --git a/Toolkit/Photos/Database/tables/photo2category.sql b/Toolkit/Photos/Database/tables/photo2category.sql
new file mode 100644 (file)
index 0000000..654915e
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS photos.photo2category CASCADE;
+
+CREATE TABLE photos.photo2category
+(
+    id SERIAL,
+    photo INTEGER,
+    category INTEGER,
+    pos INTEGER,
+    PRIMARY KEY (id)
+);
+
+GRANT ALL ON photos.photo2category TO  nobody;
+GRANT ALL ON photos.photo2category_id_seq TO nobody;
\ No newline at end of file
diff --git a/Toolkit/Photos/Database/tables/photo_category.sql b/Toolkit/Photos/Database/tables/photo_category.sql
new file mode 100755 (executable)
index 0000000..bd22f55
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS photos.photo_category CASCADE;
+
+CREATE TABLE photos.photo_category
+(id SERIAL,
+ category TEXT,
+ image TEXT,
+ pos INTEGER,
+ exclusive BOOLEAN DEFAULT false,
+ slideshow BOOLEAN DEFAULT false,
+ PRIMARY KEY (id));
+
+GRANT ALL ON photos.photo_category_id_seq TO nobody;
+GRANT ALL ON photos.photo_category TO nobody;
diff --git a/Toolkit/Photos/Database/tables/photo_category_bus.sql b/Toolkit/Photos/Database/tables/photo_category_bus.sql
new file mode 100755 (executable)
index 0000000..41592ec
--- /dev/null
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS photos.photo_category_bus CASCADE;
+
+CREATE TABLE photos.photo_category_bus
+(id SERIAL,
+ photocat_id INTEGER
+       REFERENCES photos.photo_category (id) 
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ buscat_id INTEGER
+       REFERENCES toolbox.pages (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ pos INTEGER,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON photos.photo_category_bus_id_seq TO nobody;
+GRANT ALL ON photos.photo_category_bus TO  nobody;
+
+CREATE UNIQUE INDEX photo_category_bus_bus_phot_indx on photos.photo_category_bus (buscat_id ,photocat_id);
diff --git a/Toolkit/Photos/Database/tables/photo_default.sql b/Toolkit/Photos/Database/tables/photo_default.sql
new file mode 100755 (executable)
index 0000000..8dc9467
--- /dev/null
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS photos.photo_default CASCADE;
+
+CREATE TABLE photos.photo_default
+(id SERIAL,
+ header TEXT,
+ description TEXT, 
+ PRIMARY KEY (id));
+
+GRANT ALL ON photos.photo_default_id_seq TO nobody;
+GRANT ALL ON photos.photo_default TO nobody;
diff --git a/Toolkit/Photos/Database/updatePhotos.sql b/Toolkit/Photos/Database/updatePhotos.sql
new file mode 100644 (file)
index 0000000..b3ee1dc
--- /dev/null
@@ -0,0 +1,20 @@
+--
+-- Update photo tables
+--
+
+\i ./tables/photo2category.sql
+
+ALTER TABLE photos.photo_category ALTER pos SET DEFAULT 1;
+ALTER TABLE photos.photo_category ADD exclusive bool;
+ALTER TABLE photos.photo_category ALTER exclusive set default false;
+UPDATE photos.photo_category SET exclusive = false;
+
+ALTER TABLE photos.photo add exclusive bool;
+ALTER TABLE photos.photo ALTER exclusive set default false;
+UPDATE photos.photo SET exclusive = false;
+
+ALTER TABLE photos.photo add download bool;
+ALTER TABLE photos.photo ALTER download set default false;
+UPDATE photos.photo SET download = false;
+
+ALTER TABLE contacts.contact ADD title TEXT;
\ No newline at end of file
diff --git a/Toolkit/Photos/Database/upgradeApp.sql b/Toolkit/Photos/Database/upgradeApp.sql
new file mode 100644 (file)
index 0000000..d8e285b
--- /dev/null
@@ -0,0 +1,15 @@
+--
+-- Upgrade (movie schemas)
+--
+
+CREATE SCHEMA oldphotos;
+GRANT ALL ON oldphotos TO nobody;
+
+ALTER TABLE public.photo SET SCHEMA oldphotos;
+ALTER TABLE public.photo_category SET SCHEMA oldphotos;
+ALTER TABLE public.photo_category_bus SET SCHEMA oldphotos;
+ALTER TABLE public.photo_default SET SCHEMA oldphotos;
+
+INSERT INTO photos.photo_category (SELECT * FROM oldphotos.photo_category);
+INSERT INTO photos.photo (SELECT * FROM oldphotos.photo);
+INSERT INTO photos.photo_category_bus (SELECT * FROM oldphotos.photo_category_bus);
\ No newline at end of file
diff --git a/Toolkit/Photos/Display.php b/Toolkit/Photos/Display.php
new file mode 100644 (file)
index 0000000..5f88ba4
--- /dev/null
@@ -0,0 +1,384 @@
+<?php
+
+/**
+ * Display.php
+ *
+ * PHP version 5
+ *
+ * @category  Photos
+ * @package   Toolkit_Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Display.php,v 1.10 2010/07/04 23:55:12 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_Display
+ *
+ * Display the Photo Gallery assoc to a toolbox page
+ *
+ * @category  Photos
+ * @package   Toolkit_Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      <>
+ */
+class Toolkit_Photos_Display
+{
+    // {{{ Properties
+
+    private $_dbh;
+
+    /**
+     * Options for Flexy Templates
+     * @var    array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    /**
+     * page name for form action and links
+     * @var    unknown
+     * @access protected
+     */
+    protected $pageName;
+
+    /**
+     * Photo Table Name
+     * @var    string
+     * @access protected
+     */
+    protected $photoTable = 'photo';
+
+    /**
+     * Photo Category Table Name
+     * @var    string
+     * @access protected
+     */
+    protected $categoryTable = 'photo_category';
+
+    /**
+     * rowCount
+     *
+     * @var float
+     * @access protected
+     */
+    protected $rowCount = 4;
+    protected $baseURL;
+    protected $glmAppBaseURL;
+    protected $pageId;
+    protected $config;
+
+    const PHOTO_GALLERY_WRAPPER_TPL = 'photoGalleryWrapper.html';
+    const PHOTO_SLIDER = 'photoSlider.html';
+    // }}}
+    // {{{ __construct()
+
+    /**
+     * __construct()
+     *
+     * @return void
+     * @access public
+     */
+    function __construct(PDO $dbh)
+    {
+        $this->pageId       = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+        $this->_dbh         = $dbh;
+        // create a Zend Config Object and store into Registry
+        $config             = new Zend_Config_Ini(
+            BASE . 'Toolkit/Photos/application.ini',
+            strtolower($_ENV['GLM_HOST_ID'])
+        );
+        $this->config       = $config;
+        $this->flexyOptions = $config->flexyOptions->toArray();
+
+        // the main display page for events to link to
+        $this->pageName      = BASE_URL . 'index.php?catid=' . $_REQUEST['catid'];
+        $this->baseURL       = ($_SERVER['HTTPS'] == 'on')
+            ? BASE_SECURE_URL
+            : BASE_URL;
+        $this->glmAppBaseURL = ($_SERVER['HTTPS'] == 'on')
+            ? GLM_APP_BASE_SECURE_URL
+            : GLM_APP_BASE_URL;
+    }
+
+    // }}}
+    public function getSlideShow(array $photoCatIds, $galleries)
+    {
+        $GLOBALS['styleSheets'][]   = $this->glmAppBaseURL
+            . 'libjs/plugins/nivoslider/3.2/prod/nivo-slider.css';
+        $GLOBALS['styleSheets'][]   = $this->glmAppBaseURL
+            . 'libjs/plugins/nivoslider/themes/default/default.css';
+        $GLOBALS['bottomScripts'][] = $this->glmAppBaseURL
+            . 'libjs/plugins/nivoslider/3.2/prod/jquery.nivo.slider.js';
+        $tpl = new HTML_Template_Flexy($this->flexyOptions);
+        $tpl->compile(self::PHOTO_SLIDER);
+
+        $page                = new stdClass();
+
+        // Filter the categories so only slideshow ones are in $categories
+        $categories          = $this->getCategories($photoCatIds);
+        $iterator      = new Toolkit_Photos_SlideShowCategoryFilter(
+            $categories->getIterator()
+        );
+        $slideShowCategories = new ArrayObject();
+        $photoCatIds = array();
+        foreach ($iterator as $index) {
+            $slideShowCategories->append($index);
+            $photoCatIds[] = $index['id'];
+        }
+        if (empty($photoCatIds)) {
+            return false;
+        }
+
+        $page->categories    = $slideShowCategories;
+        $page->searchFormUrl = $this->pageName;
+        $page->pageId        = $this->pageId;
+        $page->base_url      = BASE_URL;
+        $page->webUrl        = BASE_URL . 'download-photo-web/';
+        $page->printUrl      = BASE_URL . 'download-photo-print/';
+        $page->photoUrlLarge = FILE_SERVER_URL . IS_OWNER_ID
+            . '/rotatingImagesResized/';
+        $page->photoUrlSmall = FILE_SERVER_URL . IS_OWNER_ID
+            . '/mediaGallery/';
+        $catid               = filter_var($_REQUEST['photoCategoryId']);
+
+        if (count($page->categories) == 1) {
+            $catid = $photoCatIds[0];
+        }
+        $photoCatSelected = ($catid)
+            ? $catid
+            : $photoCatIds[0];
+        $page->photos     = $this->getPhotosByCatid(
+            $photoCatSelected,
+            $this->getLoginStatus()
+        );
+
+
+        return $tpl->bufferedOutputObject($page);
+    }
+    // {{{ toHTML()
+
+    /**
+     * toHTML()
+     *
+     * call to listPhotos function for display of Photos
+     *
+     * @return void
+     * @access public
+     */
+    public function toHTML(array $photoCatIds, $galleries)
+    {
+        $GLOBALS['styleSheets'][]
+            = $this->baseURL . 'photoswipe/photoswipe.css';
+        $GLOBALS['styleSheets'][]
+            = $this->baseURL . 'Toolkit/Photos/css/gallery.css';
+        $GLOBALS['bottomScripts'][]
+            = $this->baseURL . 'photoswipe/lib/klass.min.js';
+        $GLOBALS['bottomScripts'][]
+            = $this->baseURL . 'photoswipe/code.photoswipe.jquery-3.0.5.js';
+        $GLOBALS['bottomScripts'][]
+            = $this->baseURL . 'Toolkit/Photos/js/photoGallery.js';
+
+        $GLOBALS['styleSheets'][] = $this->glmAppBaseURL
+            . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+        $GLOBALS['styleSheets'][] = $this->baseURL
+            . 'Toolkit/Photos/css/style.css';
+        $GLOBALS['styleSheets'][] = $this->baseURL
+            . 'css/contactform.css';
+        $GLOBALS['topScripts'][]  = $this->glmAppBaseURL
+            . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+
+        $tpl = new HTML_Template_Flexy($this->flexyOptions);
+        $tpl->compile(self::PHOTO_GALLERY_WRAPPER_TPL);
+
+        $page                             = new stdClass();
+        $page->mediaExclusive             = $this->config->settings->mediaExclusive;
+        $page->photoNameSearch            = $this->config->settings->photoNameSearch;
+        $page->galleries                  = $galleries;
+        $page->pageId                     = $this->pageId;
+        $page->base_url                   = BASE_URL;
+        $page->mediaGalleryRequestFormUrl = BASE_URL . 'media-gallery-request-form-'
+            . MEDIA_GALLERY_REQUEST_FORM . '/';
+        $page->isLoggedIn                 = $this->getLoginStatus();
+        $page->loginUrl                   = BASE_URL . 'Toolkit/Photos/login.php';
+        $page->logoutUrl                  = BASE_URL . 'Toolkit/Photos/login.php?catid='
+            . $_REQUEST['catid'] . '&logout=1';
+        $page->photoSearchFormUrl         = $this->pageName;
+        $page->photoUrlLarge              = PHOTO_LARGE_URL;
+        $page->photoUrlSmall              = FILE_SERVER_URL . IS_OWNER_ID . '/mediaGallery/';
+        $page->photoDownWeb               = BASE_URL . "download-photo-web/";
+        $page->photoDownPrint             = BASE_URL . "download-photo-print/";
+
+        // Filter out the slideshow categories
+        $categories = $this->getCategories($photoCatIds);
+        $iterator   = new Toolkit_Photos_GalleryCategoryFilter(
+            $categories->getIterator()
+        );
+        $galleryCategories = new ArrayObject();
+        $photoCatIds = array();
+        foreach ($iterator as $index) {
+            $galleryCategories->append($index);
+            $photoCatIds[] = $index['id'];
+        }
+        if (empty($photoCatIds)) {
+            return false;
+        }
+        $page->categories                 = $galleryCategories;
+
+        $failedStatus                     = $this->_getFailedLoginStatus();
+        $page->expired                    = false;
+        if ($failedStatus == -4) {
+            $page->failedStatus = '';
+            $page->expired      = true;
+        } else {
+            $page->failedStatus = $failedStatus;
+        }
+
+        $catid     = filter_var($_REQUEST['photoCategoryId'], FILTER_VALIDATE_INT);
+        $photoName = filter_var($_REQUEST['photo_name'], FILTER_SANITIZE_STRING);
+
+        if (count($page->categories) == 1) {
+            $catid = $photoCatIds[0];
+        }
+        if ($photoName) {
+            $mediaMapper  = new Toolkit_Photos_Models_MediaMapper(
+                $this->_dbh
+            );
+            $gallery      = new Toolkit_Photos_Gallery(
+                Toolkit_Database::getInstance(),
+                new Toolkit_Photos_Display(
+                    Toolkit_Database::getInstance()
+                )
+            );
+            $page->photos = $mediaMapper->fetchPhotosByNameAndCats(
+                $photoName, $gallery->getPageGalleries($this->pageId)
+            );
+        } else if ($catid) {
+            $photoCatSelected        = ($catid)
+                ? $catid
+                : $photoCatIds[0];
+            $photoCategory           = $this->getPhotoCategory($photoCatSelected);
+            $page->photoCategoryName = $photoCategory['category'];
+            $page->photos            = $this->getPhotosByCatid(
+                $photoCatSelected, $page->isLoggedIn
+            );
+        } else {
+            $page->photoUrl  = $this->pageName . '&amp;photoCategoryId=';
+            $page->catPhotos = $this->getCategoriesForPage($photoCatIds);
+        }
+
+        return $tpl->bufferedOutputObject($page);
+    }
+
+    // }}}
+
+    public function getLoginStatus()
+    {
+        $authContainer = new Toolkit_Photos_AuthContainer(
+            Toolkit_Database::getInstance()
+        );
+
+        $photoAuth = new Toolkit_Photos_Auth(
+            $authContainer, '', false
+        );
+        $photoAuth->start();
+        return $photoAuth->checkAuth();
+    }
+
+    private function _getFailedLoginStatus()
+    {
+        $failStatus = filter_var($_REQUEST['status'], FILTER_VALIDATE_INT);
+        switch ($failStatus) {
+        case -1:
+            $status = 'Idled';
+            break;
+        case -2:
+            $status = 'Expired';
+            break;
+        case -3:
+            $status = 'Incorrect Username/Password supplied';
+            break;
+        case -4:
+            return -4;
+            $url     = BASE_URL . 'media-gallery-request-form-'
+                . MEDIA_GALLERY_REQUEST_FORM . '/';
+            $status  = 'Your login has expired';
+            $status .= ' <a href="' . $url . '">Ask for more time</a>';
+            break;
+        default:
+            $status = '';
+            break;
+        }
+        return $status;
+    }
+
+    public function getPhotosByCatid($catid, $isLogedIn)
+    {
+        $filter = (!$isLogedIn)
+            ? array("exclusive <> true")
+            : '';
+
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->_dbh
+        );
+        $photos      = $mediaMapper->fetchAllPhotosByCatid(
+            $catid, $filter
+        );
+        return $photos;
+    }
+
+    public function getCategoriesForPage($photoCats)
+    {
+        $categories  = new ArrayObject();
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->_dbh
+        );
+        if (is_array($photoCats) && !empty($photoCats)) {
+            $index = 0;
+            foreach ($photoCats as $cat) {
+                $categories->offsetSet(
+                    $index, $mediaMapper->fetchCategoryById($cat)
+                );
+                ++$index;
+            }
+        }
+        return $categories;
+    }
+
+    public function getCategories($photoCats)
+    {
+        $categories = new ArrayObject();
+        if (is_array($photoCats) && !empty($photoCats)) {
+            $index = 0;
+            foreach ($photoCats as $cat) {
+                $categories->offsetSet(
+                    $index, $this->getPhotoCategory($cat)
+                );
+                ++$index;
+            }
+        }
+        return $categories;
+    }
+
+    public function getPhotoCategory($categoryId)
+    {
+        try {
+            $sql  = "
+              SELECT *
+                FROM photos.photo_category
+               WHERE id = :id
+            ORDER BY pos";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $categoryId, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+}
diff --git a/Toolkit/Photos/Exception.php b/Toolkit/Photos/Exception.php
new file mode 100644 (file)
index 0000000..82dfa3c
--- /dev/null
@@ -0,0 +1,3 @@
+<?php
+class Toolkit_Photos_Exception extends Exception {}
+?>
diff --git a/Toolkit/Photos/Factory.php b/Toolkit/Photos/Factory.php
new file mode 100644 (file)
index 0000000..048c5f5
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Factory
+ *
+ * @author steve
+ */
+class Toolkit_Photos_Factory
+{
+    static public function createPhotoFromValues($values)
+    {
+        $newPhoto = new Toolkit_Photos_Photo();
+        $photo = $newPhoto->createByValues($values);
+        return $photo;
+    }
+}
+?>
diff --git a/Toolkit/Photos/Gallery.php b/Toolkit/Photos/Gallery.php
new file mode 100644 (file)
index 0000000..e658738
--- /dev/null
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * Fetches photo galleries assigned to pages
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit_Photos
+ * @package   Photos
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: Gallery.php,v 1.1 2010/06/09 19:52:24 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Fetches photo galleries assigned to pages
+ *
+ * @category  Toolkit_Photos
+ * @package   Photos
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Photos_Gallery
+{
+    //    {{{    properties
+
+    /**
+     * Database handler
+     * @var    object
+     * @access private
+     */
+    private $_dbh;
+
+    /**
+     * Toolkit_Photos_Display object
+     * @var    object
+     * @access private
+     */
+    private $_photoApp;
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO                    $dbh      Database handler
+     * @param Toolkit_Photos_Display $photoApp Photo Gallery Display object
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $dbh, Toolkit_Photos_Display $photoApp)
+    {
+        $this->_dbh      = $dbh;
+        $this->_photoApp = $photoApp;
+    }
+
+    //    }}}
+    //    {{{    _getGalleriesAssignedToPage()
+
+    /**
+     * Gets an array of photo galleries assigned to page
+     *
+     * If we were on a page that contained multiple galleries
+     * and we move into a single gallery page - this function
+     * returns that single gallery, so we can view all its photos
+     *
+     * @param integer $pageId Id of page we're on
+     *
+     * @return array multi-dimensional array of photo galleries assigned to page
+     * @access private
+     * @throws Toolkit_Photos_Exception If can't query for galleries on page
+     */
+    private function _getGalleriesAssignedToPage($pageId)
+    {
+        if (isset($_GET['photo_catid']) && ctype_digit($_GET['photo_catid'])) {
+            return array(
+                array('photocat_id' => $_GET['photo_catid'])
+            );
+        }
+
+        try {
+            $where
+                = (!$this->_photoApp->getLoginStatus())
+                ? " AND exclusive <> true"
+                : '';
+            $sql = "
+              SELECT pcb.photocat_id
+                FROM photo_category_bus pcb, photo_category pc
+               WHERE buscat_id = :pageId
+                 AND pc.id = pcb.photocat_id
+                 AND pc.id IN (
+                     SELECT distinct category
+                       FROM photo2category
+                     )
+                 $where
+            ORDER BY pc.pos";
+
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':pageId', $pageId, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Photos_Exception(
+                "Unable to check if page has a gallery `$pageId`"
+            );
+        }
+    }
+
+    //    }}}
+    public function getPageGalleries($pageId)
+    {
+        $cats = array();
+        $galleries = $this->_getGalleriesAssignedToPage($pageId);
+        foreach ($galleries as $galley) {
+            $cats[] = $galley['photocat_id'];
+        }
+        return $cats;
+    }
+    //    {{{    getPageGallery()
+
+    /**
+     * Gets the page galleries / gallery
+     *
+     * @param integer $pageId Id of page we're on
+     *
+     * @return string photo gallery output
+     * @access public
+     */
+    public function getPageGallery($pageId)
+    {
+        $galleries = $this->_getGalleriesAssignedToPage($pageId);
+
+        $photoCatIds = array();
+        foreach ($galleries as $gallery) {
+            $photoCatIds[] = $gallery['photocat_id'];
+        }
+
+        return !empty($photoCatIds)
+            ? $this->_photoApp->toHTML($photoCatIds, $galleries)
+            : '';
+    }
+
+    //    }}}
+
+    public function getPageSlideShow($pageId)
+    {
+        $galleries = $this->_getGalleriesAssignedToPage($pageId);
+
+        $photoCatIds = array();
+        foreach ($galleries as $gallery) {
+            $photoCatIds[] = $gallery['photocat_id'];
+        }
+
+        return !empty($photoCatIds)
+            ? $this->_photoApp->getSlideShow($photoCatIds, $galleries)
+            : '';
+    }
+}
diff --git a/Toolkit/Photos/GalleryCategoryFilter.php b/Toolkit/Photos/GalleryCategoryFilter.php
new file mode 100644 (file)
index 0000000..d90e91e
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * GalleryCategoryFilter.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_GalleryCategoryFilter
+ *
+ * Description of SlideShowFilter
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_GalleryCategoryFilter
+    extends FilterIterator
+{
+    public function __construct(Iterator $iterator)
+    {
+        parent::__construct($iterator);
+    }
+
+    public function accept()
+    {
+        $category = $this->getInnerIterator()->current();
+        if (!$category['slideshow']) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/Toolkit/Photos/Models/Category.php b/Toolkit/Photos/Models/Category.php
new file mode 100644 (file)
index 0000000..eccd7c8
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * Category.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_Models_Category
+ *
+ * Represents the Category for Media Gallery
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_Category
+{
+    private $_id;
+    private $_category;
+    private $_exclusive;
+    private $_slideshow;
+    private $_pos;
+
+    const TABLE_NAME = 'photos.photo_category';
+    const PRI_KEY    = 'id';
+    const SORT       = 'pos';
+
+    private function __construct($values)
+    {
+        extract($values);
+        $this->setCategory($category)
+            ->setExclusive($exclusive)
+            ->setSlideShow($slideshow)
+            ->setPos($pos);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    static public function createByValues($values)
+    {
+        return new Toolkit_Photos_Models_Category($values);
+    }
+
+    public function getFirstPhoto()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        try {
+            $sql = "
+            SELECT p.image
+              FROM photos.photo p, photos.photo2category p2c
+             WHERE p2c.pos = 1
+               AND p2c.photo = p.id
+               AND p2c.category = :catid";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':catid', $this->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    public function getTotalNumberOfPhotos()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        try {
+            $sql = "
+            SELECT count(p.id)
+              FROM photos.photo p, photos.photo2category p2c
+             WHERE p2c.photo = p.id
+               AND p2c.category = :catid";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':catid', $this->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    public function getId()
+    {
+        return (int)$this->_id;
+    }
+
+    public function setId($id)
+    {
+        $this->_id
+            = (filter_var($id, FILTER_VALIDATE_INT))
+            ? $id
+            : null;
+        return $this;
+    }
+
+    public function getPos()
+    {
+        return (int)$this->_pos;
+    }
+
+    public function setPos($pos)
+    {
+        $this->_pos = (int)$pos;
+        return $this;
+    }
+
+
+    public function getCategory()
+    {
+        return $this->_category;
+    }
+
+    public function setCategory($category)
+    {
+        $this->_category = $category;
+        return $this;
+    }
+
+    public function getExclusive()
+    {
+        return (bool)$this->_exclusive;
+    }
+
+    public function setExclusive($exclusive)
+    {
+        $this->_exclusive = (bool)$exclusive;
+        return $this;
+    }
+
+    public function getSlideShow()
+    {
+        return (bool)$this->_slideshow;
+    }
+
+    public function setSlideShow($slideshow)
+    {
+        $this->_slideshow = (bool)$slideshow;
+        return $this;
+    }
+
+
+}
diff --git a/Toolkit/Photos/Models/MediaMapper.php b/Toolkit/Photos/Models/MediaMapper.php
new file mode 100644 (file)
index 0000000..53226e7
--- /dev/null
@@ -0,0 +1,1003 @@
+<?php
+
+/**
+ * MediaMapper.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_Models_MediaMapper
+ *
+ * Maps the Media Objects to methods to create and generate list of
+ * categories and photos.
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_MediaMapper
+{
+    private $_dbh;
+
+    public function __construct(PDO $dbh)
+    {
+        $this->_dbh = $dbh;
+    }
+
+    public function fetchAllCategories()
+    {
+        $categories = new ArrayObject();
+        try {
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Photos_Models_Category::TABLE_NAME . "
+            ORDER BY " . Toolkit_Photos_Models_Category::SORT;
+            $stmt = $this->_dbh->query($sql);
+            while ($category = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $categories->append(
+                    Toolkit_Photos_Models_Category::createByValues($category)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $categories;
+    }
+
+    public function fetchAllPhotos($catId)
+    {
+        $photos = new ArrayObject();
+        try {
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+               WHERE catid = :catid
+            ORDER BY " . Toolkit_Photos_Models_Photo::SORT;
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':catid', $catId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photos->append(
+                    Toolkit_Photos_Models_Photo::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photos;
+    }
+
+    public function fetchPhotosByName($distinct = false)
+    {
+        $nameSearch = filter_var($_REQUEST['photoName'], FILTER_SANITIZE_STRING);
+        if (!$nameSearch) {
+            return false;
+        }
+        $photos     = new ArrayObject();
+        try {
+            $nameSearch = $this->_dbh->quote('%' . $nameSearch . '%');
+            $select
+                = (!$distinct)
+                ? '*'
+                : 'DISTINCT ON (title) title';
+            $orderBy
+                = (!$distinct)
+                ? Toolkit_Photos_Models_Photo::SORT
+                : 'title';
+            $sql = "
+              SELECT $select
+                FROM " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+               WHERE title ilike $nameSearch
+            ORDER BY $orderBy";
+            $stmt = $this->_dbh->query($sql);
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photos->append(
+                    Toolkit_Photos_Models_Photo::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photos;
+    }
+
+    public function fetchPhotosByNameAndCats(
+        $nameSearch,
+        $galleries,
+        $distinct = false
+    ) {
+        if (!$nameSearch) {
+            die('here');
+            return false;
+        }
+        $photos     = new ArrayObject();
+        try {
+            $nameSearch = $this->_dbh->quote('%' . $nameSearch . '%');
+            $select
+                = (!$distinct)
+                ? '*'
+                : 'DISTINCT ON (title) title';
+            $where
+                = (!empty($galleries))
+                ? " AND id IN (SELECT photo FROM photo2category WHERE category IN ("
+                . implode(",", $galleries) . "))"
+                : '';
+            $orderBy
+                = (!$distinct)
+                ? Toolkit_Photos_Models_Photo::SORT
+                : 'title';
+            $sql = "
+              SELECT $select
+                FROM " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+               WHERE title ilike $nameSearch
+                     $where
+            ORDER BY $orderBy";
+            $stmt = $this->_dbh->query($sql);
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photos->append(
+                    Toolkit_Photos_Models_Photo::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photos;
+    }
+
+    public function fetchAllPhotosByCatid($catId, $filter = null)
+    {
+        $photos = new ArrayObject();
+        try {
+            $where
+                = ($filter)
+                ? "AND " . implode(" AND ", $filter)
+                : '';
+            $sql = "
+              SELECT p.*
+                FROM " . Toolkit_Photos_Models_Photo::TABLE_NAME . " p,
+                     " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . " p2c
+               WHERE p2c.category = :catid
+                 AND p2c.photo = p.id
+                 $where
+            ORDER BY p2c.pos";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':catid', $catId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photos->append(
+                    Toolkit_Photos_Models_Photo::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photos;
+    }
+
+    public function fetchPhoto2CategoryByPhotoId($photoId)
+    {
+        $photo2Categories = new ArrayObject();
+        try {
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+               WHERE photo = :photo
+            ORDER BY " . Toolkit_Photos_Models_Photo2Category::SORT;
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':photo', $photoId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photo2Categories->offsetSet(
+                    $photo['category'],
+                    Toolkit_Photos_Models_Photo2Category::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photo2Categories;
+    }
+
+    public function fetchPhoto2Category($catId)
+    {
+        $photo2Categories = new ArrayObject();
+        try {
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+               WHERE category = :catid
+            ORDER BY " . Toolkit_Photos_Models_Photo2Category::SORT;
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':catid', $catId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photo2Categories->append(
+                    Toolkit_Photos_Models_Photo2Category::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photo2Categories;
+    }
+
+    public function fetchCategoryPages($catId)
+    {
+        $pages2Categories = new ArrayObject();
+        try {
+            $sql = "
+              SELECT pcb.buscat_id as page, p.navigation_name as page_name, pcb.pos as pos
+                FROM photo_category_bus pcb, photo_category pc, pages p
+               WHERE pc.id IN (
+                    SELECT photocat_id
+                      FROM photo_category_bus
+                     WHERE photocat_id = :catid)
+                 AND pcb.photocat_id = pc.id
+                 AND p.id = pcb.buscat_id
+            ORDER BY pcb.pos";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':catid', $catId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $pages2Categories->append(
+                    $photo
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $pages2Categories;
+    }
+
+    public function fixAllPhoto2Cats()
+    {
+        $sql = "
+        DELETE FROM photos.photo2category";
+        $this->_dbh->query($sql);
+        // first grab all categories
+        $categories = $this->fetchAllCategories();
+        foreach ($categories as $category) {
+            var_dump($category);
+            $photos = $this->fetchAllPhotos($category->getId());
+            var_dump($photos);
+            if ($photos) {
+                foreach ($photos as $photo) {
+                    $photo2Category
+                        = Toolkit_Photos_Models_Photo2Category::createByValues(
+                            array(
+                                'category' => $category->getId(),
+                                'photo'    => $photo->getId(),
+                                'pos'      => $photo->getPos()
+                            )
+                        );
+                    $this->savePhoto2Category($photo2Category);
+                }
+            }
+        }
+        exit;
+    }
+
+    public function fetchPhotoById($photoId)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+             WHERE " . Toolkit_Photos_Models_Photo::PRI_KEY . " = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $photoId, PDO::PARAM_INT);
+            $stmt->execute();
+            return Toolkit_Photos_Models_Photo::createByValues(
+                $stmt->fetch(PDO::FETCH_ASSOC)
+            );
+
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function saveCategoryWithPost()
+    {
+        if ($_REQUEST['Cancel']) {
+            return true;
+        }
+        if ($_REQUEST['Delete']) {
+            // delete the category
+            $category = $this->fetchCategoryById($_REQUEST['id']);
+            $this->removeCategory($category);
+            return true;
+        }
+        $postedData = filter_var_array(
+            $_POST,
+            array(
+                'id' => FILTER_VALIDATE_INT,
+                'pos' => FILTER_VALIDATE_INT,
+                'pages' => array(
+                    'filter' => FILTER_VALIDATE_INT,
+                    'flags'  => FILTER_FORCE_ARRAY
+                ),
+                'category' => array(
+                    'filter' => FILTER_SANITIZE_STRIPPED,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'exclusive' => FILTER_VALIDATE_BOOLEAN,
+                'slideshow' => FILTER_VALIDATE_BOOLEAN
+            )
+        );
+        if (get_magic_quotes_gpc()) {
+            $postedData['category'] = stripslashes($postedData['category']);
+        }
+
+        $category = Toolkit_Photos_Models_Category::createByValues(
+            $postedData
+        );
+        //var_dump($category);exit;
+        $id = $this->saveCategory($category);
+
+        try {
+            $this->_dbh->beginTransaction();
+            if ($postedData['id']) {
+                $sql = "
+                DELETE
+                  FROM " . Toolkit_Photos_Models_Page2Category::TABLE_NAME . "
+                 WHERE photocat_id = :category";
+                $stmt = $this->_dbh->prepare($sql);
+                $stmt->bindParam(':category', $postedData['id'], PDO::PARAM_INT);
+                $stmt->execute();
+            }
+            if (   is_array($postedData['pages'])
+                && !empty($postedData['pages'])
+            ) {
+                foreach ($postedData['pages'] as $pageId) {
+                    $page2Category
+                        = Toolkit_Photos_Models_Page2Category::createByValues(
+                            array(
+                                'buscat_id'   => $pageId,
+                                'photocat_id' => $category->getId()
+                            )
+                    );
+                    $this->savePage2Category($page2Category);
+                }
+            }
+
+            $this->_dbh->commit();
+        } catch (PDOException $e) {
+            $this->_dbh->rollBack();
+            Toolkit_Common::handleError($e);
+        }
+
+        return true;
+    }
+
+    public function saveCategory(
+        Toolkit_Photos_Models_Category $category
+    ) {
+        if ($category->getId()) {
+            $sql = "
+            UPDATE " . Toolkit_Photos_Models_Category::TABLE_NAME . "
+               SET category = :category,
+                   exclusive = :exclusive,
+                   slideshow = :slideshow,
+                   pos = :pos
+             WHERE " . Toolkit_Photos_Models_Category::PRI_KEY . " = :id";
+
+        } else {
+            $sql = "
+            INSERT INTO " . Toolkit_Photos_Models_Category::TABLE_NAME . "
+            (category, exclusive, slideshow, pos)
+            VALUES
+            (:category, :exclusive, :slideshow, :pos)
+            RETURNING " . Toolkit_Photos_Models_Category::PRI_KEY;
+        }
+        $stmt = $this->_dbh->prepare($sql);
+        $stmt->bindParam(':category', $category->getCategory());
+        $stmt->bindParam(':pos', $category->getPos(), PDO::PARAM_INT);
+        $stmt->bindParam(':exclusive', $category->getExclusive(), PDO::PARAM_BOOL);
+        $stmt->bindParam(':slideshow', $category->getSlideShow(), PDO::PARAM_BOOL);
+        if ($category->getId()) {
+            $stmt->bindParam(':id', $category->getId(), PDO::PARAM_INT);
+        }
+        $stmt->execute();
+        if (!$category->getId()) {
+            $category->setId($stmt->fetchColumn());
+        }
+        return $category->getId();
+    }
+
+    public function savePage2Category(
+        Toolkit_Photos_Models_Page2Category $page2Category
+    ) {
+        if ($page2Category->getId()) {
+            $sql = "
+            UPDATE " . Toolkit_Photos_Models_Page2Category::TABLE_NAME . "
+               SET photocat_id = :category,
+                   buscat_id = :page
+             WHERE " . Toolkit_Photos_Models_Page2Category::PRI_KEY . " = :id";
+
+        } else {
+            $sql = "
+            INSERT INTO " . Toolkit_Photos_Models_Page2Category::TABLE_NAME . "
+            (photocat_id, buscat_id)
+            VALUES
+            (:category, :page)
+            RETURNING " . Toolkit_Photos_Models_Page2Category::PRI_KEY;
+        }
+        $stmt = $this->_dbh->prepare($sql);
+        $stmt->bindParam(':page', $page2Category->getBuscatId(), PDO::PARAM_INT);
+        $stmt->bindParam(':category', $page2Category->getPhotocatId(), PDO::PARAM_INT);
+        if ($page2Category->getId()) {
+            $stmt->bindParam(':id', $page2Category->getId(), PDO::PARAM_INT);
+        }
+        $stmt->execute();
+        return ($page2Category->getId())
+            ? $page2Category->getId()
+            : $stmt->fetchColumn();
+    }
+
+    public function savePhoto2Category(
+        Toolkit_Photos_Models_Photo2Category $photo2Category
+    ) {
+        if ($photo2Category->getId()) {
+            $sql = "
+            UPDATE " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+               SET photo = :photo,
+                   category = :category,
+                   pos = :pos
+             WHERE " . Toolkit_Photos_Models_Photo2Category::PRI_KEY . " = :id";
+
+        } else {
+            $sql = "
+            INSERT INTO " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+            (photo, category, pos)
+            VALUES
+            (:photo, :category, :pos)
+            RETURNING " . Toolkit_Photos_Models_Photo2Category::PRI_KEY;
+        }
+        $stmt = $this->_dbh->prepare($sql);
+        $stmt->bindParam(':photo', $photo2Category->getPhoto(), PDO::PARAM_INT);
+        $stmt->bindParam(':category', $photo2Category->getCategory(), PDO::PARAM_INT);
+        $stmt->bindParam(':pos', $photo2Category->getPos(), PDO::PARAM_INT);
+        if ($photo2Category->getId()) {
+            $stmt->bindParam(':id', $photo2Category->getId(), PDO::PARAM_INT);
+        }
+        $stmt->execute();
+        return ($photo2Category->getId())
+            ? $photo2Category->getId()
+            : $stmt->fetchColumn();
+    }
+
+    public function fetchCategoryById($categoryId)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM " . Toolkit_Photos_Models_Category::TABLE_NAME . "
+             WHERE " . Toolkit_Photos_Models_Category::PRI_KEY . " = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $categoryId, PDO::PARAM_INT);
+            $stmt->execute();
+            return Toolkit_Photos_Models_Category::createByValues(
+                $stmt->fetch(PDO::FETCH_ASSOC)
+            );
+
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function savePhotoPositionsByCategory($photos, $categoryId)
+    {
+        if (!is_array($photos) || empty($photos)) {
+            return false;
+        }
+        var_dump($photos);
+        var_dump($categoryId);
+        try {
+            $this->_dbh->beginTransaction();
+            $sql = "
+            DELETE
+              FROM " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+             WHERE category = :category";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':category', $categoryId, PDO::PARAM_INT);
+            $stmt->execute();
+
+            foreach ($photos as $position => $photoId) {
+                $photo2Category
+                    = Toolkit_Photos_Models_Photo2Category::createByValues(
+                        array(
+                            'photo'    => $photoId,
+                            'category' => $categoryId,
+                            'pos'      => $position + 1
+                        )
+                    );
+                $this->savePhoto2Category($photo2Category);
+                var_dump($photo2Category);
+            }
+
+            $this->_dbh->commit();
+        } catch (PDOException $e) {
+            $this->_dbh->rollBack();
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function saveCategoryPositionsByArray($cats)
+    {
+        if (!is_array($cats) || empty($cats)) {
+            return false;
+        }
+//        var_dump($cats);exit;
+        foreach ($cats as $position => $catId) {
+            $category = $this->fetchCategoryById($catId);
+            $category->setPos((int)$position + 1);
+            $this->saveCategory($category);
+        }
+    }
+
+    public function savePhotoWithPost()
+    {
+        if ($_REQUEST['Cancel']) {
+            return true;
+        }
+        if ($_REQUEST['Delete']) {
+            // delete the photo
+            $photo = $this->fetchPhotoById($_REQUEST['id']);
+            $this->removePhoto($photo);
+            return true;
+        }
+
+        if (   $_FILES['image']['name']
+            && !$_FILES['image']['error']
+        ) {
+            $fs = new Toolkit_FileServer_ImageAdapter();
+            $image = $fs->upload('image');
+        }
+        $postedData = filter_var_array(
+            $_POST,
+            array(
+                'id' => FILTER_VALIDATE_INT,
+                'catid' => FILTER_VALIDATE_INT,
+                'title' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'image' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'description' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'photo2Categories' => array(
+                    'filter' => FILTER_VALIDATE_INT,
+                    'flags'  => FILTER_FORCE_ARRAY
+                ),
+                'pos'       => FILTER_VALIDATE_INT,
+                'del_img'   => FILTER_VALIDATE_BOOLEAN,
+                'download'  => FILTER_VALIDATE_BOOLEAN,
+                'exclusive' => FILTER_VALIDATE_BOOLEAN,
+            )
+        );
+
+        if (!$image && $postedData['del_img']) {
+            $postedData['image'] = '';
+        } else if ($image) {
+            $postedData['image'] = $image['name'];
+        }
+        $photo = Toolkit_Photos_Models_Photo::createByValues(
+            $postedData
+        );
+        $photoId = $this->savePhoto($photo);
+        $postedData['id'] = $photoId;
+        $this->savePhoto2CategoryData($postedData);
+        return $photoId;
+    }
+
+    public function savePhoto2CategoryData($data)
+    {
+        if (!is_array($data) || empty($data)) {
+            return false;
+        }
+//        var_dump($data);
+        // need to get the current photo2category records for this photo
+        $currentCategories = $this->fetchPhoto2CategoryByPhotoId($data['id']);
+//        var_dump($currentCategories);
+        foreach ($data['photo2Categories'] as $catId) {
+            // if the offest does not exists then the photo need to be
+            // added to the category
+            if (!$currentCategories->offsetExists($catId)) {
+                $photoCatsNew = $this->fetchPhoto2Category($catId);
+                $newPosition = $photoCatsNew->count() + 1;
+                // need to add photo to the photo2category record as last pos
+                $newPhoto2Cat
+                    = Toolkit_Photos_Models_Photo2Category::createByValues(
+                        array(
+                            'photo'    => $data['id'],
+                            'category' => $catId,
+                            'pos'      => $newPosition
+                        )
+                    );
+//                var_dump($newPhoto2Cat);
+                $this->savePhoto2Category($newPhoto2Cat);
+//                var_dump('adding record for category ' . $catId);
+            }
+            // for each step through remove the iteration in the ArrayObject
+            // that way we know if we have to remove any records and reorder
+            $currentCategories->offsetUnset($catId);
+        }
+        foreach ($currentCategories as $photo2Category) {
+//            var_dump($photo2Category);
+            $this->removePhoto2Category($photo2Category);
+        }
+    }
+
+    public function removeCategory(
+        Toolkit_Photos_Models_Category $category
+    ) {
+        // if there's any photos in the category that are only in that
+        // category then it will need to delete them.
+        try {
+            // 1. delete this category
+            $sql = "
+            DELETE
+              FROM photos.photo_category
+             WHERE id = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $category->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+            // 2. delete all references in photo2category table for this category
+            $sql = "
+            DELETE
+              FROM photos.photo2category
+             WHERE category = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $category->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function removePhoto(
+        Toolkit_Photos_Models_Photo $photo
+    ) {
+        $photo2Categories = $this->fetchPhoto2CategoryByPhotoId(
+            $photo->getId()
+        );
+
+        foreach ($photo2Categories as $photoCat) {
+            $this->removePhoto2Category($photoCat);
+        }
+        try {
+            $sql = "
+            DELETE
+              FROM photos.photo
+             WHERE id = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $photo->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function removePhoto2Category(
+        Toolkit_Photos_Models_Photo2Category $photo2Category
+    ) {
+        try {
+            $sql = "
+            DELETE
+              FROM " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+             WHERE id = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $photo2Category->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+            $sql = "
+            UPDATE " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+               SET pos = pos - 1
+             WHERE pos >= :pos
+               AND category = :category";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':pos', $photo2Category->getPos(), PDO::PARAM_INT);
+            $stmt->bindParam(':category', $photo2Category->getCategory(), PDO::PARAM_INT);
+            $stmt->execute();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function savePhoto(
+        Toolkit_Photos_Models_Photo $photo
+    ) {
+        try {
+            if ($photo->getId()) {
+                $sql = "
+                UPDATE " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+                   SET title = :title,
+                       catid = :catid,
+                       description = :description,
+                       image = :image,
+                       pos = :pos,
+                       download = :download,
+                       exclusive = :exclusive
+                 WHERE " . Toolkit_Photos_Models_Photo::PRI_KEY . " = :id";
+
+            } else {
+                $sql = "
+                INSERT INTO " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+                (title, catid, description, image, pos, download, exclusive)
+                VALUES
+                (:title, :catid, :description, :image, :pos, :download, :exclusive)
+                RETURNING " . Toolkit_Photos_Models_Photo::PRI_KEY;
+            }
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':title', $photo->getTitle());
+            $stmt->bindParam(':catid', $photo->getCatid(), PDO::PARAM_INT);
+            $stmt->bindParam(':description', $photo->getDescription());
+            $stmt->bindParam(':image', $photo->getImage());
+            $stmt->bindParam(':pos', $photo->getPos(), PDO::PARAM_INT);
+            $stmt->bindParam(':download', $photo->getDownload(), PDO::PARAM_BOOL);
+            $stmt->bindParam(':exclusive', $photo->getExclusive(), PDO::PARAM_BOOL);
+            if ($photo->getId()) {
+                $stmt->bindParam(':id', $photo->getId(), PDO::PARAM_INT);
+            }
+            $stmt->execute();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+        return ($photo->getId())
+            ? $photo->getId()
+            : $stmt->fetchColumn();
+    }
+
+    public function saveUserWithPost()
+    {
+        $postedData = filter_var_array(
+            $_POST,
+            array(
+                'id'     => FILTER_VALIDATE_INT,
+                'status' => FILTER_SANITIZE_STRING,
+                'expire' => array(
+                    'filter'  => FILTER_VALIDATE_REGEXP,
+                    'options' => array(
+                        'regexp' => '%[0-9]{2}/[0-9]{2}/[0-9]{4}%'
+                    )
+                ),
+                'phone'      => FILTER_SANITIZE_STRING,
+                'email'      => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'media_pass' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'company'  => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'title'  => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'fname'  => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'lname' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'address' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'address2' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'city' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'state' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'zip' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                )
+            )
+        );
+        if ($postedData['status'] == 'approved') {
+            $postedData['approved'] = true;
+            $postedData['denied']   = false;
+        } else {
+            $postedData['approved'] = false;
+            $postedData['denied']   = true;
+        }
+        if (!$postedData['expire']) {
+            $postedData['expire'] = null;
+        }
+
+        $user = Toolkit_Photos_Models_User::createByValues(
+            $postedData
+        );
+        return $this->saveUser($user);
+    }
+
+    public function saveUser(
+        Toolkit_Photos_Models_User $user
+    ) {
+        if ($user->getId()) {
+            $sql = "
+            UPDATE " . Toolkit_Photos_Models_User::TABLE_NAME . "
+               SET company = :company,
+                   title = :title,
+                   fname = :fname,
+                   lname = :lname,
+                   email = :email,
+                   phone = :phone,
+                   address = :address,
+                   address2 = :address2,
+                   city = :city,
+                   state = :state,
+                   zip = :zip,
+                   media = true,
+                   approved = :approved,
+                   denied = :denied,
+                   expire = :expire
+             WHERE " . Toolkit_Photos_Models_User::PRI_KEY . " = :id";
+
+        } else {
+            $sql = "
+            INSERT INTO " . Toolkit_Photos_Models_User::TABLE_NAME . "
+            (fname, lname, email, phone,
+            address, address2, city, state, zip,
+            approved, denied, expire, media)
+            VALUES
+            (:fname, :lname, :email, :phone,
+            :address, :address2, :city, :state, :zip,
+            :approved, :denied, :expire, true)
+            RETURNING " . Toolkit_Photos_Models_User::PRI_KEY;
+        }
+        $stmt = $this->_dbh->prepare($sql);
+        $stmt->bindParam(':expire', $user->getExpire());
+        $stmt->bindParam(':company', $user->getCompany());
+        $stmt->bindParam(':title', $user->getTitle());
+        $stmt->bindParam(':fname', $user->getFname());
+        $stmt->bindParam(':lname', $user->getLname());
+        $stmt->bindParam(':email', $user->getEmail());
+        $stmt->bindParam(':phone', $user->getPhone());
+        $stmt->bindParam(':address', $user->getAddress());
+        $stmt->bindParam(':address2', $user->getAddress2());
+        $stmt->bindParam(':city', $user->getCity());
+        $stmt->bindParam(':state', $user->getState());
+        $stmt->bindParam(':zip', $user->getZip());
+        $stmt->bindParam(':approved', $user->getApproved(), PDO::PARAM_BOOL);
+        $stmt->bindParam(':denied', $user->getDenied(), PDO::PARAM_BOOL);
+        if ($user->getId()) {
+            $stmt->bindParam(':id', $user->getId(), PDO::PARAM_INT);
+        }
+        $stmt->execute();
+        return ($user->getId())
+            ? $user->getId()
+            : $stmt->fetchColumn();
+    }
+
+    public function fetchUserById($userId)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM " . Toolkit_Photos_Models_User::TABLE_NAME . "
+             WHERE " . Toolkit_Photos_Models_User::PRI_KEY . " = :id
+               AND " . Toolkit_Photos_Models_User::WHERE;
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $userId, PDO::PARAM_INT);
+            $stmt->execute();
+            return Toolkit_Photos_Models_User::createByValues(
+                $stmt->fetch(PDO::FETCH_ASSOC)
+            );
+
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function fetchUserByEmail($email)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM " . Toolkit_Photos_Models_User::TABLE_NAME . "
+             WHERE " . Toolkit_Photos_Models_User::EMAIL . " = :email
+               AND " . Toolkit_Photos_Models_User::WHERE;
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':email', $email, PDO::PARAM_INT);
+            $stmt->execute();
+            return Toolkit_Photos_Models_User::createByValues(
+                $stmt->fetch(PDO::FETCH_ASSOC)
+            );
+
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function fetchAllUsers()
+    {
+        $users = new ArrayObject();
+        try {
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Media_Models_User::TABLE_NAME . "
+               WHERE media = true
+            ORDER BY " . Toolkit_Media_Models_User::SORT;
+            $sql .= " LIMIT 10 OFFSET 0";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $users->append(
+                    Toolkit_Media_Models_User::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $users;
+    }
+
+    public function fetchUsers(array $filters = null)
+    {
+        $users = new ArrayObject();
+        try{
+            $where = (is_array($filters) && !empty($filters))
+                ? "AND " . implode(" AND ", $filters)
+                : '';
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Photos_Models_User::TABLE_NAME . "
+               WHERE media = true
+              $where
+            ORDER BY " . Toolkit_Photos_Models_User::SORT;
+            $sql .= " LIMIT 10 OFFSET 0";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $users->append(
+                    Toolkit_Photos_Models_User::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $users;
+    }
+
+}
diff --git a/Toolkit/Photos/Models/Page2Category.php b/Toolkit/Photos/Models/Page2Category.php
new file mode 100644 (file)
index 0000000..f79ec4d
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * Photo2Category.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Photo2Category
+ *
+ * Description of Photo2Category
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_Page2Category
+{
+    private $_id;
+    private $_photocatId;
+    private $_buscatId;
+
+    const TABLE_NAME = 'photos.photo_category_bus';
+    const PRI_KEY    = 'id';
+    const SORT       = 'pos';
+
+    private function __construct($values)
+    {
+        extract($values);
+        $this->setBuscatId($buscat_id)
+            ->setPhotocatId($photocat_id);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    static public function createByValues($values)
+    {
+        return new Toolkit_Photos_Models_Page2Category($values);
+    }
+
+    public function getId()
+    {
+        return (int)$this->_id;
+    }
+
+    public function setId($id)
+    {
+        $this->_id
+            = (filter_var($id, FILTER_VALIDATE_INT))
+            ? $id
+            : null;
+        return $this;
+    }
+
+    public function getPhotocatId()
+    {
+        return $this->_photocatId;
+    }
+
+    public function setPhotocatId($photocatId)
+    {
+        $this->_photocatId = $photocatId;
+        return $this;
+    }
+
+    public function getBuscatId()
+    {
+        return $this->_buscatId;
+    }
+
+    public function setBuscatId($buscatId)
+    {
+        $this->_buscatId = $buscatId;
+        return $this;
+    }
+
+
+}
diff --git a/Toolkit/Photos/Models/Photo.php b/Toolkit/Photos/Models/Photo.php
new file mode 100644 (file)
index 0000000..0d3d8be
--- /dev/null
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * Photo.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_Models_Photo
+ *
+ * Represents the Photo for Media Gallery
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_Photo
+{
+    private $_id;
+    private $_title;
+    private $_description;
+    private $_image;
+    private $_catid;
+    private $_pos;
+    private $_download;
+    private $_exclusive;
+
+    const TABLE_NAME = 'photos.photo';
+    const PRI_KEY    = 'id';
+    const SORT       = 'pos';
+
+    private function __construct($values)
+    {
+        extract($values);
+        $this->setTitle($title)
+            ->setDescription($description)
+            ->setImage($image)
+            ->setCatid($catid)
+            ->setPos($pos)
+            ->setDownload($download)
+            ->setExclusive($exclusive);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    static public function createByValues($values)
+    {
+        return new Toolkit_Photos_Models_Photo($values);
+    }
+
+    public function getId()
+    {
+        return (int)$this->_id;
+    }
+
+    public function setId($id)
+    {
+        $this->_id
+            = (filter_var($id, FILTER_VALIDATE_INT))
+            ? $id
+            : null;
+        return $this;
+    }
+
+    public function getTitle()
+    {
+        return $this->_title;
+    }
+
+    public function setTitle($title)
+    {
+        $this->_title = $title;
+        return $this;
+    }
+
+    public function getDescription()
+    {
+        return $this->_description;
+    }
+
+    public function setDescription($description)
+    {
+        $this->_description = $description;
+        return $this;
+    }
+
+    public function getImage()
+    {
+        return $this->_image;
+    }
+
+    public function setImage($image)
+    {
+        $this->_image = $image;
+        return $this;
+    }
+
+    public function getCatid()
+    {
+        return (int)$this->_catid;
+    }
+
+    public function setCatid($catid)
+    {
+        $this->_catid = (int)$catid;
+        return $this;
+    }
+
+    public function getPos()
+    {
+        return (int)$this->_pos;
+    }
+
+    public function setPos($pos)
+    {
+        $this->_pos = (int)$pos;
+        return $this;
+    }
+
+    public function getDownload()
+    {
+        return (bool)$this->_download;
+    }
+
+    public function setDownload($download)
+    {
+        $this->_download = (bool)$download;
+        return $this;
+    }
+
+    public function getExclusive()
+    {
+        return (bool)$this->_exclusive;
+    }
+
+    public function setExclusive($exclusive)
+    {
+        $this->_exclusive = (bool)$exclusive;
+        return $this;
+    }
+
+
+}
diff --git a/Toolkit/Photos/Models/Photo2Category.php b/Toolkit/Photos/Models/Photo2Category.php
new file mode 100644 (file)
index 0000000..76b5249
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * Photo2Category.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Photo2Category
+ *
+ * Description of Photo2Category
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_Photo2Category
+{
+    private $_id;
+    private $_photo;
+    private $_category;
+    private $_pos;
+
+    const TABLE_NAME = 'photos.photo2category';
+    const PRI_KEY    = 'id';
+    const SORT       = 'pos';
+
+    private function __construct($values)
+    {
+        extract($values);
+        $this->setCategory($category)
+            ->setPhoto($photo)
+            ->setPos($pos);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    static public function createByValues($values)
+    {
+        return new Toolkit_Photos_Models_Photo2Category($values);
+    }
+
+    public function getId()
+    {
+        return (int)$this->_id;
+    }
+
+    public function setId($id)
+    {
+        $this->_id
+            = (filter_var($id, FILTER_VALIDATE_INT))
+            ? $id
+            : null;
+        return $this;
+    }
+
+    public function getPhoto()
+    {
+        return (int)$this->_photo;
+    }
+
+    public function setPhoto($photo)
+    {
+        $this->_photo = (int)$photo;
+        return $this;
+    }
+
+    public function getCategory()
+    {
+        return (int)$this->_category;
+    }
+
+    public function setCategory($category)
+    {
+        $this->_category = (int)$category;
+        return $this;
+    }
+
+    public function getPos()
+    {
+        return (int)$this->_pos;
+    }
+
+    public function setPos($pos)
+    {
+        $this->_pos = (int)$pos;
+        return $this;
+    }
+
+
+}
diff --git a/Toolkit/Photos/Models/User.php b/Toolkit/Photos/Models/User.php
new file mode 100644 (file)
index 0000000..9345b34
--- /dev/null
@@ -0,0 +1,279 @@
+<?php
+
+/**
+ * User.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_Model_User
+ *
+ * Class representation of the User
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_User
+{
+    private $_id;
+    private $_company;
+    private $_title;
+    private $_fname;
+    private $_lname;
+    private $_email;
+    private $_address;
+    private $_address2;
+    private $_city;
+    private $_state;
+    private $_zip;
+    private $_phone;
+    private $_media;
+    private $_expire;
+    private $_mediaPass;
+    private $_approved;
+    private $_denied;
+
+    const TABLE_NAME = 'contacts.contact';
+    const PRI_KEY    = 'id';
+    const SORT       = 'lname,fname';
+    const EMAIL      = 'email';
+    const WHERE      = 'media = true';
+
+    private function __construct($values)
+    {
+        extract($values);
+        $this->setCompany($company)
+            ->setTitle($title)
+            ->setAddress($address)
+            ->setAddress2($address2)
+            ->setApproved($approved)
+            ->setMediaPass($media_pass)
+            ->setCity($city)
+            ->setDenied($denied)
+            ->setEmail($email)
+            ->setExpire($expire)
+            ->setFname($fname)
+            ->setLname($lname)
+            ->setMedia($media)
+            ->setPhone($phone)
+            ->setState($state)
+            ->setZip($zip);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    public function createByValues($values)
+    {
+        if (is_array($values) && !empty($values)) {
+            return new Toolkit_Photos_Models_User($values);
+        } else {
+            return false;
+        }
+    }
+
+    public function getMediaPass()
+    {
+        return $this->_mediaPass;
+    }
+
+    public function setMediaPass($mediaPass)
+    {
+        $this->_mediaPass = $mediaPass;
+        return $this;
+    }
+
+
+    public function getId()
+    {
+        return (int)$this->_id;
+    }
+
+    public function setId($id)
+    {
+        $this->_id = (int)$id;
+        return $this;
+    }
+
+    public function getCompany()
+    {
+        return $this->_company;
+    }
+
+    public function setCompany($company)
+    {
+        $this->_company = $company;
+        return $this;
+    }
+
+    public function getTitle()
+    {
+        return $this->_title;
+    }
+
+    public function setTitle($title)
+    {
+        $this->_title = $title;
+        return $this;
+    }
+
+
+    public function getFname()
+    {
+        return $this->_fname;
+    }
+
+    public function setFname($fname)
+    {
+        $this->_fname = $fname;
+        return $this;
+    }
+
+    public function getLname()
+    {
+        return $this->_lname;
+    }
+
+    public function setLname($lname)
+    {
+        $this->_lname = $lname;
+        return $this;
+    }
+
+    public function getEmail()
+    {
+        return $this->_email;
+    }
+
+    public function setEmail($email)
+    {
+        $this->_email = $email;
+        return $this;
+    }
+
+    public function getAddress()
+    {
+        return $this->_address;
+    }
+
+    public function setAddress($address)
+    {
+        $this->_address = $address;
+        return $this;
+    }
+
+    public function getAddress2()
+    {
+        return $this->_address2;
+    }
+
+    public function setAddress2($address2)
+    {
+        $this->_address2 = $address2;
+        return $this;
+    }
+
+    public function getCity()
+    {
+        return $this->_city;
+    }
+
+    public function setCity($city)
+    {
+        $this->_city = $city;
+        return $this;
+    }
+
+    public function getState()
+    {
+        return $this->_state;
+    }
+
+    public function setState($state)
+    {
+        $this->_state = $state;
+        return $this;
+    }
+
+    public function getZip()
+    {
+        return $this->_zip;
+    }
+
+    public function setZip($zip)
+    {
+        $this->_zip = $zip;
+        return $this;
+    }
+
+    public function getPhone()
+    {
+        return $this->_phone;
+    }
+
+    public function setPhone($phone)
+    {
+        $this->_phone = $phone;
+        return $this;
+    }
+
+    public function getMedia()
+    {
+        return $this->_media;
+    }
+
+    public function setMedia($media)
+    {
+        $this->_media = (bool)$media;
+        return $this;
+    }
+
+    public function getExpire()
+    {
+        return $this->_expire;
+    }
+
+    public function setExpire($expire)
+    {
+        $this->_expire = $expire;
+        return $this;
+    }
+
+    public function getApproved()
+    {
+        return (bool)$this->_approved;
+    }
+
+    public function setApproved($approved)
+    {
+        $this->_approved = (bool)$approved;
+        return $this;
+    }
+
+    public function getDenied()
+    {
+        return (bool)$this->_denied;
+    }
+
+    public function setDenied($denied)
+    {
+        $this->_denied = (bool)$denied;
+        return $this;
+    }
+
+
+}
diff --git a/Toolkit/Photos/Photo.php b/Toolkit/Photos/Photo.php
new file mode 100644 (file)
index 0000000..12c9b08
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Photo
+ *
+ * @author steve
+ */
+class Toolkit_Photos_Photo
+    extends Toolkit_Table
+{
+    public $tableName = 'photo';
+    protected $id;
+    protected $title;
+    protected $description;
+    protected $image;
+    protected $catid;
+    protected $pos;
+}
+
diff --git a/Toolkit/Photos/SlideShowCategoryFilter.php b/Toolkit/Photos/SlideShowCategoryFilter.php
new file mode 100644 (file)
index 0000000..df6d871
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * SlideShowCategoryFilter.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_SlideShowCategoryFilter
+ *
+ * Description of SlideShowFilter
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_SlideShowCategoryFilter
+    extends FilterIterator
+{
+    public function __construct(Iterator $iterator)
+    {
+        parent::__construct($iterator);
+    }
+
+    public function accept()
+    {
+        $category = $this->getInnerIterator()->current();
+        if ($category['slideshow']) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/Toolkit/Photos/application.ini b/Toolkit/Photos/application.ini
new file mode 100644 (file)
index 0000000..d576e1c
--- /dev/null
@@ -0,0 +1,45 @@
+; Production server configuration data
+[production]
+; Application settings
+application.name = "Image Library"
+application.path = BASE "Toolkit/Photos/Controllers"
+application.application = "Photos/Controllers"
+
+; turn on media exclusive flag for categories and photos
+; This option also may need other new fields not added yet to the contact table
+settings.mediaExclusive  = Off
+settings.photoNameSearch = Off
+settings.slideShowOption = Off
+
+; default Flexy Options for this application
+flexyOptions.templateDir     = BASE "Toolkit/Photos/templates"
+flexyOptions.compileDir      = BASE "Toolkit/Photos/templates/compiled"
+flexyOptions.url_rewrite     = "baseurl/::" BASE_URL ",basesecureurl/::" BASE_SECURE_URL ",glmappbaseurl/::" GLM_APP_BASE_URL
+flexyOptions.forceCompile    = Off
+flexyOptions.locale          = "en"
+flexyOptions.debug           = Off
+flexyOptions.allowPHP        = On
+flexyOptions.flexyIgnore     = On
+flexyOptions.globals         = On
+flexyOptions.globalfunctions = On
+flexyOptions.privates        = On
+flexyOptions.compiler        = "Flexy"
+
+; development server configuration data inherits from production and
+; overrides values as necessary
+[development : production]
+
+; chuck's server configuration data inherits from development
+; and overrides values as necessary
+[chuck : development]
+
+; john's server configuration data inherits from development
+; and overrides values as necessary
+[john : development]
+
+; steve's server configuration data inherits from development
+; and overrides values as necessary
+[steve : development]
+; Vagrant server configuration inherits from development and
+; overrides values as needed
+[vagrant : development]
diff --git a/Toolkit/Photos/assets/.keepme b/Toolkit/Photos/assets/.keepme
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Toolkit/Photos/css/gallery.css b/Toolkit/Photos/css/gallery.css
new file mode 100755 (executable)
index 0000000..545a01c
--- /dev/null
@@ -0,0 +1,28 @@
+.header {
+    margin: 10px 20px;
+    }
+#photo-gallery {
+    margin: 10px 20px;
+}
+#glmMediaGallery {
+    background: #F5F3E5;
+    border-width: 0 1px 1px;
+    border: 1px solid #A6C9E2;
+    padding: 15px;
+}
+div.thumb {
+    margin: 3px 9px;
+    border: 0px solid #A0ACC0;
+    height: auto;
+    float: left;
+    text-align: center;
+}
+.thumb img{
+    display: inline;
+    margin: 3px;
+    border: 1px solid #A0ACC0;
+    }
+.thumb a:hover img {border: 1px solid black;}
+.photocattitle {text-align: center; font-weight: bold;}
+.phototitle {text-align: center; font-weight: normal; width: 110px;}
+.galleryRow {clear: left;}
diff --git a/Toolkit/Photos/css/style.css b/Toolkit/Photos/css/style.css
new file mode 100644 (file)
index 0000000..f26d4c0
--- /dev/null
@@ -0,0 +1,344 @@
+.containerobj .widget {
+    background-color: white;
+}
+.containerobj .active .widget {
+    color: white;
+    background-color: #3671cf;
+}
+.containerobj .inpath .widget {
+    background-color: #d0d0d0;
+}
+#demo1 {
+    height: 200px;
+}
+#demo1 a {
+    padding: 0 3px;
+}
+.ui-dialog-buttonpane {
+    padding: 0 10px 0 20px !important;
+}
+#dialog-modal {
+    height: auto !important;
+}
+
+#addPageTo {
+    /*display: inline;
+    width: 82px;
+    height: 28px;
+    border: none;
+    text-align: left;
+    padding: 0 0 0 24px;*/
+    margin: 6px 0 0 0;
+    float: right;
+}
+#deleteBtn2 {
+    display: block;
+    position:absolute;
+    bottom: 36px;
+    right: 5px;
+    width: 55px;
+    height: 28px;
+    background: url(../assets/btn_delete.gif) no-repeat;
+    border: none;
+    padding: 0 0 0 27px;
+    font-size: 14px;
+    font-size: 1.4rem;
+    line-height: 28px;
+    text-decoration: none;
+    margin: 0px;
+}
+.ui-state-highlight {
+    color: #09F;
+    border-color: #09F;
+    background: #09F;
+}
+.ui-widget-content {
+    margin: 0;
+    padding: 0;
+}
+#photoGalleryFooter {
+    display: none;
+    padding: 10px;
+    text-align: center;
+    width: 100%;
+    float: bottom;
+}
+
+/* Search Form styles */
+#photoAdminSearch input[type="search"],
+    #categoryAdminSearch select {
+        height: 26px;
+        font-size: 18px;
+        width: 300px;
+        border: 1px solid #CCCCCC;
+        background-color: #EFEFEF !important;
+    }
+    #mediaGallerySearchForm {
+        padding: 0;
+        margin: 20px 0 20px;
+    }
+    #categorySearchWrapper {
+        width: 800px;
+        height: 34px;
+        position: relative;
+        border-bottom: 1px solid #CCC;
+    }
+    #categoryAdminSearch select {
+        display: block;
+        position: relative;
+        float: left;
+        z-index: 99;
+        left: 0;
+        top: 0;
+        width: 37.75%;
+        margin: 0 .5% 0 0;
+        font-size: 14px;
+        font-weight: normal;
+        color: #666;
+        height: 27px;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        padding:.2em;/.3em;
+    }
+    #categoryAdminSearch input[type="submit"], #photoAdminSearch input[type="submit"] {
+        display: block;
+        position: relative;
+        float: left;
+        z-index: 99;
+        right: 0;
+        top: 0;
+        width: 10%;
+        margin: 0;
+        font-size: 14px;
+        font-weight: normal;
+        color: #666;
+        margin: 0;
+        height: 28px;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+    }
+    #photoAdminSearch input[type="search"] {
+        display: block;
+        position: relative;
+        float: left;
+        z-index: 99;
+        left: 0;
+        top: 0;
+        width: 37.75%;
+        margin: 0 .5% 0 3%;
+        font-size: 14px;
+        font-weight: normal;
+        color: #333;
+        height: 27px;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        padding:.2em;/.3em;
+        padding-left: 6px;
+    }
+    div.photo-category-list {
+        background: #EFEFEF;
+        border-radius: 5px;
+        display: block;
+        float: left;
+        height: 148px;
+        margin: 0 20px 20px 0;
+        padding: 10px;
+        position: relative;
+        width: 120px;
+        border: 1px solid #CCC;
+        -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         0px 0px 5px rgba(0, 0, 0, 0.2);
+    }
+    div.pCount {
+        background-color: #333;
+        border-radius: 30px 30px 30px 30px;
+        -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         0px 0px 5px rgba(0, 0, 0, 0.2);
+        color: white;
+        display: inline;
+        float: right;
+        font-weight: bold;
+        padding: 1px 5px;
+        position: absolute;
+        right: -8px;
+        text-shadow: 1px 1px grey;
+        top: 0;
+    }
+    div.photo-category-list h1 {
+        bottom: 10px;
+        left: 10px;
+        margin: 0;
+        overflow: hidden;
+        padding: 0;
+        position: absolute;
+        width: 123px;
+    }
+    div.photo-category-list img {
+        border: medium none;
+        border-radius: 5px;
+        -webkit-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        border: 1px solid #CCC;
+    }
+
+/* edit category form styles */
+#editCategoryForm .webform {
+        margin: 10px 0;
+        width: 400px;
+        border: 1px solid #CCC;
+        border-radius: 5px;
+        padding: 10px 2%;
+    }
+    #editCategoryForm .webform td {
+        width: 100%;
+        float: left;
+        border: 0;
+        text-align: left;
+    }
+    #editCategoryForm .webform td input[type="text"] {
+        display: block;
+        position: relative;
+        z-index: 99;
+        left: 0;
+        top: 0;
+        width: 100%;
+        margin: 0;
+        font-size: 14px;
+        font-weight: normal;
+        color: #333;
+        height: 27px;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        padding:.2em;/.3em;
+        padding-left: 6px;
+        background: #EFEFEF !important;
+        border: 1px solid #CCC;
+    }
+    .category-category-list {
+        position: relative;
+        box-shadow: 1px 2px 2px grey;
+        border-radius: 10px;
+        -moz-border-radius: 10px;
+        -webkit-border-radius: 10px;
+        display:block;
+        width: 120px;
+        float: left;
+        background: #D6DFC3;
+        margin: 5px;
+        padding: 10px;
+        height: 130px;
+    }
+    .category-category-list h1 {
+        position: absolute;
+        bottom: 0;
+        left: 5px;
+        margin: 0;
+        padding: 0;
+        overflow: hidden;
+        width: 123px;;
+    }
+    .category-category-list h1 a {
+        color: #006BB4;
+        font-size: 12px;
+        text-decoration: none;
+        padding: 0;
+        margin: 0;
+        overflow: hidden;
+        white-space: nowrap;
+    }
+    .category-category-list img {
+        box-shadow: 1px 2px 2px grey;
+        border-radius: 10px;
+        -moz-border-radius: 10px;
+        -webkit-border-radius: 10px;
+        border: none;
+    }
+    #addPageTo, form#editCategoryForm input[type="submit"] {
+        /*box-shadow: 1px 2px 2px grey;
+        background-color: #D6DFC3;
+        font-size: 16px;
+        font-weight: normal;
+        text-align: center;
+        margin: 0;
+        padding: 1px 5px 1px 5px;
+        width: auto;*/
+    }
+    .pageLink {
+        /*border-radius: 10px;*/
+        /*box-shadow: 1px 2px 2px grey;
+        width: 280px;
+        background-color: #D6DFC3;
+        padding: 5px;
+        margin: 5px 0;
+        float: left;*/
+        width: 96%;
+        padding: 5px 2%;
+        border-bottom: 1px solid #CCC;
+    }
+    .btnDelete {
+        float: right;
+    }
+    #displayWrapper {
+        margin: 3px 0;
+        border-left: 1px solid #CCC;
+        border-right: 1px solid #CCC;
+        border-top: 1px solid #CCC;
+        background: #EFEFEF;
+    }
+    #categorySave {
+        width: 31.333333%;
+        margin: 0 2% 0 0;
+    }
+    #categoryCancel {
+        width: 31.333333%;
+        margin: 0 2% 0 0;
+    }
+    #categoryDelete {
+        width: 31.333333%;
+        margin: 0;
+    }
+    #pagesLabel {
+        padding-top: 5px;
+    }
+    div.category-category-list {
+        background: #EFEFEF;
+        border-radius: 5px;
+        display: block;
+        float: left;
+        height: 148px;
+        margin: 0 20px 20px 0;
+        padding: 10px;
+        position: relative;
+        width: 120px;
+        border: 1px solid #CCC;
+        -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         0px 0px 5px rgba(0, 0, 0, 0.2);
+    }
+    div.category-category-list h1 {
+        bottom: 10px;
+        left: 10px;
+        margin: 0;
+        overflow: hidden;
+        padding: 0;
+        position: absolute;
+        width: 123px;
+    }
+    div.category-category-list img {
+        border: medium none;
+        border-radius: 5px;
+        -webkit-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        border: 1px solid #CCC;
+    }
+    div#categoryPhotos {
+        margin-top: 20px;
+    }
diff --git a/Toolkit/Photos/css/thickbox.css b/Toolkit/Photos/css/thickbox.css
new file mode 100755 (executable)
index 0000000..f69f4e4
--- /dev/null
@@ -0,0 +1,168 @@
+/* ----------------------------------------------------------------------------------------------------------------*/
+/* ---------->>> global settings needed for thickbox <<<-----------------------------------------------------------*/
+/* ----------------------------------------------------------------------------------------------------------------*/
+/* *{padding: 0; margin: 0;} */
+
+/* ----------------------------------------------------------------------------------------------------------------*/
+/* ---------->>> thickbox specific link and font settings <<<------------------------------------------------------*/
+/* ----------------------------------------------------------------------------------------------------------------*/
+#TB_window {
+    font: 12px Arial, Helvetica, sans-serif;
+    color: #333333;
+}
+
+#TB_secondLine {
+    font: 10px Arial, Helvetica, sans-serif;
+    color:#666666;
+}
+
+#TB_window a:link {color: #666666;}
+#TB_window a:visited {color: #666666;}
+#TB_window a:hover {color: #000;}
+#TB_window a:active {color: #666666;}
+#TB_window a:focus{color: #666666;}
+
+/* ----------------------------------------------------------------------------------------------------------------*/
+/* ---------->>> thickbox settings <<<-----------------------------------------------------------------------------*/
+/* ----------------------------------------------------------------------------------------------------------------*/
+#TB_overlay {
+    position: fixed;
+    z-index:100;
+    top: 0px;
+    left: 0px;
+    height:100%;
+    width:100%;
+}
+
+.TB_overlayMacFFBGHack {background: url(macFFBgHack.png) repeat;}
+.TB_overlayBG {
+    background-color:#000;
+    filter:alpha(opacity=75);
+    -moz-opacity: 0.75;
+    opacity: 0.75;
+}
+
+* html #TB_overlay { /* ie6 hack */
+     position: absolute;
+     height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
+}
+
+#TB_window {
+    position: fixed;
+    background: #ffffff;
+    z-index: 102;
+    color:#000000;
+    display:none;
+    border: 4px solid #525252;
+    text-align:left;
+    top:50%;
+    left:50%;
+}
+
+* html #TB_window { /* ie6 hack */
+position: absolute;
+margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
+}
+
+#TB_window img#TB_Image {
+    display:block;
+    margin: 15px 0 0 15px;
+    border-right: 1px solid #ccc;
+    border-bottom: 1px solid #ccc;
+    border-top: 1px solid #666;
+    border-left: 1px solid #666;
+}
+
+#TB_caption{
+/*    height:25px; */
+    padding:7px 30px 10px 25px;
+    float:left;
+}
+
+#TB_DownloadLinks {
+    padding:7px 30px 10px 25px;
+    float:left;
+}
+
+#TB_closeWindow{
+    height:25px;
+    padding:11px 25px 10px 0;
+    float:right;
+}
+
+#TB_closeAjaxWindow{
+    padding:7px 10px 5px 0;
+    margin-bottom:1px;
+    text-align:right;
+    float:right;
+}
+
+#TB_ajaxWindowTitle{
+    float:left;
+    padding:7px 0 5px 10px;
+    margin-bottom:1px;
+}
+
+#TB_title{
+    background-color:#e8e8e8;
+    height:27px;
+}
+
+#TB_ajaxContent{
+    clear:both;
+    padding:2px 15px 15px 15px;
+    overflow:auto;
+    text-align:left;
+    line-height:1.4em;
+}
+
+#TB_ajaxContent.TB_modal{
+    padding:15px;
+}
+
+#TB_ajaxContent p{
+    padding:5px 0px 5px 0px;
+}
+
+#TB_load{
+    position: fixed;
+    display:none;
+    height:13px;
+    width:208px;
+    z-index:103;
+    top: 50%;
+    left: 50%;
+    margin: -6px 0 0 -104px; /* -height/2 0 0 -width/2 */
+}
+
+* html #TB_load { /* ie6 hack */
+position: absolute;
+margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
+}
+
+#TB_HideSelect{
+    z-index:99;
+    position:fixed;
+    top: 0;
+    left: 0;
+    background-color:#fff;
+    border:none;
+    filter:alpha(opacity=0);
+    -moz-opacity: 0;
+    opacity: 0;
+    height:100%;
+    width:100%;
+}
+
+* html #TB_HideSelect { /* ie6 hack */
+     position: absolute;
+     height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
+}
+
+#TB_iframeContent{
+    clear:both;
+    border:none;
+    margin-bottom:-1px;
+    margin-top:1px;
+    _margin-bottom:1px;
+}
diff --git a/Toolkit/Photos/js/jquery.columnview.js b/Toolkit/Photos/js/jquery.columnview.js
new file mode 100755 (executable)
index 0000000..53a3834
--- /dev/null
@@ -0,0 +1,267 @@
+/**
+ * jquery.columnview-1.2.js
+ *
+ * Created by Chris Yates on 2009-02-26.
+ * http://christianyates.com
+ * Copyright 2009 Christian Yates and ASU Mars Space Flight Facility. All rights reserved.
+ *
+ * Supported under jQuery 1.2.x or later
+ * Keyboard navigation supported under 1.3.x or later
+ * 
+ * Dual licensed under MIT and GPL.
+ */
+
+(function($){
+  $.fn.columnview = function(options){
+
+    var settings = $.extend({}, $.fn.columnview.defaults, options);
+        
+    // Add stylesheet, but only once
+    if(!$('.containerobj').get(0)){
+      $('head').prepend('\
+      <style type="text/css" media="screen">\
+        .containerobj {\
+          border: 1px solid #ccc;\
+          height:5em;\
+          overflow-x:auto;\
+          overflow-y:hidden;\
+          white-space:nowrap;\
+          position:relative;\
+        }\
+        .containerobj div {\
+          height:100%;\
+          overflow-y:scroll;\
+          overflow-x:hidden;\
+          position:absolute;\
+        }\
+        .containerobj a {\
+          display:block;\
+          white-space:nowrap;\
+          clear:both;\
+          padding-right:15px;\
+          overflow:hidden;\
+          text-decoration:none;\
+        }\
+        .containerobj a:focus {\
+          outline:none;\
+        }\
+        .containerobj a canvas {\
+        }\
+        .containerobj .feature {\
+          min-width:200px;\
+          overflow-y:auto;\
+        }\
+        .containerobj .feature a {\
+          white-space:normal;\
+        }\
+        .containerobj .hasChildMenu {\
+        }\
+        .containerobj .active {\
+          background-color:#3671cf;\
+          color:#fff;\
+        }\
+        .containerobj .inpath {\
+          background-color:#d0d0d0;\
+          color:#000;\
+        }\
+        .containerobj .hasChildMenu .widget {\
+          color:black;\
+          position:absolute;\
+          right:0;\
+          text-decoration:none;\
+          font-size:0.7em;\
+        }\
+      </style>');
+    }
+
+    // Hide original list
+    $(this).hide();
+    // Reset the original list's id
+    var origid = $(this).attr('id');
+    if (origid) {
+      $(this).attr('id', origid + "-processed");
+    }
+
+    // Create new top container from top-level LI tags
+    var top = $(this).children('li');
+    var container = $('<div/>').addClass('containerobj').attr('id', origid).insertAfter(this);
+    var topdiv = $('<div class="top"></div>').appendTo(container);
+    // Set column width
+    if (settings.fixedwidth || $.browser.msie) { // MSIE doesn't support auto-width
+      var width = typeof settings.fixedwidth == "string" ? settings.fixedwidth : '200px';
+      $('.top').width(width);
+    }
+    $.each(top,function(i,item){
+      var topitem = $(':eq(0)',item).clone(true).wrapInner("<span/>").data('sub',$(item).children('ul')).appendTo(topdiv);
+      if (settings.fixedwidth || $.browser.msie)
+      $(topitem).css({'text-overflow':'ellipsis', '-o-text-overflow':'ellipsis','-ms-text-overflow':'ellipsis'});
+      if($(topitem).data('sub').length) {
+        $(topitem).addClass('hasChildMenu');
+        addWidget(container, topitem);
+      }
+    });
+
+    // Firefox doesn't repeat keydown events when the key is held, so we use
+    // keypress with FF/Gecko/Mozilla to enable continuous keyboard scrolling.
+    var key_event = $.browser.mozilla ? 'keypress' : 'keydown';
+    
+    // Event handling functions
+    $(container).bind("click " + key_event, function(event){
+      if ($(event.target).is("a,span")) {
+        if ($(event.target).is("span")){
+          var self = $(event.target).parent();
+        }
+        else {
+          var self = event.target;          
+        }
+        if (!settings.multi) {
+          delete event.shiftKey;
+          delete event.metaKey;
+        }
+        self.focus();
+        var container = $(self).parents('.containerobj');
+        // Handle clicks
+        if (event.type == "click"){
+          var level = $('div',container).index($(self).parents('div'));
+          var isleafnode = false;
+          // Remove blocks to the right in the tree, and 'deactivate' other
+          // links within the same level, if metakey is not being used
+          $('div:gt('+level+')',container).remove();
+          if (!event.metaKey && !event.shiftKey) {
+            $('div:eq('+level+') a',container).removeClass('active').removeClass('inpath');
+            $('.active',container).addClass('inpath');
+            $('div:lt('+level+') a',container).removeClass('active');
+          }
+          // Select intermediate items when shift clicking
+          // Sorry, only works with jQuery 1.4 due to changes in the .index() function
+          if (event.shiftKey) {
+            var first = $('a.active:first', $(self).parent()).index();
+            var cur = $(self).index();
+            var range = [first,cur].sort(function(a,b){return a - b;});
+            $('div:eq('+level+') a', container).slice(range[0], range[1]).addClass('active');
+          }
+          $(self).addClass('active');
+          if ($(self).data('sub').children('li').length && !event.metaKey) {
+            // Menu has children, so add another submenu
+            var w = false;
+            if (settings.fixedwidth || $.browser.msie)
+            w = typeof settings.fixedwidth == "string" ? settings.fixedwidth : '200px';
+            submenu(container,self,w);
+          }
+          else if (!event.metaKey && !event.shiftKey) {
+            // No children, show title instead (if it exists, or a link)
+            isleafnode = true;
+            var previewcontainer = $('<div/>').addClass('feature').appendTo(container);
+            // Fire preview handler function
+            if ($.isFunction(settings.preview)) {
+              // We're passing the element back to the callback
+              var preview = settings.preview($(self));
+            }
+            // If preview is specifically disabled, do nothing with the previewbox
+            else if (!settings.preview) {
+            }
+            // If no preview function is specificied, use a default behavior
+            else {
+              var title = $('<a/>').attr({href:$(self).attr('href')}).text($(self).attr('title') ? $(self).attr('title') : $(self).text());
+              $(previewcontainer).html(title);
+            }
+            // Set the width
+            var remainingspace = 0; 
+            $.each($(container).children('div').slice(0,-1),function(i,item){
+              remainingspace += $(item).width();
+            });
+            var fillwidth = $(container).width() - remainingspace;
+            $(previewcontainer).css({'top':0,'left':remainingspace}).width(fillwidth).show();  
+          }
+          // Fire onchange handler function, but only if multi-select is off.
+          // FIXME Need to deal multiple selections.
+          if ($.isFunction(settings.onchange) && !settings.multi) {
+            // We're passing the element back to the callback
+            var onchange = settings.onchange($(self), isleafnode);
+          }
+        }
+        // Handle Keyboard navigation
+        if(event.type == key_event){
+          switch(event.keyCode){
+            case(37): //left
+              $(self).parent().prev().children('.inpath').focus().trigger("click");
+              break;
+            case(38): //up
+              $(self).prev().focus().trigger("click");
+              break;
+            case(39): //right
+              if($(self).hasClass('hasChildMenu')){
+                $(self).parent().next().children('a:first').focus().trigger("click");
+              }
+              break;
+            case(40): //down
+              $(self).next().focus().trigger("click");
+              break;
+            case(13): //enter
+              $(self).trigger("dblclick");
+              break;
+          }
+        }
+        event.preventDefault();
+      }
+    });
+
+  };
+  
+  $.fn.columnview.defaults = {
+    multi: false,     // Allow multiple selections
+    preview: true,    // Handler for preview pane
+    fixedwidth: false,// Use fixed width columns
+    onchange: false   // Handler for selection change
+  };
+
+  // Generate deeper level menus
+  function submenu(container,item,width){
+    var leftPos = 0;
+    $.each($(container).children('div'),function(i,mydiv){
+      leftPos += $(mydiv).width();
+    });
+    var submenu = $('<div/>').css({'top':0,'left':leftPos}).appendTo(container);
+    // Set column width
+    if (width)
+    $(submenu).width(width);
+    var subitems = $(item).data('sub').children('li');
+    $.each(subitems,function(i,subitem){
+      var subsubitem = $(':eq(0)',subitem).clone(true).wrapInner("<span/>").data('sub',$(subitem).children('ul')).appendTo(submenu);
+      if (width)
+      $(subsubitem).css({'text-overflow':'ellipsis', '-o-text-overflow':'ellipsis','-ms-text-overflow':'ellipsis'});
+      if($(subsubitem).data('sub').length) {
+        $(subsubitem).addClass('hasChildMenu');
+        addWidget(container, subsubitem);
+      }
+    });
+  }
+
+  // Uses canvas, if available, to draw a triangle to denote that item is a parent
+  function addWidget(container, item, color){
+    var triheight = $(item).height();
+    var canvas = $("<canvas></canvas>").attr({height:triheight,width:10}).addClass('widget').appendTo(item);    if(!color){ color = $(canvas).css('color'); }
+    canvas = $(canvas).get(0);
+    if(canvas.getContext){
+      var context = canvas.getContext('2d');
+      context.fillStyle = color;
+      context.beginPath();
+      context.moveTo(3,(triheight/2 - 3));
+      context.lineTo(10,(triheight/2));
+      context.lineTo(3,(triheight/2 + 3));
+      context.fill();
+    } else {
+      /**
+       * Canvas not supported - put something in there anyway that can be
+       * suppressed later if desired. We're using a decimal character here
+       * representing a "black right-pointing pointer" in Windows since IE
+       * is the likely case that doesn't support canvas.
+       */
+      $("<span>&#9658;</span>").addClass('widget').css({'height':triheight,'width':10}).prependTo(item);
+    }
+    $(container).find('.widget').bind('click', function(event){
+      event.preventDefault();
+    });
+
+  }
+})(jQuery);
\ No newline at end of file
diff --git a/Toolkit/Photos/js/photoGallery.js b/Toolkit/Photos/js/photoGallery.js
new file mode 100644 (file)
index 0000000..d05c97f
--- /dev/null
@@ -0,0 +1,173 @@
+$(function(){
+   $("#photoCategoryId").change(function(){
+       $(this).parent().submit();
+   });
+});
+
+(function(window, PhotoSwipe){
+   //if ($(".photoimg").length > 0) {
+         if(document.addEventListener) {
+            document.addEventListener('DOMContentLoaded', function(){
+            var
+                options = {
+                    getImageCaption: function(el){
+                        var captionText, captionDesc, captionId, captionEl;
+
+                        if (el.nodeName === "A") {
+                            captionText = el.getAttribute('title');
+                            captionDesc = el.getAttribute('rel');
+                            captionId = el.getAttribute('data-photoid');
+                            captionDownload = el.getAttribute('data-download');
+                        }
+
+                        // Return a DOM element with custom styling
+                        if((captionText != null && captionText != "") || (captionDesc != null && captionDesc != "")) {
+                            captionEl = document.createElement('div');
+                            captionEl.className = 'captionWrapper';
+                            // Add Caption Title.
+                            if(captionText != null && captionText != "") {
+                                captionTitleOutput = document.createElement('h2');
+                                captionTitleOutput.className = "captionTitleWrapper";
+                                captionTitleOutput.appendChild(document.createTextNode(captionText));
+                                captionEl.appendChild(captionTitleOutput);
+                            }
+                            // Add Caption Description.
+                            if(captionDesc != null && captionDesc != "") {
+                                captionDescOutput = document.createElement('p');
+                                captionDescOutput.className = "captionDescWrapper";
+                                captionDescOutput.appendChild(document.createTextNode(captionDesc));
+                                captionEl.appendChild(captionDescOutput);
+                            }
+                        } else {
+                            //captionEl = document.createElement('div');
+                            //captionEl.style.cssText = 'display:none; width:0; height:0;';
+                            captionEl = "";
+                        }
+                        // Add Download links.
+                        if(captionId != null && captionId != "" && captionDownload == "1")  {
+                            captionIdOutput = document.createElement('div');
+                            captionIdOutput.className = 'download_wrapper';
+                            captionEl.appendChild(captionIdOutput);
+                            // Add Link Text.
+                            captionLinkText = document.createElement('span');
+                            captionText = document.createTextNode("Download for ");
+                            captionLinkText.appendChild(captionText);
+                            captionIdOutput.appendChild(captionLinkText);
+                            // Add Web Link.
+                            captionWebLink = base_url + "download-photo-web/" + captionId + "/";
+                            downloadWebLink = document.createElement('a');
+                            linkText = document.createTextNode("Web");
+                            downloadWebLink.appendChild(linkText);
+                            downloadWebLink.title = "Download for Web";
+                            downloadWebLink.href = captionWebLink;
+                            captionIdOutput.appendChild(downloadWebLink);
+                            // Add Spacer Text.
+                            captionSpacerText = document.createElement('span');
+                            captionText = document.createTextNode(" - ");
+                            captionSpacerText.appendChild(captionText);
+                            captionIdOutput.appendChild(captionSpacerText);
+                            // Add Print Link.
+                            captionPrintLink = base_url + "download-photo-print/" + captionId + "/";
+                            downloadPrintLink = document.createElement('a');
+                            linkPrintText = document.createTextNode("Print");
+                            downloadPrintLink.appendChild(linkPrintText);
+                            downloadPrintLink.title = "Download for Print";
+                            downloadPrintLink.href = captionPrintLink;
+                            captionIdOutput.appendChild(downloadPrintLink);
+                        }
+
+                        return captionEl;
+
+                        // Alternatively you can just pass back a string. However, any HTML
+                        // markup will be escaped
+
+                    }, captionAndToolbarAutoHideDelay:0, imageScaleMethod: "fitNoUpscale", captionAndToolbarFlipPosition: true, backButtonHideEnabled: true
+                },
+                instance = PhotoSwipe.attach( window.document.querySelectorAll('a.photoimg'), options );
+            }, false);
+        } else { // You are using IE8- and you should feel bad.
+            document.attachEvent('onreadystatechange', function(){
+            var
+                options = {
+                    getImageCaption: function(el){
+                        var captionText, captionDesc, captionId, captionEl;
+
+                        if (el.nodeName === "A") {
+                            captionText = el.getAttribute('title');
+                            captionDesc = el.getAttribute('rel');
+                            captionId = el.getAttribute('data-photoid');
+                            captionDownload = el.getAttribute('data-download');
+                        }
+
+                        // Return a DOM element with custom styling
+                        if((captionText != null && captionText != "") || (captionDesc != null && captionDesc != "")) {
+                            //captionEl = document.createElement('div');
+                            captionEl = $('<div style="display: block;width: 100%;overflow: hidden;"></div>');
+                            captionEl.addClass = 'captionWrapper';
+                            // Add Caption Title.
+                            if(captionText != null && captionText != "") {
+                                //captionTitleOutput = document.createElement('h2');
+                                captionTitleOutput = $('<h2 style="display: block;width: 50%;float: left;font-size: 18px;font-weight: bold;color: #FFF;text-align: left;padding: 10px 20px;margin: 0;"></h2>');
+                                captionTitleOutput.addClass = "captionTitleWrapper";
+                                captionTitleOutput.append(captionText);
+                                captionEl.append(captionTitleOutput);
+                            }
+                            // Add Caption Description.
+                            if(captionDesc != null && captionDesc != "") {
+                                //captionDescOutput = document.createElement('p');
+                                captionDescOutput = $('<p style="display: block;width: 50%;float: left;clear: left;font-size: 14px;color: #FFF;text-align: left;padding: 0 20px 10px 20px;margin: 0;"></p>');
+                                captionDescOutput.addClass = "captionDescWrapper";
+                                captionDescOutput.append(document.createTextNode(captionDesc));
+                                captionEl.append(captionDescOutput);
+                            }
+                        } else {
+                            //captionEl = document.createElement('div');
+                            //captionEl.style.cssText = 'display:none; width:0; height:0;';
+                            captionEl = "";
+                        }
+                        // Add Download links.
+                        if(captionId != null && captionId != "" && captionDownload == "1") {
+                            captionIdOutput = $('<div style="display: block;position: absolute;z-index: 99;top: 10px;right: 20px;width: 30%;font-size: 14px;color: #FFF;text-align: left;padding: 6px 0 10px 20px;margin: 0;text-align: right;"></div>');
+                            captionIdOutput.addClass = 'download_wrapper';
+                            captionEl.append(captionIdOutput);
+                            // Add Link Text.
+                            captionLinkText = $('<span></span>');
+                            captionText = document.createTextNode("Download for ");
+                            captionLinkText.append(captionText);
+                            captionIdOutput.append(captionLinkText);
+                            // Add Web Link.
+                            captionWebLink = base_url + "download-photo-web/" + captionId + "/";
+                            downloadWebLink = $('<a style="color: #CCC;"></a>');
+                            linkText = document.createTextNode("Web");
+                            downloadWebLink.append(linkText);
+                            downloadWebLink.title = "Download for Web";
+                            downloadWebLink.attr('href', captionWebLink);
+                            captionIdOutput.append(downloadWebLink);
+                            // Add Spacer Text.
+                            captionSpacerText = $('<span></span>');
+                            captionText = document.createTextNode(" - ");
+                            captionSpacerText.append(captionText);
+                            captionIdOutput.append(captionSpacerText);
+                            // Add Print Link.
+                            captionPrintLink = base_url + "download-photo-print/" + captionId + "/";
+                            downloadPrintLink = $('<a style="color: #CCC;"></a>');
+                            linkPrintText = document.createTextNode("Print");
+                            downloadPrintLink.append(linkPrintText);
+                            downloadPrintLink.title = "Download for Print";
+                            downloadPrintLink.attr('href', captionPrintLink);
+                            captionIdOutput.append(downloadPrintLink);
+                        }
+
+                        return captionEl;
+
+                        // Alternatively you can just pass back a string. However, any HTML
+                        // markup will be escaped
+
+                    }, captionAndToolbarAutoHideDelay:0, imageScaleMethod: "fitNoUpscale", captionAndToolbarFlipPosition: true, backButtonHideEnabled: true
+                },
+                instance = PhotoSwipe.attach( window.document.querySelectorAll('a.photoimg'), options );
+            }, false);
+        }
+    //}
+}(window, window.Code.PhotoSwipe));
+
diff --git a/Toolkit/Photos/login.php b/Toolkit/Photos/login.php
new file mode 100644 (file)
index 0000000..b30a681
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+require_once '../../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+$catid = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+$dbh = Toolkit_Database::getInstance();
+$authContainer = new Toolkit_Photos_AuthContainer($dbh);
+$photoAuth = new Toolkit_Photos_Auth(
+    $authContainer,
+    '',
+    false
+);
+$photoAuth->start();
+
+if (isset($_REQUEST['logout'])) {
+    $photoAuth->logout();
+    header('Location: ' . BASE_URL . 'index.php?catid=' . $catid);
+    exit;
+}
+
+if (!$photoAuth->checkAuth()) {
+    // see if the user exists but has expired
+    $userName = filter_var($_REQUEST['username'], FILTER_SANITIZE_STRING);
+    $passWord = filter_var($_REQUEST['password'], FILTER_SANITIZE_STRING);
+    $status = $photoAuth->getStatus();
+    if ($passWord && $userName) {
+        try {
+            $sql = "
+            SELECT id
+              FROM contacts.contact
+             WHERE email = :uname
+               AND media_pass = :pword
+               AND media = true
+               AND approved = true
+               AND expire < current_date";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':uname', $userName, PDO::PARAM_STR);
+            $stmt->bindParam(':pword', $passWord, PDO::PARAM_STR);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($row !== FALSE) {
+                $status = -4;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    //    Manually adjust the authentication status for empty credentials
+    if (empty($_POST['username']) || empty($_POST['password'])) {
+        $status = -3;
+    }
+    header('Location: '
+        . BASE_URL . 'index.php?catid=' . $catid . '&status=' . $status);
+    exit;
+} else {
+    header('Location: ' . BASE_URL . 'index.php?catid=' . $catid);
+}
diff --git a/Toolkit/Photos/photoProxy.php b/Toolkit/Photos/photoProxy.php
new file mode 100644 (file)
index 0000000..560bf58
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+require_once '../../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+$dbh = Toolkit_Database::getInstance();
+$catid = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+$authContainer = new Toolkit_Photos_AuthContainer($dbh);
+
+$photoAuth = new Auth(
+    $authContainer,
+    array(
+        'sessionName' => 'glmMedia',
+        'allowLogin' => true,
+        'dbFields' => array('id', 'fname', 'lname')
+    ),
+    '',
+    false
+);
+$photoAuth->setIdle(86400, true);
+$photoAuth->start();
+//var_dump($_REQUEST);
+
+// get image name from db
+$photoId = filter_var($_REQUEST['photo_id'], FILTER_VALIDATE_INT);
+if (!$photoId) {
+    die('Sorry no image found');
+}
+$isAuthorized = $photoAuth->checkAuth();
+try {
+    // here we'll need to also check to make sure that if they're not
+    // logged in they cannot get the exclusive photos.
+    $where
+        = (!$isAuthorized)
+        ? "AND exclusive <> true"
+        : '';
+    $sql = "
+    SELECT image
+      FROM photo
+     WHERE id = :id
+    $where";
+    $stmt = $dbh->prepare($sql);
+    $stmt->bindParam(':id', $photoId, PDO::PARAM_INT);
+    $stmt->execute();
+    $imageName = $stmt->fetchColumn();
+    if (!$imageName) {
+        return false;
+    }
+} catch (PDOException $e) {
+    Toolkit_Common::handleError($e);
+}
+//var_dump($imageName);
+//exit;
+if (!$isAuthorized) {
+    // use watermarked image style
+    $style = ($_REQUEST['type'] == 'print') ? 'downPrint': 'downWeb';
+    $fileUrlPath = FILE_SERVER_URL . IS_OWNER_ID .  "/{$style}/{$imageName}";
+
+    $ch = curl_init($fileUrlPath);
+    curl_setopt($ch, CURLOPT_TIMEOUT, 50);
+    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+    $imageResponse = curl_exec($ch);
+    if (!curl_errno($ch)) {
+        $bytes = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
+        header("Pragma: public");
+        header("Expires: 0");
+        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+        header("Content-Type: application/force-download");
+        header("Content-Type: application/octet-stream");
+        header("Content-Type: application/download");
+        header("Content-Disposition: attachment;filename=photo-" . $photoId.".jpeg");
+        header("Content-Transfer-Encoding: binary ");
+        curl_close($ch);
+        echo $imageResponse;
+    }
+
+    exit;
+} else {
+    // use style without watermarks
+    $style = ($_REQUEST['type'] == 'print') ? 'downPrintMedia': 'downWebMedia';
+    $fileUrlPath = FILE_SERVER_URL . IS_OWNER_ID .  "/{$style}/{$imageName}";
+    $ch = curl_init($fileUrlPath);
+    curl_setopt($ch, CURLOPT_TIMEOUT, 50);
+    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+    $imageResponse = curl_exec($ch);
+    header("Pragma: public");
+    header("Expires: 0");
+    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+    header("Content-Type: application/force-download");
+    header("Content-Type: application/octet-stream");
+    header("Content-Type: application/download");
+    header("Content-Disposition: attachment;filename=photo-" . $photoId.".jpeg");
+    header("Content-Transfer-Encoding: binary ");
+    curl_close($ch);
+    echo $imageResponse;
+    exit;
+}
diff --git a/Toolkit/Photos/photoSearch.php b/Toolkit/Photos/photoSearch.php
new file mode 100644 (file)
index 0000000..8d51338
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+require_once '../../setup.phtml';
+$jsonData = array();
+$mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+    Toolkit_Database::getInstance()
+);
+$photoSearch = filter_var($_REQUEST['photoName'], FILTER_SANITIZE_STRING);
+$pageId = filter_var($_REQUEST['pageId'], FILTER_VALIDATE_INT);
+$gallery = new Toolkit_Photos_Gallery(
+    Toolkit_Database::getInstance(),
+    new Toolkit_Photos_Display(
+         Toolkit_Database::getInstance()
+    )
+);
+$galleries = $gallery->getPageGalleries($pageId);
+
+$photos      = $mediaMapper->fetchPhotosByNameAndCats(
+    $photoSearch,
+    $galleries,
+    true
+);
+if ($photos) {
+    foreach ($photos as $photo) {
+        $jsonData[] = $photo->getTitle();
+    }
+}
+echo json_encode($jsonData);
diff --git a/Toolkit/Photos/setupNewPhotos.php b/Toolkit/Photos/setupNewPhotos.php
new file mode 100644 (file)
index 0000000..7e492a1
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+require_once '../../setup.phtml';
+
+$mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+    Toolkit_Database::getInstance()
+);
+
+$mediaMapper->fixAllPhoto2Cats();
diff --git a/Toolkit/Photos/templates/Admin/editCategory.html b/Toolkit/Photos/templates/Admin/editCategory.html
new file mode 100644 (file)
index 0000000..47219b2
--- /dev/null
@@ -0,0 +1,187 @@
+<div id="editCategorySection">
+    <header>
+        <hgroup>
+            <h1>
+               Edit Category
+            </h1>
+        </hgroup>
+    </header>
+    <form
+        id="editCategoryForm"
+        name="editCategoryForm"
+        action="{formAction:h}"
+        flexy:ignore="yes"
+        method="post">
+        {if:category}
+            <input type="hidden" name="id" value="{category.getId()}">
+            <input type="hidden" name="pos" value="{category.getPos()}">
+            {if:!mediaExclusive}
+                <input type="hidden" name="exclusive" value="0">
+            {end:}
+        {end:}
+        <table class="webform">
+            <tr>
+                <td class="labelcell">
+                    Name
+                </td>
+                <td class="fieldcell">
+                    {if:category}
+                    <input type="text" name="category" value="{category.getCategory()}">
+                    {else:}
+                    <input type="text" name="category">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell" id="pagesLabel">
+                    Pages for Display
+                </td>
+                <td class="fieldcell">
+                    <div id="displayWrapper">
+                    <div id="pages2PhotosDiv">
+                        {if:pages2Categories}
+                            <div class="pageLink" flexy:foreach="pages2Categories,page">
+                                <input type="hidden" name="pages[]" value="{page[page]}">
+                                {page[page_name]}
+                                <a href="#" class="ui-icon ui-icon-trash btnDelete">Delete</a>
+                            </div>
+                        {end:}
+                    </div>
+                    <div id="dialog-modal" title="Page Selector" style="display:none;">
+                        <div id="pages"></div>
+                    </div>
+                    <input type="submit" id="addPageTo" value="Add Pages">
+                    </div><!--/#displayWrapper-->
+                </td>
+            </tr>
+            <tr flexy:if="slideShow">
+                <td class="labelcell">
+                    SlideShow Option?
+                </td>
+                <td class="fieldcell">
+                    <input type="hidden" name="slideshow" value="0"><label>
+                    {if:category}
+                      {if:category.getSlideShow()}
+                      <input type="checkbox" name="slideshow" value="1" checked>
+                      {else:}
+                      <input type="checkbox" name="slideshow" value="1">
+                      {end:}
+                    {else:}
+                    <input type="checkbox" name="slideshow" value="1">
+                    {end:}Yes</label>
+                </td>
+            </tr>
+            <tr flexy:if="mediaExclusive">
+                <td class="labelcell">
+                    Media Exclusive?
+                </td>
+                <td class="fieldcell">
+                    <input type="hidden" name="exclusive" value="0"><label>
+                    {if:category}
+                      {if:category.getExclusive()}
+                      <input type="checkbox" name="exclusive" value="1" checked>
+                      {else:}
+                      <input type="checkbox" name="exclusive" value="1">
+                      {end:}
+                    {else:}
+                    <input type="checkbox" name="exclusive" value="1">
+                    {end:}Yes</label>
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2" align="center">
+                    <input id="categorySave" type="submit" value="Save">
+                    {if:category}
+                    <input id="categoryCancel" type="submit" value="Cancel" name="Cancel">
+                    <input id="categoryDelete" type="submit" value="Delete Category" name="Delete">
+                    {end:}
+                </td>
+            </tr>
+        </table>
+    </form>
+</div>
+<div id="categoryPhotos">
+    <form id="sortable">
+        <input type="hidden" name="ac" value="movePhotos">
+        <input type="hidden" name="categoryId" value="{category.getId()}">
+        <div
+            class="category-category-list"
+            flexy:foreach="photos,photo">
+            <input type="hidden" name="photos[]" value="{photo.getId()}">
+            {if:photo.getImage()}
+            <a href="{editUrl:h}{photo.getId()}">
+                <img width="120" height="120" src="{imgPath:h}{photo.getImage():h}">
+            </a>
+            {end:}
+            <header>
+                <hgroup>
+                    <h1>
+                        <a href="{editUrl:h}{photo.getId()}">
+                            {photo.getTitle()}
+                        </a>
+                    </h1>
+                </hgroup>
+            </header>
+        </div>
+    </form>
+</div>
+<script>
+    $(function(){
+        if ($("#categoryDelete").length) {
+            $("#categoryDelete").click(function(){
+                return confirm("This will Delete this Category.\nAre you sure?");
+            });
+        }
+        $('body').delegate('.btnDelete', 'click', function(){
+            $(this).parent().remove();
+        });
+        $("#sortable").sortable({
+            update: function() {
+                var inputs = $("#sortable").serialize();
+                $.get('./photos.php', inputs, console.log('positions saved'));
+            }
+        });
+        $("#addPageTo").click(function(){
+            var selectedPageId   = null;
+            var selectedPageName = null;
+            $("#dialog-modal").dialog({
+                height: 240,
+                width: 800,
+                modal: true,
+                buttons: {
+                    "Select Page": function() {
+                        if (selectedPageId != '') {
+                            // here is where it adds the page
+
+                            console.log('Selected PageId: ' + selectedPageId);
+                            console.log('Selected Pagename: ' + selectedPageName);
+                            $("#pages2PhotosDiv").append('<div class="pageLink">'
+                                    + '<input type="hidden" name="pages[]" value="'
+                                    + selectedPageId + '">'
+                                    + selectedPageName
+                                    + '<a href="#" class="ui-icon ui-icon-trash btnDelete">Delete</a>'
+                                    + '</div>');
+                            $(this).dialog("close");
+                        } else {
+                            alert("Select a Page");
+                        }
+                    },
+                    Cancel: function() {
+                        $(this).dialog("close");
+                    }
+                }
+            });
+            $("#pages").load('photos.php?ac=showPages', function(){
+                $('#demo1').columnview({
+                    preview:false,
+                    onchange: function(element) {
+                        selectedPageId = $(element).attr('data-page');
+                        selectedPageName = $(element).attr('data-name');
+                    }
+                });
+            });
+            return false;
+        });
+    });
+
+</script>
diff --git a/Toolkit/Photos/templates/Admin/editPhoto.html b/Toolkit/Photos/templates/Admin/editPhoto.html
new file mode 100644 (file)
index 0000000..77d6c69
--- /dev/null
@@ -0,0 +1,264 @@
+<style>
+    #editPhotoForm .webform {
+        margin: 10px 0;
+        width: 400px;
+        border: 1px solid #CCC;
+        border-radius: 5px;
+        padding: 10px 2%;
+    }
+    #editPhotoForm .webform td {
+        width: 100%;
+        float: left;
+        border: 0;
+        text-align: left;
+    }
+    #editPhotoForm .webform td input[type="text"], #editPhotoForm .webform td select {
+        display: block;
+        position: relative;
+        z-index: 99;
+        left: 0;
+        top: 0;
+        width: 100%;
+        margin: 0;
+        font-size: 14px;
+        font-weight: normal;
+        color: #333;
+        height: 27px;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        padding:.2em;/.3em;
+        padding-left: 6px;
+        background: #EFEFEF !important;
+        border: 1px solid #CCC;
+    }
+    ol.asmList {
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        width: 100%;
+        border: 0;
+        background: #EFEFEF;
+        margin: 0;
+        padding: 0;
+    }
+    ol.asmList .asmListItem {
+        border: none;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        border-bottom: 1px solid #CCC;
+        border-right: 1px solid #CCC;
+        border-left: 1px solid #CCC;
+        padding-bottom: 5px;
+        padding-left: 6px;
+        display: block;
+        min-height: 26px;
+        background: #FFFFFF;
+    }
+    #editPhotoForm .webform textarea {
+        resize: vertical;
+        width: 98.2%;
+        max-width: 98.2%;
+        min-width: 98.2%;
+    }
+    #photoSave {
+        width: 31.333333%;
+        margin: 0 2% 0 0;
+    }
+    #photoCancel {
+        width: 31.333333%;
+        margin: 0 2% 0 0;
+    }
+    #photoDelete {
+        width: 31.333333%;
+        margin: 0;
+    }
+</style>
+<div id="editPhotoSection">
+    <header>
+        <hgroup>
+            <h1>
+                Edit Photo in {categoryName}
+            </h1>
+        </hgroup>
+    </header>
+    <form
+        id="editPhotoForm"
+        name="editPhotoForm"
+        action="{formAction:h}"
+        flexy:ignore="yes"
+        method="post"
+        enctype="multipart/form-data">
+        {if:photo}
+            <input type="hidden" name="id" value="{photo.getId()}">
+            {if:!mediaExclusive}
+                <input type="hidden" name="exclusive" value="0">
+            {end:}
+        {end:}
+        <input type="hidden" name="catid" value="{_REQUEST[catid]}">
+        <table class="webform">
+            <tr>
+                <td class="labelcell">
+                    Name
+                </td>
+                <td class="fieldcell">
+                    {if:photo}
+                    <input type="text" name="title" value="{photo.getTitle()}">
+                    {else:}
+                    <input type="text" name="title">
+                    {end:}
+                </td>
+            </tr>
+            <tr flexy:if="mediaExclusive">
+                <td class="labelcell">
+                    Media Exclusive?
+                </td>
+                <td class="fieldcell">
+                    <input type="hidden" name="exclusive" value="0"><label>
+                        {if:photo}
+                        {if:photo.getExclusive()}
+                        <input type="checkbox" name="exclusive" value="1" checked>
+                        {else:}
+                        <input type="checkbox" name="exclusive" value="1">
+                        {end:}
+                        {else:}
+                        <input type="checkbox" name="exclusive" value="1">
+                        {end:}Yes</label>
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Download?
+                </td>
+                <td class="fieldcell">
+                    <input type="hidden" name="download" value="0"><label>
+                        {if:photo}
+                        {if:photo.getDownload()}
+                        <input type="checkbox" name="download" value="1" checked>
+                        {else:}
+                        <input type="checkbox" name="download" value="1">
+                        {end:}
+                        {else:}
+                        <input type="checkbox" name="download" value="1">
+                        {end:}Yes</label>
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Category
+                </td>
+                <td class="fieldcell">
+                    <select
+                        id="P2CSelect"
+                        name="photo2Categories[]"
+                        title="Select Categories"
+                        multiple>
+                        <?php
+                        foreach ($t->categories as $category) {
+                        echo '<option value="'.$category->getId().'"';
+                        if ($t->photo2Categories->offsetExists($category->getId())
+                        || $category->getId() == $_REQUEST['catid']
+                        ) {
+                        echo ' selected';
+                        }
+                        echo '>'.$category->getCategory().'</option>';
+                        }
+                        ?>
+                    </select>
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Description
+                </td>
+                <td class="fieldcell">
+                    {if:photo}
+                    <textarea name="description">{photo.getDescription()}</textarea>
+                    {else:}
+                    <textarea name="description"></textarea>
+                    {end:}
+                </td>
+            </tr>
+            {if:photo}
+            <tr>
+                <td class="labelcell">
+                    Current Photo
+                </td>
+                <td class="fieldcell">
+                    <input type="hidden" name="image" value="{photo.getImage()}">
+                    <img src="{imgPath:h}{photo.getImage()}">
+                </td>
+            </tr>
+            {end:}
+            <tr>
+                <td class="labelcell">
+                    New Photo
+                </td>
+                <td class="fieldcell">
+                    {if:photo}
+                    <input
+                        id="imageUpload"
+                        accept="image/*"
+                        type="file"
+                        name="image">
+                    {else:}
+                    <input
+                        id="imageUpload"
+                        accept="image/*"
+                        type="file"
+                        name="image"
+                        required>
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2">
+                    <input id="photoSave" type="submit" value="Save Photo">
+                    {if:photo}
+                    <input id="photoCancel" type="submit" value="Cancel" name="Cancel">
+                    <input id="photoDelete" type="submit" value="Delete Photo" name="Delete">
+                    {end:}
+                </td>
+            </tr>
+        </table>
+    </form>
+</div>
+<script>
+    $(function() {
+        $('INPUT[type="file"]').change(function() {
+            var ext = this.value.match(/(?:\.([^.]+))?$/)[1];
+            switch (ext.toLowerCase()) {
+                case 'jpg':
+                case 'jpeg':
+                case 'png':
+                case 'gif':
+                    $('#uploadButton').attr('disabled', false);
+                    break;
+                default:
+                    alert('This is not an allowed file type.');
+                    this.value = '';
+            }
+        });
+        $("#editPhotoForm").submit(function() {
+            var atLeastOneSeleted = false;
+            $("#P2CSelect option:selected").each(function() {
+                atLeastOneSeleted = true;
+            });
+            if (!atLeastOneSeleted) {
+                alert('You must select at least one Category');
+            }
+            return atLeastOneSeleted;
+        });
+        $("select[multiple]").asmSelect({
+            animate: true,
+            sortable: false,
+            highlight: true
+        });
+        if ($("#photoDelete").length) {
+            $("#photoDelete").click(function() {
+                return confirm("This will Delete this photo.\nAre you sure?");
+            });
+        }
+    });
+</script>
diff --git a/Toolkit/Photos/templates/Admin/editUser.html b/Toolkit/Photos/templates/Admin/editUser.html
new file mode 100644 (file)
index 0000000..128b30e
--- /dev/null
@@ -0,0 +1,233 @@
+<style>
+    form#editUserForm input[type="submit"] {
+        box-shadow: 1px 2px 2px grey;
+        border-radius: 10px;
+        background-color: #D6DFC3;
+        font-size: 16px;
+        font-weight: normal;
+        text-align: center;
+        margin: 0;
+        padding: 1px 5px 1px 5px;
+        width: auto;
+    }
+</style>
+<div>
+    <form
+        id="editUserForm"
+        name="editUserForm"
+        action="{formAction:h}"
+        flexy:ignore="yes"
+        method="post"
+        enctype="multipart/form-data">
+        <input type="hidden" name="userStatus" value="{_REQUEST[userStatus]}">
+        {if:user}
+        <input type="hidden" name="id" value="{user.getId()}">
+        {end:}
+        <table class="webform">
+            <tr>
+                <td class="labelcell">
+                    Status
+                </td>
+                <td class="fieldcell">
+
+                    {if:user}
+                    <label>
+                        {if:user.getApproved()}
+                        <input type="radio" name="status" value="approved" checked>
+                        {else:}
+                        <input type="radio" name="status" value="approved">
+                        {end:}
+                         Approved
+                    </label>
+                    <label>
+                        {if:user.getDenied()}
+                        <input type="radio" name="status" value="denied" checked>
+                        {else:}
+                        <input type="radio" name="status" value="denied">
+                        {end:}
+                        Denied
+                    </label>
+                    {else:}
+                    <label>
+                        <input type="radio" name="status" value="approved">
+                        Approved
+                    </label>
+                    <label>
+                        <input type="radio" name="status" value="denied">
+                        Denied
+                    </label>
+
+                    {end:}
+
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Company Name
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="company" value="{user.getCompany()}">
+                    {else:}
+                    <input name="company">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Title
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="title" value="{user.gettitle()}">
+                    {else:}
+                    <input name="title">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Expire Date
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input id="expire" name="expire" value="{user.getExpire()}">
+                    {else:}
+                    <input id="expire" name="expire">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Email Address
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="email" value="{user.getEmail()}" type="email" required>
+                    {else:}
+                    <input name="email" type="email" required>
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Password
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="media_pass" value="{user.getMediaPass()}" required>
+                    {else:}
+                    <input name="media_pass" required>
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    First Name
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="fname" value="{user.getFname()}" required>
+                    {else:}
+                    <input name="fname">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Last Name
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="lname" value="{user.getLname()}" required>
+                    {else:}
+                    <input name="lname" required>
+                    {end:}
+                </td>
+            </tr>
+
+            <tr>
+                <td class="labelcell">
+                    Phone
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="phone" value="{user.getPhone()}">
+                    {else:}
+                    <input name="phone">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Address 1
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="address" value="{user.getAddress()}">
+                    {else:}
+                    <input name="address">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Address2
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="address2" value="{user.getAddress2()}">
+                    {else:}
+                    <input name="address2">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    City
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="city" value="{user.getCity()}">
+                    {else:}
+                    <input name="city">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    State
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="state" value="{user.getState()}">
+                    {else:}
+                    <input name="state">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Zip
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="zip" value="{user.getZip()}">
+                    {else:}
+                    <input name="zip">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2" align="center">
+                    <input type="submit" value="Save">
+                </td>
+            </tr>
+        </table>
+    </form>
+</div>
+<script>
+    $(function(){
+        $('#expire').datepicker();
+    });
+</script>
diff --git a/Toolkit/Photos/templates/Admin/emailUser.tpl b/Toolkit/Photos/templates/Admin/emailUser.tpl
new file mode 100644 (file)
index 0000000..ac8da03
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+    <head>
+        <meta http-equiv="content-type" content="text/html;charset=utf-8">
+        <title>{title:h}</title>
+    </head>
+    <body>
+    <p>
+        <font size="4" face="arial, sans-serif">
+            <b>{subject:h}</b>
+        </font>
+    </p>
+    <table
+        cellspacing="0"
+        cellpadding="0"
+        bgcolor="#ffffff"
+        border="0"
+        width="400">
+        <tr>
+            <td>
+        {if:approved}
+            <p>Thank you for submitting your request for photos to the Traverse
+            City Convention & Visitors Bureau. Your request has been approved
+            and your login information is below. Please note that your access
+            to this site will be active for 2 weeks, beginning today. Should
+            you require additional time once your access period has expired, we
+            ask that you fill out a new request form.</p>
+            <br>
+            <p>Login: {user.getEmail()}</p>
+            <p>Password: {user.getMediaPass()}</p>
+            <br>
+            <p>Sincerely,</p>
+            <br>
+            <p>The Traverse City Tourism Marketing Team</p>
+        {end:}
+        {if:denied}
+            <p>We're sorry, but your request for media-quality photos did not meet
+            our criteria. However, we encourage you to choose from the wide range
+            of photos available on our general photo library. You do not require
+            a special login to view and download those photos.</p>
+            <br>
+            <p>Thanks for your interest in Traverse City!</p>
+            <p>The Traverse City Tourism Marketing Team</p>
+        {end:}
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <table
+                    cellspacing="0"
+                    cellpadding="10"
+                    border="0"
+                    width="400">
+                    <tr>
+                        <td bgcolor="#eeeeee">
+                            <font size="1" face="arial, sans-serif">
+                    To ensure the delivery of these e-mails to your inbox, please
+                     add {email_from:h} to your e-mail Address Book or Safe List.
+                            </font>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+
+    </table>
+        </body>
+    </html>
diff --git a/Toolkit/Photos/templates/Admin/index.html b/Toolkit/Photos/templates/Admin/index.html
new file mode 100644 (file)
index 0000000..4ce6dfe
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <title>{AppName}</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <link rel="stylesheet" href="main.css">
+        {styles:h}
+        <script>
+            function showMyError(e) {
+                console.log('Error: ' + e.message);
+                console.log('Line: ' + e.lineno);
+                console.log('URL: ' + e.filename);
+            }
+            addEventListener('error', showMyError);
+        </script>
+        {topScripts:h}
+    </head>
+    <body>
+        <header>
+            <hgroup>
+                <h1>{AppName}</h1>
+            </hgroup>
+            <flexy:include src="Admin/nav.html">
+                <flexy:include src="Admin/searchForm.html">
+        </header>
+        {content:h}
+    {bottomScripts:h}
+    </body>
+</html>
diff --git a/Toolkit/Photos/templates/Admin/listCategories.html b/Toolkit/Photos/templates/Admin/listCategories.html
new file mode 100644 (file)
index 0000000..d0e9508
--- /dev/null
@@ -0,0 +1,93 @@
+<style>
+    .photo-category-list {
+        position: relative;
+        box-shadow: 1px 2px 2px grey;
+        border-radius: 10px;
+        -moz-border-radius: 10px;
+        -webkit-border-radius: 10px;
+        display:block;
+        width: 120px;
+        float: left;
+        background: #D6DFC3;
+        margin: 5px;
+        padding: 10px;
+        height: 140px;
+    }
+    .photo-category-list h1 {
+        position: absolute;
+        bottom: 0;
+        left: 5px;
+        margin: 0;
+        padding: 0;
+        overflow: hidden;
+        width: 123px;
+    }
+    .photo-category-list h1 a {
+        color: #006BB4;
+        font-size: 12px;
+        text-decoration: none;
+        padding: 0;
+        margin: 0;
+        overflow: hidden;
+        white-space: nowrap;
+    }
+    .pCount {
+        text-shadow: 1px 1px grey;
+        position: absolute;
+        top: 0;
+        right: -10px;
+        padding: 1px 5px 1px 5px;
+        background-color: #250;
+        border-radius: 30px;
+        -moz-border-radius: 30px;
+        -webkit-border-radius: 30px;
+        box-shadow: 1px 2px 2px grey;
+        display:inline;
+        float: right;
+        color: white;
+        text-size: 18px;
+        font-weight: bold;
+    }
+    .photo-category-list img {
+        box-shadow: 1px 2px 2px grey;
+        border-radius: 10px;
+        -moz-border-radius: 10px;
+        -webkit-border-radius: 10px;
+        border: none;
+    }
+</style>
+<section>
+    <form id="sortable" method="get">
+        <input type="hidden" name="ac" value="moveCategories">
+        <div
+            class="photo-category-list"
+            flexy:foreach="categories,category">
+            <input type="hidden" name="catPhotos[]" value="{category.getId()}">
+            {if:category.getFirstPhoto()}
+            <a href="{editUrl:h}{category.getId()}">
+                <img width="120" height="120" src="{imgPath:h}{category.getFirstPhoto():h}">
+            </a>
+            {end:}
+            <div class="pCount">
+                {category.getTotalNumberOfPhotos():h}
+            </div>
+            <header>
+                <hgroup>
+                    <h1>
+                        <a href="{editUrl:h}{category.getId()}">
+                            {category.getCategory()}
+                        </a>
+                    </h1>
+                </hgroup>
+            </header>
+        </div>
+    </form>
+</section>
+<script>
+    $("#sortable").sortable({
+        update: function() {
+            var inputs = $("#sortable").serialize();
+            $.get('./photos.php', inputs, console.log('positions saved'));
+        }
+    });
+</script>
diff --git a/Toolkit/Photos/templates/Admin/listPhotos.html b/Toolkit/Photos/templates/Admin/listPhotos.html
new file mode 100644 (file)
index 0000000..7711ee8
--- /dev/null
@@ -0,0 +1,63 @@
+<style>
+    .category-category-list {
+        background: #EFEFEF;
+        border-radius: 5px;
+        display: block;
+        float: left;
+        height: 148px;
+        margin: 0 20px 20px 0;
+        padding: 10px;
+        position: relative;
+        width: 120px;
+        border: 1px solid #CCC;
+        -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         0px 0px 5px rgba(0, 0, 0, 0.2);
+    }
+    .category-category-list h1 {
+        bottom: 10px;
+        left: 10px;
+        margin: 0;
+        overflow: hidden;
+        padding: 0;
+        position: absolute;
+        width: 123px;
+    }
+    .category-category-list h1 a {
+        color: #006BB4;
+        font-size: 12px;
+        text-decoration: none;
+        padding: 0;
+        margin: 0;
+        overflow: hidden;
+        white-space: nowrap;
+    }
+    .category-category-list img {
+        border: medium none;
+        border-radius: 5px;
+        -webkit-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        border: 1px solid #CCC;
+    }
+</style>
+<div id="categoryPhotos">
+    <div
+        class="category-category-list"
+        flexy:foreach="photos,photo">
+        {if:photo.getImage()}
+        <a href="{editUrl:h}&amp;catid={photo.getCatid()}&amp;id={photo.getId()}">
+            <img width="120" height="120" src="{imgPath:h}{photo.getImage():h}">
+        </a>
+        {end:}
+        <header>
+            <hgroup>
+                <h1>
+                    <a href="{editUrl:h}&amp;catid={photo.getCatid()}&amp;id={photo.getId()}">
+                        {photo.getTitle()}
+                    </a>
+                </h1>
+            </hgroup>
+        </header>
+    </div>
+</div>
diff --git a/Toolkit/Photos/templates/Admin/listUsers.html b/Toolkit/Photos/templates/Admin/listUsers.html
new file mode 100644 (file)
index 0000000..1d0d90c
--- /dev/null
@@ -0,0 +1,26 @@
+<flexy:include src="Admin/userNav.html">
+<div>
+    <h2>{statusText}</h2>
+    <table id="admin-list-table">
+        <tr>
+            <th width="15">Function</th>
+            <th>Name</th>
+            <th width="50">Expires</th>
+            <th width="10">Approved</th>
+        </tr>
+        <tr flexy:foreach="users,user">
+            <td nowrap="nowrap" align="left">
+                <a href="{editUrl:h}{user.getId()}&userStatus={_REQUEST[userStatus]}">[Edit]</a>
+            </td>
+            <td align="left">{user.getFname()} {user.getLname()}</td>
+            <td align="right">{user.getExpire()}</td>
+            <td align="right">
+                {if:user.getApproved()}
+                Yes
+                {else:}
+                No
+                {end:}
+            </td>
+        </tr>
+    </table>
+</div>
diff --git a/Toolkit/Photos/templates/Admin/nav.html b/Toolkit/Photos/templates/Admin/nav.html
new file mode 100644 (file)
index 0000000..953174b
--- /dev/null
@@ -0,0 +1,17 @@
+<ul class="admin_nav">
+    <li flexy:if="addCategoryUrl">
+        <a href="{addCategoryUrl}">Add New Category</a>
+    </li>
+    <li flexy:if="addPhotoUrl">
+        <a href="{addPhotoUrl:h}">Add Photo</a>
+    </li>
+    <li flexy:if="addMultipleUrl" id="multiple-link">
+        <a href="{addMultipleUrl:h}">Add Multiple Photos</a>
+    </li>
+    <li flexy:if="listCategoryUrl">
+        <a href="{listCategoryUrl:h}">List Categories</a>
+    </li>
+    <li flexy:if="listUsersUrl">
+        <a href="{listUsersUrl:h}">List Users</a>
+    </li>
+</ul>
diff --git a/Toolkit/Photos/templates/Admin/searchForm.html b/Toolkit/Photos/templates/Admin/searchForm.html
new file mode 100644 (file)
index 0000000..6b5ac51
--- /dev/null
@@ -0,0 +1,76 @@
+<script>
+    $(function() {
+        $("#photoSearch").autocomplete({
+            source: function(request, response) {
+                $.ajax({
+                    url: "photos.php?ac=ajaxPhotoSearch",
+                    dataType: "json",
+                    data: {
+                        photoName: request.term
+                    },
+                    success: function(data) {
+                        response(data);
+                    }
+                });
+            },
+            minLength: 2
+        });
+    });
+</script>
+<section id="mediaGallerySearchForm">
+    <div id="categorySearchWrapper">
+    <form
+        method="get"
+        action="{categorySearchUrl:h}"
+        id="categoryAdminSearch"
+        name="photoAdminSearch"
+        flexy:ignore="yes">
+        <input type="hidden" name="ac" value="editCategory">
+        <select id="categorySearch" name="id">
+            <option value="">Search By Category</option>
+            <?php
+            if (!empty($t->categories)) {
+            foreach ($t->categories as $category) {
+            echo '<option value="' . $category->getId() . '"';
+            if ($category->getId() == $t->categoryId) {
+            echo ' selected';
+            }
+            echo '>'.$category->getCategory().'</option>';
+            }
+            }
+            ?>
+        </select>
+        <input
+            type="submit"
+            value="Search">
+    </form>
+    <form
+        method="get"
+        action="{photoSearchUrl:h}"
+        id="photoAdminSearch"
+        name="photoAdminSearch"
+        flexy:ignore="yes">
+        <input
+            type="hidden"
+            name="ac"
+            value="searchPhotos">
+        {if:_REQUEST[photoName]}
+        <input
+            type="search"
+            id="photoSearch"
+            name="photoName"
+            value="{_REQUEST[photoName]}"
+            placeholder="Find Photo By Title">
+        {else:}
+        <input
+            type="search"
+            id="photoSearch"
+            name="photoName"
+            placeholder="Find Photo By Title">
+        {end:}
+        <input
+            type="submit"
+            value="Search">
+    </form>
+    </div><!--/#categorySearchWrapper-->
+</section>
diff --git a/Toolkit/Photos/templates/Admin/userNav.html b/Toolkit/Photos/templates/Admin/userNav.html
new file mode 100644 (file)
index 0000000..721ba30
--- /dev/null
@@ -0,0 +1,14 @@
+<ul class="admin_nav">
+    <li>
+        <a href="{userAdminUrl:h}?ac=listUsers">Pending</a>
+    </li>
+    <li>
+        <a href="{userAdminUrl:h}?ac=listUsers&amp;userStatus=approved">Approved</a>
+    </li>
+    <li>
+        <a href="{userAdminUrl:h}?ac=listUsers&amp;userStatus=denied">Denied</a>
+    </li>
+    <li>
+        <a href="{userAdminUrl:h}?ac=listUsers&amp;userStatus=expired">Expired</a>
+    </li>
+</ul>
diff --git a/Toolkit/Photos/templates/photoCats.html b/Toolkit/Photos/templates/photoCats.html
new file mode 100755 (executable)
index 0000000..a01d715
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+if ($t->catPhotos) {
+    foreach ($t->catPhotos as $category) {
+        ?>
+        <li>
+            <a
+                href="<?php echo $t->photoUrl;?><?php echo $category->getId();?>">
+                <img
+                    title="<?php echo $category->getCategory()?>"
+                    src="<?php echo $t->photoUrlSmall.$category->getFirstPhoto()?>">
+            </a>
+            <span class="photoHead"><?php echo $category->getCategory();?></span>
+        </li>
+        <?php
+    }
+}
+?>
\ No newline at end of file
diff --git a/Toolkit/Photos/templates/photoGalleryWrapper.html b/Toolkit/Photos/templates/photoGalleryWrapper.html
new file mode 100644 (file)
index 0000000..e48bb57
--- /dev/null
@@ -0,0 +1,336 @@
+<style>
+#media-photo-gallery {
+    font-size:12px;
+}
+#gallery {
+    clear:both;
+}
+.gallery {
+    text-align: center;
+    list-style: none outside none;
+    padding: 10px 0 0 0;
+}
+.gallery:after {
+    clear: both;
+    content: ".";
+    display: block;
+    height: 0;
+    visibility: hidden;
+}
+.gallery li {
+    background: none repeat scroll 0 0 #F5F3E5;
+    float: left;
+    margin: 0 2% 26px 0;
+    overflow: hidden;
+    position: relative;
+    width: 23%
+}
+/*.gallery li:nth-child(4n+1) {
+    clear: both;
+    margin: 0 16px 26px 0;
+}
+.gallery li:nth-child(4n+2) {
+    margin: 0 16px 26px 0;
+}
+.gallery li:nth-child(4n+3) {
+    margin: 0 16px 26px 0;
+}
+.gallery li:nth-child(4n+4) {
+    margin: 0 0 26px 0;
+}*/
+.gallery li img {
+    width: 100%;
+    border: 1px solid #2797AB;
+    display: block;
+    height: auto;/*width: 143px;*/
+    -webkit-box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box;
+}
+.gallery li:hover img {
+    border: 1px solid #B8C728;
+}
+.gallery .ui-content {
+    overflow: hidden;
+}
+/*#inside #main {
+    width: 720px;
+}*/
+#staticBanners {
+    /*display:none;*/
+}
+.media-gallery-download {
+    font-size: 10px;
+    min-height: 22px;
+}
+#media-photo-gallery div {
+    clear:none;
+    font-size: 1em;
+    height: auto;
+    margin: 0;
+    padding: 0;
+}
+#media-photo-gallery div p {
+    display: block;
+    color: black;
+    padding: 5px;
+    text-shadow: none;
+    font-weight: normal;
+}
+#media-photo-gallery div a {
+}
+#filter_wrapper {
+    overflow: hidden;
+}
+#cat_filter_wrapper {
+    float: left;
+    height: 56px;
+}
+#photoCategoryLabel {
+    float: left;
+}
+#photoCategoryId {
+    float: left;
+    clear: left;
+    width: 180px;
+    height: 24px;
+    border: 1px solid #ABADB3;
+    box-sizing: border-box;
+    padding:.2em;/.3em;
+}
+#photoCategoryId option {
+    padding: 3px;
+}
+#media-login #username {
+    float: left;
+    width: 100px;
+    height: 10px;
+    padding: 5px;
+    margin: 0 5px 0 0;
+    border: 1px solid #ABADB3;
+}
+#media-login #password {
+    float: left;
+    width: 100px;
+    height: 10px;
+    padding: 5px;
+    margin: 0 5px 0 0;
+    border: 1px solid #ABADB3;
+}
+#media-login #submit {
+    display: block;
+    float: left;
+    width: 24px;
+    height: 22px;
+    cursor: pointer;
+    background: url(<?php echo $t->base_url;?>assets/play.gif) 50% 50% no-repeat;
+    border: 1px solid #ABADB3;
+    -bracket-:hack(;
+        height: 22px;
+    );
+}
+.photoHead {
+    display: block;
+    text-align: center;
+    min-height: 38px;
+    margin: 0 !important;
+    padding: 5px;
+    background: none;
+}
+.captionWrapper {
+    display: block;
+    width: 100%;
+    overflow: hidden;
+}
+.captionTitleWrapper {
+    display: block;
+    width: 50%;
+    float: left;
+    font-size: 18px;
+    font-size: 1.8rem;
+    font-weight: bold;
+    color: #FFF;
+    text-align: left;
+    padding: 10px 20px;
+    margin: 0;
+}
+.captionDescWrapper {
+    display: block;
+    width: 50%;
+    float: left;
+    clear: left;
+    font-size: 14px;
+    font-size: 1.4rem;
+    color: #FFF;
+    text-align: left;
+    padding: 0 20px 10px 20px;
+    margin: 0;
+}
+.download_wrapper {
+    display: block;
+    position: absolute;
+    z-index: 99;
+    top: 10px;
+    right: 20px;
+    width: 30%;
+    font-size: 14px;
+    font-size: 1.4rem;
+    color: #FFF;
+    text-align: left;
+    padding: 6px 0 10px 20px;
+    margin: 0;
+    text-align: right;
+}
+.download_wrapper a {
+    color: #CCC;
+}
+.download_wrapper a:hover {
+    color: #FFF;
+}
+.ps-caption-content {
+    width: 100%;
+    overflow: hidden;
+    position: relative;
+}
+</style>
+<flexy:toJavascript flexy:prefix="" base_url="base_url" ></flexy:toJavascript>
+<flexy:toJavascript flexy:prefix="" pageId="pageId" ></flexy:toJavascript>
+<div id="media-photo-gallery">
+    <div id="filter_wrapper">
+        <div id="cat_filter_wrapper">
+            <div class="name_wrapper" flexy:if="photoNameSearch">
+            <h2>Filter By Name</h2>
+            <form
+                method="get"
+                id="photoGallery"
+                action="{photoSearchFormUrl:h}"
+                flexy:ignore="yes">
+                <input type="hidden" name="catid" value="{_REQUEST[catid]}">
+                <label id="photoNameLabel"></label>
+                <input
+                    type="search"
+                    name="photo_name"
+                    id="photoSearch"
+                    value="{_REQUEST[photo_name]}"
+                    placeholder="Name">
+                <input type="submit" value="Search">
+            </form>
+            </div>
+            <?php if (count($t->categories) > 1) {?>
+            <div class="cat_wrapper">
+            <h2>Filter By Category</h2>
+            <form
+                method="get"
+                id="photoGallery"
+                action="{photoSearchFormUrl:h}"
+                flexy:ignore="yes">
+                <input type="hidden" name="catid" value="{_REQUEST[catid]}">
+                <label id="photoCategoryLabel"></label><!--/Filter By Category-->
+                <select id="photoCategoryId" name="photoCategoryId">
+                    <option value="">Show Categories</option>
+                    <?php foreach ($t->categories as $category) {
+                        echo '<option value="'.$category['id'].'"';
+                        if ($_REQUEST['photoCategoryId'] == $category['id']) {
+                            echo ' selected';
+                        }
+                        echo '>'.$category['category'].'</option>';
+                    }?>
+                </select>
+            </form>
+            </div>
+            <?php }?>
+
+        </div><!--/#cat_filter_wrapper-->
+        <div style="float:right;" flexy:if="mediaExclusive">
+            {if:isLoggedIn}
+            You're logged in! <a href="{logoutUrl:h}">Logout</a>
+            {else:}
+            <label>Media Login</label>
+            <form
+                id="media-login"
+                method="post"
+                action="{loginUrl:h}"
+                flexy:ignore="yes">
+                <input
+                    type="hidden"
+                    name="catid"
+                    value="{_REQUEST[catid]}">
+                <input
+                    id="username"
+                    name="username"
+                    placeholder="Username"
+                    size="10"
+                    required>
+                <input
+                    id="password"
+                    type="password"
+                    name="password"
+                    placeholder="Password"
+                    size="10"
+                    required>
+                <input
+                    id="submit"
+                    type="submit"
+                    value="">
+            </form>
+            {if:failedStatus}
+            <div style="clear:both;width:250px;color:red;text-wrap: normal;">
+                {failedStatus:h}
+            </div>
+            {end:}
+        {end:}
+        </div>
+    </div>
+    <div class="gallery_wrapper">
+        <div flexy:if="expired">
+            <p>We’re sorry, but your access to this page has expired. If you
+            require additional time to select photos, please fill out and
+            submit a new
+            <a href="{mediaGalleryRequestFormUrl:h}">photo request form</a>.
+            If approved, you will receive your new login information by email
+            within 1 to 2 days.</p>
+
+            <p>Sincerely,</p>
+
+            <p>The Traverse City Tourism Marketing Team</p>
+        </div>
+        <h2 flexy:if="photoCategoryName">{photoCategoryName}</h2>
+        <ul id="Gallery" class="gallery">
+            {if:photos}
+            <flexy:include src="photos.html">
+            {end:}
+            {if:catPhotos}
+            <flexy:include src="photoCats.html">
+            {end:}
+        </ul>
+        {if:mediaExclusive}
+        <div flexy:if="!isLoggedIn">
+            <p>Are you a member of the media and looking for additional or
+                specific photos?
+        <a href="{mediaGalleryRequestFormUrl:h}">Click Here</a>.</p>
+        </div>
+        {end:}
+    </div>
+</div>
+{if:photoNameSearch}
+<script>
+    $(function() {
+        $("#photoSearch").autocomplete({
+            source: function(request, response) {
+                $.ajax({
+                    url: "photoSearch",
+                    dataType: "json",
+                    data: {
+                        photoName: request.term,
+                        pageId: pageId
+                    },
+                    success: function(data) {
+                        response(data);
+                    }
+                });
+            },
+            minLength: 2
+        });
+    });
+</script>
+{end:}
\ No newline at end of file
diff --git a/Toolkit/Photos/templates/photos.html b/Toolkit/Photos/templates/photos.html
new file mode 100755 (executable)
index 0000000..90e3171
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+if ($t->photos) {
+    foreach ($t->photos as $photo) {
+        ?>
+        <li>
+            <a data-download="<?php if($photo->getDownload()){echo '1';}else{echo '0';}?>" data-photoid="<?php echo $photo->getId()?>" data-download="<?php echo $photo->getDownload()?>" rel="<?php echo htmlspecialchars($photo->getDescription())?>" title="<?php echo htmlspecialchars($photo->getTitle())?>" class="photoimg" href="<?php echo $t->photoUrlLarge.$photo->getImage()?>">
+                <img title="<?php echo htmlspecialchars($photo->getTitle())?>" alt="<?php echo htmlspecialchars($photo->getDescription())?>" src="<?php echo $t->photoUrlSmall.$photo->getImage()?>">
+                <?php if ($photo->getTitle()) {?>
+                <span class="photoHead">
+                <?php
+                    $tempTitle = $photo->getTitle();
+                    if(strlen($tempTitle) > 50) {
+                        $tempTitle = substr($tempTitle, 0, 47);
+                        $tempTitle .= "...";
+                    }
+                    echo $tempTitle;
+                    //echo $photo->getTitle();
+                ?>
+                </span>
+                <?php } ?>
+            </a>
+            <?php if ($photo->getDownload()) {?>
+                <span class="media-gallery-download">
+                    Download for
+                    <a href="<?php echo $t->photoDownWeb.$photo->getId()?>/">Web</a>&nbsp;-&nbsp;<a href="<?php echo $t->photoDownPrint.$photo->getId()?>/">Print</a>
+                </span>
+            <?php } ?>
+        </li>
+    <?php }
+}
+?>
diff --git a/admin/photos.php b/admin/photos.php
new file mode 100644 (file)
index 0000000..9ee553f
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+require_once '../setup.phtml';
+// Registry Object to hold global objects and setting for application
+$registry = new Toolkit_Registry;
+// create a Zend Config Object and store into Registry
+$config = new Zend_Config_Ini(
+    BASE . 'Toolkit/Photos/application.ini',
+    strtolower($_ENV['GLM_HOST_ID'])
+);
+$registry->config = $config;
+$registry->page   = MEDIA_BASE_URL . 'admin/photos.php';
+$appConfig        = $config->application->toArray();
+$registry->dbh    = Toolkit_Database::getInstance();
+$registry->logger = Toolkit_Logger::getLogger();
+$registry->router = new Toolkit_Router($registry);
+$registry->router->setPath($appConfig['path']);
+$registry->router->setApplication($appConfig['application']);
+
+$html = $registry->router->loader();
+
+echo $html;
\ No newline at end of file