From fcc076446c61205ac36cfea7dd3b4e1e657df5dc Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Fri, 26 Aug 2011 09:45:27 -0500 Subject: [PATCH 01/50] Page: Added new 'Navigation Title' field to page so the nav title can be different from the page title itself --- app/Module/Page/Entity.php | 10 ++++++++++ .../Module/Navigation/views/indexAction.html.php | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/Module/Page/Entity.php b/app/Module/Page/Entity.php index d453190..85b045a 100755 --- a/app/Module/Page/Entity.php +++ b/app/Module/Page/Entity.php @@ -28,6 +28,7 @@ public static function fields() { 'site_id' => array('type' => 'int', 'default' => 0, 'unique' => 'site_page'), 'parent_id' => array('type' => 'int', 'index' => true, 'default' => 0), 'title' => array('type' => 'string', 'required' => true), + 'navigation_title' => array('type' => 'string'), 'url' => array('type' => 'string', 'required' => true, 'unique' => 'site_page'), 'meta_keywords' => array('type' => 'string'), 'meta_description' => array('type' => 'string'), @@ -149,4 +150,13 @@ public function isVisible() { return ($this->visibility == self::VISIBILITY_VISIBLE); } + + + /** + * Navigation title + */ + public function navigationTitle() + { + return $this->navigation_title ? $this->navigation_title : $this->title; + } } \ No newline at end of file diff --git a/app/www/content/Module/Navigation/views/indexAction.html.php b/app/www/content/Module/Navigation/views/indexAction.html.php index 711509e..9973ece 100755 --- a/app/www/content/Module/Navigation/views/indexAction.html.php +++ b/app/www/content/Module/Navigation/views/indexAction.html.php @@ -3,7 +3,7 @@ $tree = $view->generic('treeview'); $tree->data($pages) ->item(function($page) use($view) { - return '' . $page->title . ''; + return '' . $page->navigationTitle() . ''; }) ->itemChildren(function($page) { return $page->children; From 1e29c765e32af3625b559c014d2b7bad427931eb Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Fri, 26 Aug 2011 09:54:32 -0500 Subject: [PATCH 02/50] Page Entity: passing page title through htmlentities to help reduce template parsing errors --- app/Module/Page/Entity.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Module/Page/Entity.php b/app/Module/Page/Entity.php index 85b045a..9ef3217 100755 --- a/app/Module/Page/Entity.php +++ b/app/Module/Page/Entity.php @@ -72,6 +72,7 @@ public function beforeSave(Spot\Mapper $mapper) { $this->__set('site_id', \Kernel()->config('cms.site.id')); $this->__set('url', self::formatPageUrl($this->__get('url'))); + $this->__set('title', htmlentities($this->__get('title'), ENT_QUOTES, "UTF-8")); return parent::beforeSave($mapper); } From 64fc3357c0f8d29e1d821045c887063f839d5ce5 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Tue, 30 Aug 2011 11:00:44 -0500 Subject: [PATCH 03/50] Page Entity: Updated code for 'getPageTemplates' to include both global and site template directories and provide a default fallback --- app/Module/Page/Entity.php | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/app/Module/Page/Entity.php b/app/Module/Page/Entity.php index 9ef3217..bc3c3dd 100755 --- a/app/Module/Page/Entity.php +++ b/app/Module/Page/Entity.php @@ -104,14 +104,29 @@ public static function getPageTemplates() // Build array of theme directories to look in $tplDir = $kernel->config('cms.path.themes'); - $tplDirs = array(); + $themeDirs = array(); foreach($site->themes() as $theme) { - $tplDirs[] = rtrim($tplDir, '/') . '/' . $theme . '/'; + // Global themes folder + $themeDirs[] = rtrim($tplDir, '/') . '/' . $theme . '/'; + // Site themes folder + $themeDirs[] = rtrim($site->dirThemes(), '/') . '/' . $theme . '/'; + } + + // Ensure directories exist before giving them to Finder + foreach($themeDirs as $ti => $themeDir) { + if(!is_dir($themeDir)) { + unset($themeDirs[$ti]); + } + } + + // Ensure there is at least ONE directory if no others exist (fallback to default template) + if(0 == count($themeDirs)) { + $themeDirs[] = rtrim($tplDir, '/') . '/default/'; } // Find template files $templates = $kernel->finder() - ->in($tplDirs) + ->in($themeDirs) ->files() ->name('*.html.tpl') ->depth(0) @@ -121,6 +136,7 @@ public static function getPageTemplates() foreach($templates as $tpl) { // Remove path info $tplRelPath = str_replace($tplDir, '', $tpl->getPathname()); + $tplRelPath = str_replace($site->dirThemes(), '', $tpl->getPathname()); // Remove extensions $tplRelPath = str_replace('.html.tpl', '', $tplRelPath); // Set in array to use From 3c0ded1eaf74bbe306e63863a0d067bc95a9311d Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Tue, 15 Nov 2011 00:59:15 -0600 Subject: [PATCH 04/50] Enabled first site auto-creation --- app/Module/Site/Domain.php | 10 ++++++++-- app/Module/Site/Entity.php | 6 +++--- app/Plugin/Stackbox/Plugin.php | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/app/Module/Site/Domain.php b/app/Module/Site/Domain.php index fbd1e18..3673f03 100644 --- a/app/Module/Site/Domain.php +++ b/app/Module/Site/Domain.php @@ -1,6 +1,6 @@ array('type' => 'string', 'required' => true), 'type' => array('type' => 'int', 'length' => 1, 'default' => self::TYPE_NORMAL), 'redirect_url' => array('type' => 'string'), - 'date_created' => array('type' => 'datetime') + 'date_created' => array('type' => 'datetime', 'default' => new \DateTime()) ); } + + + /** + * Overridden without calling parent so base Entity will not try to set 'site_id' automatically + */ + public function beforeSave(Spot\Mapper $mapper) {} } \ No newline at end of file diff --git a/app/Module/Site/Entity.php b/app/Module/Site/Entity.php index 11afbe9..2120e7e 100644 --- a/app/Module/Site/Entity.php +++ b/app/Module/Site/Entity.php @@ -24,7 +24,7 @@ public static function fields() { 'title' => array('type' => 'string', 'required' => true), 'theme' => array('type' => 'string'), 'status' => array('type' => 'int', 'length' => 1, 'default' => self::STATUS_ACTIVE), - 'date_created' => array('type' => 'datetime'), + 'date_created' => array('type' => 'datetime', 'default' => new \DateTime()), 'date_modified' => array('type' => 'datetime') ); } @@ -51,8 +51,8 @@ public static function relations() { */ public function beforeSave(Spot\Mapper $mapper) { - $this->__set('shortname', strtolower(preg_replace("/[^a-zA-Z0-9]/g", "", $this->__get('shortname')))); - return parent::beforeSave($mapper); + $this->__set('shortname', strtolower(preg_replace("/[^a-zA-Z0-9]/", "", $this->__get('shortname')))); + $this->__set('date_modified', new \DateTime()); } diff --git a/app/Plugin/Stackbox/Plugin.php b/app/Plugin/Stackbox/Plugin.php index 5406f85..1ee7151 100644 --- a/app/Plugin/Stackbox/Plugin.php +++ b/app/Plugin/Stackbox/Plugin.php @@ -27,8 +27,7 @@ public function __construct(Alloy\Kernel $kernel) $cfg = $kernel->config(); $app = $cfg['app']; - // @todo Determine which site HOST is and set site_id - // @todo Set file paths with current site_id + // Hostname lookup $hostname = $kernel->request()->server('HTTP_HOST'); // Get site by hostname @@ -41,6 +40,37 @@ public function __construct(Alloy\Kernel $kernel) exit(); } + // Site not found - setup first site automaticlly on first viewed hostname + if(!$site) { + // Count sites + $siteCount = $siteMapper->all('Module\Site\Entity')->count(); + if(0 == $siteCount) { + // Add first site with current hostname + $newSite = $siteMapper->create('Module\Site\Entity', array( + 'reseller_id' => 0, + 'shortname' => $hostname, + 'title' => 'StackboxCMS Website', + 'theme' => 'default', + 'status' => \Module\Site\Entity::STATUS_ACTIVE, + 'date' + )); + $siteSaved = $siteMapper->save($newSite); + + // Add site domain record + if($siteSaved) { + $siteDomain = $siteMapper->create('Module\Site\Domain', array( + 'site_id' => $newSite->id, + 'domain' => $hostname, + 'type' => \Module\Site\Domain::TYPE_NORMAL + )); + $siteMapper->save($siteDomain); + + // Set site + $site = $newSite; + } + } + } + // Site not found - no hostname match if(!$site) { throw new \Stackbox\Exception\SiteNotFound("Site " . $hostname . " not found."); From 968c384cdf8ec7e6f0d946261d5c728583550a60 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sun, 27 Nov 2011 15:06:28 -0600 Subject: [PATCH 05/50] Fixed issue with AJAX requests in modal windows to allow non-POST requests --- app/www/assets/admin/scripts/cms_admin.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/www/assets/admin/scripts/cms_admin.js b/app/www/assets/admin/scripts/cms_admin.js index 1e10183..b413686 100755 --- a/app/www/assets/admin/scripts/cms_admin.js +++ b/app/www/assets/admin/scripts/cms_admin.js @@ -107,11 +107,17 @@ cms.modal = (function (cms, $) { tData[field.name] = field.value; }); + var fMethod = tForm.attr('method'); $.ajax({ - type: "POST", + type: fMethod ? fMethod.toUpperCase() : "POST", url: tForm.attr('action'), data: tForm.serialize(), success: function(data, textStatus, req) { + if("GET" === fMethod.toUpperCase()) { + m.content(req.responseText); + return; + } + nData = $(data); if(tData._method == 'DELETE') { if(tData.item_id) { From f355ab19809c114e0da04bfe7fb26b76a4df9b5e Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sun, 27 Nov 2011 15:07:42 -0600 Subject: [PATCH 06/50] Page Module: Fixed a few minor issues --- app/Module/Page/Controller.php | 5 +++-- app/Module/Page/Mapper.php | 36 ++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/app/Module/Page/Controller.php b/app/Module/Page/Controller.php index 2dfcb7b..4db5b57 100755 --- a/app/Module/Page/Controller.php +++ b/app/Module/Page/Controller.php @@ -40,11 +40,12 @@ public function viewUrl($pageUrl) if($pageUrl == '/') { // Create new page for the homepage automatically if it does not exist $page = $pageMapper->get('Module\Page\Entity'); - $page->site_id = $kernel->config('cms.site.id'); + $page->site_id = $site->id; $page->parent_id = 0; $page->title = "Home"; $page->url = $pageUrl; - $page->date_created = $pageMapper->connection('Module\Page\Entity')->dateTime(); + $page->template = $site->theme . '/index'; + $page->date_created = new \DateTime(); $page->date_modified = $page->date_created; if(!$pageMapper->save($page)) { throw new Alloy\Exception("Unable to automatically create homepage at '" . $pageUrl . "' - Please check data source permissions"); diff --git a/app/Module/Page/Mapper.php b/app/Module/Page/Mapper.php index 6cef40f..2e27150 100755 --- a/app/Module/Page/Mapper.php +++ b/app/Module/Page/Mapper.php @@ -59,11 +59,6 @@ public function pageTree(Entity $currentPage, $startPage = null) // Step 1: Build index-based array of page IDs to their respective page objects foreach($pages as $page) { - // Mark page as 'current page' - if($page->id == $currentPage->id) { - $page->is_in_path = true; - } - // Add to index by ID self::$_pageIndex[$page->id] = $page; } @@ -73,25 +68,32 @@ public function pageTree(Entity $currentPage, $startPage = null) if(isset(self::$_pageIndex[$page->parent_id])) { // Set page children self::$_pageIndex[$page->parent_id]->children[] = $page; - - // Add to path to current page - self::$_pageIndex[$page->id]->id_path = trim(self::$_pageIndex[$page->parent_id]->id_path . '.' . $page->parent_id, '.'); } } - // Mark all pages in path of current page - $currentPage = self::$_pageIndex[$currentPage->id]; - if($currentPage->id_path) { - foreach(explode('.', $currentPage->id_path) as $pId) { - self::$_pageIndex[$pId]->is_in_path = true; - } - } + // Step 3: Start at $currentPage and step back up through parents marking them in the current path + $this->markPagesInPathFromPage(self::$_pageIndex[$currentPage->id]); } return $this->buildPageTree(self::$_pageIndex, $startPageId); } + /** + * Mark all parent pages from first given page as in the current path + */ + protected function markPagesInPathFromPage(Entity $page) { + // Mark page in path + self::$_pageIndex[$page->id]->is_in_path = true; + + // Mark parent page in path recursively + if($page->parent_id) { + $fn = __FUNCTION__; + $this->$fn(self::$_pageIndex[$page->parent_id]); + } + } + + /** * Build a page tree from index-based array */ @@ -117,8 +119,8 @@ private function buildPageTree($index, $parentId = 0, $level = 0) $hasChildren = isset($index[$p->id]); if($hasChildren) { - $func = __FUNCTION__; - $p->children = $this->$func($index, $p->id, $level+1); + $fn = __FUNCTION__; + $p->children = $this->$fn($index, $p->id, $level+1); } // Add page to item array From e9c83af5229711837fff00add5c7a14806eae8ff Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Mon, 28 Nov 2011 17:22:31 -0600 Subject: [PATCH 07/50] Updates for Adminbar UI, Page, and Site-specific modules * Added Twitter bootstrap styles to adminbar and settings tabs * Re-structured the way Site-specific modules are defined and load new way uses 'Module\Site' namespace instead of 'Module' namespace and allows site-specific modules to extend global modules without naming conflicts. Site-specific modules also now install in their own namespaced database table to avoid global conflicts * Added base Site-specific ControllerAbstract to extend from * Added base Site-specific EntityAbstract to extend from * Updated base ControllerAbstract 'formView' to use self::ENTITY if defined, fall back on the old naming scheme, and take a custom class name if given --- app/Module/Page/Controller.php | 43 +- .../Settings/views/editlistAction.html.php | 63 +- app/Module/Page/views/_adminBar.html.php | 47 +- app/Module/User/Controller.php | 2 +- app/Module/User/Session/Controller.php | 2 +- .../Stackbox/Module/ControllerAbstract.php | 11 +- .../lib/Stackbox/Module/EntityAbstract.php | 2 +- .../Module/Site/ControllerAbstract.php | 29 + .../Stackbox/Module/Site/EntityAbstract.php | 20 + app/www/assets/admin/scripts/cms_admin.js | 143 ++++- app/www/assets/admin/styles/cms_admin.css | 578 +++++++++++++++--- 11 files changed, 822 insertions(+), 118 deletions(-) create mode 100644 app/Plugin/Stackbox/lib/Stackbox/Module/Site/ControllerAbstract.php create mode 100644 app/Plugin/Stackbox/lib/Stackbox/Module/Site/EntityAbstract.php diff --git a/app/Module/Page/Controller.php b/app/Module/Page/Controller.php index 4db5b57..d5e4948 100755 --- a/app/Module/Page/Controller.php +++ b/app/Module/Page/Controller.php @@ -82,11 +82,15 @@ public function viewUrl($pageUrl) } // Load requested module - $moduleObject = $kernel->module($moduleName); + // Try 'Site' module first + $moduleObject = $kernel->module('Site_' . $moduleName); + if(false === $moduleObject) { + $moduleObject = $kernel->module($moduleName); + } // Ensure module is a placeable module on the page if(!($moduleObject instanceof Stackbox\Module\ControllerAbstract)) { - throw new Alloy\Exception("Module '" . $moduleName . "' must extend 'Stackbox\Module\ControllerAbstract' to be a placeable Stackbox module"); + throw new Alloy\Exception("Module '" . $moduleName . "' must extend 'Stackbox\Module\ControllerAbstract' or 'Stackbox\Site\Module\ControllerAbstract' to be a placeable Stackbox module"); } // Ensure user can execute requested action @@ -173,8 +177,14 @@ public function viewUrl($pageUrl) $modules->orWhere(array('site_id' => $kernel->config('cms.site.id'), 'region' => $template->regionsType('global'))); } foreach($modules as $module) { - // Loop over modules, building content for each region - $moduleResponse = $kernel->dispatch($module->name, 'indexAction', array($request, $page, $module)); + // Try 'Site' module first, then normal module ('Site' modules override global ones) + $moduleObject = $kernel->module('Site_' . $module->name); + if(false === $moduleObject) { + $moduleObject = $kernel->module($module->name); + } + + // Dispatch to module's 'indexAction' to display module content on page + $moduleResponse = $kernel->dispatch($moduleObject, 'indexAction', array($request, $page, $module)); if(!isset($regionModules[$module->region]) || !is_array($regionModules[$module->region])) { $regionModules[$module->region] = array(); } @@ -430,9 +440,9 @@ public function pagesAction($request) /** * Return view object for the add/edit form */ - protected function formView() + protected function formView($entityName = null) { - $view = parent::formView(); + $view = parent::formView($entityName); $fields = $view->fields(); // Override int 'parent_id' with option select box @@ -539,6 +549,27 @@ protected function regionModuleFormat($request, $page, $module, $user, $moduleRe } return $content; } + + + /** + * Settings init + * + * Define all settings fields and values that will be needed + */ + public function settings($page, $module) + { + return array( + // Group + 'advanced' => array( + // Fields + 'force_https' => array( + 'type' => 'boolean', + 'default' => false, + 'after' => 'Force page to be viewed via secure connection (HTTPS requres active SSL certificate)' + ) + ) + ); + } /** diff --git a/app/Module/Page/Module/Settings/views/editlistAction.html.php b/app/Module/Page/Module/Settings/views/editlistAction.html.php index 0ddfe5d..5ee024d 100644 --- a/app/Module/Page/Module/Settings/views/editlistAction.html.php +++ b/app/Module/Page/Module/Settings/views/editlistAction.html.php @@ -7,27 +7,54 @@ ?>

Settings

+
- $fields): ?> -

formatUnderscoreWord($group); ?>

+ + - tags or submit buttons - $form = $view->generic('Form') - ->fields($fields) - ->data($fieldData) - ->formTags(false) - ->submit(false); - echo $form->content(); - ?> - -
- - Cancel -
+ +
+ $fields): + ++$i; + ?> +
+ tags or submit buttons + $form = $view->generic('Form') + ->fields($fields) + ->data($fieldData) + ->formTags(false) + ->submit(false); + echo $form->content(); + ?> +
+
+ +
+ + +
+ + Cancel +
+ + \ No newline at end of file diff --git a/app/Module/Page/views/_adminBar.html.php b/app/Module/Page/views/_adminBar.html.php index 0b5b43f..a222046 100755 --- a/app/Module/Page/views/_adminBar.html.php +++ b/app/Module/Page/views/_adminBar.html.php @@ -1,30 +1,41 @@ -
-
- -
-
+ +
+
-
+ + +
kernel->spotForm('Module\User\Entity') ->removeFields(array('site_id', 'salt')); diff --git a/app/Module/User/Session/Controller.php b/app/Module/User/Session/Controller.php index baff514..26620f8 100755 --- a/app/Module/User/Session/Controller.php +++ b/app/Module/User/Session/Controller.php @@ -174,7 +174,7 @@ public function uninstall() /** * Return view object for the add/edit form */ - protected function formView() + protected function formView($entityName = null) { $view = new \Alloy\View\Generic\Form('form'); $view->action($this->kernel->url('login')) diff --git a/app/Plugin/Stackbox/lib/Stackbox/Module/ControllerAbstract.php b/app/Plugin/Stackbox/lib/Stackbox/Module/ControllerAbstract.php index 594c99b..e79351f 100755 --- a/app/Plugin/Stackbox/lib/Stackbox/Module/ControllerAbstract.php +++ b/app/Plugin/Stackbox/lib/Stackbox/Module/ControllerAbstract.php @@ -69,9 +69,16 @@ public function urlName() /** * Return view object for the add/edit form */ - protected function formView() + protected function formView($entityName = null) { - $fields = $this->kernel->mapper()->fields("Module\\" . $this->name() . "\\Entity"); + if(null === $entityName) { + if(defined('static::ENTITY')) { + $entityName = static::ENTITY; + } else { + $entityName = "Module\\" . $this->name() . "\\Entity"; + } + } + $fields = $this->kernel->mapper()->fields($entityName); $view = new \Alloy\View\Generic\Form('form'); $view->action("") ->fields($fields) diff --git a/app/Plugin/Stackbox/lib/Stackbox/Module/EntityAbstract.php b/app/Plugin/Stackbox/lib/Stackbox/Module/EntityAbstract.php index 4928a7e..cda7fa2 100755 --- a/app/Plugin/Stackbox/lib/Stackbox/Module/EntityAbstract.php +++ b/app/Plugin/Stackbox/lib/Stackbox/Module/EntityAbstract.php @@ -14,5 +14,5 @@ public static function fields() 'page_id' => array('type' => 'int', 'index' => true, 'default' => 0), 'module_id' => array('type' => 'int', 'index' => true, 'required' => true) )); - } + } } \ No newline at end of file diff --git a/app/Plugin/Stackbox/lib/Stackbox/Module/Site/ControllerAbstract.php b/app/Plugin/Stackbox/lib/Stackbox/Module/Site/ControllerAbstract.php new file mode 100644 index 0000000..c2eead0 --- /dev/null +++ b/app/Plugin/Stackbox/lib/Stackbox/Module/Site/ControllerAbstract.php @@ -0,0 +1,29 @@ +_path) { + return $this->_path; + } + + $class = get_called_class(); + $path = str_replace('\\', '/', str_replace('\\Controller', '', $class)); + return $this->kernel->site()->dir() . 'content/' . $path; + } +} \ No newline at end of file diff --git a/app/Plugin/Stackbox/lib/Stackbox/Module/Site/EntityAbstract.php b/app/Plugin/Stackbox/lib/Stackbox/Module/Site/EntityAbstract.php new file mode 100644 index 0000000..4dc19d6 --- /dev/null +++ b/app/Plugin/Stackbox/lib/Stackbox/Module/Site/EntityAbstract.php @@ -0,0 +1,20 @@ +" to prevent global table conflicts + return 'site_' . \Kernel()->site()->shortname . '_' . static::$_datasource; + } +} \ No newline at end of file diff --git a/app/www/assets/admin/scripts/cms_admin.js b/app/www/assets/admin/scripts/cms_admin.js index b413686..764692e 100755 --- a/app/www/assets/admin/scripts/cms_admin.js +++ b/app/www/assets/admin/scripts/cms_admin.js @@ -602,4 +602,145 @@ $.extend($.ui.dialog.overlay, { create: function(dialog){ beforeCreateFunction: null, // Remember: these are function _references_ not function calls afterCreateFunction: null }; -})(jQuery); \ No newline at end of file +})(jQuery); + + + + +/* ============================================================ + * bootstrap-dropdown.js v1.4.0 + * http://twitter.github.com/bootstrap/javascript.html#dropdown + * ============================================================ + * Copyright 2011 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + + +!function( $ ){ + + "use strict" + + /* DROPDOWN PLUGIN DEFINITION + * ========================== */ + + $.fn.dropdown = function ( selector ) { + return this.each(function () { + $(this).delegate(selector || d, 'click', function (e) { + var li = $(this).parent('li') + , isActive = li.hasClass('open') + + clearMenus() + !isActive && li.toggleClass('open') + return false + }) + }) + } + + /* APPLY TO STANDARD DROPDOWN ELEMENTS + * =================================== */ + + var d = 'a.menu, .dropdown-toggle' + + function clearMenus() { + $(d).parent('li').removeClass('open') + } + + $(function () { + $('html').bind("click", clearMenus) + $('body').dropdown( '[data-dropdown] a.menu, [data-dropdown] .dropdown-toggle' ) + }) + +}( window.jQuery || window.ender ); + + +/* ======================================================== + * bootstrap-tabs.js v1.4.0 + * http://twitter.github.com/bootstrap/javascript.html#tabs + * ======================================================== + * Copyright 2011 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================== */ + + +!function( $ ){ + + "use strict" + + function activate ( element, container ) { + container + .find('> .active') + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active') + + element.addClass('active') + + if ( element.parent('.dropdown-menu') ) { + element.closest('li.dropdown').addClass('active') + } + } + + function tab( e ) { + var $this = $(this) + , $ul = $this.closest('ul:not(.dropdown-menu)') + , href = $this.attr('href') + , previous + , $href + + if ( /^#\w+/.test(href) ) { + e.preventDefault() + + if ( $this.parent('li').hasClass('active') ) { + return + } + + previous = $ul.find('.active a').last()[0] + $href = $(href) + + activate($this.parent('li'), $ul) + activate($href, $href.parent()) + + $this.trigger({ + type: 'change' + , relatedTarget: previous + }) + } + } + + + /* TABS/PILLS PLUGIN DEFINITION + * ============================ */ + + $.fn.tabs = $.fn.pills = function ( selector ) { + return this.each(function () { + $(this).delegate(selector || '.tabs li > a, .pills > li > a', 'click', tab) + }) + } + + $(document).ready(function () { + $('body').tabs('ul[data-tabs] li > a, ul[data-pills] > li > a') + }) + +}( window.jQuery || window.ender ); \ No newline at end of file diff --git a/app/www/assets/admin/styles/cms_admin.css b/app/www/assets/admin/styles/cms_admin.css index 182d07b..165fb9b 100755 --- a/app/www/assets/admin/styles/cms_admin.css +++ b/app/www/assets/admin/styles/cms_admin.css @@ -2,7 +2,7 @@ * Admin Styles for Cont-xt CMS */ body { - padding-top: 25px; + padding-top: 40px; } /* @@ -103,69 +103,37 @@ ADMIN UI /* ADMIN BAR ================================================== */ -#cms_admin_bar { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 24px; - background: #333; - border-bottom: 1px solid #000; - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#444), to(#222)); - background-image: -moz-linear-gradient(100% 100% 90deg, #222, #444); - z-index: 999; +a.cms_admin_bar_edit { + display: block; + float: left; + font-size: 12px; + font-weight: bold; + text-transform: uppercase; + text-decoration: none; + background-color: #0CA009; + color: #fff!important; + border-radius: 5px; + border-color: #089B07; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -moz-border-radius-topleft: 0; + -moz-border-radius-topright: 0; + -webkit-border-top-left-radius: 0; + -webkit-border-top-right-radius: 0; + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#61E510), to(#089B07)); + background-image: -moz-linear-gradient(100% 100% 90deg, #089B07, #61E510); +} +#cms_admin_bar_editPage { + margin-right: 0; + -moz-border-radius-bottomright: 0; + -webkit-border-bottom-right-radius: 0; +} +#cms_admin_bar_addContent { + margin-left: 0; + border-left: 0; + -moz-border-radius-bottomleft: 0; + -webkit-border-bottom-left-radius: 0; } - #cms_admin_bar_inside { height: 24px; overflow: hidden; } - /* Navigation */ - #cms_admin_bar ul { list-style: none; margin: 0; padding: 0; } - #cms_admin_bar ul li { float: left; margin: 0; padding: 0; } - #cms_admin_bar ul li a { - display: block; - padding: 7px 10px 8px 10px; - color: #eee; - font-size: 11px; - border-left: 1px solid #666; - border-right: 1px solid #333; - } - #cms_admin_bar ul li a:hover, #imNav li.active a { background-color: #444; } - /* add content button */ - #cms_admin_bar_primary ul li a { - display: block; - float: left; - font-size: 12px; - font-weight: bold; - text-transform: uppercase; - text-decoration: none; - background-color: #0CA009; - color: #fff; - margin: 0 10px; - padding: 10px 15px; - border-radius: 5px; - border-color: #089B07; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - -moz-border-radius-topleft: 0; - -moz-border-radius-topright: 0; - -webkit-border-top-left-radius: 0; - -webkit-border-top-right-radius: 0; - background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#61E510), to(#089B07)); - background-image: -moz-linear-gradient(100% 100% 90deg, #089B07, #61E510); - } - #cms_admin_bar_primary a#cms_admin_bar_editPage { - margin-right: 0; - -moz-border-radius-bottomright: 0; - -webkit-border-bottom-right-radius: 0; - } - #cms_admin_bar_primary a#cms_admin_bar_addContent { - margin-left: 0; - border-left: 0; - -moz-border-radius-bottomleft: 0; - -webkit-border-bottom-left-radius: 0; - } - /* user menu */ - #cms_admin_nav_user { - float: right; - } /* MODAL / DIALOG @@ -187,10 +155,8 @@ MODAL / DIALOG #cms_modal .cms_module.cms_ui_modulebar { display: none!important; } -/* -.ui-dialog { - width: 600px!important; -} + + .ui-dialog-titlebar, .ui-dialog-title { font-size: 12px; font-weight: bold; @@ -204,7 +170,7 @@ MODAL / DIALOG #cms_modal .cms_module.cms_ui_modulebar { display: none!important; } -*/ + /* @@ -280,7 +246,7 @@ div.cms_ui_pane { #cms_admin_modules { position: fixed; - top: 25px; + top: 40px; left: 0; width: 100%; height: 98px; @@ -477,4 +443,476 @@ REGION / MODULE UI } /* jQuery UI overrides */ -.ui-dialog-buttonpane .ui-dialog-buttonset { float: left!important; margin-left: 172px; } \ No newline at end of file +.ui-dialog-buttonpane .ui-dialog-buttonset { float: left!important; margin-left: 172px; } + + + +/* +Twitter Bootstrap Styles (hi-jacked!) +================================================== */ +.topbar { + height: 40px; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 10000; + overflow: visible; +} +.topbar a { + color: #bfbfbf; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.topbar h3 a:hover, .topbar .brand:hover, .topbar ul .active > a { + background-color: #333; + background-color: rgba(255, 255, 255, 0.05); + color: #ffffff; + text-decoration: none; +} +.topbar h3 { + position: relative; +} +.topbar h3 a, .topbar .brand { + float: left; + display: block; + padding: 8px 20px 12px; + margin-left: -20px; + color: #ffffff; + font-size: 20px; + font-weight: 200; + line-height: 1; +} +.topbar p { + margin: 0; + line-height: 40px; +} +.topbar p a:hover { + background-color: transparent; + color: #ffffff; +} +.topbar form { + float: left; + margin: 5px 0 0 0; + position: relative; + filter: alpha(opacity=100); + -khtml-opacity: 1; + -moz-opacity: 1; + opacity: 1; +} +.topbar form.pull-right { + float: right; +} +.topbar input { + background-color: #444; + background-color: rgba(255, 255, 255, 0.3); + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: normal; + font-weight: 13px; + line-height: 1; + padding: 4px 9px; + color: #ffffff; + color: rgba(255, 255, 255, 0.75); + border: 1px solid #111; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); + -webkit-transition: none; + -moz-transition: none; + -ms-transition: none; + -o-transition: none; + transition: none; +} +.topbar input:-moz-placeholder { + color: #e6e6e6; +} +.topbar input::-webkit-input-placeholder { + color: #e6e6e6; +} +.topbar input:hover { + background-color: #bfbfbf; + background-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} +.topbar input:focus, .topbar input.focused { + outline: 0; + background-color: #ffffff; + color: #404040; + text-shadow: 0 1px 0 #ffffff; + border: 0; + padding: 5px 10px; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); +} +.topbar-inner, .topbar .fill { + background-color: #222; + background-color: #222222; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222)); + background-image: -moz-linear-gradient(top, #333333, #222222); + background-image: -ms-linear-gradient(top, #333333, #222222); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222)); + background-image: -webkit-linear-gradient(top, #333333, #222222); + background-image: -o-linear-gradient(top, #333333, #222222); + background-image: linear-gradient(top, #333333, #222222); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); +} +.topbar div > ul, .nav { + display: block; + float: left; + margin: 0 10px 0 0; + position: relative; + left: 0; +} +.topbar div > ul > li, .nav > li { + display: block; + float: left; +} +.topbar div > ul a, .nav a { + display: block; + float: none; + padding: 10px 10px 11px; + line-height: 19px; + text-decoration: none; +} +.topbar div > ul a:hover, .nav a:hover { + color: #ffffff; + text-decoration: none; +} +.topbar div > ul .active > a, .nav .active > a { + background-color: #222; + background-color: rgba(0, 0, 0, 0.5); +} +.topbar div > ul.secondary-nav, .nav.secondary-nav { + float: right; + margin-left: 10px; + margin-right: 0; +} +.topbar div > ul.secondary-nav .menu-dropdown, +.nav.secondary-nav .menu-dropdown, +.topbar div > ul.secondary-nav .dropdown-menu, +.nav.secondary-nav .dropdown-menu { + right: 0; + border: 0; +} +.topbar div > ul a.menu:hover, +.nav a.menu:hover, +.topbar div > ul li.open .menu, +.nav li.open .menu, +.topbar div > ul .dropdown-toggle:hover, +.nav .dropdown-toggle:hover, +.topbar div > ul .dropdown.open .dropdown-toggle, +.nav .dropdown.open .dropdown-toggle { + background: #444; + background: rgba(255, 255, 255, 0.05); +} +.topbar div > ul .menu-dropdown, +.nav .menu-dropdown, +.topbar div > ul .dropdown-menu, +.nav .dropdown-menu { + background-color: #333; +} +.topbar div > ul .menu-dropdown a.menu, +.nav .menu-dropdown a.menu, +.topbar div > ul .dropdown-menu a.menu, +.nav .dropdown-menu a.menu, +.topbar div > ul .menu-dropdown .dropdown-toggle, +.nav .menu-dropdown .dropdown-toggle, +.topbar div > ul .dropdown-menu .dropdown-toggle, +.nav .dropdown-menu .dropdown-toggle { + color: #ffffff; +} +.topbar div > ul .menu-dropdown a.menu.open, +.nav .menu-dropdown a.menu.open, +.topbar div > ul .dropdown-menu a.menu.open, +.nav .dropdown-menu a.menu.open, +.topbar div > ul .menu-dropdown .dropdown-toggle.open, +.nav .menu-dropdown .dropdown-toggle.open, +.topbar div > ul .dropdown-menu .dropdown-toggle.open, +.nav .dropdown-menu .dropdown-toggle.open { + background: #444; + background: rgba(255, 255, 255, 0.05); +} +.topbar div > ul .menu-dropdown li a, +.nav .menu-dropdown li a, +.topbar div > ul .dropdown-menu li a, +.nav .dropdown-menu li a { + color: #999; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5); +} +.topbar div > ul .menu-dropdown li a:hover, +.nav .menu-dropdown li a:hover, +.topbar div > ul .dropdown-menu li a:hover, +.nav .dropdown-menu li a:hover { + background-color: #191919; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#292929), to(#191919)); + background-image: -moz-linear-gradient(top, #292929, #191919); + background-image: -ms-linear-gradient(top, #292929, #191919); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #292929), color-stop(100%, #191919)); + background-image: -webkit-linear-gradient(top, #292929, #191919); + background-image: -o-linear-gradient(top, #292929, #191919); + background-image: linear-gradient(top, #292929, #191919); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#292929', endColorstr='#191919', GradientType=0); + color: #ffffff; +} +.topbar div > ul .menu-dropdown .active a, +.nav .menu-dropdown .active a, +.topbar div > ul .dropdown-menu .active a, +.nav .dropdown-menu .active a { + color: #ffffff; +} +.topbar div > ul .menu-dropdown .divider, +.nav .menu-dropdown .divider, +.topbar div > ul .dropdown-menu .divider, +.nav .dropdown-menu .divider { + background-color: #222; + border-color: #444; +} +.topbar ul .menu-dropdown li a, .topbar ul .dropdown-menu li a { + padding: 4px 15px; +} +li.menu, .dropdown { + position: relative; +} +a.menu:after, .dropdown-toggle:after { + width: 0; + height: 0; + display: inline-block; + content: "↓"; + text-indent: -99999px; + vertical-align: top; + margin-top: 8px; + margin-left: 4px; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid #ffffff; + filter: alpha(opacity=50); + -khtml-opacity: 0.5; + -moz-opacity: 0.5; + opacity: 0.5; +} +.menu-dropdown, .dropdown-menu { + background-color: #ffffff; + float: left; + display: none; + position: absolute; + top: 40px; + z-index: 900; + min-width: 160px; + max-width: 220px; + _width: 160px; + margin-left: 0; + margin-right: 0; + padding: 6px 0; + zoom: 1; + border-color: #999; + border-color: rgba(0, 0, 0, 0.2); + border-style: solid; + border-width: 0 1px 1px; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} +.menu-dropdown li, .dropdown-menu li { + float: none; + display: block; + background-color: none; +} +.menu-dropdown .divider, .dropdown-menu .divider { + height: 1px; + margin: 5px 0; + overflow: hidden; + background-color: #eee; + border-bottom: 1px solid #ffffff; +} +.topbar .dropdown-menu a, .dropdown-menu a { + display: block; + padding: 4px 15px; + clear: both; + font-weight: normal; + line-height: 18px; + color: #808080; + text-shadow: 0 1px 0 #ffffff; +} +.topbar .dropdown-menu a:hover, +.dropdown-menu a:hover, +.topbar .dropdown-menu a.hover, +.dropdown-menu a.hover { + background-color: #dddddd; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#eeeeee), to(#dddddd)); + background-image: -moz-linear-gradient(top, #eeeeee, #dddddd); + background-image: -ms-linear-gradient(top, #eeeeee, #dddddd); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #dddddd)); + background-image: -webkit-linear-gradient(top, #eeeeee, #dddddd); + background-image: -o-linear-gradient(top, #eeeeee, #dddddd); + background-image: linear-gradient(top, #eeeeee, #dddddd); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dddddd', GradientType=0); + color: #404040; + text-decoration: none; + -webkit-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); +} +.open .menu, +.dropdown.open .menu, +.open .dropdown-toggle, +.dropdown.open .dropdown-toggle { + color: #ffffff; + background: #ccc; + background: rgba(0, 0, 0, 0.3); +} +.open .menu-dropdown, +.dropdown.open .menu-dropdown, +.open .dropdown-menu, +.dropdown.open .dropdown-menu { + display: block; +} +.tabs, .pills { + margin: 0 0 18px; + padding: 0; + list-style: none; + zoom: 1; +} +.tabs:before, +.pills:before, +.tabs:after, +.pills:after { + display: table; + content: ""; + zoom: 1; +} +.tabs:after, .pills:after { + clear: both; +} +.tabs > li, .pills > li { + float: left; +} +.tabs > li > a, .pills > li > a { + display: block; +} +.tabs { + border-color: #ddd; + border-style: solid; + border-width: 0 0 1px; +} +.tabs > li { + position: relative; + margin-bottom: -1px; +} +.tabs > li > a { + padding: 0 15px; + margin-right: 2px; + line-height: 34px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.tabs > li > a:hover { + text-decoration: none; + background-color: #eee; + border-color: #eee #eee #ddd; +} +.tabs .active > a, .tabs .active > a:hover { + color: #808080; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} +.tabs .menu-dropdown, .tabs .dropdown-menu { + top: 35px; + border-width: 1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} +.tabs a.menu:after, .tabs .dropdown-toggle:after { + border-top-color: #999; + margin-top: 15px; + margin-left: 5px; +} +.tabs li.open.menu .menu, .tabs .open.dropdown .dropdown-toggle { + border-color: #999; +} +.tabs li.open a.menu:after, .tabs .dropdown.open .dropdown-toggle:after { + border-top-color: #555; +} +.pills a { + margin: 5px 3px 5px 0; + padding: 0 15px; + line-height: 30px; + text-shadow: 0 1px 1px #ffffff; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +.pills a:hover { + color: #ffffff; + text-decoration: none; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); + background-color: #00438a; +} +.pills .active a { + color: #ffffff; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); + background-color: #0069d6; +} +.pills-vertical > li { + float: none; +} +.tab-content > .tab-pane, +.pill-content > .pill-pane, +.tab-content > div, +.pill-content > div { + display: none; +} +.tab-content > .active, .pill-content > .active { + display: block; +} +.breadcrumb { + padding: 7px 14px; + margin: 0 0 18px; + background-color: #f5f5f5; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#f5f5f5)); + background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f5f5f5)); + background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -o-linear-gradient(top, #ffffff, #f5f5f5); + background-image: linear-gradient(top, #ffffff, #f5f5f5); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0); + border: 1px solid #ddd; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} +.breadcrumb li { + display: inline; + text-shadow: 0 1px 0 #ffffff; +} +.breadcrumb .divider { + padding: 0 5px; + color: #bfbfbf; +} +.breadcrumb .active a { + color: #404040; +} \ No newline at end of file From 198de6355d6d05959eb45ecbd244df46be514144 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 30 Nov 2011 13:21:44 -0600 Subject: [PATCH 08/50] Big updates to settings and base modules. BREAKING changes here. * New settings module that allows settings for site and page instead of only placeable modules. * Updated Filebrowser to accept fixed target image size * Updated all base CMS entites to prefix table with cms_ * Added 'name' method on Module objects to lowercase & return name --- app/Module/Filebrowser/File.php | 4 +- app/Module/Filebrowser/Plugin.php | 8 ++-- .../Filebrowser/views/directoryList.html.php | 14 ++++++- app/Module/Page/Controller.php | 14 +++---- app/Module/Page/Entity.php | 30 +++++++++++++- app/Module/Page/Module/Entity.php | 37 ++++++------------ app/Module/Page/Module/Settings/Entity.php | 23 ----------- .../{Page/Module => }/Settings/Collection.php | 8 +++- .../{Page/Module => }/Settings/Controller.php | 38 ++++++++++++------ app/Module/Settings/Entity.php | 36 +++++++++++++++++ app/Module/Settings/Mapper.php | 25 ++++++++++++ .../Settings/views/editlistAction.html.php | 9 ++--- app/Module/Site/Domain.php | 2 +- app/Module/Site/Entity.php | 39 ++++++++++++++++++- app/Module/User/Entity.php | 2 +- app/Module/User/Session/Entity.php | 2 +- .../Stackbox/Module/ControllerAbstract.php | 19 ++++----- app/config/routes.php | 7 +--- .../content/Module/Blog/Post/Controller.php | 4 +- .../content/Module/Slideshow/Controller.php | 2 +- app/www/content/Module/Text/Controller.php | 4 +- 21 files changed, 215 insertions(+), 112 deletions(-) delete mode 100644 app/Module/Page/Module/Settings/Entity.php rename app/Module/{Page/Module => }/Settings/Collection.php (80%) rename app/Module/{Page/Module => }/Settings/Controller.php (68%) create mode 100644 app/Module/Settings/Entity.php create mode 100644 app/Module/Settings/Mapper.php rename app/Module/{Page/Module => }/Settings/views/editlistAction.html.php (91%) diff --git a/app/Module/Filebrowser/File.php b/app/Module/Filebrowser/File.php index 937517e..3e914c1 100644 --- a/app/Module/Filebrowser/File.php +++ b/app/Module/Filebrowser/File.php @@ -58,11 +58,11 @@ public function getUrl() public function getSizeUrl($width, $height) { $kernel = \Kernel(); - $siteId = $kernel->site()->id; + $site = $kernel->site(); $filesDir = $kernel->config('cms.path.files') . 'images/'; $imageFile = str_replace($filesDir, '', $this->getPathname()); return $kernel->url(array( - 'site_id' => $siteId, + 'site' => $site->shortname(), 'width' => $width, 'height' => $height, 'image' => $imageFile diff --git a/app/Module/Filebrowser/Plugin.php b/app/Module/Filebrowser/Plugin.php index c5b0a34..49923ea 100644 --- a/app/Module/Filebrowser/Plugin.php +++ b/app/Module/Filebrowser/Plugin.php @@ -19,13 +19,13 @@ public function __construct(Alloy\Kernel $kernel) $this->kernel = $kernel; // Link to FileBrowser module to get an image link - $kernel->addMethod('filebrowserSelectImageLink', function($fieldId = null) use($kernel) { - return 'Select Image...'; + $kernel->addMethod('filebrowserSelectImageLink', function($field_fill_id = null, $size = null) use($kernel) { + return 'Select Image...'; }); // Link to FileBrowser module to get an image link - $kernel->addMethod('filebrowserSelectFileLink', function($fieldId = null) use($kernel) { - return 'Select File...'; + $kernel->addMethod('filebrowserSelectFileLink', function($fieldId = null, $size = null) use($kernel) { + return 'Select File...'; }); } } \ No newline at end of file diff --git a/app/Module/Filebrowser/views/directoryList.html.php b/app/Module/Filebrowser/views/directoryList.html.php index 5029a31..19b6296 100644 --- a/app/Module/Filebrowser/views/directoryList.html.php +++ b/app/Module/Filebrowser/views/directoryList.html.php @@ -2,6 +2,14 @@ use Module\Filebrowser\File; $request = $kernel->request(); +$size = null; +$width = $height = 100; +if($request->size) { + // Requested image size dimensions + $size = explode('x', $request->size, 2); + $width = $size[0]; + $height = isset($size[1]) ? $size[1] : $width; +} ?>
@@ -39,12 +47,13 @@ $grid = $view->generic('cellgrid'); $grid->data($files) ->columns(6) - ->cell(function($mFile) { + ->cell(function($mFile) use($size, $width, $height) { // Get into File object $file = new File($mFile); ?> isImage()): // Image ?>
+ - <?php echo $file->getFilename(); ?> + + <?php echo $file->getFilename(); ?>
diff --git a/app/Module/Page/Controller.php b/app/Module/Page/Controller.php index d5e4948..be0e418 100755 --- a/app/Module/Page/Controller.php +++ b/app/Module/Page/Controller.php @@ -63,19 +63,15 @@ public function viewUrl($pageUrl) $moduleName = $request->module_name; $moduleAction = $request->module_action; - if($moduleId) { + $module = false; + if($moduleName != 'page' && $moduleName != 'site' && $moduleId) { // Get module by ID $module = $mapper->first('Module\Page\Module\Entity', array('id' => $moduleId)); - } else { - // Get new module entity, no ID supplied - // @todo Possibly restrict callable action with ID of '0' to 'new', etc. because other functions may depend on saved and valid module record - $module = $mapper->get('Module\Page\Module\Entity'); - $module->id = $moduleId; - $module->name = $moduleName; } // Setup dummy module object if there is none loaded - if(!$module) { + // @todo Possibly restrict callable action with ID of '0' to 'new', etc. because other functions may depend on saved and valid module record + if(false === $module) { $module = $mapper->get('Module\Page\Module\Entity'); $module->id = $moduleId; $module->name = $moduleName; @@ -586,7 +582,7 @@ public function install($action = null, array $params = array()) // Page $this->kernel->mapper()->migrate('Module\Page\Entity'); $this->kernel->mapper()->migrate('Module\Page\Module\Entity'); - $this->kernel->mapper()->migrate('Module\Page\Module\Settings\Entity'); + $this->kernel->mapper()->migrate('Module\Settings\Entity'); return parent::install($action, $params); } diff --git a/app/Module/Page/Entity.php b/app/Module/Page/Entity.php index bc3c3dd..c0d315d 100755 --- a/app/Module/Page/Entity.php +++ b/app/Module/Page/Entity.php @@ -10,7 +10,7 @@ class Entity extends Stackbox\EntityAbstract // Table - protected static $_datasource = "pages"; + protected static $_datasource = "cms_pages"; // Public property that will contain child pages when Mapper::pageTree() is called public $children = array(); @@ -176,4 +176,32 @@ public function navigationTitle() { return $this->navigation_title ? $this->navigation_title : $this->title; } + + + /** + * Get and return settings in special collction with direct access to settings by 'setting_key' name + */ + public function settings() + { + if(null !== $this->_settings) { + return $this->_settings; + } + + $kernel = \Kernel(); + $this->_settings = $kernel->mapper('Module\Settings\Mapper')->getSettingsForModule('page', $this->id); + return $this->_settings; + } + + + /** + * Get setting value by key name + */ + public function setting($key, $default = null) + { + $settings = $this->settings(); + if($v = $settings->$key) { + return $v; + } + return $default; + } } \ No newline at end of file diff --git a/app/Module/Page/Module/Entity.php b/app/Module/Page/Module/Entity.php index 4969ea1..e06f08a 100755 --- a/app/Module/Page/Module/Entity.php +++ b/app/Module/Page/Module/Entity.php @@ -5,7 +5,7 @@ class Entity extends Stackbox\EntityAbstract { // Setup table and fields - protected static $_datasource = "page_modules"; + protected static $_datasource = "cms_page_modules"; protected $_settings; /** @@ -24,41 +24,18 @@ public static function fields() { ) + parent::fields(); } - /** - * Relations - */ - /* - public static function relations() { - return array( - // Settings as key => value - 'settings' => array( - 'type' => 'HasManyKV', // HasMany Key/Values - 'entity' => 'Module\Page\Module\Settings\Entity', - 'keyField' => 'setting_key', - 'valueField' => 'setting_value', - 'where' => array('site_id' => ':entity.site_id', 'module_id' => ':entity.id') - ) - ) + parent::relations(); - } - */ - /** * Get and return settings in special collction with direct access to settings by 'setting_key' name */ public function settings() { - if($this->_settings) { + if(null !== $this->_settings) { return $this->_settings; } $kernel = \Kernel(); - $mapper = $kernel->mapper(); - $settings = $mapper->all('Module\Page\Module\Settings\Entity') - ->where(array('site_id' => $this->site_id, 'module_id' => $this->id)) - ->order(array('ordering' => 'ASC')); - - $this->_settings = new Settings\Collection($settings); + $this->_settings = $kernel->mapper('Module\Settings\Mapper')->getSettingsForModule($this->name, $this->id); return $this->_settings; } @@ -74,4 +51,12 @@ public function setting($key, $default = null) } return $default; } + + /** + * Get lowercase module name + */ + public function name() + { + return strtolower($this->name); + } } \ No newline at end of file diff --git a/app/Module/Page/Module/Settings/Entity.php b/app/Module/Page/Module/Settings/Entity.php deleted file mode 100644 index 4994450..0000000 --- a/app/Module/Page/Module/Settings/Entity.php +++ /dev/null @@ -1,23 +0,0 @@ - array('type' => 'int', 'primary' => true, 'serial' => true), - 'site_id' => array('type' => 'int', 'index' => 'site_module', 'default' => 0), - 'module_id' => array('type' => 'int', 'index' => 'site_module', 'required' => true), - 'setting_key' => array('type' => 'string', 'required' => true), - 'setting_value' => array('type' => 'string'), // Not "required" because values can be NULL - 'ordering' => array('type' => 'int', 'default' => 0) - ) + parent::fields(); - } -} \ No newline at end of file diff --git a/app/Module/Page/Module/Settings/Collection.php b/app/Module/Settings/Collection.php similarity index 80% rename from app/Module/Page/Module/Settings/Collection.php rename to app/Module/Settings/Collection.php index a0fcdeb..68a9d98 100644 --- a/app/Module/Page/Module/Settings/Collection.php +++ b/app/Module/Settings/Collection.php @@ -1,5 +1,5 @@ _settingsEntities = $settings; // Store as array key => value - $this->_settings = $settings->toArray('setting_key', 'setting_value'); + if($settings instanceof \Spot\Entity\Collection || $settings instanceof \Spot\Query) { + $this->_settings = $settings->toArray('setting_key', 'setting_value'); + } else { + $this->_settings = $settings; + } } diff --git a/app/Module/Page/Module/Settings/Controller.php b/app/Module/Settings/Controller.php similarity index 68% rename from app/Module/Page/Module/Settings/Controller.php rename to app/Module/Settings/Controller.php index 382cfe2..2cb6678 100644 --- a/app/Module/Page/Module/Settings/Controller.php +++ b/app/Module/Settings/Controller.php @@ -1,12 +1,18 @@ settings()->toArray(); @@ -42,16 +48,25 @@ public function editlistAction(Alloy\Request $request, Module\Page\Entity $page, * * @method POST */ - public function postMethod(Alloy\Request $request, Module\Page\Entity $page, Module\Page\Module\Entity $module) + public function postMethod(Request $request, Page $page, Module $module) { - $postSettings = $request->post(); // POST payload $mapper = $this->kernel->mapper(); + $target_module_name = $request->target_module_name; + $postSettings = $request->post(); // POST payload + if(isset($postSettings['target_module_name'])) { + unset($postSettings['target_module_name']); + } // Steps: // - Loop over all settings to see which ones already exist and update them // - Add settings that don't exist - - $settings = $module->settings(); + if('site' == $target_module_name) { + $settings = $this->kernel->site()->settings(); + } elseif('page' == $target_module_name) { + $settings = $page->settings(); + } else { + $settings = $module->settings(); + } $currentSettings = $settings->toArray(); $updateSettings = array_intersect_key($postSettings, $currentSettings); @@ -69,7 +84,8 @@ public function postMethod(Alloy\Request $request, Module\Page\Entity $page, Mod $setting = new Entity(); $setting->data(array( 'site_id' => $module->site_id, - 'module_id' => $module->id, + 'type' => $target_module_name, + 'type_id' => (int) $module->id, 'setting_key' => $key, 'setting_value' => $value )); @@ -90,7 +106,7 @@ public function postMethod(Alloy\Request $request, Module\Page\Entity $page, Mod */ public function install($action = null, array $params = array()) { - $this->kernel->mapper()->migrate('Module\Page\Module\Settings\Entity'); + $this->kernel->mapper()->migrate(self::ENTITY); return parent::install($action, $params); } @@ -102,6 +118,6 @@ public function install($action = null, array $params = array()) */ public function uninstall() { - return $this->kernel->mapper()->dropDatasource('Module\Page\Module\Settings\Entity'); + return $this->kernel->mapper()->dropDatasource(self::ENTITY); } } \ No newline at end of file diff --git a/app/Module/Settings/Entity.php b/app/Module/Settings/Entity.php new file mode 100644 index 0000000..3088b8f --- /dev/null +++ b/app/Module/Settings/Entity.php @@ -0,0 +1,36 @@ + array('type' => 'int', 'primary' => true, 'serial' => true), + 'site_id' => array('type' => 'int', 'index' => 'site_type_id', 'required' => true), + 'type' => array('type' => 'string', 'index' => 'site_type_id', 'required' => true), + 'type_id' => array('type' => 'int', 'index' => 'site_type_id', 'default' => 0), + 'setting_key' => array('type' => 'string', 'required' => true), + 'setting_value' => array('type' => 'string') // Not "required" because values can be NULL + )); + } + + + /** + * Ensures 'type' and 'setting_key' are lowercase strings + */ + public function beforeSave(Spot\Mapper $mapper) + { + $this->__set('type', strtolower($this->__get('type'))); + $this->__set('setting_key', strtolower($this->__get('setting_key'))); + return parent::beforeSave($mapper); + } +} \ No newline at end of file diff --git a/app/Module/Settings/Mapper.php b/app/Module/Settings/Mapper.php new file mode 100644 index 0000000..c57b161 --- /dev/null +++ b/app/Module/Settings/Mapper.php @@ -0,0 +1,25 @@ +mapper(); + $settings = $mapper->all('Module\Settings\Entity') + // Module name+id match + ->where(array('site_id' => $kernel->site()->id, 'type' => $moduleName, 'type_id' => $moduleId)) + // Module name match with id of 0 (global module setting) + ->orWhere(array('site_id' => $kernel->site()->id, 'type' => $moduleName, 'type_id' => 0)); + + $settingsCollection = new \Module\Settings\Collection($settings); + return $settingsCollection; + } +} \ No newline at end of file diff --git a/app/Module/Page/Module/Settings/views/editlistAction.html.php b/app/Module/Settings/views/editlistAction.html.php similarity index 91% rename from app/Module/Page/Module/Settings/views/editlistAction.html.php rename to app/Module/Settings/views/editlistAction.html.php index 5ee024d..dbfc8e6 100644 --- a/app/Module/Page/Module/Settings/views/editlistAction.html.php +++ b/app/Module/Settings/views/editlistAction.html.php @@ -1,7 +1,7 @@ url(array( - 'module_name' => 'page_module_settings', + 'module_name' => 'settings', 'module_id' => (int) $module->id), 'module'); ?> @@ -11,7 +11,7 @@
-
    +
      $fields): @@ -49,12 +49,9 @@
      + Cancel
      - - \ No newline at end of file diff --git a/app/Module/Site/Domain.php b/app/Module/Site/Domain.php index 3673f03..5feb0eb 100644 --- a/app/Module/Site/Domain.php +++ b/app/Module/Site/Domain.php @@ -12,7 +12,7 @@ class Domain extends Stackbox\EntityAbstract const TYPE_REDIRECT = 3; // Table - protected static $_datasource = "site_domains"; + protected static $_datasource = "cms_site_domains"; /** * Fields diff --git a/app/Module/Site/Entity.php b/app/Module/Site/Entity.php index 2120e7e..1988a41 100644 --- a/app/Module/Site/Entity.php +++ b/app/Module/Site/Entity.php @@ -11,7 +11,7 @@ class Entity extends Stackbox\EntityAbstract const STATUS_DISABLED = 0; // Table - protected static $_datasource = "sites"; + protected static $_datasource = "cms_sites"; /** * Fields @@ -114,4 +114,41 @@ public function urlThemes() { return $this->url() . '/themes/'; } + + + /** + * Get site shortname + */ + public function shortname() + { + return $this->shortname; + } + + + /** + * Get and return settings in special collction with direct access to settings by 'setting_key' name + */ + public function settings() + { + if(null !== $this->_settings) { + return $this->_settings; + } + + $kernel = \Kernel(); + $this->_settings = $kernel->mapper('Module\Settings\Mapper')->getSettingsForModule('site', $this->id); + return $this->_settings; + } + + + /** + * Get setting value by key name + */ + public function setting($key, $default = null) + { + $settings = $this->settings(); + if($v = $settings->$key) { + return $v; + } + return $default; + } } \ No newline at end of file diff --git a/app/Module/User/Entity.php b/app/Module/User/Entity.php index 29e0bc8..a7dfa52 100755 --- a/app/Module/User/Entity.php +++ b/app/Module/User/Entity.php @@ -6,7 +6,7 @@ class Entity extends Stackbox\EntityAbstract { // Table - protected static $_datasource = "users"; + protected static $_datasource = "cms_users"; /** * Fields diff --git a/app/Module/User/Session/Entity.php b/app/Module/User/Session/Entity.php index abf0997..e73ec1e 100755 --- a/app/Module/User/Session/Entity.php +++ b/app/Module/User/Session/Entity.php @@ -5,7 +5,7 @@ class Entity extends Stackbox\EntityAbstract { // Table - protected static $_datasource = "user_session"; + protected static $_datasource = "cms_user_sessions"; /** * Fields diff --git a/app/Plugin/Stackbox/lib/Stackbox/Module/ControllerAbstract.php b/app/Plugin/Stackbox/lib/Stackbox/Module/ControllerAbstract.php index e79351f..0056845 100755 --- a/app/Plugin/Stackbox/lib/Stackbox/Module/ControllerAbstract.php +++ b/app/Plugin/Stackbox/lib/Stackbox/Module/ControllerAbstract.php @@ -1,6 +1,11 @@ settings to be collection object with all module settings loaded: - - Spot Relation (maybe new custom relation type?) - - Object access: $module->settings->display_type - NULL or default value if no setting exists yet in datastore - - */ - $settings = array(); if(method_exists($this, 'settings')) { $settings = $this->settings($page, $module); } // Dispatch to settings module to handle this and pass in retrieved settings to template - return $this->kernel->dispatch('Page_Module_Settings', 'editlistAction', array($request, $page, $module)) + return $this->kernel->dispatch('Settings', 'editlistAction', array($request, $page, $module)) ->set(array('settingsInit' => $settings)); } } \ No newline at end of file diff --git a/app/config/routes.php b/app/config/routes.php index 8750aa3..d0f6da1 100755 --- a/app/config/routes.php +++ b/app/config/routes.php @@ -31,13 +31,10 @@ ->delete(array('action' => 'delete')); // Dynamic image resize reserved route -$router->route('image_size', '/site/<#site_id>/images/_size/<#width>x<#height>/<*image>') +$router->route('image_size', '/site/<:site>/images/_size/<#width>x<#height>/<*image>') ->defaults(array('module' => 'Filebrowser', 'action' => 'imageSize', 'format' => 'html')); -/* -$router->route('module_item_action', $pageRouteItem . 'm,<:module_name>,<#module_id>/<#module_item>/<:module_action>(.<:format>)') - ->defaults(array('page' => '/', 'module' => 'Page', 'action' => 'index', 'format' => 'html')); -/*/ + // Normal Routes $router->route('module_item', $pageRouteItem . 'm,<:module_name>,<#module_id>/<#module_item>(/<:module_action>)(.<:format>)') diff --git a/app/www/content/Module/Blog/Post/Controller.php b/app/www/content/Module/Blog/Post/Controller.php index 08e8348..420f021 100644 --- a/app/www/content/Module/Blog/Post/Controller.php +++ b/app/www/content/Module/Blog/Post/Controller.php @@ -215,9 +215,9 @@ public function uninstall() /** * Return view object for the add/edit form */ - protected function formView() + protected function formView($entityName = null) { - $view = parent::formView(); + $view = parent::formView($entityName); $fields = $view->fields(); // Setup editor diff --git a/app/www/content/Module/Slideshow/Controller.php b/app/www/content/Module/Slideshow/Controller.php index 69ecfd1..f687ce8 100755 --- a/app/www/content/Module/Slideshow/Controller.php +++ b/app/www/content/Module/Slideshow/Controller.php @@ -225,7 +225,7 @@ public function settings($page, $module) /** * Return view object for the add/edit form */ - protected function formView() + protected function formView($entityName = null) { $view = $this->kernel->spotForm('Module\Slideshow\Item'); $fields = $view->fields(); diff --git a/app/www/content/Module/Text/Controller.php b/app/www/content/Module/Text/Controller.php index 05155ce..078888b 100755 --- a/app/www/content/Module/Text/Controller.php +++ b/app/www/content/Module/Text/Controller.php @@ -166,9 +166,9 @@ public function uninstall() /** * Return view object for the add/edit form */ - protected function formView() + protected function formView($entityName = null) { - $view = parent::formView(); + $view = parent::formView($entityName); $fields = $view->fields(); // Set text 'content' as type 'editor' to get WYSIWYG From c2135397a19f66e6b7431e031c3cbfb28e1bfd64 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 30 Nov 2011 13:47:17 -0600 Subject: [PATCH 09/50] Alloy Framework and Filebrowser update * Updated 'alloy' folder to newest version of Alloy Framework * Updated Filebrowser to maintain query string parameters after upload * so file selection process is not broken after upload [closes #6] --- alloy/Plugin/Spot/Plugin.php | 8 +- alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php | 673 +++++++++--------- .../Spot/lib/Spot/Adapter/PDO/Abstract.php | 5 +- alloy/Plugin/Spot/lib/Spot/Entity.php | 29 +- alloy/config/routes.php | 5 +- alloy/lib/Alloy/App.php | 33 + alloy/lib/Alloy/Kernel.php | 4 +- alloy/lib/Alloy/Request.php | 92 ++- alloy/lib/Alloy/View/Generic/Treeview.php | 11 - .../View/Generic/templates/treeview.html.php | 6 +- app/Module/Filebrowser/Controller.php | 4 +- .../Filebrowser/views/directoryList.html.php | 2 +- .../Filebrowser/views/newAction.html.php | 2 +- 13 files changed, 481 insertions(+), 393 deletions(-) create mode 100644 alloy/lib/Alloy/App.php diff --git a/alloy/Plugin/Spot/Plugin.php b/alloy/Plugin/Spot/Plugin.php index a86554a..2bd54ee 100644 --- a/alloy/Plugin/Spot/Plugin.php +++ b/alloy/Plugin/Spot/Plugin.php @@ -134,8 +134,14 @@ public function autoinstallOnException($content) if($content instanceof \Spot\Exception_Datasource_Missing ||'42S02' == $content->getCode() || false !== stripos($content->getMessage(), 'Base table or view not found')) { - // Table not found - auto-install module to cause Entity migrations + // Last dispatch attempt $ld = $kernel->lastDispatch(); + + // Debug trace message + $mName = is_object($ld['module']) ? get_class($ld['module']) : $ld['module']; + $kernel->trace("PDO Exception on module '" . $mName . "' when dispatching '" . $ld['action'] . "' Attempting auto-install in Spot plugin at " . __METHOD__ . "", $content); + + // Table not found - auto-install module to cause Entity migrations $content = $kernel->dispatch($ld['module'], 'install'); } } diff --git a/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php b/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php index 6c0226a..590ca15 100644 --- a/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php +++ b/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php @@ -9,100 +9,100 @@ */ class Mysql extends PDO_Abstract implements AdapterInterface { - // Format for date columns, formatted for PHP's date() function - protected $_format_date = "Y-m-d"; - protected $_format_time = " H:i:s"; - protected $_format_datetime = "Y-m-d H:i:s"; - - // Driver-Specific settings - protected $_engine = 'InnoDB'; - protected $_charset = 'utf8'; - protected $_collate = 'utf8_unicode_ci'; - - // Map datamapper field types to actual database adapter types - // @todo Have to improve this to allow custom types, callbacks, and validation - protected $_fieldTypeMap = array( - 'string' => array( - 'adapter_type' => 'varchar', - 'length' => 255 - ), - 'email' => array( - 'adapter_type' => 'varchar', - 'length' => 255 - ), - 'url' => array( - 'adapter_type' => 'varchar', - 'length' => 255 - ), - 'tel' => array( - 'adapter_type' => 'varchar', - 'length' => 255 - ), - 'password' => array( - 'adapter_type' => 'varchar', - 'length' => 255 - ), - 'text' => array('adapter_type' => 'text'), - 'int' => array('adapter_type' => 'int'), - 'integer' => array('adapter_type' => 'int'), - 'bool' => array('adapter_type' => 'tinyint', 'length' => 1), - 'boolean' => array('adapter_type' => 'tinyint', 'length' => 1), - 'float' => array('adapter_type' => 'float'), - 'double' => array('adapter_type' => 'double'), - 'date' => array('adapter_type' => 'date'), - 'datetime' => array('adapter_type' => 'datetime'), - 'year' => array('adapter_type' => 'year', 'length' => 4), - 'month' => array('adapter_type' => 'month', 'length' => 2), - 'time' => array('adapter_type' => 'time'), - 'timestamp' => array('adapter_type' => 'int', 'length' => 11) - ); - - - /** - * Set database engine (InnoDB, MyISAM, etc) - */ - public function engine($engine = null) - { - if(null !== $engine) { - $this->_engine = $engine; - } - return $this->_engine; - } - - - /** - * Set character set and MySQL collate string - */ - public function characterSet($charset, $collate = 'utf8_unicode_ci') - { - $this->_charset = $charset; - $this->_collate = $collate; - } - - - /** - * Get columns for current table - * - * @param String $table Table name - * @return Array - */ - protected function getColumnsForTable($table, $source) - { - $tableColumns = array(); - $tblCols = $this->connection()->query("SELECT * FROM information_schema.columns WHERE table_schema = '" . $source . "' AND table_name = '" . $table . "'"); - - if($tblCols) { - while($columnData = $tblCols->fetch(\PDO::FETCH_ASSOC)) { - $tableColumns[$columnData['COLUMN_NAME']] = $columnData; - } - return $tableColumns; - } else { - return false; - } - } - - - /** + // Format for date columns, formatted for PHP's date() function + protected $_format_date = "Y-m-d"; + protected $_format_time = " H:i:s"; + protected $_format_datetime = "Y-m-d H:i:s"; + + // Driver-Specific settings + protected $_engine = 'InnoDB'; + protected $_charset = 'utf8'; + protected $_collate = 'utf8_unicode_ci'; + + // Map datamapper field types to actual database adapter types + // @todo Have to improve this to allow custom types, callbacks, and validation + protected $_fieldTypeMap = array( + 'string' => array( + 'adapter_type' => 'varchar', + 'length' => 255 + ), + 'email' => array( + 'adapter_type' => 'varchar', + 'length' => 255 + ), + 'url' => array( + 'adapter_type' => 'varchar', + 'length' => 255 + ), + 'tel' => array( + 'adapter_type' => 'varchar', + 'length' => 255 + ), + 'password' => array( + 'adapter_type' => 'varchar', + 'length' => 255 + ), + 'text' => array('adapter_type' => 'text'), + 'int' => array('adapter_type' => 'int'), + 'integer' => array('adapter_type' => 'int'), + 'bool' => array('adapter_type' => 'tinyint', 'length' => 1), + 'boolean' => array('adapter_type' => 'tinyint', 'length' => 1), + 'float' => array('adapter_type' => 'float'), + 'double' => array('adapter_type' => 'double'), + 'date' => array('adapter_type' => 'date'), + 'datetime' => array('adapter_type' => 'datetime'), + 'year' => array('adapter_type' => 'year', 'length' => 4), + 'month' => array('adapter_type' => 'month', 'length' => 2), + 'time' => array('adapter_type' => 'time'), + 'timestamp' => array('adapter_type' => 'int', 'length' => 11) + ); + + + /** + * Set database engine (InnoDB, MyISAM, etc) + */ + public function engine($engine = null) + { + if(null !== $engine) { + $this->_engine = $engine; + } + return $this->_engine; + } + + + /** + * Set character set and MySQL collate string + */ + public function characterSet($charset, $collate = 'utf8_unicode_ci') + { + $this->_charset = $charset; + $this->_collate = $collate; + } + + + /** + * Get columns for current table + * + * @param String $table Table name + * @return Array + */ + protected function getColumnsForTable($table, $source) + { + $tableColumns = array(); + $tblCols = $this->connection()->query("SELECT * FROM information_schema.columns WHERE table_schema = '" . $source . "' AND table_name = '" . $table . "'"); + + if($tblCols) { + while($columnData = $tblCols->fetch(\PDO::FETCH_ASSOC)) { + $tableColumns[$columnData['COLUMN_NAME']] = $columnData; + } + return $tableColumns; + } else { + return false; + } + } + + + /** * Ensure migration options are full and have all keys required */ public function formatMigrateOptions(array $options) @@ -115,248 +115,245 @@ public function formatMigrateOptions(array $options) } - /** - * Syntax for each column in CREATE TABLE command - * - * @param string $fieldName Field name - * @param array $fieldInfo Array of field settings - * @return string SQL syntax - */ - public function migrateSyntaxFieldCreate($fieldName, array $fieldInfo) - { - // Ensure field type exists - if(!isset($this->_fieldTypeMap[$fieldInfo['type']])) { - throw new \Spot\Exception("Field type '" . $fieldInfo['type'] . "' not supported"); - } - - $fieldInfo = array_merge($fieldInfo, $this->_fieldTypeMap[$fieldInfo['type']]); - - $syntax = "`" . $fieldName . "` " . $fieldInfo['adapter_type']; - // Column type and length - $syntax .= ($fieldInfo['length']) ? '(' . $fieldInfo['length'] . ')' : ''; - // Unsigned - $syntax .= ($fieldInfo['unsigned']) ? ' unsigned' : ''; - // Collate - $syntax .= ($fieldInfo['type'] == 'string' || $fieldInfo['type'] == 'text') ? ' COLLATE ' . $this->_collate : ''; - // Nullable - $isNullable = true; - if($fieldInfo['required'] || !$fieldInfo['null']) { - $syntax .= ' NOT NULL'; - $isNullable = false; - } - // Default value - if($fieldInfo['default'] === null && $isNullable) { - $syntax .= " DEFAULT NULL"; - } elseif($fieldInfo['default'] !== null) { - $default = $fieldInfo['default']; - // If it's a boolean and $default is boolean then it should be 1 or 0 - if ( is_bool($default) && $fieldInfo['type'] == "boolean" ) { - $default = $default ? 1 : 0; - } - - if(is_scalar($default)) { - $syntax .= " DEFAULT '" . $default . "'"; - } - } - // Extra - $syntax .= ($fieldInfo['primary'] && $fieldInfo['serial']) ? ' AUTO_INCREMENT' : ''; - return $syntax; - } - - - /** - * Syntax for CREATE TABLE with given fields and column syntax - * - * @param string $table Table name - * @param array $formattedFields Array of fields with all settings - * @param array $columnsSyntax Array of SQL syntax of columns produced by 'migrateSyntaxFieldCreate' function - * @param Array $options Options that may affect migrations or how tables are setup - * @return string SQL syntax - */ - public function migrateSyntaxTableCreate($table, array $formattedFields, array $columnsSyntax, array $options) - { - $options = $this->formatMigrateOptions($options); - - // Begin syntax soup - $syntax = "CREATE TABLE IF NOT EXISTS `" . $table . "` (\n"; - // Columns - $syntax .= implode(",\n", $columnsSyntax); - - // Keys... - $ki = 0; - $tableKeys = array( - 'primary' => array(), - 'unique' => array(), - 'index' => array() - ); - $fulltextFields = array(); - $usedKeyNames = array(); - foreach($formattedFields as $fieldName => $fieldInfo) { - // Determine key field name (can't use same key name twice, so we have to append a number) - $fieldKeyName = $fieldName; - while(in_array($fieldKeyName, $usedKeyNames)) { - $fieldKeyName = $fieldName . '_' . $ki; - } - // Key type - if($fieldInfo['primary']) { - $tableKeys['primary'][] = $fieldName; - } - if($fieldInfo['unique']) { - if(is_string($fieldInfo['unique'])) { - // Named group - $fieldKeyName = $fieldInfo['unique']; - } - $tableKeys['unique'][$fieldKeyName][] = $fieldName; - $usedKeyNames[] = $fieldKeyName; - } - if($fieldInfo['index']) { - if(is_string($fieldInfo['index'])) { - // Named group - $fieldKeyName = $fieldInfo['index']; - } - $tableKeys['index'][$fieldKeyName][] = $fieldName; - $usedKeyNames[] = $fieldKeyName; - } - // FULLTEXT search - if($fieldInfo['fulltext']) { - $fulltextFields[] = $fieldName; - } - } - - // FULLTEXT - if($fulltextFields) { - // Ensure table type is MyISAM if FULLTEXT columns have been specified - if('myisam' !== strtolower($options['engine'])) { - $options['engine'] = 'MyISAM'; - } - $syntax .= "\n, FULLTEXT(`" . implode('`, `', $fulltextFields) . "`)"; - } - - // PRIMARY - if($tableKeys['primary']) { - $syntax .= "\n, PRIMARY KEY(`" . implode('`, `', $tableKeys['primary']) . "`)"; - } - // UNIQUE - foreach($tableKeys['unique'] as $keyName => $keyFields) { - $syntax .= "\n, UNIQUE KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; - } - // INDEX - foreach($tableKeys['index'] as $keyName => $keyFields) { - $syntax .= "\n, KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; - } - - // Extra - $syntax .= "\n) ENGINE=" . $options['engine'] . " DEFAULT CHARSET=" . $options['charset'] . " COLLATE=" . $options['collate'] . ";"; - - return $syntax; - } - - - /** - * Syntax for each column in CREATE TABLE command - * - * @param string $fieldName Field name - * @param array $fieldInfo Array of field settings - * @return string SQL syntax - */ - public function migrateSyntaxFieldUpdate($fieldName, array $fieldInfo, $add = false) - { - return ( $add ? "ADD COLUMN " : "MODIFY " ) . $this->migrateSyntaxFieldCreate($fieldName, $fieldInfo); - } - - - /** - * Syntax for ALTER TABLE with given fields and column syntax - * - * @param string $table Table name - * @param array $formattedFields Array of fields with all settings - * @param array $columnsSyntax Array of SQL syntax of columns produced by 'migrateSyntaxFieldUpdate' function - * @return string SQL syntax - */ - public function migrateSyntaxTableUpdate($table, array $formattedFields, array $columnsSyntax, array $options) - { - /* - Example: - - ALTER TABLE `posts` - CHANGE `title` `title` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , - CHANGE `status` `status` VARCHAR( 40 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT 'draft' - */ - - $options = $this->formatMigrateOptions($options); - - // Begin syntax soup - $syntax = "ALTER TABLE `" . $table . "` \n"; - - // Columns - $syntax .= implode(",\n", $columnsSyntax); - - // Keys... - $ki = 0; - $tableKeys = array( - 'primary' => array(), - 'unique' => array(), - 'index' => array() - ); - $fulltextFields = array(); - $usedKeyNames = array(); - foreach($formattedFields as $fieldName => $fieldInfo) { - // Determine key field name (can't use same key name twice, so we have to append a number) - $fieldKeyName = $fieldName; - while(in_array($fieldKeyName, $usedKeyNames)) { - $fieldKeyName = $fieldName . '_' . $ki; - } - // Key type - if($fieldInfo['primary']) { - $tableKeys['primary'][] = $fieldName; - } - if($fieldInfo['unique']) { - if(is_string($fieldInfo['unique'])) { - // Named group - $fieldKeyName = $fieldInfo['unique']; - } - $tableKeys['unique'][$fieldKeyName][] = $fieldName; - $usedKeyNames[] = $fieldKeyName; - } - if($fieldInfo['index']) { - if(is_string($fieldInfo['index'])) { - // Named group - $fieldKeyName = $fieldInfo['index']; - } - $tableKeys['index'][$fieldKeyName][] = $fieldName; - $usedKeyNames[] = $fieldKeyName; - } - // FULLTEXT search - if($fieldInfo['fulltext']) { - $fulltextFields[] = $fieldName; - } - } - - // FULLTEXT - if($fulltextFields) { - // Ensure table type is MyISAM if FULLTEXT columns have been specified - if('myisam' !== strtolower($options['engine'])) { - $options['engine'] = 'MyISAM'; - } - $syntax .= "\n, FULLTEXT(`" . implode('`, `', $fulltextFields) . "`)"; - } - - // PRIMARY - if($tableKeys['primary']) { - $syntax .= "\n, PRIMARY KEY(`" . implode('`, `', $tableKeys['primary']) . "`)"; - } - // UNIQUE - foreach($tableKeys['unique'] as $keyName => $keyFields) { - $syntax .= "\n, UNIQUE KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; - } - // INDEX - foreach($tableKeys['index'] as $keyName => $keyFields) { - $syntax .= "\n, KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; - } - - // Extra - $syntax .= "\n) ENGINE=" . $options['engine'] . " DEFAULT CHARSET=" . $options['charset'] . " COLLATE=" . $options['collate'] . ";"; - - return $syntax; - } + /** + * Syntax for each column in CREATE TABLE command + * + * @param string $fieldName Field name + * @param array $fieldInfo Array of field settings + * @return string SQL syntax + */ + public function migrateSyntaxFieldCreate($fieldName, array $fieldInfo) + { + // Ensure field type exists + if(!isset($this->_fieldTypeMap[$fieldInfo['type']])) { + throw new \Spot\Exception("Field type '" . $fieldInfo['type'] . "' not supported"); + } + + $fieldInfo = array_merge($fieldInfo, $this->_fieldTypeMap[$fieldInfo['type']]); + + $syntax = "`" . $fieldName . "` " . $fieldInfo['adapter_type']; + // Column type and length + $syntax .= ($fieldInfo['length']) ? '(' . $fieldInfo['length'] . ')' : ''; + // Unsigned + $syntax .= ($fieldInfo['unsigned']) ? ' unsigned' : ''; + // Collate + $syntax .= ($fieldInfo['type'] == 'string' || $fieldInfo['type'] == 'text') ? ' COLLATE ' . $this->_collate : ''; + // Nullable + $isNullable = true; + if($fieldInfo['required'] || !$fieldInfo['null']) { + $syntax .= ' NOT NULL'; + $isNullable = false; + } + // Default value + if($fieldInfo['default'] === null && $isNullable) { + $syntax .= " DEFAULT NULL"; + } elseif($fieldInfo['default'] !== null) { + $default = $fieldInfo['default']; + // If it's a boolean and $default is boolean then it should be 1 or 0 + if ( is_bool($default) && $fieldInfo['type'] == "boolean" ) { + $default = $default ? 1 : 0; + } + + if(is_scalar($default)) { + $syntax .= " DEFAULT '" . $default . "'"; + } + } + // Extra + $syntax .= ($fieldInfo['primary'] && $fieldInfo['serial']) ? ' AUTO_INCREMENT' : ''; + return $syntax; + } + + + /** + * Syntax for CREATE TABLE with given fields and column syntax + * + * @param string $table Table name + * @param array $formattedFields Array of fields with all settings + * @param array $columnsSyntax Array of SQL syntax of columns produced by 'migrateSyntaxFieldCreate' function + * @param Array $options Options that may affect migrations or how tables are setup + * @return string SQL syntax + */ + public function migrateSyntaxTableCreate($table, array $formattedFields, array $columnsSyntax, array $options) + { + $options = $this->formatMigrateOptions($options); + + // Begin syntax soup + $syntax = "CREATE TABLE IF NOT EXISTS `" . $table . "` (\n"; + // Columns + $syntax .= implode(",\n", $columnsSyntax); + + // Keys... + $ki = 0; + $tableKeys = array( + 'primary' => array(), + 'unique' => array(), + 'index' => array() + ); + $fulltextFields = array(); + $usedKeyNames = array(); + foreach($formattedFields as $fieldName => $fieldInfo) { + // Determine key field name (can't use same key name twice, so we have to append a number) + $fieldKeyName = $fieldName; + while(in_array($fieldKeyName, $usedKeyNames)) { + $fieldKeyName = $fieldName . '_' . $ki; + } + // Key type + if($fieldInfo['primary']) { + $tableKeys['primary'][] = $fieldName; + } + if($fieldInfo['unique']) { + if(is_string($fieldInfo['unique'])) { + // Named group + $fieldKeyName = $fieldInfo['unique']; + } + $tableKeys['unique'][$fieldKeyName][] = $fieldName; + $usedKeyNames[] = $fieldKeyName; + } + if($fieldInfo['index']) { + if(is_string($fieldInfo['index'])) { + // Named group + $fieldKeyName = $fieldInfo['index']; + } + $tableKeys['index'][$fieldKeyName][] = $fieldName; + $usedKeyNames[] = $fieldKeyName; + } + // FULLTEXT search + if($fieldInfo['fulltext']) { + $fulltextFields[] = $fieldName; + } + } + + // FULLTEXT + if($fulltextFields) { + // Ensure table type is MyISAM if FULLTEXT columns have been specified + if('myisam' !== strtolower($options['engine'])) { + $options['engine'] = 'MyISAM'; + } + $syntax .= "\n, FULLTEXT(`" . implode('`, `', $fulltextFields) . "`)"; + } + + // PRIMARY + if($tableKeys['primary']) { + $syntax .= "\n, PRIMARY KEY(`" . implode('`, `', $tableKeys['primary']) . "`)"; + } + // UNIQUE + foreach($tableKeys['unique'] as $keyName => $keyFields) { + $syntax .= "\n, UNIQUE KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; + } + // INDEX + foreach($tableKeys['index'] as $keyName => $keyFields) { + $syntax .= "\n, KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; + } + + // Extra + $syntax .= "\n) ENGINE=" . $options['engine'] . " DEFAULT CHARSET=" . $options['charset'] . " COLLATE=" . $options['collate'] . ";"; + + return $syntax; + } + + + /** + * Syntax for each column in CREATE TABLE command + * + * @param string $fieldName Field name + * @param array $fieldInfo Array of field settings + * @return string SQL syntax + */ + public function migrateSyntaxFieldUpdate($fieldName, array $fieldInfo, $add = false) + { + return ( $add ? "ADD COLUMN " : "MODIFY " ) . $this->migrateSyntaxFieldCreate($fieldName, $fieldInfo); + } + + + /** + * Syntax for ALTER TABLE with given fields and column syntax + * + * @param string $table Table name + * @param array $formattedFields Array of fields with all settings + * @param array $columnsSyntax Array of SQL syntax of columns produced by 'migrateSyntaxFieldUpdate' function + * @return string SQL syntax + */ + public function migrateSyntaxTableUpdate($table, array $formattedFields, array $columnsSyntax, array $options) + { + /* + Example: + + ALTER TABLE `posts` + CHANGE `title` `title` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , + CHANGE `status` `status` VARCHAR( 40 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT 'draft' + */ + + $options = $this->formatMigrateOptions($options); + + // Begin syntax soup + $syntax = "ALTER TABLE `" . $table . "` \n"; + + // Columns + $syntax .= implode(",\n", $columnsSyntax); + + // Keys... + $ki = 0; + $tableKeys = array( + 'primary' => array(), + 'unique' => array(), + 'index' => array() + ); + $fulltextFields = array(); + $usedKeyNames = array(); + foreach($formattedFields as $fieldName => $fieldInfo) { + // Determine key field name (can't use same key name twice, so we have to append a number) + $fieldKeyName = $fieldName; + while(in_array($fieldKeyName, $usedKeyNames)) { + $fieldKeyName = $fieldName . '_' . $ki; + } + // Key type + if($fieldInfo['primary']) { + $tableKeys['primary'][] = $fieldName; + } + if($fieldInfo['unique']) { + if(is_string($fieldInfo['unique'])) { + // Named group + $fieldKeyName = $fieldInfo['unique']; + } + $tableKeys['unique'][$fieldKeyName][] = $fieldName; + $usedKeyNames[] = $fieldKeyName; + } + if($fieldInfo['index']) { + if(is_string($fieldInfo['index'])) { + // Named group + $fieldKeyName = $fieldInfo['index']; + } + $tableKeys['index'][$fieldKeyName][] = $fieldName; + $usedKeyNames[] = $fieldKeyName; + } + // FULLTEXT search + if($fieldInfo['fulltext']) { + $fulltextFields[] = $fieldName; + } + } + + // FULLTEXT + if($fulltextFields) { + // Ensure table type is MyISAM if FULLTEXT columns have been specified + if('myisam' !== strtolower($options['engine'])) { + throw new \Spot\Exception("FULLTEXT columns are only allowed using the MyISAM storage engine. Engine is currently: '" . $options['engine'] . "'."); + } + $syntax .= "\n, FULLTEXT(`" . implode('`, `', $fulltextFields) . "`)"; + } + + // PRIMARY + if($tableKeys['primary']) { + $syntax .= "\n, PRIMARY KEY(`" . implode('`, `', $tableKeys['primary']) . "`)"; + } + // UNIQUE + foreach($tableKeys['unique'] as $keyName => $keyFields) { + $syntax .= "\n, UNIQUE KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; + } + // INDEX + foreach($tableKeys['index'] as $keyName => $keyFields) { + $syntax .= "\n, KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; + } + + return $syntax; + } } diff --git a/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php b/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php index a5f193e..c002d95 100644 --- a/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php +++ b/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php @@ -141,6 +141,9 @@ public function migrateTableUpdate($table, array $formattedFields, array $option * Use column syntax array to get table syntax * Run SQL */ + + //var_dump($formattedFields); + //exit(__FILE__); // Prepare fields and get syntax for each $tableColumns = $this->getColumnsForTable($table, $this->_database); @@ -571,7 +574,7 @@ public function statementConditions(array $conditions = array(), $ci = 0) // MATCH(col) AGAINST(search) case ':fulltext': $colParam = preg_replace('/\W+/', '_', $col) . $ci; - $whereClause = "MATCH(" . $col . ") AGAINST(:" . $colParam . ")"; + $whereClause = "MATCH(" . $col . ") AGAINST(:" . $colParam . " IN BOOLEAN MODE)"; break; // ALL - Find ALL values in a set - Kind of like IN(), but seeking *all* the values case ':all': diff --git a/alloy/Plugin/Spot/lib/Spot/Entity.php b/alloy/Plugin/Spot/lib/Spot/Entity.php index 36c5322..96ea690 100644 --- a/alloy/Plugin/Spot/lib/Spot/Entity.php +++ b/alloy/Plugin/Spot/lib/Spot/Entity.php @@ -128,33 +128,32 @@ public function data($data = null, $modified = true) } } - /** - * Return array of field data with data from the field names listed removed - * - * @param array List of field names to exclude in data list returned + * Alias of self::data() */ - public function dataExcept(array $except) + public function toArray() { - return array_diff_key($this->data(), array_flip($except)); + return $this->data(); } /** - * Gets data that has been modified since object construct + * Return array of field data with data from the field names listed removed + * + * @param array List of field names to exclude in data list returned */ - public function dataModified() - { - return $this->_dataModified; - } - + public function dataExcept(array $except) + { + return array_diff_key($this->data(), array_flip($except)); + } + /** - * Alias of self::data() + * Gets data that has been modified since object construct */ - public function toArray() + public function dataModified() { - return $this->data(); + return $this->_dataModified; } diff --git a/alloy/config/routes.php b/alloy/config/routes.php index 27e4076..013e173 100644 --- a/alloy/config/routes.php +++ b/alloy/config/routes.php @@ -16,7 +16,8 @@ ->post(array('action' => 'post')); $router->route('module', '/<:module>(.<:format>)') // :format optional - ->defaults(array('action' => 'index', 'format' => 'html')); + ->defaults(array('action' => 'index', 'format' => 'html')) + ->post(array('action' => 'post')); $router->route('default', '/') - ->defaults(array('module' => 'Home', 'action' => 'index', 'format' => 'html')); + ->defaults(array('module' => 'Home', 'action' => 'index', 'format' => 'html')); \ No newline at end of file diff --git a/alloy/lib/Alloy/App.php b/alloy/lib/Alloy/App.php new file mode 100644 index 0000000..29195fe --- /dev/null +++ b/alloy/lib/Alloy/App.php @@ -0,0 +1,33 @@ +header('USER_AGENT') == 'Shockwave Flash'); + } +} \ No newline at end of file diff --git a/alloy/lib/Alloy/Kernel.php b/alloy/lib/Alloy/Kernel.php index 3dc07bc..4fe3be5 100644 --- a/alloy/lib/Alloy/Kernel.php +++ b/alloy/lib/Alloy/Kernel.php @@ -547,7 +547,7 @@ public function url($params = array(), $routeName = null, $queryParams = array() } if(count($queryParams) > 0) { // Build query string from array $qsData - $queryString = http_build_query($queryParams, '', '&'); + $queryString = http_build_query($queryParams, '', '&'); } else { $queryString = false; } @@ -559,7 +559,7 @@ public function url($params = array(), $routeName = null, $queryParams = array() if($this->config('url.rewrite')) { $url = $urlBase . $url . (($queryString !== false) ? '?' . $queryString : ''); } else { - $url = $urlBase . '?u=' . $url . (($queryString !== false) ? '&' . $queryString : ''); + $url = $urlBase . '?u=' . $url . (($queryString !== false) ? '&' . $queryString : ''); } // Return fully assembled URL diff --git a/alloy/lib/Alloy/Request.php b/alloy/lib/Alloy/Request.php index 15ba86b..5069000 100644 --- a/alloy/lib/Alloy/Request.php +++ b/alloy/lib/Alloy/Request.php @@ -13,9 +13,12 @@ */ class Request { + // Request URL + protected $_url; + // Request parameters protected $_params = array(); - + /** * Ensure magic quotes are not mucking up request data @@ -32,16 +35,61 @@ public function __construct() array_walk_recursive($_COOKIE, $stripslashes_gpc); array_walk_recursive($_REQUEST, $stripslashes_gpc); } + + // Properly handle PUT and DELETE request params + if($this->isPut() || $this->isDelete()) { + parse_str(file_get_contents('php://input'), $params); + $this->params($params); + } + } + + + /** + * Return requested URL path + * + * Works for HTTP(S) requests and CLI requests using the -u flag for URL dispatch emulation + * + * @return string Requested URL path segement + */ + public function url() + { + if(null === $this->_url) { + if($this->isCli()) { + // CLI request + $cliArgs = getopt("u:"); + + $requestUrl = isset($cliArgs['u']) ? $cliArgs['u'] : '/'; + $qs = parse_url($requestUrl, PHP_URL_QUERY); + $cliRequestParams = array(); + parse_str($qs, $cliRequestParams); + + // Set parsed query params back on request object + $this->setParams($cliRequestParams); + + // Set requestUrl and remove query string if present so router can parse it as expected + if($qsPos = strpos($requestUrl, '?')) { + $requestUrl = substr($requestUrl, 0, $qsPos); + } + + } else { + // HTTP request + $requestUrl = $this->get('u', '/'); + } + $this->_url = $requestUrl; + } + + return $this->_url; } + /** - * Access values contained in the superglobals as public members - * Order of precedence: 1. GET, 2. POST, 3. COOKIE, 4. SERVER, 5. ENV - * - * @see http://msdn.microsoft.com/en-us/library/system.web.httprequest.item.aspx - * @param string $key - * @return mixed - */ + * Access values contained in the superglobals as public members + * Order of precedence: 1. GET, 2. POST, 3. COOKIE, 4. SERVER, 5. ENV + * + * @see http://msdn.microsoft.com/en-us/library/system.web.httprequest.item.aspx + * @param string $key + * @return mixed + */ public function get($key, $default = null) { switch (true) { @@ -219,7 +267,8 @@ public function param($key = null, $default = null) public function query($key = null, $default = null) { if (null === $key) { - return $_GET; + // Return _GET params without routing param or other params set by Alloy or manually on the request object + return array_diff_key($_GET, $this->param() + array('u' => 1)); } return (isset($_GET[$key])) ? $_GET[$key] : $default; @@ -333,20 +382,20 @@ public function header($header) /** - * Return the method by which the request was made + * Return the method by which the request was made. Always returns HTTP_METHOD in UPPERCASE. * - * @return string + * @return string HTTP Request method in UPPERCASE */ public function method() { - $method = isset($_SERVER['REQUEST_METHOD']) ? strtoupper($_SERVER['REQUEST_METHOD']) : 'GET'; + $sm = strtoupper($this->server('REQUEST_METHOD', 'GET')); - // Emulate REST for browsers - if($method == "POST" && $this->post('_method')) { - $method = strtoupper($this->post('_method')); + // POST + '_method' override to emulate REST behavior in browsers that do not support it + if('POST' == $sm && $this->get('_method')) { + return strtoupper($this->get('_method')); } - return $method; + return $sm; } @@ -435,6 +484,17 @@ public function isHead() { return ($this->method() == "HEAD"); } + + + /** + * Determine is incoming request is OPTIONS + * + * @return boolean + */ + public function isOptions() + { + return ($this->method() == "OPTIONS"); + } /** diff --git a/alloy/lib/Alloy/View/Generic/Treeview.php b/alloy/lib/Alloy/View/Generic/Treeview.php index 50932bf..edc44d3 100644 --- a/alloy/lib/Alloy/View/Generic/Treeview.php +++ b/alloy/lib/Alloy/View/Generic/Treeview.php @@ -220,17 +220,6 @@ public function levelMax($level = null) $this->set('levelMax', $level); return $this; } - - - /** - * Get currnet level - * - * @param int $level Current level - */ - public function level() - { - return self::$_level; - } /** diff --git a/alloy/lib/Alloy/View/Generic/templates/treeview.html.php b/alloy/lib/Alloy/View/Generic/templates/treeview.html.php index ea9f2ac..a2c2df5 100644 --- a/alloy/lib/Alloy/View/Generic/templates/treeview.html.php +++ b/alloy/lib/Alloy/View/Generic/templates/treeview.html.php @@ -2,10 +2,10 @@ $currentLevel = $view::$_level; // Check if we're okay to display against min level set -$levelMinCheck = (!$levelMin || $currentLevel > $levelMin); +$levelMinCheck = (!$levelMin || $currentLevel >= $levelMin); // Check if we're okay to display against max level set -$levelMaxCheck = (!$levelMax || $currentLevel < $levelMax); +$levelMaxCheck = (!$levelMax || $currentLevel <= $levelMax); if($levelMaxCheck): @@ -38,7 +38,7 @@ // Ensure we can go to next level // Don't show children if current level is equal to max - if(!$levelMax || ($levelMaxCheck && $currentLevel != $levelMax)): + if($levelMaxCheck && $currentLevel != $levelMax): // Item children (hierarchy) if(isset($itemChildrenCallback)): $children = $itemChildrenCallback($item); diff --git a/app/Module/Filebrowser/Controller.php b/app/Module/Filebrowser/Controller.php index fc67081..3c7f6af 100755 --- a/app/Module/Filebrowser/Controller.php +++ b/app/Module/Filebrowser/Controller.php @@ -218,9 +218,9 @@ public function postMethod(Alloy\Request $request) // Redirect to images or files if($subDir = 'images') { - return $kernel->redirect($kernel->url(array('action' => 'images'), 'filebrowser')); + return $kernel->redirect($kernel->url(array('action' => 'images'), 'filebrowser', $request->query())); } - return $kernel->redirect($kernel->url(array('action' => 'files'), 'filebrowser')); + return $kernel->redirect($kernel->url(array('action' => 'files'), 'filebrowser', $request->query())); } else { return $kernel->resource() ->status(400) diff --git a/app/Module/Filebrowser/views/directoryList.html.php b/app/Module/Filebrowser/views/directoryList.html.php index 19b6296..7ddb36e 100644 --- a/app/Module/Filebrowser/views/directoryList.html.php +++ b/app/Module/Filebrowser/views/directoryList.html.php @@ -65,7 +65,7 @@
    - <?php echo $file->getFilename(); ?> + <?php echo $file->getFilename(); ?>
diff --git a/app/Module/Filebrowser/views/newAction.html.php b/app/Module/Filebrowser/views/newAction.html.php index bb4aa85..7dd7670 100644 --- a/app/Module/Filebrowser/views/newAction.html.php +++ b/app/Module/Filebrowser/views/newAction.html.php @@ -1,7 +1,7 @@ generic('form') ->type('upload') - ->action($view->url(array('action' => 'new'), 'filebrowser')) + ->action($view->url(array('action' => 'new'), 'filebrowser', \Kernel()->request()->query())) ->method('post') ->fields(array( 'upload' => array('type' => 'file') From a0627b4968231aed8b403f5b7bb3aa731565906a Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 30 Nov 2011 14:06:16 -0600 Subject: [PATCH 10/50] Updated Alloy generic Treeview with 'level' method added back in --- alloy/lib/Alloy/View/Generic/Treeview.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/alloy/lib/Alloy/View/Generic/Treeview.php b/alloy/lib/Alloy/View/Generic/Treeview.php index edc44d3..ad092d8 100644 --- a/alloy/lib/Alloy/View/Generic/Treeview.php +++ b/alloy/lib/Alloy/View/Generic/Treeview.php @@ -198,6 +198,17 @@ public function filter($callback) } + /** + * Get currnet level + * + * @param int $level Current level + */ + public function level() + { + return self::$_level; + } + + /** * Set minimum level at which to begin item display * From 0bce07c7c336f9c7611efa8e321a76dc44abeb77 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 7 Dec 2011 11:04:33 -0600 Subject: [PATCH 11/50] Updating formView() method to match abstract --- app/Module/Page/Module/Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Module/Page/Module/Controller.php b/app/Module/Page/Module/Controller.php index d445667..e7f8ec3 100755 --- a/app/Module/Page/Module/Controller.php +++ b/app/Module/Page/Module/Controller.php @@ -136,7 +136,7 @@ public function saveSortAction($request, $page, $module) /** * Return view object for the add/edit form */ - protected function formView() + protected function formView($entityName = null) { $view = $this->kernel->spotForm('Module\Page\Module\Entity') ->removeFields(array('id', 'date_created', 'date_modified')); From 9b767fccc608d64b751a1d829708cd028721b5cd Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Thu, 8 Dec 2011 00:18:53 -0600 Subject: [PATCH 12/50] Changed the way placeable modules are detected * Now requires file named '_module.php' to exist in module directories * Will enable **ANY** module with the above module file as placeable * Allows for nested placeable modules within a single parent module * New '_module.php' file can be used for module config in the future --- app/Module/Page/Controller.php | 10 +++++- app/Module/Page/views/_adminBar.html.php | 32 +++++++++++++++---- app/www/content/Module/Blog/_module.php | 4 +++ app/www/content/Module/Code/_module.php | 4 +++ app/www/content/Module/Navigation/_module.php | 4 +++ app/www/content/Module/Slideshow/_module.php | 4 +++ app/www/content/Module/Text/_module.php | 4 +++ 7 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 app/www/content/Module/Blog/_module.php create mode 100644 app/www/content/Module/Code/_module.php create mode 100644 app/www/content/Module/Navigation/_module.php create mode 100644 app/www/content/Module/Slideshow/_module.php create mode 100644 app/www/content/Module/Text/_module.php diff --git a/app/Module/Page/Controller.php b/app/Module/Page/Controller.php index be0e418..65f9103 100755 --- a/app/Module/Page/Controller.php +++ b/app/Module/Page/Controller.php @@ -180,7 +180,15 @@ public function viewUrl($pageUrl) } // Dispatch to module's 'indexAction' to display module content on page - $moduleResponse = $kernel->dispatch($moduleObject, 'indexAction', array($request, $page, $module)); + try { + $moduleResponse = $kernel->dispatch($moduleObject, 'indexAction', array($request, $page, $module)); + } catch(\Exception $e) { + // Catch module exeptions and render them inside region where module would have gone + // (allows users to delete and edit bad modules instead of killing the whole page and forcing manual database modification) + $moduleResponse = $kernel->events('cms')->filter('module_dispatch_exception', $e); + } + + // Setup named region array for module content if(!isset($regionModules[$module->region]) || !is_array($regionModules[$module->region])) { $regionModules[$module->region] = array(); } diff --git a/app/Module/Page/views/_adminBar.html.php b/app/Module/Page/views/_adminBar.html.php index a222046..c0d8ae3 100755 --- a/app/Module/Page/views/_adminBar.html.php +++ b/app/Module/Page/views/_adminBar.html.php @@ -39,19 +39,37 @@
site(); + $moduleConfig = function(\SplFileInfo $file) use($site, $kernel) { + // Require config file + $cfg = require $file->getRealPath(); + + // Set standardized config values based on SplFileInfo object + $cfg['dirname'] = strrchr($file->getPath(), '/'); + $cfg['path_dir'] = $file->getRealPath(); + $cfg['is_site'] = !(false === strpos($cfg['path_dir'], $site->dir())); + if($cfg['is_site']) { + $cfg['url_dir'] = $site->url() . $kernel->config('cms.dir.modules') . 'Module/Site' . $cfg['dirname']; + } else { + $cfg['url_dir'] = $kernel->config('url.root') . $kernel->config('cms.dir.modules') . 'Module' . $cfg['dirname']; + } + return $cfg; + }; + // Module Files $moduleDirsPath = $kernel->config('cms.path.modules'); $moduleDirs = $kernel->finder() - ->directories() + ->files() + ->name('_module.php') ->in($kernel->site()->moduleDirs()) - ->depth(1) ->sortByName(); - foreach($moduleDirs as $mDir): - $moduleAssetsUrl = $kernel->config('url.root') . $kernel->config('cms.dir.modules') . 'Module/' . $mDir->getFilename() . '/assets/'; + foreach($moduleDirs as $mFile): + $mCfg = $moduleConfig($mFile); ?> -
-

getFilename(); ?>

-
<?php echo $mDir->getFilename(); ?> Module
+
+

+
<?php echo $mCfg['name']; ?> Module
diff --git a/app/www/content/Module/Blog/_module.php b/app/www/content/Module/Blog/_module.php new file mode 100644 index 0000000..6152ddb --- /dev/null +++ b/app/www/content/Module/Blog/_module.php @@ -0,0 +1,4 @@ + substr(strrchr(__DIR__, '/'), 1) +); \ No newline at end of file diff --git a/app/www/content/Module/Code/_module.php b/app/www/content/Module/Code/_module.php new file mode 100644 index 0000000..6152ddb --- /dev/null +++ b/app/www/content/Module/Code/_module.php @@ -0,0 +1,4 @@ + substr(strrchr(__DIR__, '/'), 1) +); \ No newline at end of file diff --git a/app/www/content/Module/Navigation/_module.php b/app/www/content/Module/Navigation/_module.php new file mode 100644 index 0000000..6152ddb --- /dev/null +++ b/app/www/content/Module/Navigation/_module.php @@ -0,0 +1,4 @@ + substr(strrchr(__DIR__, '/'), 1) +); \ No newline at end of file diff --git a/app/www/content/Module/Slideshow/_module.php b/app/www/content/Module/Slideshow/_module.php new file mode 100644 index 0000000..6152ddb --- /dev/null +++ b/app/www/content/Module/Slideshow/_module.php @@ -0,0 +1,4 @@ + substr(strrchr(__DIR__, '/'), 1) +); \ No newline at end of file diff --git a/app/www/content/Module/Text/_module.php b/app/www/content/Module/Text/_module.php new file mode 100644 index 0000000..6152ddb --- /dev/null +++ b/app/www/content/Module/Text/_module.php @@ -0,0 +1,4 @@ + substr(strrchr(__DIR__, '/'), 1) +); \ No newline at end of file From bb31e4970bc1f090dd54b127c692456d4e5e9a44 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Thu, 8 Dec 2011 00:23:44 -0600 Subject: [PATCH 13/50] Updated base config file to add more i18n entries for currency --- app/config/app.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/config/app.php b/app/config/app.php index 085cf97..740f7a8 100755 --- a/app/config/app.php +++ b/app/config/app.php @@ -91,7 +91,11 @@ 'language' => 'en_US', 'timezone' => 'America/Chicago', 'date_format' => 'M d, Y', - 'time_format' => 'H:i:s' + 'time_format' => 'H:i:s', + 'currency' => array( + 'code' => 'USD', + 'symbol' => '$' + ) ); return $cfg + array('app' => $app); \ No newline at end of file From a83f13619f079ec2eb85e51f4c35e4cba62f048d Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Thu, 8 Dec 2011 23:11:20 -0600 Subject: [PATCH 14/50] Fix for Nginx URL paths --- app/config/app.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/config/app.php b/app/config/app.php index 740f7a8..19c8f80 100755 --- a/app/config/app.php +++ b/app/config/app.php @@ -25,6 +25,9 @@ $requestPath = parse_url(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/', PHP_URL_PATH); $urlBase = str_replace($requestUrl, '', $requestPath); +// Fixes nginx try_files uri passing (thanks Alon Rohter) +$urlBase = (substr($urlBase,0,1) === '/') ? $urlBase : '/' . $urlBase; + // URL info $isHttps = (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) != 'on') ? false : true; $urlHost = (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'); From 03bd8c15cf4a2172901e0be0db924e73babd263d Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 10 Dec 2011 20:38:54 -0600 Subject: [PATCH 15/50] Alloy: Generic view templates update --- alloy/lib/Alloy/View/Generic/templates/cellgrid.html.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alloy/lib/Alloy/View/Generic/templates/cellgrid.html.php b/alloy/lib/Alloy/View/Generic/templates/cellgrid.html.php index a306e5c..875ae43 100644 --- a/alloy/lib/Alloy/View/Generic/templates/cellgrid.html.php +++ b/alloy/lib/Alloy/View/Generic/templates/cellgrid.html.php @@ -23,11 +23,11 @@ $ri = 0; endif; endforeach; - else: + elseif(isset($noDataCallback)): ?> - + From 43ae9d9e58123fcb7b094396cf27f2978cd5a50a Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 17 Dec 2011 23:01:55 -0600 Subject: [PATCH 16/50] Updated Spot Plugin with files from newest project version --- .../Spot/lib/Spot/Adapter/AdapterAbstract.php | 28 +- .../lib/Spot/Adapter/AdapterInterface.php | 8 +- .../Plugin/Spot/lib/Spot/Adapter/Mongodb.php | 20 +- alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php | 675 +++++++++--------- .../Spot/lib/Spot/Adapter/PDO/Abstract.php | 57 +- alloy/Plugin/Spot/lib/Spot/Entity.php | 29 +- alloy/Plugin/Spot/lib/Spot/Mapper.php | 26 +- alloy/Plugin/Spot/lib/Spot/Query.php | 8 +- 8 files changed, 465 insertions(+), 386 deletions(-) diff --git a/alloy/Plugin/Spot/lib/Spot/Adapter/AdapterAbstract.php b/alloy/Plugin/Spot/lib/Spot/Adapter/AdapterAbstract.php index d555d67..f3ea4a2 100644 --- a/alloy/Plugin/Spot/lib/Spot/Adapter/AdapterAbstract.php +++ b/alloy/Plugin/Spot/lib/Spot/Adapter/AdapterAbstract.php @@ -150,17 +150,17 @@ protected function dateTimeObject($format) * * The format of the supplied DSN is in its fullest form: * - * adapter(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true + * adapter(dbsyntax)://username:password@protocol+host/database?option=8&another=true * * * Most variations are allowed: * - * adapter://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644 - * adapter://username:password@hostspec/database_name - * adapter://username:password@hostspec - * adapter://username@hostspec - * adapter://hostspec/database - * adapter://hostspec + * adapter://username:password@protocol+host:110//usr/db_file.db?mode=0644 + * adapter://username:password@host/database_name + * adapter://username:password@host + * adapter://username@host + * adapter://host/database + * adapter://host * adapter(dbsyntax) * adapter * @@ -173,7 +173,7 @@ protected function dateTimeObject($format) * + adapter: Database backend used in PHP (mysql, odbc etc.) * + dbsyntax: Database used with regards to SQL syntax etc. * + protocol: Communication protocol to use (tcp, unix etc.) - * + hostspec: Host specification (hostname[:port]) + * + host: Host specification (hostname[:port]) * + database: Database to use on the DBMS server * + username: User name for login * + password: Password for login @@ -191,7 +191,7 @@ public static function parseDSN( $dsn ) 'username' => FALSE, 'password' => FALSE, 'protocol' => FALSE, - 'hostspec' => FALSE, + 'host' => FALSE, 'port' => FALSE, 'socket' => FALSE, 'database' => FALSE, @@ -238,7 +238,7 @@ public static function parseDSN( $dsn ) } // Get (if found): username and password - // $dsn => username:password@protocol+hostspec/database + // $dsn => username:password@protocol+host/database if ( ( $at = strrpos( (string) $dsn, '@' ) ) !== FALSE ) { $str = substr( $dsn, 0, $at ); @@ -254,7 +254,7 @@ public static function parseDSN( $dsn ) } } - // Find protocol and hostspec + // Find protocol and host if ( preg_match( '|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match ) ) { @@ -265,7 +265,7 @@ public static function parseDSN( $dsn ) } else { - // $dsn => protocol+hostspec/database (old format) + // $dsn => protocol+host/database (old format) if ( strpos( $dsn, '+' ) !== FALSE ) { list( $proto, $dsn ) = explode( '+', $dsn, 2 ); @@ -288,11 +288,11 @@ public static function parseDSN( $dsn ) { if ( strpos( $proto_opts, ':' ) !== FALSE ) { - list( $parsed['hostspec'], $parsed['port'] ) = explode( ':', $proto_opts ); + list( $parsed['host'], $parsed['port'] ) = explode( ':', $proto_opts ); } else { - $parsed['hostspec'] = $proto_opts; + $parsed['host'] = $proto_opts; } } elseif ( $parsed['protocol'] == 'unix' ) diff --git a/alloy/Plugin/Spot/lib/Spot/Adapter/AdapterInterface.php b/alloy/Plugin/Spot/lib/Spot/Adapter/AdapterInterface.php index ca8f21f..4e04810 100644 --- a/alloy/Plugin/Spot/lib/Spot/Adapter/AdapterInterface.php +++ b/alloy/Plugin/Spot/lib/Spot/Adapter/AdapterInterface.php @@ -79,6 +79,12 @@ public function create($source, array $data, array $options = array()); public function read(\Spot\Query $query, array $options = array()); + /* + * Count number of rows in source based on conditions + */ + public function count(\Spot\Query $query, array $options = array()); + + /** * Update entity */ @@ -117,4 +123,4 @@ public function createDatabase($database); * Will throw errors if user does not have proper permissions */ public function dropDatabase($database); -} +} \ No newline at end of file diff --git a/alloy/Plugin/Spot/lib/Spot/Adapter/Mongodb.php b/alloy/Plugin/Spot/lib/Spot/Adapter/Mongodb.php index f82647d..b661f4d 100644 --- a/alloy/Plugin/Spot/lib/Spot/Adapter/Mongodb.php +++ b/alloy/Plugin/Spot/lib/Spot/Adapter/Mongodb.php @@ -206,6 +206,24 @@ public function read(\Spot\Query $query, array $options = array()) return $this->toCollection($query, $cursor); } + /* + * Count number of rows in source based on criteria + */ + public function count(\Spot\Query $query, array $options = array()) + { + // Load criteria + $criteria = $this->queryConditions($query); + + //find and return count + $count = $this->mongoCollection($query->datasource)->find($criteria)->count(); + + // Add query to log + Spot_Log::addQuery($this, $criteria); + + // Return count + return is_numeric($count) ? (int)$count : 0; + } + /** * Update entity */ @@ -437,4 +455,4 @@ public function mongoCollection($collectionName) { return $this->mongoDatabase()->$collectionName; } -} +} \ No newline at end of file diff --git a/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php b/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php index 590ca15..8210563 100644 --- a/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php +++ b/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php @@ -9,100 +9,100 @@ */ class Mysql extends PDO_Abstract implements AdapterInterface { - // Format for date columns, formatted for PHP's date() function - protected $_format_date = "Y-m-d"; - protected $_format_time = " H:i:s"; - protected $_format_datetime = "Y-m-d H:i:s"; - - // Driver-Specific settings - protected $_engine = 'InnoDB'; - protected $_charset = 'utf8'; - protected $_collate = 'utf8_unicode_ci'; - - // Map datamapper field types to actual database adapter types - // @todo Have to improve this to allow custom types, callbacks, and validation - protected $_fieldTypeMap = array( - 'string' => array( - 'adapter_type' => 'varchar', - 'length' => 255 - ), - 'email' => array( - 'adapter_type' => 'varchar', - 'length' => 255 - ), - 'url' => array( - 'adapter_type' => 'varchar', - 'length' => 255 - ), - 'tel' => array( - 'adapter_type' => 'varchar', - 'length' => 255 - ), - 'password' => array( - 'adapter_type' => 'varchar', - 'length' => 255 - ), - 'text' => array('adapter_type' => 'text'), - 'int' => array('adapter_type' => 'int'), - 'integer' => array('adapter_type' => 'int'), - 'bool' => array('adapter_type' => 'tinyint', 'length' => 1), - 'boolean' => array('adapter_type' => 'tinyint', 'length' => 1), - 'float' => array('adapter_type' => 'float'), - 'double' => array('adapter_type' => 'double'), - 'date' => array('adapter_type' => 'date'), - 'datetime' => array('adapter_type' => 'datetime'), - 'year' => array('adapter_type' => 'year', 'length' => 4), - 'month' => array('adapter_type' => 'month', 'length' => 2), - 'time' => array('adapter_type' => 'time'), - 'timestamp' => array('adapter_type' => 'int', 'length' => 11) - ); - - - /** - * Set database engine (InnoDB, MyISAM, etc) - */ - public function engine($engine = null) - { - if(null !== $engine) { - $this->_engine = $engine; - } - return $this->_engine; - } - - - /** - * Set character set and MySQL collate string - */ - public function characterSet($charset, $collate = 'utf8_unicode_ci') - { - $this->_charset = $charset; - $this->_collate = $collate; - } - - - /** - * Get columns for current table - * - * @param String $table Table name - * @return Array - */ - protected function getColumnsForTable($table, $source) - { - $tableColumns = array(); - $tblCols = $this->connection()->query("SELECT * FROM information_schema.columns WHERE table_schema = '" . $source . "' AND table_name = '" . $table . "'"); - - if($tblCols) { - while($columnData = $tblCols->fetch(\PDO::FETCH_ASSOC)) { - $tableColumns[$columnData['COLUMN_NAME']] = $columnData; - } - return $tableColumns; - } else { - return false; - } - } - - - /** + // Format for date columns, formatted for PHP's date() function + protected $_format_date = "Y-m-d"; + protected $_format_time = " H:i:s"; + protected $_format_datetime = "Y-m-d H:i:s"; + + // Driver-Specific settings + protected $_engine = 'InnoDB'; + protected $_charset = 'utf8'; + protected $_collate = 'utf8_unicode_ci'; + + // Map datamapper field types to actual database adapter types + // @todo Have to improve this to allow custom types, callbacks, and validation + protected $_fieldTypeMap = array( + 'string' => array( + 'adapter_type' => 'varchar', + 'length' => 255 + ), + 'email' => array( + 'adapter_type' => 'varchar', + 'length' => 255 + ), + 'url' => array( + 'adapter_type' => 'varchar', + 'length' => 255 + ), + 'tel' => array( + 'adapter_type' => 'varchar', + 'length' => 255 + ), + 'password' => array( + 'adapter_type' => 'varchar', + 'length' => 255 + ), + 'text' => array('adapter_type' => 'text'), + 'int' => array('adapter_type' => 'int'), + 'integer' => array('adapter_type' => 'int'), + 'bool' => array('adapter_type' => 'tinyint', 'length' => 1), + 'boolean' => array('adapter_type' => 'tinyint', 'length' => 1), + 'float' => array('adapter_type' => 'float'), + 'double' => array('adapter_type' => 'double'), + 'date' => array('adapter_type' => 'date'), + 'datetime' => array('adapter_type' => 'datetime'), + 'year' => array('adapter_type' => 'year', 'length' => 4), + 'month' => array('adapter_type' => 'month', 'length' => 2), + 'time' => array('adapter_type' => 'time'), + 'timestamp' => array('adapter_type' => 'int', 'length' => 11) + ); + + + /** + * Set database engine (InnoDB, MyISAM, etc) + */ + public function engine($engine = null) + { + if(null !== $engine) { + $this->_engine = $engine; + } + return $this->_engine; + } + + + /** + * Set character set and MySQL collate string + */ + public function characterSet($charset, $collate = 'utf8_unicode_ci') + { + $this->_charset = $charset; + $this->_collate = $collate; + } + + + /** + * Get columns for current table + * + * @param String $table Table name + * @return Array + */ + protected function getColumnsForTable($table, $source) + { + $tableColumns = array(); + $tblCols = $this->connection()->query("SELECT * FROM information_schema.columns WHERE table_schema = '" . $source . "' AND table_name = '" . $table . "'"); + + if($tblCols) { + while($columnData = $tblCols->fetch(\PDO::FETCH_ASSOC)) { + $tableColumns[$columnData['COLUMN_NAME']] = $columnData; + } + return $tableColumns; + } else { + return false; + } + } + + + /** * Ensure migration options are full and have all keys required */ public function formatMigrateOptions(array $options) @@ -115,245 +115,250 @@ public function formatMigrateOptions(array $options) } - /** - * Syntax for each column in CREATE TABLE command - * - * @param string $fieldName Field name - * @param array $fieldInfo Array of field settings - * @return string SQL syntax - */ - public function migrateSyntaxFieldCreate($fieldName, array $fieldInfo) - { - // Ensure field type exists - if(!isset($this->_fieldTypeMap[$fieldInfo['type']])) { - throw new \Spot\Exception("Field type '" . $fieldInfo['type'] . "' not supported"); - } - - $fieldInfo = array_merge($fieldInfo, $this->_fieldTypeMap[$fieldInfo['type']]); - - $syntax = "`" . $fieldName . "` " . $fieldInfo['adapter_type']; - // Column type and length - $syntax .= ($fieldInfo['length']) ? '(' . $fieldInfo['length'] . ')' : ''; - // Unsigned - $syntax .= ($fieldInfo['unsigned']) ? ' unsigned' : ''; - // Collate - $syntax .= ($fieldInfo['type'] == 'string' || $fieldInfo['type'] == 'text') ? ' COLLATE ' . $this->_collate : ''; - // Nullable - $isNullable = true; - if($fieldInfo['required'] || !$fieldInfo['null']) { - $syntax .= ' NOT NULL'; - $isNullable = false; - } - // Default value - if($fieldInfo['default'] === null && $isNullable) { - $syntax .= " DEFAULT NULL"; - } elseif($fieldInfo['default'] !== null) { - $default = $fieldInfo['default']; - // If it's a boolean and $default is boolean then it should be 1 or 0 - if ( is_bool($default) && $fieldInfo['type'] == "boolean" ) { - $default = $default ? 1 : 0; - } - - if(is_scalar($default)) { - $syntax .= " DEFAULT '" . $default . "'"; - } - } - // Extra - $syntax .= ($fieldInfo['primary'] && $fieldInfo['serial']) ? ' AUTO_INCREMENT' : ''; - return $syntax; - } - - - /** - * Syntax for CREATE TABLE with given fields and column syntax - * - * @param string $table Table name - * @param array $formattedFields Array of fields with all settings - * @param array $columnsSyntax Array of SQL syntax of columns produced by 'migrateSyntaxFieldCreate' function - * @param Array $options Options that may affect migrations or how tables are setup - * @return string SQL syntax - */ - public function migrateSyntaxTableCreate($table, array $formattedFields, array $columnsSyntax, array $options) - { - $options = $this->formatMigrateOptions($options); - - // Begin syntax soup - $syntax = "CREATE TABLE IF NOT EXISTS `" . $table . "` (\n"; - // Columns - $syntax .= implode(",\n", $columnsSyntax); - - // Keys... - $ki = 0; - $tableKeys = array( - 'primary' => array(), - 'unique' => array(), - 'index' => array() - ); - $fulltextFields = array(); - $usedKeyNames = array(); - foreach($formattedFields as $fieldName => $fieldInfo) { - // Determine key field name (can't use same key name twice, so we have to append a number) - $fieldKeyName = $fieldName; - while(in_array($fieldKeyName, $usedKeyNames)) { - $fieldKeyName = $fieldName . '_' . $ki; - } - // Key type - if($fieldInfo['primary']) { - $tableKeys['primary'][] = $fieldName; - } - if($fieldInfo['unique']) { - if(is_string($fieldInfo['unique'])) { - // Named group - $fieldKeyName = $fieldInfo['unique']; - } - $tableKeys['unique'][$fieldKeyName][] = $fieldName; - $usedKeyNames[] = $fieldKeyName; - } - if($fieldInfo['index']) { - if(is_string($fieldInfo['index'])) { - // Named group - $fieldKeyName = $fieldInfo['index']; - } - $tableKeys['index'][$fieldKeyName][] = $fieldName; - $usedKeyNames[] = $fieldKeyName; - } - // FULLTEXT search - if($fieldInfo['fulltext']) { - $fulltextFields[] = $fieldName; - } - } - - // FULLTEXT - if($fulltextFields) { - // Ensure table type is MyISAM if FULLTEXT columns have been specified - if('myisam' !== strtolower($options['engine'])) { - $options['engine'] = 'MyISAM'; - } - $syntax .= "\n, FULLTEXT(`" . implode('`, `', $fulltextFields) . "`)"; - } - - // PRIMARY - if($tableKeys['primary']) { - $syntax .= "\n, PRIMARY KEY(`" . implode('`, `', $tableKeys['primary']) . "`)"; - } - // UNIQUE - foreach($tableKeys['unique'] as $keyName => $keyFields) { - $syntax .= "\n, UNIQUE KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; - } - // INDEX - foreach($tableKeys['index'] as $keyName => $keyFields) { - $syntax .= "\n, KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; - } - - // Extra - $syntax .= "\n) ENGINE=" . $options['engine'] . " DEFAULT CHARSET=" . $options['charset'] . " COLLATE=" . $options['collate'] . ";"; - - return $syntax; - } - - - /** - * Syntax for each column in CREATE TABLE command - * - * @param string $fieldName Field name - * @param array $fieldInfo Array of field settings - * @return string SQL syntax - */ - public function migrateSyntaxFieldUpdate($fieldName, array $fieldInfo, $add = false) - { - return ( $add ? "ADD COLUMN " : "MODIFY " ) . $this->migrateSyntaxFieldCreate($fieldName, $fieldInfo); - } - - - /** - * Syntax for ALTER TABLE with given fields and column syntax - * - * @param string $table Table name - * @param array $formattedFields Array of fields with all settings - * @param array $columnsSyntax Array of SQL syntax of columns produced by 'migrateSyntaxFieldUpdate' function - * @return string SQL syntax - */ - public function migrateSyntaxTableUpdate($table, array $formattedFields, array $columnsSyntax, array $options) - { - /* - Example: - - ALTER TABLE `posts` - CHANGE `title` `title` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , - CHANGE `status` `status` VARCHAR( 40 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT 'draft' - */ - - $options = $this->formatMigrateOptions($options); - - // Begin syntax soup - $syntax = "ALTER TABLE `" . $table . "` \n"; - - // Columns - $syntax .= implode(",\n", $columnsSyntax); - - // Keys... - $ki = 0; - $tableKeys = array( - 'primary' => array(), - 'unique' => array(), - 'index' => array() - ); - $fulltextFields = array(); - $usedKeyNames = array(); - foreach($formattedFields as $fieldName => $fieldInfo) { - // Determine key field name (can't use same key name twice, so we have to append a number) - $fieldKeyName = $fieldName; - while(in_array($fieldKeyName, $usedKeyNames)) { - $fieldKeyName = $fieldName . '_' . $ki; - } - // Key type - if($fieldInfo['primary']) { - $tableKeys['primary'][] = $fieldName; - } - if($fieldInfo['unique']) { - if(is_string($fieldInfo['unique'])) { - // Named group - $fieldKeyName = $fieldInfo['unique']; - } - $tableKeys['unique'][$fieldKeyName][] = $fieldName; - $usedKeyNames[] = $fieldKeyName; - } - if($fieldInfo['index']) { - if(is_string($fieldInfo['index'])) { - // Named group - $fieldKeyName = $fieldInfo['index']; - } - $tableKeys['index'][$fieldKeyName][] = $fieldName; - $usedKeyNames[] = $fieldKeyName; - } - // FULLTEXT search - if($fieldInfo['fulltext']) { - $fulltextFields[] = $fieldName; - } - } - - // FULLTEXT - if($fulltextFields) { - // Ensure table type is MyISAM if FULLTEXT columns have been specified - if('myisam' !== strtolower($options['engine'])) { - throw new \Spot\Exception("FULLTEXT columns are only allowed using the MyISAM storage engine. Engine is currently: '" . $options['engine'] . "'."); - } - $syntax .= "\n, FULLTEXT(`" . implode('`, `', $fulltextFields) . "`)"; - } - - // PRIMARY - if($tableKeys['primary']) { - $syntax .= "\n, PRIMARY KEY(`" . implode('`, `', $tableKeys['primary']) . "`)"; - } - // UNIQUE - foreach($tableKeys['unique'] as $keyName => $keyFields) { - $syntax .= "\n, UNIQUE KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; - } - // INDEX - foreach($tableKeys['index'] as $keyName => $keyFields) { - $syntax .= "\n, KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; - } - - return $syntax; - } + /** + * Syntax for each column in CREATE TABLE command + * + * @param string $fieldName Field name + * @param array $fieldInfo Array of field settings + * @return string SQL syntax + */ + public function migrateSyntaxFieldCreate($fieldName, array $fieldInfo) + { + // Ensure field type exists + if(!isset($this->_fieldTypeMap[$fieldInfo['type']])) { + throw new \Spot\Exception("Field type '" . $fieldInfo['type'] . "' not supported"); + } + //Ensure this class will choose adapter type + unset($fieldInfo['adapter_type']); + + $fieldInfo = array_merge($this->_fieldTypeMap[$fieldInfo['type']],$fieldInfo); + + $syntax = "`" . $fieldName . "` " . $fieldInfo['adapter_type']; + // Column type and length + $syntax .= is_int($fieldInfo['length']) ? '(' . $fieldInfo['length'] . ')' : ''; + // Unsigned + $syntax .= ($fieldInfo['unsigned']) ? ' unsigned' : ''; + // Collate + $syntax .= ($fieldInfo['type'] == 'string' || $fieldInfo['type'] == 'text') ? ' COLLATE ' . $this->_collate : ''; + // Nullable + $isNullable = true; + if($fieldInfo['required'] || !$fieldInfo['null']) { + $syntax .= ' NOT NULL'; + $isNullable = false; + } + // Default value + if($fieldInfo['default'] === null && $isNullable) { + $syntax .= " DEFAULT NULL"; + } elseif($fieldInfo['default'] !== null) { + $default = $fieldInfo['default']; + // If it's a boolean and $default is boolean then it should be 1 or 0 + if ( is_bool($default) && $fieldInfo['type'] == "boolean" ) { + $default = $default ? 1 : 0; + } + + if(is_scalar($default)) { + $syntax .= " DEFAULT '" . $default . "'"; + } + } + // Extra + $syntax .= ($fieldInfo['primary'] && $fieldInfo['serial']) ? ' AUTO_INCREMENT' : ''; + return $syntax; + } + + + /** + * Syntax for CREATE TABLE with given fields and column syntax + * + * @param string $table Table name + * @param array $formattedFields Array of fields with all settings + * @param array $columnsSyntax Array of SQL syntax of columns produced by 'migrateSyntaxFieldCreate' function + * @param Array $options Options that may affect migrations or how tables are setup + * @return string SQL syntax + */ + public function migrateSyntaxTableCreate($table, array $formattedFields, array $columnsSyntax, array $options) + { + $options = $this->formatMigrateOptions($options); + + // Begin syntax soup + $syntax = "CREATE TABLE IF NOT EXISTS `" . $table . "` (\n"; + // Columns + $syntax .= implode(",\n", $columnsSyntax); + + // Keys... + $ki = 0; + $tableKeys = array( + 'primary' => array(), + 'unique' => array(), + 'index' => array() + ); + $fulltextFields = array(); + $usedKeyNames = array(); + foreach($formattedFields as $fieldName => $fieldInfo) { + // Determine key field name (can't use same key name twice, so we have to append a number) + $fieldKeyName = $fieldName; + while(in_array($fieldKeyName, $usedKeyNames)) { + $fieldKeyName = $fieldName . '_' . $ki; + } + // Key type + if($fieldInfo['primary']) { + $tableKeys['primary'][] = $fieldName; + } + if($fieldInfo['unique']) { + if(is_string($fieldInfo['unique'])) { + // Named group + $fieldKeyName = $fieldInfo['unique']; + } + $tableKeys['unique'][$fieldKeyName][] = $fieldName; + $usedKeyNames[] = $fieldKeyName; + } + if($fieldInfo['index']) { + if(is_string($fieldInfo['index'])) { + // Named group + $fieldKeyName = $fieldInfo['index']; + } + $tableKeys['index'][$fieldKeyName][] = $fieldName; + $usedKeyNames[] = $fieldKeyName; + } + // FULLTEXT search + if($fieldInfo['fulltext']) { + $fulltextFields[] = $fieldName; + } + } + + // FULLTEXT + if($fulltextFields) { + // Ensure table type is MyISAM if FULLTEXT columns have been specified + if('myisam' !== strtolower($options['engine'])) { + $options['engine'] = 'MyISAM'; + } + $syntax .= "\n, FULLTEXT(`" . implode('`, `', $fulltextFields) . "`)"; + } + + // PRIMARY + if($tableKeys['primary']) { + $syntax .= "\n, PRIMARY KEY(`" . implode('`, `', $tableKeys['primary']) . "`)"; + } + // UNIQUE + foreach($tableKeys['unique'] as $keyName => $keyFields) { + $syntax .= "\n, UNIQUE KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; + } + // INDEX + foreach($tableKeys['index'] as $keyName => $keyFields) { + $syntax .= "\n, KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; + } + + // Extra + $syntax .= "\n) ENGINE=" . $options['engine'] . " DEFAULT CHARSET=" . $options['charset'] . " COLLATE=" . $options['collate'] . ";"; + + return $syntax; + } + + + /** + * Syntax for each column in CREATE TABLE command + * + * @param string $fieldName Field name + * @param array $fieldInfo Array of field settings + * @return string SQL syntax + */ + public function migrateSyntaxFieldUpdate($fieldName, array $fieldInfo, $add = false) + { + return ( $add ? "ADD COLUMN " : "MODIFY " ) . $this->migrateSyntaxFieldCreate($fieldName, $fieldInfo); + } + + + /** + * Syntax for ALTER TABLE with given fields and column syntax + * + * @param string $table Table name + * @param array $formattedFields Array of fields with all settings + * @param array $columnsSyntax Array of SQL syntax of columns produced by 'migrateSyntaxFieldUpdate' function + * @return string SQL syntax + */ + public function migrateSyntaxTableUpdate($table, array $formattedFields, array $columnsSyntax, array $options) + { + /* + Example: + + ALTER TABLE `posts` + CHANGE `title` `title` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , + CHANGE `status` `status` VARCHAR( 40 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT 'draft' + */ + + $options = $this->formatMigrateOptions($options); + + // Begin syntax soup + $syntax = "ALTER TABLE `" . $table . "` \n"; + + // Columns + $syntax .= implode(",\n", $columnsSyntax); + + // Keys... + $ki = 0; + $tableKeys = array( + 'primary' => array(), + 'unique' => array(), + 'index' => array() + ); + $fulltextFields = array(); + $usedKeyNames = array(); + foreach($formattedFields as $fieldName => $fieldInfo) { + // Determine key field name (can't use same key name twice, so we have to append a number) + $fieldKeyName = $fieldName; + while(in_array($fieldKeyName, $usedKeyNames)) { + $fieldKeyName = $fieldName . '_' . $ki; + } + // Key type + if($fieldInfo['primary']) { + $tableKeys['primary'][] = $fieldName; + } + if($fieldInfo['unique']) { + if(is_string($fieldInfo['unique'])) { + // Named group + $fieldKeyName = $fieldInfo['unique']; + } + $tableKeys['unique'][$fieldKeyName][] = $fieldName; + $usedKeyNames[] = $fieldKeyName; + } + if($fieldInfo['index']) { + if(is_string($fieldInfo['index'])) { + // Named group + $fieldKeyName = $fieldInfo['index']; + } + $tableKeys['index'][$fieldKeyName][] = $fieldName; + $usedKeyNames[] = $fieldKeyName; + } + // FULLTEXT search + if($fieldInfo['fulltext']) { + $fulltextFields[] = $fieldName; + } + } + + // FULLTEXT + if($fulltextFields) { + // Ensure table type is MyISAM if FULLTEXT columns have been specified + if('myisam' !== strtolower($options['engine'])) { + $options['engine'] = 'MyISAM'; + } + $syntax .= "\n, FULLTEXT(`" . implode('`, `', $fulltextFields) . "`)"; + } + + // PRIMARY + if($tableKeys['primary']) { + $syntax .= "\n, PRIMARY KEY(`" . implode('`, `', $tableKeys['primary']) . "`)"; + } + // UNIQUE + foreach($tableKeys['unique'] as $keyName => $keyFields) { + $syntax .= "\n, UNIQUE KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; + } + // INDEX + foreach($tableKeys['index'] as $keyName => $keyFields) { + $syntax .= "\n, KEY `" . $keyName . "` (`" . implode('`, `', $keyFields) . "`)"; + } + + // Extra + $syntax .= "\n) ENGINE=" . $options['engine'] . " DEFAULT CHARSET=" . $options['charset'] . " COLLATE=" . $options['collate'] . ";"; + + return $syntax; + } } diff --git a/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php b/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php index c002d95..c4a6bf0 100644 --- a/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php +++ b/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php @@ -28,7 +28,7 @@ public function connection() // Establish connection try { - $dsn = $dsnp['adapter'].':host='.$dsnp['hostspec'].';dbname='.$dsnp['database']; + $dsn = $dsnp['adapter'].':host='.$dsnp['host'].';dbname='.$dsnp['database']; $this->_connection = new \PDO($dsn, $dsnp['username'], $dsnp['password'], $this->_options); // Throw exceptions by default $this->_connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); @@ -141,9 +141,6 @@ public function migrateTableUpdate($table, array $formattedFields, array $option * Use column syntax array to get table syntax * Run SQL */ - - //var_dump($formattedFields); - //exit(__FILE__); // Prepare fields and get syntax for each $tableColumns = $this->getColumnsForTable($table, $this->_database); @@ -334,6 +331,54 @@ public function read(\Spot\Query $query, array $options = array()) return $result; } + /* + * Count number of rows in source based on conditions + */ + public function count(\Spot\Query $query, array $options = array()) + { + $conditions = $this->statementConditions($query->conditions); + $binds = $this->statementBinds($query->params()); + $sql = " + SELECT COUNT(*) as count + FROM " . $query->datasource . " + " . ($conditions ? 'WHERE ' . $conditions : '') . " + " . ($query->group ? 'GROUP BY ' . implode(', ', $query->group) : ''); + + // Unset any NULL values in binds (compared as "IS NULL" and "IS NOT NULL" in SQL instead) + if($binds && count($binds) > 0) { + foreach($binds as $field => $value) { + if(null === $value) { + unset($binds[$field]); + } + } + } + + // Add query to log + \Spot\Log::addQuery($this, $sql,$binds); + + $result = false; + try { + // Prepare count query + $stmt = $this->connection()->prepare($sql); + + //if prepared, execute + if($stmt && $stmt->execute($binds)) { + //the count is returned in the first column + $result = (int) $stmt->fetchColumn(); + } else { + $result = false; + } + } catch(PDOException $e) { + // Table does not exist + if($e->getCode() == "42S02") { + throw new \Spot\Exception_Datasource_Missing("Table or datasource '" . $query->datasource . "' does not exist"); + } + throw $e; + } + + return $result; + } + /** * Update entity */ @@ -574,7 +619,7 @@ public function statementConditions(array $conditions = array(), $ci = 0) // MATCH(col) AGAINST(search) case ':fulltext': $colParam = preg_replace('/\W+/', '_', $col) . $ci; - $whereClause = "MATCH(" . $col . ") AGAINST(:" . $colParam . " IN BOOLEAN MODE)"; + $whereClause = "MATCH(" . $col . ") AGAINST(:" . $colParam . ")"; break; // ALL - Find ALL values in a set - Kind of like IN(), but seeking *all* the values case ':all': @@ -748,4 +793,4 @@ protected function bindValues($stmt, array $binds) } return true; } -} +} \ No newline at end of file diff --git a/alloy/Plugin/Spot/lib/Spot/Entity.php b/alloy/Plugin/Spot/lib/Spot/Entity.php index 96ea690..36c5322 100644 --- a/alloy/Plugin/Spot/lib/Spot/Entity.php +++ b/alloy/Plugin/Spot/lib/Spot/Entity.php @@ -128,26 +128,18 @@ public function data($data = null, $modified = true) } } - /** - * Alias of self::data() - */ - public function toArray() - { - return $this->data(); - } - - + /** * Return array of field data with data from the field names listed removed * * @param array List of field names to exclude in data list returned */ - public function dataExcept(array $except) - { - return array_diff_key($this->data(), array_flip($except)); - } + public function dataExcept(array $except) + { + return array_diff_key($this->data(), array_flip($except)); + } + - /** * Gets data that has been modified since object construct */ @@ -155,6 +147,15 @@ public function dataModified() { return $this->_dataModified; } + + + /** + * Alias of self::data() + */ + public function toArray() + { + return $this->data(); + } /** diff --git a/alloy/Plugin/Spot/lib/Spot/Mapper.php b/alloy/Plugin/Spot/lib/Spot/Mapper.php index 01793e5..1ea7799 100644 --- a/alloy/Plugin/Spot/lib/Spot/Mapper.php +++ b/alloy/Plugin/Spot/lib/Spot/Mapper.php @@ -210,6 +210,12 @@ public function collection($entityName, $cursor) $results = array(); $resultsIdentities = array(); + // Ensure PDO only gives key => value pairs, not index-based fields as well + // Raw PDOStatement objects generally only come from running raw SQL queries or other custom stuff + if($cursor instanceof \PDOStatement) { + $cursor->setFetchMode(\PDO::FETCH_ASSOC); + } + // Fetch all results into new entity class // @todo Move this to collection class so entities will be lazy-loaded by Collection iteration foreach($cursor as $data) { @@ -310,21 +316,17 @@ public function create($entityClass, array $data) /** * Find records with custom query * - * @throws \Spot\Exception + * @param string $entityName Name of the entity class + * @param string $sql Raw query or SQL to run against the datastore + * @param array Optional $conditions Array of binds in column => value pairs to use for prepared statement */ - public function query() - { - $args = func_get_args(); - - // Remove entityName (first element) - $entityName = array_shift($args); - - $result = $this->connection($entityName)->query($args); + public function query($entityName, $sql, array $params = array()) + { + $result = $this->connection($entityName)->query($sql, $params); if($result) { - return $this->collection($result); - } else { - return false; + return $this->collection($entityName, $result); } + return false; } diff --git a/alloy/Plugin/Spot/lib/Spot/Query.php b/alloy/Plugin/Spot/lib/Spot/Query.php index 4471de5..65d9ea4 100644 --- a/alloy/Plugin/Spot/lib/Spot/Query.php +++ b/alloy/Plugin/Spot/lib/Spot/Query.php @@ -280,9 +280,11 @@ public function params() */ public function count() { - // Execute query and return count - $result = $this->execute(); - return ($result !== false) ? count($result) : 0; + // Execute query + $result = $this->mapper()->connection($this->entityName())->count($this); + + //return count + return is_numeric($result) ? $result : 0; } From a5fc3369b070883fdaadde344c7483f594eea70e Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Mon, 19 Dec 2011 14:17:28 -0600 Subject: [PATCH 17/50] Removed old page-specific routes * Removed 'index_action' and 'page_action' routes due to problematic setup on some systems * Changed add/edit/pages links in adminBar to use standard routes --- app/Module/Page/views/_adminBar.html.php | 12 ++++-------- app/config/routes.php | 6 ------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/app/Module/Page/views/_adminBar.html.php b/app/Module/Page/views/_adminBar.html.php index c0d8ae3..ae297c6 100755 --- a/app/Module/Page/views/_adminBar.html.php +++ b/app/Module/Page/views/_adminBar.html.php @@ -8,19 +8,15 @@ diff --git a/app/config/routes.php b/app/config/routes.php index d0f6da1..3c53333 100755 --- a/app/config/routes.php +++ b/app/config/routes.php @@ -51,12 +51,6 @@ ->put(array('module_action' => 'put')) ->delete(array('module_action' => 'delete')); -$router->route('page_action', $pageRouteItem . '/<:action>\.<:format>') - ->defaults(array('page' => '/', 'module' => 'Page', 'format' => 'html')); - -$router->route('index_action', '/<:action>\.<:format>') - ->defaults(array('page' => '/', 'module' => 'Page', 'format' => 'html')); - $router->route('page', $pageRouteItem) ->defaults(array('page' => '/', 'module' => 'Page', 'action' => 'index', 'format' => 'html')) ->post(array('action' => 'post')) From 07473ece5230f4c405c159d3700e500645c21a6b Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 21 Dec 2011 15:30:14 -0600 Subject: [PATCH 18/50] Fixed page sitemap data saving * Updated Alloy Router to latest version allowing POSTs to any actions * Updated route in sitemap template --- alloy/lib/Alloy/Kernel.php | 4 +- alloy/lib/Alloy/Router.php | 11 +++-- alloy/tests/AllTests.php | 2 +- alloy/tests/Test/Router/Url/Rest.php | 50 ++++++++++++++++++++++ app/Module/Page/views/pagesAction.html.php | 2 +- 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/alloy/lib/Alloy/Kernel.php b/alloy/lib/Alloy/Kernel.php index 4fe3be5..3dc07bc 100644 --- a/alloy/lib/Alloy/Kernel.php +++ b/alloy/lib/Alloy/Kernel.php @@ -547,7 +547,7 @@ public function url($params = array(), $routeName = null, $queryParams = array() } if(count($queryParams) > 0) { // Build query string from array $qsData - $queryString = http_build_query($queryParams, '', '&'); + $queryString = http_build_query($queryParams, '', '&'); } else { $queryString = false; } @@ -559,7 +559,7 @@ public function url($params = array(), $routeName = null, $queryParams = array() if($this->config('url.rewrite')) { $url = $urlBase . $url . (($queryString !== false) ? '?' . $queryString : ''); } else { - $url = $urlBase . '?u=' . $url . (($queryString !== false) ? '&' . $queryString : ''); + $url = $urlBase . '?u=' . $url . (($queryString !== false) ? '&' . $queryString : ''); } // Return fully assembled URL diff --git a/alloy/lib/Alloy/Router.php b/alloy/lib/Alloy/Router.php index 0bfae8c..30aeaf5 100644 --- a/alloy/lib/Alloy/Router.php +++ b/alloy/lib/Alloy/Router.php @@ -145,14 +145,17 @@ protected function routeMatch(Router_Route $route, $method, $url) throw new \InvalidArgumentException("Error matching URL to route params: matched(" . count($matches) . ") != named(" . count($namedParamsMatched) . ")"); } $params = array_combine(array_keys($namedParamsMatched), $matches); - - + if(strtoupper($method) != "GET") { - // Default REST behavior is to be 'greedy' and always use the REST method defaults if supplied - $params = array_merge($route->namedParams(), $route->defaults(), $params, $route->methodDefaults($method)); + // 1) Determine which actions are set in $params that are also in 'methodDefaults' + // 2) Override the 'methodDefaults' with the explicitly set $params + $setParams = array_intersect_key($params, $route->methodDefaults($method)); + $methodParams = array_merge($route->namedParams(), $route->defaults(), $params, $route->methodDefaults($method), $setParams); + $params = $methodParams; } else { $params = array_merge($route->namedParams(), $route->defaults(), $route->methodDefaults($method), $params); } + //$params = array_merge($route->namedParams(), $route->defaults(), $route->methodDefaults($method), $params); } } return array_map('urldecode', $params); diff --git a/alloy/tests/AllTests.php b/alloy/tests/AllTests.php index 46490b4..b56acda 100644 --- a/alloy/tests/AllTests.php +++ b/alloy/tests/AllTests.php @@ -34,4 +34,4 @@ public static function suite() } return $suite; } -} \ No newline at end of file +} diff --git a/alloy/tests/Test/Router/Url/Rest.php b/alloy/tests/Test/Router/Url/Rest.php index 33ffb9f..9ed7b9b 100644 --- a/alloy/tests/Test/Router/Url/Rest.php +++ b/alloy/tests/Test/Router/Url/Rest.php @@ -64,4 +64,54 @@ public function testRouteRestWithOptionalParam() $this->assertEquals('164', $params['id']); $this->assertEquals('html', $params['format']); } + + public function testRouteRestWithAction() + { + $router = $this->router; + $route = $router->route('test', '/<:module>/<:action>') + ->defaults(array('format' => 'html', 'action' => 'index')) + ->get(array('action' => 'view')); + + // Match URL + $params = $router->match("GET", "/user/list"); + + // Check matched params + $this->assertEquals('user', $params['module']); + $this->assertEquals('list', $params['action']); + $this->assertEquals('html', $params['format']); + } + + public function testRouteRestWithActionPost() + { + $router = $this->router; + $route = $router->route('test', '/<:module>(/<:action>)') + ->defaults(array('format' => 'html', 'action' => 'index')) + ->post(array('action' => 'new')); + + // Match URL with action + $params = $router->match("POST", "/user/list"); + + // Check matched params + $this->assertEquals('user', $params['module']); + // Expect to preserve action POSTED to + $this->assertEquals('list', $params['action']); + $this->assertEquals('html', $params['format']); + } + + public function testRouteRestWithoutActionPost() + { + $router = $this->router; + $route = $router->route('test', '/<:module>(/<:action>)') + ->defaults(array('format' => 'html', 'action' => 'index')) + ->post(array('action' => 'new')); + + // Match URL without action + $params = $router->match("POST", "/user"); + + // Check matched params + $this->assertEquals('user', $params['module']); + // Expect to fill-in with supplied method action 'new' + $this->assertEquals('new', $params['action']); + $this->assertEquals('html', $params['format']); + } } \ No newline at end of file diff --git a/app/Module/Page/views/pagesAction.html.php b/app/Module/Page/views/pagesAction.html.php index 364de59..313a762 100644 --- a/app/Module/Page/views/pagesAction.html.php +++ b/app/Module/Page/views/pagesAction.html.php @@ -3,7 +3,7 @@

Pages

Drag & drop pages to re-arrange them. Changes will not be permanent until you click 'Save'.

-
+ generic('treeview') From 0ee034f18daa37bc418a90ec684a38c798e05f48 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 21 Dec 2011 16:14:27 -0600 Subject: [PATCH 19/50] Router: Filtering out empty values --- alloy/lib/Alloy/Router.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alloy/lib/Alloy/Router.php b/alloy/lib/Alloy/Router.php index 30aeaf5..76efb6b 100644 --- a/alloy/lib/Alloy/Router.php +++ b/alloy/lib/Alloy/Router.php @@ -149,7 +149,7 @@ protected function routeMatch(Router_Route $route, $method, $url) if(strtoupper($method) != "GET") { // 1) Determine which actions are set in $params that are also in 'methodDefaults' // 2) Override the 'methodDefaults' with the explicitly set $params - $setParams = array_intersect_key($params, $route->methodDefaults($method)); + $setParams = array_filter(array_intersect_key($params, $route->methodDefaults($method))); $methodParams = array_merge($route->namedParams(), $route->defaults(), $params, $route->methodDefaults($method), $setParams); $params = $methodParams; } else { From 3b81bf93232314aa769a450cbbea3b9e9bcf64d3 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 21 Dec 2011 17:37:04 -0600 Subject: [PATCH 20/50] Admin JS: updated module save URLs --- app/www/assets/admin/scripts/cms_admin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/www/assets/admin/scripts/cms_admin.js b/app/www/assets/admin/scripts/cms_admin.js index 764692e..b84bf09 100755 --- a/app/www/assets/admin/scripts/cms_admin.js +++ b/app/www/assets/admin/scripts/cms_admin.js @@ -366,7 +366,7 @@ $(function() { var nModuleName = nModule.attr('rel').replace('cms_module_tile_', ''); $.ajax({ type: "POST", - url: cms.config.url + cms.page.url + 'm,Page_Module,0.html', + url: cms.config.url + cms.page.url + 'm,page_module,0', data: {'region': nRegionName, 'name': nModuleName}, success: function(data, textStatus, req) { // Replace content on page with new content from AJAX response @@ -388,7 +388,7 @@ $(function() { // Serialize modules to save module/region positions $.ajax({ type: "GET", - url: cms.config.url + cms.page.url + 'm,page_module,0/saveSort.html', + url: cms.config.url + cms.page.url + 'm,page_module,0/saveSort', data: 'ajax=1' + cms_serializeRegionModules(), success: function(data, textStatus, req) { // Show edit controls if CMS in edit mode From 6898855c89601bd9266bfde71c928a6951d30527 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Thu, 22 Dec 2011 10:48:50 -0600 Subject: [PATCH 21/50] Code module update for E_STRICT error --- app/www/content/Module/Code/Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/www/content/Module/Code/Controller.php b/app/www/content/Module/Code/Controller.php index cbc492f..453b532 100755 --- a/app/www/content/Module/Code/Controller.php +++ b/app/www/content/Module/Code/Controller.php @@ -156,9 +156,9 @@ public function deleteMethod($request, $page, $module) /** * Return view object for the add/edit form */ - protected function formView() + protected function formView($entityName = null) { - $view = parent::formView(); + $view = parent::formView($entityName); $fields = $view->fields(); // Set type and options for 'type' select From ab53f02212432409925f7a63ff91e5854622266f Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Thu, 22 Dec 2011 10:49:02 -0600 Subject: [PATCH 22/50] Slideshow module display and style updates * Slideshow now supports left, center, and right alignment in settings * Added settings for background color and CSS class to apply to box --- .../content/Module/Slideshow/Controller.php | 20 +++++++ .../Slideshow/views/indexAction.html.php | 55 ++++++++++++++----- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/app/www/content/Module/Slideshow/Controller.php b/app/www/content/Module/Slideshow/Controller.php index f687ce8..1f8f679 100755 --- a/app/www/content/Module/Slideshow/Controller.php +++ b/app/www/content/Module/Slideshow/Controller.php @@ -197,6 +197,16 @@ public function settings($page, $module) // Group 'display' => array( // Fields + 'alignment' => array( + 'type' => 'select', + 'default' => 'left', + 'after' => 'Alignment of slideshow', + 'options' => array( + 'left' => 'Left', + 'center' => 'Center', + 'right' => 'Right' + ) + ), 'width' => array( 'type' => 'int', 'default' => 600, @@ -207,6 +217,16 @@ public function settings($page, $module) 'default' => 350, 'after' => 'Pixel height of the slideshow' ), + 'background_color' => array( + 'type' => 'string', + 'default' => null, + 'after' => 'CSS Color of background (hex or color name)' + ), + 'slide_container_class' => array( + 'type' => 'string', + 'default' => null, + 'after' => 'CSS class that will be applied to top-level slide container' + ), 'slide_delay' => array( 'type' => 'int', 'default' => 5000, diff --git a/app/www/content/Module/Slideshow/views/indexAction.html.php b/app/www/content/Module/Slideshow/views/indexAction.html.php index 4346c04..b656d89 100755 --- a/app/www/content/Module/Slideshow/views/indexAction.html.php +++ b/app/www/content/Module/Slideshow/views/indexAction.html.php @@ -1,31 +1,60 @@ id; $assetsUrl = $kernel->config('url.root') . $kernel->config('cms.dir.modules') . 'Module/' . $module->name . '/assets/'; + +$sWidth = (int) $module->setting('width', 600); +$sHeight = (int) $module->setting('height', 375); ?> -
- -
- link): ?><?php echo $item->caption; ?>link): ?> - caption): ?> -

caption; ?>

- + +
+
+ +
+ link): ?><?php echo $item->caption; ?>link): ?> + caption): ?> +

caption; ?>

+ +
+
- +
+ + Date: Thu, 22 Dec 2011 17:28:05 -0600 Subject: [PATCH 23/50] Updated Spot plugin --- alloy/Plugin/Spot/lib/Spot/Entity/Manager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alloy/Plugin/Spot/lib/Spot/Entity/Manager.php b/alloy/Plugin/Spot/lib/Spot/Entity/Manager.php index 439e0dc..3ac15d0 100644 --- a/alloy/Plugin/Spot/lib/Spot/Entity/Manager.php +++ b/alloy/Plugin/Spot/lib/Spot/Entity/Manager.php @@ -110,7 +110,7 @@ public function fields($entityName) } // Format field will full set of default options - if(isset($fieldInfo['type']) && isset($fieldTypeDefaults[$fieldOpts['type']])) { + if(isset($fieldOpts['type']) && isset($fieldTypeDefaults[$fieldOpts['type']])) { // Include type defaults $fieldOpts = array_merge($fieldDefaults, $fieldTypeDefaults[$fieldOpts['type']], $fieldOpts); } else { From cb061b53589413639db35755b792998b4d7f6e97 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Thu, 22 Dec 2011 17:28:47 -0600 Subject: [PATCH 24/50] Filebrowser: Fixed issue with images not uploading --- app/Module/Filebrowser/Controller.php | 8 ++++++++ app/Module/Filebrowser/views/newAction.html.php | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/Module/Filebrowser/Controller.php b/app/Module/Filebrowser/Controller.php index 3c7f6af..c02d918 100755 --- a/app/Module/Filebrowser/Controller.php +++ b/app/Module/Filebrowser/Controller.php @@ -228,6 +228,14 @@ public function postMethod(Alloy\Request $request) 'file' => array('Unable to upload file') )); } + return $kernel->redirect($kernel->url(array('action' => 'images'), 'filebrowser', $request->query())); + } + public function postAction($request) + { + if($request->isPost()) { + return $this->postMethod($request); + } + return false; } diff --git a/app/Module/Filebrowser/views/newAction.html.php b/app/Module/Filebrowser/views/newAction.html.php index 7dd7670..305b2ef 100644 --- a/app/Module/Filebrowser/views/newAction.html.php +++ b/app/Module/Filebrowser/views/newAction.html.php @@ -1,7 +1,7 @@ generic('form') ->type('upload') - ->action($view->url(array('action' => 'new'), 'filebrowser', \Kernel()->request()->query())) + ->action($view->url(array('action' => 'post'), 'filebrowser', \Kernel()->request()->query())) ->method('post') ->fields(array( 'upload' => array('type' => 'file') From 7c83edda9b4bd39d06087a986504b0cec6f6e452 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Thu, 22 Dec 2011 17:50:11 -0600 Subject: [PATCH 25/50] Added HTTPS page redirect * Added code to make page redirect if HTTPS setting is checked * Added code to filter module exceptions through custom named filter * Added module exception handling to Stackbox plugin --- app/Module/Page/Controller.php | 36 ++++++++++++++----- app/Plugin/Stackbox/Plugin.php | 36 +++++++++++++++++++ .../Stackbox/lib/Stackbox/EntityAbstract.php | 2 +- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/app/Module/Page/Controller.php b/app/Module/Page/Controller.php index 65f9103..4e9d951 100755 --- a/app/Module/Page/Controller.php +++ b/app/Module/Page/Controller.php @@ -55,6 +55,13 @@ public function viewUrl($pageUrl) } } + // Force HTTPS if set + if($page->setting('force_https', false) && !$request->isSecure() && !$request->no_redirect) { + $uri = $request->server('REQUEST_URI'); + \Kernel()->redirect('https://' . $request->server('HTTP_HOST') . $uri); + exit; + } + // Single module call? // @todo Check against matched route name instead of general request params (? - may restict query string params from being used) $mainContent = false; @@ -111,7 +118,12 @@ public function viewUrl($pageUrl) if(!is_callable(array($moduleObject, $moduleAction))) { throw new \BadMethodCallException("Module '" . $moduleName ."' does not have a callable method '" . $moduleAction . "'"); } - $moduleResponse = $kernel->dispatch($moduleObject, $moduleAction, array($request, $page, $module)); + try { + $moduleResponse = $kernel->dispatch($moduleObject, $moduleAction, array($request, $page, $module)); + } catch(\Exception $e) { + // Catch module exeptions and pass them through filter + $moduleResponse = $kernel->events('cms')->filter('module_dispatch_exception', $e); + } // Set content as main content (detail view) $mainContent = $this->regionModuleFormat($request, $page, $module, $user, $moduleResponse); @@ -324,12 +336,14 @@ public function editAction($request) */ public function postMethod($request) { - $mapper = $this->kernel->mapper('Module\Page\Mapper'); - $entity = $mapper->get('Module\Page\Entity')->data($request->post()); - $entity->site_id = $this->kernel->config('cms.site.id'); + $kernel = \Kernel(); + $mapper = $kernel->mapper('Module\Page\Mapper'); + $entity = $mapper->get('Module\Page\Entity'); + $entity->data($request->post()); + $entity->site_id = $kernel->site()->id; $entity->parent_id = (int) $request->parent_id; - $entity->date_created = $mapper->connection('Module\Page\Entity')->dateTime(); - $entity->date_modified = $entity->date_created; + $entity->date_created = new \DateTime(); + $entity->date_modified = new \DateTime(); // Auto-genereate URL if not filled in if(!$request->url) { @@ -512,8 +526,14 @@ protected function regionModuleFormat($request, $page, $module, $user, $moduleRe $content .= '
'; endif; - // Call 'content' explicitly so Exceptions are not trapped in __toString - $moduleResponse = $moduleResponse->content(); + try { + // Call 'content' explicitly so Exceptions are not trapped in __toString + $moduleResponse = $moduleResponse->content(); + } catch(\Exception $e) { + // Catch module exeptions and render them inside region where module would have gone + // (allows users to delete and edit bad modules instead of killing the whole page and forcing manual database modification) + $moduleResponse = $kernel->events('cms')->filter('module_dispatch_exception', $e); + } } // Build module HTML diff --git a/app/Plugin/Stackbox/Plugin.php b/app/Plugin/Stackbox/Plugin.php index 1ee7151..3f96bc0 100644 --- a/app/Plugin/Stackbox/Plugin.php +++ b/app/Plugin/Stackbox/Plugin.php @@ -130,6 +130,9 @@ public function __construct(Alloy\Kernel $kernel) // Layout / API output $kernel->events()->addFilter('dispatch_content', 'cms_layout_api_output', array($this, 'layoutOrApiOutput')); + // Add 'autoinstall' method as callback for cms 'module_dispatch_exception' filter when exceptions are encountered + $kernel->events('cms')->addFilter('module_dispatch_exception', 'stackbox_autoinstall_on_exception', array($this, 'autoinstallOnException')); + // If debugging, track execution time and memory usage if($kernel->config('app.mode.development') || $kernel->config('app.debug')) { $timeStart = microtime(true); @@ -217,4 +220,37 @@ public function layoutOrApiOutput($content) return $content; } + + + /** + * Autoinstall missing tables on exception + */ + public function autoinstallOnException($content) + { + $kernel = \Kernel(); + + // Database error + if($content instanceof \PDOException + || $content instanceof \Spot\Exception_Datasource_Missing) { + if($content instanceof \Spot\Exception_Datasource_Missing + ||'42S02' == $content->getCode() + || false !== stripos($content->getMessage(), 'Base table or view not found')) { + // Last dispatch attempt + $ld = $kernel->lastDispatch(); + + // Debug trace message + $mName = is_object($ld['module']) ? get_class($ld['module']) : $ld['module']; + $kernel->trace("PDO Exception on module '" . $mName . "' when dispatching '" . $ld['action'] . "' Attempting auto-install in Stackbox plugin at " . __METHOD__ . "", $content); + + // Table not found - auto-install module to cause Entity migrations + $content = $kernel->dispatch($ld['module'], 'install'); + + if(false !== $content) { + $content = "[[ Auto-installed module '" . $mName . "'. Please refresh page ]]"; + } + } + } + + return $content; + } } \ No newline at end of file diff --git a/app/Plugin/Stackbox/lib/Stackbox/EntityAbstract.php b/app/Plugin/Stackbox/lib/Stackbox/EntityAbstract.php index 508bdc1..ef651b1 100755 --- a/app/Plugin/Stackbox/lib/Stackbox/EntityAbstract.php +++ b/app/Plugin/Stackbox/lib/Stackbox/EntityAbstract.php @@ -15,7 +15,7 @@ public static function fields() // Site id for multi-site installations return array( 'id' => array('type' => 'int', 'primary' => true, 'serial' => true), - 'site_id' => array('type' => 'int', 'index' => true, 'default' => 0) + 'site_id' => array('type' => 'int', 'index' => true, 'default' => 0) ); } From f65cee6164739d98f10160462aa821ae350c2fb8 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 28 Dec 2011 14:31:47 -0600 Subject: [PATCH 26/50] Updated Code module and styles * Fixed error in Code module * Added styles to reduce conflict with Twitter Bootstrap --- app/www/assets/admin/styles/cms_admin.css | 4 ++++ app/www/assets/styles/cms_modules.css | 7 +++++++ app/www/content/Module/Code/Controller.php | 4 ---- app/www/content/Module/Code/Entity.php | 2 +- app/www/content/Module/Code/views/indexAction.html.php | 4 ++-- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/www/assets/admin/styles/cms_admin.css b/app/www/assets/admin/styles/cms_admin.css index 165fb9b..6bd06fa 100755 --- a/app/www/assets/admin/styles/cms_admin.css +++ b/app/www/assets/admin/styles/cms_admin.css @@ -103,6 +103,10 @@ ADMIN UI /* ADMIN BAR ================================================== */ +#cms_admin_bar { + background-color: #222; +} + a.cms_admin_bar_edit { display: block; float: left; diff --git a/app/www/assets/styles/cms_modules.css b/app/www/assets/styles/cms_modules.css index ccba5df..a51f746 100644 --- a/app/www/assets/styles/cms_modules.css +++ b/app/www/assets/styles/cms_modules.css @@ -55,6 +55,13 @@ .niji_method { color: #FF6400; } +/* ensure bootstrap styles don't conflict */ +.module_code pre { + background: transparent; + margin: 0; + padding: 0; + border: 0; +} /** * Slideshow Module diff --git a/app/www/content/Module/Code/Controller.php b/app/www/content/Module/Code/Controller.php index 453b532..e3d0f52 100755 --- a/app/www/content/Module/Code/Controller.php +++ b/app/www/content/Module/Code/Controller.php @@ -118,8 +118,6 @@ public function putMethod($request, $page, $module) /** * Install Module - * - * @see Cx_Module_Controller_Abstract */ public function install($action = null, array $params = array()) { @@ -130,8 +128,6 @@ public function install($action = null, array $params = array()) /** * Uninstall Module - * - * @see Cx_Module_Controller_Abstract */ public function uninstall() { diff --git a/app/www/content/Module/Code/Entity.php b/app/www/content/Module/Code/Entity.php index dd659c8..805ccbf 100755 --- a/app/www/content/Module/Code/Entity.php +++ b/app/www/content/Module/Code/Entity.php @@ -15,7 +15,7 @@ public static function fields() return array_merge(parent::fields(), array( 'content' => array('type' => 'text', 'required' => true), 'type' => array('type' => 'string'), - 'date_created' => array('type' => 'datetime'), + 'date_created' => array('type' => 'datetime', 'default' => new \DateTime()), 'date_modified' => array('type' => 'datetime') )); } diff --git a/app/www/content/Module/Code/views/indexAction.html.php b/app/www/content/Module/Code/views/indexAction.html.php index 762bdec..8411da2 100755 --- a/app/www/content/Module/Code/views/indexAction.html.php +++ b/app/www/content/Module/Code/views/indexAction.html.php @@ -1,3 +1,3 @@ -
+
 content, $item->type); ?>
-
+
From 1e90682687d6bfb5ca1715c44c5aba7f188d3aa3 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Thu, 29 Dec 2011 10:42:27 -0600 Subject: [PATCH 27/50] Added CKEditor plugin for and

* New CKEditor plugin for  and 
 tags, enabled by default
* Admin CSS updates around code blocks to remove any other 
 styling
* Updated Text Module 'type' styles to use Twitter Bootstrap styles
---
 .../ckeditor/plugins/code-button/plugin.js    | 42 +++++++++++++++++++
 .../wysiwyg_code_editor_ckeditor.css          | 16 +++++++
 app/www/assets/admin/scripts/cms_admin.js     |  5 ++-
 app/www/assets/admin/styles/cms_admin.css     | 19 ++++++++-
 .../Module/Text/views/indexAction.html.php    | 12 +++---
 5 files changed, 86 insertions(+), 8 deletions(-)
 create mode 100644 app/www/assets/admin/scripts/ckeditor/plugins/code-button/plugin.js
 create mode 100644 app/www/assets/admin/scripts/ckeditor/plugins/code-button/wysiwyg_code_editor_ckeditor.css

diff --git a/app/www/assets/admin/scripts/ckeditor/plugins/code-button/plugin.js b/app/www/assets/admin/scripts/ckeditor/plugins/code-button/plugin.js
new file mode 100644
index 0000000..a6ec4ca
--- /dev/null
+++ b/app/www/assets/admin/scripts/ckeditor/plugins/code-button/plugin.js
@@ -0,0 +1,42 @@
+(function($) {
+
+  /**
+   * Adds a CKEditor plugin to insert 
 and  tags.
+   *
+   * Based on blog posts by:
+   *
+   * Nikolay Ulyanitsky
+   * http://blog.lystor.org.ua/2010/11/ckeditor-plugin-and-toolbar-button-for.html
+   *
+   * and
+   *
+   * Peter Petrik
+   * http://peterpetrik.com/blog/ckeditor-and-geshi-filter
+   */
+  CKEDITOR.plugins.add('code-button', {
+    init: function (editor) {
+      var buttons = {
+        'code-button-pre': ['pre', 'PRE'],
+        'code-button-code': ['code', 'CODE']
+      };
+      for (var buttonName in buttons) {
+        var format = {'element': buttons[buttonName][0]};
+        var style = new CKEDITOR.style(format);
+
+        // Allow the button's state to be toggled.
+        // @see http://drupal.org/node/1025626 for a standardized solution to
+        //   the closure context late binding problem.
+        (function(buttonName, style) {
+          editor.attachStyleStateChange(style, function (state) {
+            editor.getCommand(buttonName).setState(state);
+          });
+        })(buttonName, style);
+
+        // Add the command and button to the editor.
+        editor.addCommand(buttonName, new CKEDITOR.styleCommand(style));
+        editor.ui.addButton(buttonName, {command: buttonName, label: buttons[buttonName][1]});
+      }
+    }
+  });
+
+})(jQuery);
diff --git a/app/www/assets/admin/scripts/ckeditor/plugins/code-button/wysiwyg_code_editor_ckeditor.css b/app/www/assets/admin/scripts/ckeditor/plugins/code-button/wysiwyg_code_editor_ckeditor.css
new file mode 100644
index 0000000..b240b85
--- /dev/null
+++ b/app/www/assets/admin/scripts/ckeditor/plugins/code-button/wysiwyg_code_editor_ckeditor.css
@@ -0,0 +1,16 @@
+/**
+ * Display the button's label as text in the editor (rather than using an icon).
+ *
+ * This is based on a blog post by Peter Petrik:
+ * http://peterpetrik.com/blog/ckeditor-and-geshi-filter
+ */
+
+.cke_skin_kama .cke_button_code-button-pre span.cke_icon,
+.cke_skin_kama .cke_button_code-button-code span.cke_icon {
+  display: none !important;
+}
+.cke_skin_kama .cke_button_code-button-pre span.cke_label,
+.cke_skin_kama .cke_button_code-button-code span.cke_label {
+  display: inline;
+  font-size: 90%;
+}
diff --git a/app/www/assets/admin/scripts/cms_admin.js b/app/www/assets/admin/scripts/cms_admin.js
index b84bf09..ea1fa93 100755
--- a/app/www/assets/admin/scripts/cms_admin.js
+++ b/app/www/assets/admin/scripts/cms_admin.js
@@ -261,15 +261,16 @@ cms.modal = (function (cms, $) {
                 ['Bold','Italic','Underline','Strike'],
                 ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'],
                 ['Link','Unlink','Anchor'],
-                ['Image','File','Flash','Table','HorizontalRule','Smiley','SpecialChar','-','About'],
+                ['Image','File','Flash','Table','HorizontalRule','Smiley','SpecialChar','-','code-button-code','code-button-pre'],
                 '/',
                 ['Format','FontSize'],
                 ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
                 ['Subscript','Superscript'],
                 ['TextColor','BGColor','-','RemoveFormat'],
                 ['Find','Replace'],
-                ['ShowBlocks','-','Source']
+                ['ShowBlocks','-','Source','-','About']
             ],
+            extraPlugins: "code-button",
             autoUpdateElementJquery: true,
             baseFloatZIndex: 9000,
 
diff --git a/app/www/assets/admin/styles/cms_admin.css b/app/www/assets/admin/styles/cms_admin.css
index 6bd06fa..80ee11d 100755
--- a/app/www/assets/admin/styles/cms_admin.css
+++ b/app/www/assets/admin/styles/cms_admin.css
@@ -919,4 +919,21 @@ a.menu:after, .dropdown-toggle:after {
 }
 .breadcrumb .active a {
   color: #404040;
-}
\ No newline at end of file
+}
+
+/**
+ * CKEditor overrides for  and 
 buttons
+ * Display the button's label as text in the editor (rather than using an icon).
+ *
+ * This is based on a blog post by Peter Petrik:
+ * http://peterpetrik.com/blog/ckeditor-and-geshi-filter
+ */
+.cke_skin_kama .cke_button_code-button-pre span.cke_icon,
+.cke_skin_kama .cke_button_code-button-code span.cke_icon {
+  display: none !important;
+}
+.cke_skin_kama .cke_button_code-button-pre span.cke_label,
+.cke_skin_kama .cke_button_code-button-code span.cke_label {
+  display: inline;
+  font-size: 90%;
+}
diff --git a/app/www/content/Module/Text/views/indexAction.html.php b/app/www/content/Module/Text/views/indexAction.html.php
index d64e9d8..c2fe432 100755
--- a/app/www/content/Module/Text/views/indexAction.html.php
+++ b/app/www/content/Module/Text/views/indexAction.html.php
@@ -2,22 +2,24 @@
 // Note
 if('note' == $item->type):
 ?>
-    
content; ?>
+
content; ?>
type): ?> -
content; ?>
+
content; ?>
type): ?> -
+  
+
     content, ENT_QUOTES, 'UTF-8'); ?>
-    
+
+
content; + echo $item->content; endif; ?> From 751258050e6dd13952992c896c4dc0ded9d9085a Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 31 Dec 2011 00:22:20 -0600 Subject: [PATCH 28/50] Updated Navigation and Treeview Generic * Treeview Generic updated from Alloy to work properly with max = 0 * Navigation level setting now accurately reflects nav level display --- .../Alloy/View/Generic/templates/treeview.html.php | 7 ++++--- app/www/content/Module/Navigation/Controller.php | 3 ++- .../Module/Navigation/views/indexAction.html.php | 13 +++++++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/alloy/lib/Alloy/View/Generic/templates/treeview.html.php b/alloy/lib/Alloy/View/Generic/templates/treeview.html.php index a2c2df5..2e38e5d 100644 --- a/alloy/lib/Alloy/View/Generic/templates/treeview.html.php +++ b/alloy/lib/Alloy/View/Generic/templates/treeview.html.php @@ -2,10 +2,10 @@ $currentLevel = $view::$_level; // Check if we're okay to display against min level set -$levelMinCheck = (!$levelMin || $currentLevel >= $levelMin); +$levelMinCheck = (!$levelMin || $currentLevel > $levelMin); // Check if we're okay to display against max level set -$levelMaxCheck = (!$levelMax || $currentLevel <= $levelMax); +$levelMaxCheck = (!$levelMax || $currentLevel < $levelMax); if($levelMaxCheck): @@ -38,7 +38,8 @@ // Ensure we can go to next level // Don't show children if current level is equal to max - if($levelMaxCheck && $currentLevel != $levelMax): + // DO show full tree if max is set to '0' + if($levelMaxCheck && ($levelMax == 0 || $currentLevel != $levelMax)): // Item children (hierarchy) if(isset($itemChildrenCallback)): $children = $itemChildrenCallback($item); diff --git a/app/www/content/Module/Navigation/Controller.php b/app/www/content/Module/Navigation/Controller.php index b48bb95..e6c3bc2 100755 --- a/app/www/content/Module/Navigation/Controller.php +++ b/app/www/content/Module/Navigation/Controller.php @@ -16,6 +16,7 @@ class Controller extends Stackbox\Module\ControllerAbstract */ public function indexAction(Request $request, Page $page, Module $module) { + $currentPage = $page; if('section' == $module->setting('type')) { $pages = $this->kernel->mapper('Module\Page\Mapper')->pageTree($page, $page); } else { @@ -23,7 +24,7 @@ public function indexAction(Request $request, Page $page, Module $module) } return $this->template(__FUNCTION__) - ->set(compact('pages', 'module')); + ->set(compact('pages', 'module', 'currentPage')); } diff --git a/app/www/content/Module/Navigation/views/indexAction.html.php b/app/www/content/Module/Navigation/views/indexAction.html.php index 9973ece..b888095 100755 --- a/app/www/content/Module/Navigation/views/indexAction.html.php +++ b/app/www/content/Module/Navigation/views/indexAction.html.php @@ -8,9 +8,14 @@ ->itemChildren(function($page) { return $page->children; }) - ->levelMin($module->setting('level_min', 0)) - ->levelMax($module->setting('level_max', 0)) - ->filter(function($page) use($module) { + ->levelMin((int) $module->setting('level_min', 0)) + ->levelMax((int) $module->setting('level_max', 0)) + ->filter(function($page) use($module, $currentPage) { + // Filter pages based on section + if('section' == $module->setting('type')) { + // Section-based navigation + } + // Setting: Show Homepage? if($page->isHomepage() && !$module->setting('show_homepage')) { return false; @@ -33,4 +38,4 @@ $cssActive = $page->is_in_path ? ' page_active' : ''; return "
  • id . $cssActive . "\">\n"; }); -echo $tree->content(); +echo $tree->content(); \ No newline at end of file From d5cec7c16e29d1ae1d45fb0e614f720afc556b5b Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 4 Jan 2012 15:58:16 -0600 Subject: [PATCH 29/50] Updated Imagine plugin to v0.2.8 PHAR --- app/Plugin/Imagine/Plugin.php | 4 +- app/Plugin/Imagine/imagine.phar | Bin 0 -> 160450 bytes .../lib/Imagine/Draw/DrawerInterface.php | 139 ---- .../lib/Imagine/Exception/Exception.php | 16 - .../Exception/InvalidArgumentException.php | 18 - .../Exception/OutOfBoundsException.php | 18 - .../Imagine/Exception/RuntimeException.php | 18 - .../lib/Imagine/Fill/FillInterface.php | 26 - .../lib/Imagine/Fill/Gradient/Horizontal.php | 26 - .../lib/Imagine/Fill/Gradient/Linear.php | 85 --- .../lib/Imagine/Fill/Gradient/Vertical.php | 26 - .../Imagine/lib/Imagine/Filter/Basic/Copy.php | 27 - .../Imagine/lib/Imagine/Filter/Basic/Crop.php | 52 -- .../Imagine/Filter/Basic/FlipHorizontally.php | 27 - .../Imagine/Filter/Basic/FlipVertically.php | 27 - .../lib/Imagine/Filter/Basic/Paste.php | 51 -- .../lib/Imagine/Filter/Basic/Resize.php | 43 -- .../lib/Imagine/Filter/Basic/Rotate.php | 50 -- .../Imagine/lib/Imagine/Filter/Basic/Save.php | 49 -- .../Imagine/lib/Imagine/Filter/Basic/Show.php | 49 -- .../lib/Imagine/Filter/Basic/Thumbnail.php | 52 -- .../lib/Imagine/Filter/FilterInterface.php | 27 - .../lib/Imagine/Filter/Transformation.php | 201 ------ app/Plugin/Imagine/lib/Imagine/Gd/Drawer.php | 274 -------- app/Plugin/Imagine/lib/Imagine/Gd/Font.php | 34 - app/Plugin/Imagine/lib/Imagine/Gd/Image.php | 592 ------------------ app/Plugin/Imagine/lib/Imagine/Gd/Imagine.php | 193 ------ .../Imagine/lib/Imagine/Gmagick/Drawer.php | 375 ----------- .../Imagine/lib/Imagine/Gmagick/Font.php | 56 -- .../Imagine/lib/Imagine/Gmagick/Image.php | 469 -------------- .../Imagine/lib/Imagine/Gmagick/Imagine.php | 95 --- .../lib/Imagine/Image/AbstractFont.php | 73 --- app/Plugin/Imagine/lib/Imagine/Image/Box.php | 115 ---- .../lib/Imagine/Image/BoxInterface.php | 71 --- .../Imagine/lib/Imagine/Image/Color.php | 222 ------- .../lib/Imagine/Image/FontInterface.php | 46 -- .../lib/Imagine/Image/Histogram/Bucket.php | 54 -- .../lib/Imagine/Image/Histogram/Range.php | 56 -- .../Imagine/lib/Imagine/Image/Point.php | 83 --- .../lib/Imagine/Image/Point/Center.php | 69 -- .../lib/Imagine/Image/PointInterface.php | 45 -- .../Imagine/lib/Imagine/ImageInterface.php | 223 ------- .../Imagine/lib/Imagine/Imagick/Drawer.php | 399 ------------ .../Imagine/lib/Imagine/Imagick/Font.php | 56 -- .../Imagine/lib/Imagine/Imagick/Image.php | 494 --------------- .../Imagine/lib/Imagine/Imagick/Imagine.php | 120 ---- .../Imagine/lib/Imagine/ImagineInterface.php | 68 -- .../Imagine/Test/Constraint/IsImageEqual.php | 158 ----- .../lib/Imagine/Test/ImagineTestCase.php | 34 - 49 files changed, 2 insertions(+), 5503 deletions(-) create mode 100644 app/Plugin/Imagine/imagine.phar delete mode 100755 app/Plugin/Imagine/lib/Imagine/Draw/DrawerInterface.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Exception/Exception.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Exception/InvalidArgumentException.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Exception/OutOfBoundsException.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Exception/RuntimeException.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Fill/FillInterface.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Horizontal.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Linear.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Vertical.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Basic/Copy.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Basic/Crop.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Basic/FlipHorizontally.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Basic/FlipVertically.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Basic/Paste.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Basic/Resize.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Basic/Rotate.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Basic/Save.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Basic/Show.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Basic/Thumbnail.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/FilterInterface.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Filter/Transformation.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Gd/Drawer.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Gd/Font.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Gd/Image.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Gd/Imagine.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Gmagick/Drawer.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Gmagick/Font.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Gmagick/Image.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Gmagick/Imagine.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Image/AbstractFont.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Image/Box.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Image/BoxInterface.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Image/Color.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Image/FontInterface.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Image/Histogram/Bucket.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Image/Histogram/Range.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Image/Point.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Image/Point/Center.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Image/PointInterface.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/ImageInterface.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Imagick/Drawer.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Imagick/Font.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Imagick/Image.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Imagick/Imagine.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/ImagineInterface.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Test/Constraint/IsImageEqual.php delete mode 100755 app/Plugin/Imagine/lib/Imagine/Test/ImagineTestCase.php diff --git a/app/Plugin/Imagine/Plugin.php b/app/Plugin/Imagine/Plugin.php index 6ffe2b3..236887d 100644 --- a/app/Plugin/Imagine/Plugin.php +++ b/app/Plugin/Imagine/Plugin.php @@ -18,8 +18,8 @@ public function __construct(Alloy\Kernel $kernel) { $this->kernel = $kernel; - // Let autoloader know where to find Imagine library files - $kernel->loader()->registerNamespace('Imagine', __DIR__ . '/lib'); + // Require PHAR package + require 'phar://' . __DIR__ . '/imagine.phar'; // Make methods globally avaialble with Kernel $kernel->addMethod('imagine', function($adapter = 'Gd') { diff --git a/app/Plugin/Imagine/imagine.phar b/app/Plugin/Imagine/imagine.phar new file mode 100644 index 0000000000000000000000000000000000000000..03379a4dab9a86cd8f7d5259c1f6561c6ff42474 GIT binary patch literal 160450 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
    )5O0KB@k79YHgV*w<6-l*-672S4^jkpsi= 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 b^tXPvv-uOh^}B!X2VQyQl{+`@-TeOnIZtoY literal 0 HcmV?d00001 diff --git a/app/Plugin/Imagine/lib/Imagine/Draw/DrawerInterface.php b/app/Plugin/Imagine/lib/Imagine/Draw/DrawerInterface.php deleted file mode 100755 index e4eae88..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Draw/DrawerInterface.php +++ /dev/null @@ -1,139 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Draw; - -use Imagine\Image\AbstractFont; -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\Image\PointInterface; -use Imagine\Filter\FilterInterface; - -interface DrawerInterface -{ - /** - * Draws an arc on a starting at a given x, y coordinates under a given - * start and end angles - * - * @param Imagine\Image\PointInterface $center - * @param Imagine\Image\BoxInterface $size - * @param integer $start - * @param integer $end - * @param Imagine\Image\Color $color - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\Draw\DrawerInterface - */ - function arc(PointInterface $center, BoxInterface $size, $start, $end, Color $color); - - /** - * Same as arc, but also connects end points with a straight line - * - * @param Imagine\Image\PointInterface $center - * @param Imagine\Image\BoxInterface $size - * @param integer $start - * @param integer $end - * @param Imagine\Image\Color $color - * @param Boolean $fill - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\Draw\DrawerInterface - */ - function chord(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false); - - /** - * Draws and ellipse with center at the given x, y coordinates, and given - * width and height - * - * @param Imagine\Image\PointInterface $center - * @param Imagine\Image\BoxInterface $size - * @param Imagine\Image\Color $color - * @param Boolean $fill - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\Draw\DrawerInterface - */ - function ellipse(PointInterface $center, BoxInterface $size, Color $color, $fill = false); - - /** - * Draws a line from start(x, y) to end(x, y) coordinates - * - * @param Imagine\Image\PointInterface $start - * @param Imagine\Image\PointInterface $end - * @param Imagine\Image\Color $outline - * - * @return Imagine\Draw\DrawerInterface - */ - function line(PointInterface $start, PointInterface $end, Color $outline); - - /** - * Same as arc, but connects end points and the center - * - * @param Imagine\Image\PointInterface $center - * @param Imagine\Image\BoxInterface $size - * @param integer $start - * @param integer $end - * @param Imagine\Image\Color $color - * @param Boolean $fill - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\Draw\DrawerInterface - */ - function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false); - - /** - * Places a one pixel point at specific coordinates and fills it with - * specified color - * - * @param Imagine\Image\PointInterface $position - * @param Imagine\Image\Color $color - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\Draw\DrawerInterface - */ - function dot(PointInterface $position, Color $color); - - /** - * Draws a polygon using array of x, y coordinates. Must contain at least - * three coordinates - * - * @param array $coordinates - * @param Imagine\Image\Color $color - * @param Boolean $fill - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\Draw\DrawerInterface - */ - function polygon(array $coordinates, Color $color, $fill = false); - - /** - * Annotates image with specified text at a given position starting on the - * top left of the final text box - * - * The rotation is done CW - * - * @param string $string - * @param Imagine\Image\AbstractFont $font - * @param Imagine\Image\PointInterface $position - * @param integer $angle - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\Draw\DrawerInterface - */ - function text($string, AbstractFont $font, PointInterface $position, $angle = 0); -} diff --git a/app/Plugin/Imagine/lib/Imagine/Exception/Exception.php b/app/Plugin/Imagine/lib/Imagine/Exception/Exception.php deleted file mode 100755 index cc1fb79..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Exception/Exception.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Exception; - -interface Exception -{ -} \ No newline at end of file diff --git a/app/Plugin/Imagine/lib/Imagine/Exception/InvalidArgumentException.php b/app/Plugin/Imagine/lib/Imagine/Exception/InvalidArgumentException.php deleted file mode 100755 index 0ce5835..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Exception/InvalidArgumentException.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Exception; - -use InvalidArgumentException as BaseInvalidArgumentException; - -class InvalidArgumentException extends BaseInvalidArgumentException implements Exception -{ -} \ No newline at end of file diff --git a/app/Plugin/Imagine/lib/Imagine/Exception/OutOfBoundsException.php b/app/Plugin/Imagine/lib/Imagine/Exception/OutOfBoundsException.php deleted file mode 100755 index aa373b0..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Exception/OutOfBoundsException.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Exception; - -use OutOfBoundsException as BaseOutOfBoundsException; - -class OutOfBoundsException extends BaseOutOfBoundsException implements Exception -{ -} diff --git a/app/Plugin/Imagine/lib/Imagine/Exception/RuntimeException.php b/app/Plugin/Imagine/lib/Imagine/Exception/RuntimeException.php deleted file mode 100755 index d72cdf3..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Exception/RuntimeException.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Exception; - -use RuntimeException as BaseRuntimeException; - -class RuntimeException extends BaseRuntimeException implements Exception -{ -} diff --git a/app/Plugin/Imagine/lib/Imagine/Fill/FillInterface.php b/app/Plugin/Imagine/lib/Imagine/Fill/FillInterface.php deleted file mode 100755 index b603437..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Fill/FillInterface.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Fill; - -use Imagine\Image\PointInterface; - -interface FillInterface -{ - /** - * Gets color of the fill for the given position - * - * @param Imagine\Image\PointInterface $position - * - * @return Imagine\Image\Color - */ - function getColor(PointInterface $position); -} diff --git a/app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Horizontal.php b/app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Horizontal.php deleted file mode 100755 index 79f4d4c..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Horizontal.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Fill\Gradient; - -use Imagine\Image\PointInterface; - -final class Horizontal extends Linear -{ - /** - * (non-PHPdoc) - * @see Imagine\Mask\Gradient\Linear::getDistance() - */ - public function getDistance(PointInterface $position) - { - return $position->getX(); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Linear.php b/app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Linear.php deleted file mode 100755 index aef9849..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Linear.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Fill\Gradient; - -use Imagine\Image\Color; -use Imagine\Image\PointInterface; -use Imagine\Fill\FillInterface; - -abstract class Linear implements FillInterface -{ - /** - * @var integer - */ - private $length; - - /** - * @var Imagine\Image\Color - */ - private $start; - - /** - * @var Imagine\Image\Color - */ - private $end; - - /** - * Constructs a linear gradient with overal gradient length, and start and - * end shades, which default to 0 and 255 accordingly - * - * @param integer $length - * @param Imagine\Image\Color $start - * @param Imagine\Image\Color $end - */ - public function __construct($length, $start, $end) - { - $this->length = $length; - $this->start = $start; - $this->end = $end; - } - - /** - * (non-PHPdoc) - * @see Imagine\Fill\FillInterface::getShade() - */ - public function getColor(PointInterface $position) - { - $l = $this->getDistance($position); - - if ($l >= $this->length) { - return $this->end; - } - - if ($l < 0) { - return $this->start; - } - - $color = new Color(array( - (int) min(255, min($this->start->getRed(), $this->end->getRed()) + round(abs($this->end->getRed() - $this->start->getRed()) * $l / $this->length)), - (int) min(255, min($this->start->getGreen(), $this->end->getGreen()) + round(abs($this->end->getGreen() - $this->start->getGreen()) * $l / $this->length)), - (int) min(255, min($this->start->getBlue(), $this->end->getBlue()) + round(abs($this->end->getBlue() - $this->start->getBlue()) * $l / $this->length)), - ), - (int) min(100, min($this->start->getAlpha(), $this->end->getAlpha()) + round(abs($this->end->getAlpha() - $this->start->getAlpha()) * $l / $this->length)) - ); - - return $color; - } - - /** - * Get the distance of the position relative to the begining of the gradient - * - * @param Imagine\Image\PointInterface $position - * - * @return integer - */ - abstract protected function getDistance(PointInterface $position); -} diff --git a/app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Vertical.php b/app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Vertical.php deleted file mode 100755 index 043b4a8..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Fill/Gradient/Vertical.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Fill\Gradient; - -use Imagine\Image\PointInterface; - -final class Vertical extends Linear -{ - /** - * (non-PHPdoc) - * @see Imagine\Mask\Gradient\Linear::getDistance() - */ - public function getDistance(PointInterface $position) - { - return $position->getY(); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Copy.php b/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Copy.php deleted file mode 100755 index 3ecb914..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Copy.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Filter\FilterInterface; -use Imagine\ImageInterface; - -class Copy implements FilterInterface -{ - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return $image->copy(); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Crop.php b/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Crop.php deleted file mode 100755 index ff11f85..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Crop.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\ImageInterface; -use Imagine\Image\BoxInterface; -use Imagine\Image\PointInterface; -use Imagine\Filter\FilterInterface; - -class Crop implements FilterInterface -{ - /** - * @var Imagine\Image\PointInterface - */ - private $start; - - /** - * @var Imagine\Image\BoxInterface - */ - private $size; - - /** - * Constructs a Crop filter with given x, y, coordinates and crop width and - * height values - * - * @param Imagine\Image\PointInterface $start - * @param Imagine\Image\BoxInterface $size - */ - public function __construct(PointInterface $start, BoxInterface $size) - { - $this->start = $start; - $this->size = $size; - } - - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return $image->crop($this->start, $this->size); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/FlipHorizontally.php b/app/Plugin/Imagine/lib/Imagine/Filter/Basic/FlipHorizontally.php deleted file mode 100755 index a71d395..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/FlipHorizontally.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\ImageInterface; -use Imagine\Filter\FilterInterface; - -class FlipHorizontally implements FilterInterface -{ - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return $image->flipHorizontally(); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/FlipVertically.php b/app/Plugin/Imagine/lib/Imagine/Filter/Basic/FlipVertically.php deleted file mode 100755 index c0e9eed..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/FlipVertically.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\ImageInterface; -use Imagine\Filter\FilterInterface; - -class FlipVertically implements FilterInterface -{ - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return $image->flipVertically(); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Paste.php b/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Paste.php deleted file mode 100755 index 6c8af31..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Paste.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\ImageInterface; -use Imagine\Image\PointInterface; -use Imagine\Filter\FilterInterface; - -class Paste implements FilterInterface -{ - /** - * @var Imagine\ImageInterface - */ - private $image; - - /** - * @var Imagine\Image\PointInterface - */ - private $start; - - /** - * Constructs a Paste filter with given ImageInterface to paste and x, y - * coordinates of target position - * - * @param Imagine\ImageInterface $image - * @param Imagine\Image\PointInterface $start - */ - public function __construct(ImageInterface $image, PointInterface $start) - { - $this->image = $image; - $this->start = $start; - } - - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return $image->paste($this->image, $this->start); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Resize.php b/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Resize.php deleted file mode 100755 index 0d10e02..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Resize.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\Filter\FilterInterface; -use Imagine\ImageInterface; -use Imagine\Image\BoxInterface; - -class Resize implements FilterInterface -{ - /** - * @var Imagine\Image\BoxInterface - */ - private $size; - - /** - * Constructs Resize filter with given width and height - * - * @param Imagine\Image\BoxInterface $size - */ - public function __construct(BoxInterface $size) - { - $this->size = $size; - } - - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return $image->resize($this->size); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Rotate.php b/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Rotate.php deleted file mode 100755 index fdcb2f7..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Rotate.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\ImageInterface; -use Imagine\Image\Color; -use Imagine\Filter\FilterInterface; - -class Rotate implements FilterInterface -{ - /** - * @var integer - */ - private $angle; - - /** - * @var Imagine\Image\Color - */ - private $background; - - /** - * Constructs Rotate filter with given angle and background color - * - * @param integer $angle - * @param Imagine\Image\Color $background - */ - public function __construct($angle, Color $background = null) - { - $this->angle = $angle; - $this->background = $background; - } - - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return $image->rotate($this->angle, $this->background); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Save.php b/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Save.php deleted file mode 100755 index 42cc99a..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Save.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\ImageInterface; -use Imagine\Filter\FilterInterface; - -class Save implements FilterInterface -{ - /** - * @var string - */ - private $path; - - /** - * @var array - */ - private $options; - - /** - * Constructs Save filter with given path and options - * - * @param string $path - * @param array $options - */ - public function __construct($path, array $options = array()) - { - $this->path = $path; - $this->options = $options; - } - - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return $image->save($this->path, $this->options); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Show.php b/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Show.php deleted file mode 100755 index bd02cf2..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Show.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\ImageInterface; -use Imagine\Filter\FilterInterface; - -class Show implements FilterInterface -{ - /** - * @var string - */ - private $format; - - /** - * @var array - */ - private $options; - - /** - * Constructs the Show filter with given format and options - * - * @param string $format - * @param array $options - */ - public function __construct($format, array $options = array()) - { - $this->format = $format; - $this->options = $options; - } - - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return $image->show($this->format, $this->options); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Thumbnail.php b/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Thumbnail.php deleted file mode 100755 index 7236b47..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Basic/Thumbnail.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter\Basic; - -use Imagine\ImageInterface; -use Imagine\Image\Color; -use Imagine\Image\BoxInterface; -use Imagine\Filter\FilterInterface; - -class Thumbnail implements FilterInterface -{ - /** - * @var Imagine\Image\BoxInterface - */ - private $size; - - /** - * @var string - */ - private $mode; - - /** - * Constructs the Thumbnail filter with given width, height, mode and - * background color - * - * @param Imagine\Image\BoxInterface $size - * @param string $mode - */ - public function __construct(BoxInterface $size, $mode = ImageInterface::THUMBNAIL_INSET) - { - $this->size = $size; - $this->mode = $mode; - } - - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return $image->thumbnail($this->size, $this->mode); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/FilterInterface.php b/app/Plugin/Imagine/lib/Imagine/Filter/FilterInterface.php deleted file mode 100755 index 9b4779a..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/FilterInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter; - -use Imagine\ImageInterface; - -interface FilterInterface -{ - /** - * Applies scheduled transformation to ImageInterface instance - * Returns processed ImageInterface instance - * - * @param Imagine\ImageInterface $image - * - * @return Imagine\ImageInterface - */ - function apply(ImageInterface $image); -} diff --git a/app/Plugin/Imagine/lib/Imagine/Filter/Transformation.php b/app/Plugin/Imagine/lib/Imagine/Filter/Transformation.php deleted file mode 100755 index 0495dbf..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Filter/Transformation.php +++ /dev/null @@ -1,201 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Filter; - -use Imagine\Filter\Basic\Copy; -use Imagine\Filter\Basic\Crop; -use Imagine\Filter\Basic\FlipVertically; -use Imagine\Filter\Basic\FlipHorizontally; -use Imagine\Filter\Basic\Paste; -use Imagine\Filter\Basic\Resize; -use Imagine\Filter\Basic\Rotate; -use Imagine\Filter\Basic\Save; -use Imagine\Filter\Basic\Show; -use Imagine\Filter\Basic\Thumbnail; -use Imagine\ImageInterface; -use Imagine\ImageFactoryInterface; -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; - -final class Transformation implements FilterInterface -{ - /** - * @var array - */ - private $filters = array(); - - /** - * Applies a given FilterInterface onto given ImageInterface and returns - * modified ImageInterface - * - * @param Imagine\Filter\FilterInterface $filter - * @param Imagine\ImageInterface $image - * - * @return Imagine\ImageInterface - */ - public function applyFilter(ImageInterface $image, FilterInterface $filter) - { - return $filter->apply($image); - } - - /** - * (non-PHPdoc) - * @see Imagine\Filter\FilterInterface::apply() - */ - public function apply(ImageInterface $image) - { - return array_reduce( - $this->filters, - array($this, 'applyFilter'), - $image - ); - } - - /** - * Stacks a copy transformation into the current transformations queue - * - * @return Imagine\Filter\Transformation - */ - public function copy() - { - return $this->add(new Copy()); - } - - /** - * Stacks a crop transformation into the current transformations queue - * - * @param Imagine\Image\PointInterface $start - * @param Imagine\Image\BoxInterface $size - * - * @return Imagine\Filter\Transformation - */ - public function crop(PointInterface $start, BoxInterface $size) - { - return $this->add(new Crop($start, $size)); - } - - /** - * Stacks a horizontal flip transformation into the current transformations - * queue - * - * @return Imagine\Filter\Transformation - */ - public function flipHorizontally() - { - return $this->add(new FlipHorizontally()); - } - - /** - * Stacks a vertical flip transformation into the current transformations - * queue - * - * @return Imagine\Filter\Transformation - */ - public function flipVertically() - { - return $this->add(new FlipVertically()); - } - - /** - * Stacks a paste transformation into the current transformations queue - * - * @param Imagine\ImageInterface $image - * @param Imagine\Image\PointInterface $start - * - * @return Imagine\Filter\Transformation - */ - public function paste(ImageInterface $image, Point $start) - { - return $this->add(new Paste($image, $start)); - } - - /** - * Stacks a resize transformation into the current transformations queue - * - * @param Imagine\Image\BoxInterface - * - * @return Imagine\Filter\Transformation - */ - public function resize(BoxInterface $size) - { - return $this->add(new Resize($size)); - } - - /** - * Stacks a rotane transformation into the current transformations queue - * - * @param integer $angle - * @param Imagine\Image\Color $background - * - * @return Imagine\Filter\Transformation - */ - public function rotate($angle, Color $background = null) - { - return $this->add(new Rotate($angle, $background)); - } - - /** - * Stacks a save transformation into the current transformations queue - * - * @param string $path - * @param array $options - * - * @return Imagine\Filter\Transformation - */ - public function save($path, array $options = array()) - { - return $this->add(new Save($path, $options)); - } - - /** - * Stacks a show transformation into the current transformations queue - * - * @param string $path - * @param array $options - * - * @return Imagine\Filter\Transformation - */ - public function show($format, array $options = array()) - { - return $this->add(new Show($format, $options)); - } - - /** - * Stacks a thumbnail transformation into the current transformation queue - * - * @param Imagine\Image\BoxInterface $size - * @param string $mode - * - * @return Imagine\Filter\Transformation - */ - public function thumbnail(BoxInterface $size, $mode = ImageInterface::THUMBNAIL_INSET) - { - return $this->add(new Thumbnail($size, $mode)); - } - - /** - * Registers a given FilterInterface in an internal array of filters for - * later application to an instance of ImageInterface - * - * @param Imagine\Filter\FilterInterface $filter - * - * @return Imagine\Filter\Transformation - */ - public function add(FilterInterface $filter) - { - $this->filters[] = $filter; - - return $this; - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Gd/Drawer.php b/app/Plugin/Imagine/lib/Imagine/Gd/Drawer.php deleted file mode 100755 index e3c1a40..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Gd/Drawer.php +++ /dev/null @@ -1,274 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gd; - -use Imagine\Draw\DrawerInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\AbstractFont; -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\Image\PointInterface; - -final class Drawer implements DrawerInterface -{ - /** - * @var resource - */ - private $resource; - - /** - * Constructs Drawer with a given gd image resource - * - * @param resource $resource - */ - public function __construct($resource) - { - $this->resource = $resource; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::arc() - */ - public function arc(PointInterface $center, BoxInterface $size, $start, $end, Color $color) - { - if (false === imagearc( - $this->resource, $center->getX(), $center->getY(), - $size->getWidth(), $size->getHeight(), $start, $end, - $this->getColor($color) - )) { - throw new RuntimeException('Draw arc operation failed'); - } - - return $this; - } - - /** - * This function doesn't work properly because of a bug in GD - * - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::chord() - */ - public function chord(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false) - { - if ($fill) { - $style = IMG_ARC_CHORD; - } else { - $style = IMG_ARC_CHORD | IMG_ARC_NOFILL; - } - - if (false === imagefilledarc( - $this->resource, $center->getX(), $center->getY(), - $size->getWidth(), $size->getHeight(), $start, $end, - $this->getColor($color), $style - )) { - throw new RuntimeException('Draw chord operation failed'); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::ellipse() - */ - public function ellipse(PointInterface $center, BoxInterface $size, Color $color, $fill = false) - { - if ($fill) { - $callback = 'imagefilledellipse'; - } else { - $callback = 'imageellipse'; - } - - if (false === $callback( - $this->resource, $center->getX(), $center->getY(), - $size->getWidth(), $size->getHeight(), $this->getColor($color)) - ) { - throw new RuntimeException('Draw ellipse operation failed'); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::line() - */ - public function line(PointInterface $start, PointInterface $end, Color $color) - { - if (false === imageline( - $this->resource, $start->getX(), $start->getY(), - $end->getX(), $end->getY(), $this->getColor($color) - )) { - throw new RuntimeException('Draw line operation failed'); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::pieSlice() - */ - public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false) - { - if ($fill) { - $style = IMG_ARC_EDGED; - } else { - $style = IMG_ARC_EDGED | IMG_ARC_NOFILL; - } - - if (false === imagefilledarc( - $this->resource, $center->getX(), $center->getY(), - $size->getWidth(), $size->getHeight(), $start, $end, - $this->getColor($color), $style - )) { - throw new RuntimeException('Draw chord operation failed'); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::dot() - */ - public function dot(PointInterface $position, Color $color) - { - if (false === imagesetpixel( - $this->resource, $position->getX(), $position->getY(), - $this->getColor($color) - )) { - throw new RuntimeException('Draw point operation failed'); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::polygon() - */ - public function polygon(array $coordinates, Color $color, $fill = false) - { - if (count($coordinates) < 3) { - throw new InvalidArgumentException(sprintf( - 'A polygon must consist of at least 3 points, %d given', - count($coordinates) - )); - } - - $points = array(); - - foreach ($coordinates as $coordinate) { - if (!$coordinate instanceof PointInterface) { - throw new InvalidArgumentException(sprintf( - 'Each entry in coordinates array must be instance of '. - 'Imagine\Image\PointInterface, %s given', var_export($coordinate) - )); - } - - $points[] = $coordinate->getX(); - $points[] = $coordinate->getY(); - } - - if ($fill) { - $callback = 'imagefilledpolygon'; - } else { - $callback = 'imagepolygon'; - } - - if (false === $callback( - $this->resource, $points, count($coordinates), - $this->getColor($color) - )) { - throw new RuntimeException('Draw polygon operation failed'); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::text() - */ - public function text($string, AbstractFont $font, PointInterface $position, $angle = 0) - { - $angle = -1 * $angle; - $fontsize = $font->getSize(); - $fontfile = $font->getFile(); - $info = imageftbbox($fontsize, $angle, $fontfile, $string); - $xs = array($info[0], $info[2], $info[4], $info[6]); - $ys = array($info[1], $info[3], $info[5], $info[7]); - $width = abs(max($xs) - min($xs)); - $height = abs(max($ys) - min($ys)); - - $xdiff = 0 - min($xs) + $position->getX(); - $ydiff = 0 - min($ys) + $position->getY(); - - foreach ($xs as &$x) { - $x += $xdiff; - } - - foreach ($ys as &$y) { - $y += $ydiff; - } - - if (false === imagealphablending($this->resource, true)) { - throw new RuntimeException('Font mask operation failed'); - } - - if (false === imagefttext( - $this->resource, $fontsize, $angle, $xs[0], $ys[0], - $this->getColor($font->getColor()), $fontfile, $string - )) { - throw new RuntimeException('Font mask operation failed'); - } - - if (false === imagealphablending($this->resource, false)) { - throw new RuntimeException('Font mask operation failed'); - } - - return $this; - } - - /** - * Internal - * - * Generates a GD color from Color instance - * - * @param Imagine\Image\Color $color - * - * @return resource - * - * @throws Imagine\Exception\RuntimeException - */ - private function getColor(Color $color) - { - $color = imagecolorallocatealpha( - $this->resource, - $color->getRed(), $color->getGreen(), $color->getBlue(), - round(127 * $color->getAlpha() / 100) - ); - if (false === $color) { - throw new RuntimeException(sprintf( - 'Unable to allocate color "RGB(%s, %s, %s)" with '. - 'transparency of %d percent', $color->getRed(), - $color->getGreen(), $color->getBlue(), $color->getAlpha() - )); - } - - return $color; - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Gd/Font.php b/app/Plugin/Imagine/lib/Imagine/Gd/Font.php deleted file mode 100755 index 0759802..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Gd/Font.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gd; - -use Imagine\Image\AbstractFont; -use Imagine\Image\Box; - -final class Font extends AbstractFont -{ - /** - * (non-PHPdoc) - * @see Imagine\Image\AbstractFont::box() - */ - public function box($string, $angle = 0) - { - $angle = -1 * $angle; - $info = imageftbbox($this->size, $angle, $this->file, $string); - $xs = array($info[0], $info[2], $info[4], $info[6]); - $ys = array($info[1], $info[3], $info[5], $info[7]); - $width = abs(max($xs) - min($xs)); - $height = abs(max($ys) - min($ys)); - - return new Box($width, $height); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Gd/Image.php b/app/Plugin/Imagine/lib/Imagine/Gd/Image.php deleted file mode 100755 index 701e43c..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Gd/Image.php +++ /dev/null @@ -1,592 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gd; - -use Imagine\ImageInterface; -use Imagine\Image\Box; -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; -use Imagine\Image\Point\Center; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\RuntimeException; -use Imagine\Fill\FillInterface; -use Imagine\Gd\Imagine; -use Imagine\Mask\MaskInterface; - -final class Image implements ImageInterface -{ - /** - * @var resource - */ - private $resource; - - /** - * Constructs a new Image instance using the result of - * imagecreatetruecolor() - * - * @param resource $resource - */ - public function __construct($resource) - { - $this->resource = $resource; - } - - /** - * Makes sure the current image resource is destroyed - */ - public function __destruct() - { - imagedestroy($this->resource); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::copy() - */ - final public function copy() - { - $size = $this->getSize(); - $copy = imagecreatetruecolor($size->getWidth(), $size->getHeight()); - - if (false === $copy) { - throw new RuntimeException('Image copy operation failed'); - } - - if (false === imagealphablending($copy, false) || - false === imagesavealpha($copy, true)) { - throw new RuntimeException('Image copy operation failed'); - } - - if (function_exists('imageantialias')) { - imageantialias($copy, true); - } - - if (false === imagecopymerge($copy, $this->resource, 0, 0, 0, - 0, $size->getWidth(), $size->getHeight(), 100)) { - throw new RuntimeException('Image copy operation failed'); - } - - return new Image($copy); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::crop() - */ - final public function crop(PointInterface $start, BoxInterface $size) - { - if (!$start->in($this->getSize())) { - throw new OutOfBoundsException( - 'Crop coordinates must start at minimum 0, 0 position from '. - 'top left corner, crop height and width must be positive '. - 'integers and must not exceed the current image borders' - ); - } - - $width = $size->getWidth(); - $height = $size->getHeight(); - - $dest = imagecreatetruecolor($width, $height); - - imagealphablending($dest, false); - imagesavealpha($dest, true); - - if (function_exists('imageantialias')) { - imageantialias($dest, true); - } - - if (false === imagecopymerge($dest, $this->resource, 0, 0, - $start->getX(), $start->getY(), $width, $height, 100)) { - throw new RuntimeException('Image crop operation failed'); - } - - imagedestroy($this->resource); - - $this->resource = $dest; - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::paste() - */ - final public function paste(ImageInterface $image, PointInterface $start) - { - if (!$image instanceof self) { - throw new InvalidArgumentException(sprintf( - 'Gd\Image can only paste() Gd\Image instances, %s given', - get_class($image) - )); - } - - $size = $image->getSize(); - if (!$this->getSize()->contains($size, $start)) { - throw new OutOfBoundsException( - 'Cannot paste image of the given size at the specified '. - 'position, as it moves outside of the current image\'s box' - ); - } - - imagealphablending($this->resource, true); - imagealphablending($image->resource, true); - - if (false === imagecopy($this->resource, $image->resource, $start->getX(), $start->getY(), - 0, 0, $size->getWidth(), $size->getHeight())) { - throw new RuntimeException('Image paste operation failed'); - } - - imagealphablending($this->resource, false); - imagealphablending($image->resource, false); - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::resize() - */ - final public function resize(BoxInterface $size) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - $dest = imagecreatetruecolor($width, $height); - - imagealphablending($dest, false); - imagesavealpha($dest, true); - - if (function_exists('imageantialias')) { - imageantialias($dest, true); - } - - if (false === imagecopyresampled($dest, $this->resource, 0, 0, 0, 0, - $width, $height, imagesx($this->resource), imagesy($this->resource) - )) { - throw new RuntimeException('Image resize operation failed'); - } - - imagedestroy($this->resource); - - $this->resource = $dest; - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::rotate() - */ - final public function rotate($angle, Color $background = null) - { - $color = $background ? $background : new Color('fff'); - - $resource = imagerotate($this->resource, $angle, - $this->getColor($background)); - - if (false === $resource) { - throw new RuntimeException('Image rotate operation failed'); - } - - imagedestroy($this->resource); - - $this->resource = $resource; - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::save() - */ - final public function save($path, array $options = array()) - { - $this->saveOrOutput(pathinfo($path, \PATHINFO_EXTENSION), $options, $path); - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::show() - */ - public function show($format, array $options = array()) - { - $this->saveOrOutput($format, $options); - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::get() - */ - public function get($format, array $options = array()) - { - ob_start(); - $this->saveOrOutput($format, $options); - return ob_get_clean(); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::__toString() - */ - public function __toString() - { - return $this->get('png'); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::flipHorizontally() - */ - final public function flipHorizontally() - { - $width = imagesx($this->resource); - $height = imagesy($this->resource); - $dest = imagecreatetruecolor($width, $height); - - imagealphablending($dest, false); - imagesavealpha($dest, true); - - if (function_exists('imageantialias')) { - imageantialias($dest, true); - } - - for ($i = 0; $i < $width; $i++) { - if (false === imagecopymerge($dest, $this->resource, $i, 0, - ($width - 1) - $i, 0, 1, $height, 100)) { - throw new RuntimeException('Horizontal flip operation failed'); - } - } - - imagedestroy($this->resource); - - $this->resource = $dest; - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::flipVertically() - */ - final public function flipVertically() - { - $width = imagesx($this->resource); - $height = imagesy($this->resource); - $dest = imagecreatetruecolor($width, $height); - - imagealphablending($dest, false); - imagesavealpha($dest, true); - - if (function_exists('imageantialias')) { - imageantialias($dest, true); - } - - for ($i = 0; $i < $height; $i++) { - if (false === imagecopymerge($dest, $this->resource, 0, $i, - 0, ($height - 1) - $i, $width, 1, 100)) { - throw new RuntimeException('Vertical flip operation failed'); - } - } - - imagedestroy($this->resource); - - $this->resource = $dest; - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::thumbnail() - */ - public function thumbnail(BoxInterface $size, $mode = ImageInterface::THUMBNAIL_INSET) - { - if ($mode !== ImageInterface::THUMBNAIL_INSET && - $mode !== ImageInterface::THUMBNAIL_OUTBOUND) { - throw new InvalidArgumentException('Invalid mode specified'); - } - - $width = $size->getWidth(); - $height = $size->getHeight(); - - $ratios = array( - $width / imagesx($this->resource), - $height / imagesy($this->resource) - ); - - $thumbnail = $this->copy(); - - if ($mode === ImageInterface::THUMBNAIL_INSET) { - $ratio = min($ratios); - } else if ($mode === ImageInterface::THUMBNAIL_OUTBOUND) { - $ratio = max($ratios); - } - - $thumbnail->resize($this->getSize()->scale($ratio)); - $thumbnailSize = $thumbnail->getSize(); - - if ($mode === ImageInterface::THUMBNAIL_OUTBOUND) { - $thumbnail->crop(new Point( - max(0, round(($thumbnailSize->getWidth() - $width) / 2)), - max(0, round(($thumbnailSize->getHeight() - $height) / 2)) - ), $size); - } - - return $thumbnail; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::draw() - */ - public function draw() - { - return new Drawer($this->resource); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::getSize() - */ - public function getSize() - { - return new Box(imagesx($this->resource), imagesy($this->resource)); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::applyMask() - */ - public function applyMask(ImageInterface $mask) - { - if (!$mask instanceof self) { - throw new InvalidArgumentException('Cannot mask non-gd images'); - } - - $size = $this->getSize(); - $maskSize = $mask->getSize(); - - if ($size != $maskSize) { - throw new InvalidArgumentException(sprintf( - 'The given mask doesn\'t match current image\'s sise, Current '. - 'mask\'s dimensions are %s, while image\'s dimensions are %s', - $maskSize, $size - )); - } - - for ($x = 0; $x < $size->getWidth(); $x++) { - for ($y = 0; $y < $size->getHeight(); $y++) { - $color = imagecolorat($this->resource, $x, $y); - $info = imagecolorsforindex($this->resource, $color); - $maskColor = $color = imagecolorat($mask->resource, $x, $y); - $maskInfo = imagecolorsforindex($mask->resource, $maskColor); - if (false === imagesetpixel( - $this->resource, - $x, $y, - imagecolorallocatealpha( - $this->resource, - $info['red'], - $info['green'], - $info['blue'], - round((127 - $info['alpha']) * $maskInfo['red'] / 255) - ) - )) { - throw new RuntimeException('Apply mask operation failed'); - } - } - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::fill() - */ - public function fill(FillInterface $fill) - { - $size = $this->getSize(); - - for ($x = 0; $x < $size->getWidth(); $x++) { - for ($y = 0; $y < $size->getHeight(); $y++) { - if (false === imagesetpixel( - $this->resource, - $x, $y, - $this->getColor($fill->getColor(new Point($x, $y)))) - ) { - throw new RuntimeException('Fill operation failed'); - } - } - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::mask() - */ - public function mask() - { - $mask = $this->copy(); - - if (false === imagefilter($mask->resource, IMG_FILTER_GRAYSCALE)) { - throw new RuntimeException('Mask operation failed'); - } - - return $mask; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::histogram() - */ - public function histogram() - { - $size = $this->getSize(); - $colors = array(); - - for ($x = 0; $x < $size->getWidth(); $x++) { - for ($y = 0; $y < $size->getHeight(); $y++) { - $index = imagecolorat($this->resource, $x, $y); - $info = imagecolorsforindex($this->resource, $index); - $color = new Color(array( - $info['red'], - $info['green'], - $info['blue'], - ), - (int) round($info['alpha'] / 127 * 100) - ); - - $colors[] = $color; - } - } - - return array_unique($colors); - } - - /** - * Internal - * - * Performs save or show operation using one of GD's image... functions - * - * @param string $format - * @param array $options - * @param string $filename - * - * @throws InvalidArgumentException - * @throws RuntimeException - */ - private function saveOrOutput($format, array $options, $filename = null) - { - - if (!$this->supported($format)) { - throw new InvalidArgumentException(sprintf( - 'Saving image in "%s" format is not supported, please use one '. - 'of the following extension: "%s"', $format, - implode('", "', $this->supported()) - )); - } - - $save = 'image'.$format; - - $args = array($this->resource, $filename); - - if (($format === 'jpeg' || $format === 'png') && - isset($options['quality'])) { - // png compression quality is 0-9, so here we get the value from percent - if ($format === 'png') { - $options['quality'] = round($options['quality'] * 9 / 100); - } - $args[] = $options['quality']; - } - - if ($format === 'png') { - imagealphablending($this->resource, false); - imagesavealpha($this->resource, true); - - if (isset($options['filters'])) { - $args[] = $options['filters']; - } - } - - if (($format === 'wbmp' || $format === 'xbm') && - isset($options['foreground'])) { - $args[] = $options['foreground']; - } - - if (false === call_user_func_array($save, $args)) { - throw new RuntimeException('Save operation failed'); - } - } - - /** - * Internal - * - * Generates a GD color from Color instance - * - * @param Imagine\Image\Color $color - * - * @return resource - * - * @throws Imagine\Exception\RuntimeException - */ - private function getColor(Color $color) - { - $c = imagecolorallocatealpha( - $this->resource, $color->getRed(), $color->getGreen(), - $color->getBlue(), round(127 * $color->getAlpha() / 100) - ); - if (false === $color) { - throw new RuntimeException(sprintf( - 'Unable to allocate color "RGB(%s, %s, %s)" with transparency '. - 'of %d percent', $color->getRed(), $color->getGreen(), - $color->getBlue(), $color->getAlpha() - )); - } - - return $c; - } - - /** - * Internal - * - * Checks whether a given format is supported by GD library - * - * @param string $format - * - * @return Boolean - */ - private function supported(&$format = null) - { - $formats = array('gif', 'jpeg', 'png', 'wbmp', 'xbm'); - - if (null === $format) { - return $formats; - } - - $format = strtolower($format); - - if ('jpg' === $format) { - $format = 'jpeg'; - } - - return in_array($format, $formats); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Gd/Imagine.php b/app/Plugin/Imagine/lib/Imagine/Gd/Imagine.php deleted file mode 100755 index ce76e26..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Gd/Imagine.php +++ /dev/null @@ -1,193 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gd; - -use Imagine\Image\Color; -use Imagine\Image\BoxInterface; -use Imagine\ImageInterface; -use Imagine\ImagineInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; - -final class Imagine implements ImagineInterface -{ - /** - * @var array - */ - private $types = array( - IMAGETYPE_BMP => 'bmp', - IMAGETYPE_COUNT => 'count', - IMAGETYPE_GIF => 'gif', - IMAGETYPE_ICO => 'ico', - IMAGETYPE_IFF => 'iff', - IMAGETYPE_JB2 => 'jb2', - IMAGETYPE_JP2 => 'jp2', - IMAGETYPE_JPC => 'jpc', - IMAGETYPE_JPEG => 'jpeg', - IMAGETYPE_JPEG2000 => 'jpeg', - IMAGETYPE_JPX => 'jpx', - IMAGETYPE_PNG => 'png', - IMAGETYPE_PSD => 'psd', - IMAGETYPE_SWF => 'swf', - IMAGETYPE_TIFF_II => 'tiff', - IMAGETYPE_TIFF_MM => 'tiff', - IMAGETYPE_UNKNOWN => 'unknown', - IMAGETYPE_WBMP => 'wbmp', - IMAGETYPE_XBM => 'xbm' - ); - - /** - * @throws Imagine\Exception\RuntimeException - */ - public function __construct() - { - if (!function_exists('gd_info')) { - throw new RuntimeException('Gd not installed'); - } - - if(defined('IMAGETYPE_SWC')) { - $this->types += array(IMAGETYPE_SWC => 'swc'); - } - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::create() - */ - public function create(BoxInterface $size, Color $color = null) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - $resource = imagecreatetruecolor($width, $height); - - if (false === $resource) { - throw new RuntimeException('Create operation failed'); - } - - $color = $color ? $color : new Color('fff'); - - if (false === imagealphablending($resource, false) || - false === imagesavealpha($resource, true)) { - throw new RuntimeException( - 'Could not set alphablending, savealpha and antialias values' - ); - } - - if (function_exists('imageantialias')) { - imageantialias($resource, true); - } - - $color = imagecolorallocatealpha( - $resource, $color->getRed(), $color->getGreen(), $color->getBlue(), - round(127 * $color->getAlpha() / 100) - ); - - if (false === $color) { - throw new RuntimeException('Unable to allocate color'); - } - - if (false === imagefilledrectangle( - $resource, 0, 0, $width, $height, $color - )) { - throw new RuntimeException('Could not set background color fill'); - } - - return new Image($resource, $this); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::open() - */ - public function open($path) - { - if (!is_file($path)) { - throw new InvalidArgumentException(sprintf( - 'File %s doesn\'t exist', $path - )); - } - - $info = getimagesize($path); - - if (false === $info) { - throw new RuntimeException('Could not collect image metadata'); - } - - list($width, $height, $type) = $info; - - $format = $this->types[$type]; - - if (!function_exists('imagecreatefrom'.$format)) { - throw new InvalidArgumentException( - 'Invalid image format specified, only "gif", "jpeg", "png", '. - '"wbmp", "xbm" images are supported' - ); - } - - $resource = call_user_func('imagecreatefrom'.$format, $path); - - if (!is_resource($resource)) { - throw new RuntimeException(sprintf( - 'File "%s" could not be opened', $path - )); - } - - if (false === imagealphablending($resource, false) || - false === imagesavealpha($resource, true)) { - throw new RuntimeException( - 'Could not set alphablending, savealpha and antialias values' - ); - } - - if (function_exists('imageantialias')) { - imageantialias($resource, true); - } - - return new Image($resource); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::load() - */ - public function load($string) - { - $resource = imagecreatefromstring($string); - - if (!is_resource($resource)) { - throw new InvalidArgumentException('An image could not be created from the given input'); - } - - if (false === imagealphablending($resource, false) || - false === imagesavealpha($resource, true)) { - throw new RuntimeException( - 'Could not set alphablending, savealpha and antialias values' - ); - } - - if (function_exists('imageantialias')) { - imageantialias($resource, true); - } - - return new Image($resource); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::font() - */ - public function font($file, $size, Color $color) - { - return new Font($file, $size, $color); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Gmagick/Drawer.php b/app/Plugin/Imagine/lib/Imagine/Gmagick/Drawer.php deleted file mode 100755 index cd7dcbe..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Gmagick/Drawer.php +++ /dev/null @@ -1,375 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gmagick; - -use Imagine\Draw\DrawerInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\AbstractFont; -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; - -final class Drawer implements DrawerInterface -{ - /** - * @var Gmagick - */ - private $gmagick; - - /** - * @param Gmagick $gmagick - */ - public function __construct(\Gmagick $gmagick) - { - $this->gmagick = $gmagick; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::arc() - */ - public function arc(PointInterface $center, BoxInterface $size, $start, $end, Color $color) - { - $x = $center->getX(); - $y = $center->getY(); - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $arc = new \GmagickDraw(); - - $arc->setstrokecolor($pixel); - $arc->setstrokewidth(1); - $arc->setfillcolor('transparent'); - $arc->arc( - $x - $width / 2, - $y - $height / 2, - $x + $width / 2, - $y + $height / 2, - $start, - $end - ); - - $this->gmagick->drawImage($arc); - - $pixel = null; - - $arc = null; - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Draw arc operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::chord() - */ - public function chord(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false) - { - $x = $center->getX(); - $y = $center->getY(); - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $chord = new \GmagickDraw(); - - $chord->setstrokecolor($pixel); - $chord->setstrokewidth(1); - - if ($fill) { - $chord->setfillcolor($pixel); - } else { - $x1 = round($x + $width / 2 * cos(deg2rad($start))); - $y1 = round($y + $height / 2 * sin(deg2rad($start))); - $x2 = round($x + $width / 2 * cos(deg2rad($end))); - $y2 = round($y + $height / 2 * sin(deg2rad($end))); - - $this->line(new Point($x1, $y1), new Point($x2, $y2), $color); - - $chord->setfillcolor('transparent'); - } - - $chord->arc($x - $width / 2, $y - $height / 2, $x + $width / 2, $y + $height / 2, $start, $end); - - $this->gmagick->drawImage($chord); - - $pixel = null; - - $chord = null; - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Draw chord operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::ellipse() - */ - public function ellipse(PointInterface $center, BoxInterface $size, Color $color, $fill = false) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $ellipse = new \GmagickDraw(); - - $ellipse->setstrokecolor($pixel); - $ellipse->setstrokewidth(1); - - if ($fill) { - $ellipse->setfillcolor($pixel); - } else { - $ellipse->setfillcolor('transparent'); - } - - $ellipse->ellipse( - $center->getX(), - $center->getY(), - $width / 2, - $height / 2, - 0, 360 - ); - - $this->gmagick->drawImage($ellipse); - - $pixel = null; - - $ellipse = null; - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Draw ellipse operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::line() - */ - public function line(PointInterface $start, PointInterface $end, Color $color) - { - try { - $pixel = $this->getColor($color); - $line = new \GmagickDraw(); - - $line->setstrokecolor($pixel); - $line->setstrokewidth(1); - $line->line( - $start->getX(), - $start->getY(), - $end->getX(), - $end->getY() - ); - - $this->gmagick->drawImage($line); - - $pixel = null; - - $line = null; - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Draw line operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::pieSlice() - */ - public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - $x1 = round($center->getX() + $width / 2 * cos(deg2rad($start))); - $y1 = round($center->getY() + $height / 2 * sin(deg2rad($start))); - $x2 = round($center->getX() + $width / 2 * cos(deg2rad($end))); - $y2 = round($center->getY() + $height / 2 * sin(deg2rad($end))); - - if ($fill) { - $this->chord($center, $size, $start, $end, $color, true); - $this->polygon( - array( - $center, - new Point($x1, $y1), - new Point($x2, $y2), - ), - $color, - true - ); - } else { - $this->arc($center, $size, $start, $end, $color); - $this->line($center, new Point($x1, $y1), $color); - $this->line($center, new Point($x2, $y2), $color); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::dot() - */ - public function dot(PointInterface $position, Color $color) - { - $x = $position->getX(); - $y = $position->getY(); - - try { - $pixel = $this->getColor($color); - $point = new \GmagickDraw(); - - $point->setfillcolor($pixel); - $point->point($x, $y); - - $this->gmagick->drawimage($point); - - $pixel = null; - - $point = null; - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Draw point operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::polygon() - */ - public function polygon(array $coordinates, Color $color, $fill = false) - { - if (count($coordinates) < 3) { - throw new InvalidArgumentException(sprintf( - 'Polygon must consist of at least 3 coordinates, %d given', - count($coordinates) - )); - } - - $points = array_map(function(PointInterface $p) - { - return array('x' => $p->getX(), 'y' => $p->getY()); - }, $coordinates); - - try { - $pixel = $this->getColor($color); - $polygon = new \GmagickDraw(); - - $polygon->setstrokecolor($pixel); - $polygon->setstrokewidth(1); - - if ($fill) { - $polygon->setfillcolor($pixel); - } else { - $polygon->setfillcolor('transparent'); - } - - $polygon->polygon($points); - - $this->gmagick->drawImage($polygon); - - $pixel = null; - - $polygon = null; - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Draw polygon operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::text() - */ - public function text($string, AbstractFont $font, PointInterface $position, $angle = 0) - { - try { - $pixel = $this->getColor($font->getColor()); - $text = new \GmagickDraw(); - - $text->setfont($font->getFile()); - $text->setfontsize($font->getSize()); - $text->setfillcolor($pixel); - - $info = $this->gmagick->queryfontmetrics($text, $string); - $rad = deg2rad($angle); - $cos = cos($rad); - $sin = sin($rad); - - $x1 = round(0 * $cos - 0 * $sin); - $x2 = round($info['textWidth'] * $cos - $info['textHeight'] * $sin); - $y1 = round(0 * $sin + 0 * $cos); - $y2 = round($info['textWidth'] * $sin + $info['textHeight'] * $cos); - - $xdiff = 0 - min($x1, $x2); - $ydiff = 0 - min($y1, $y2); - - $this->gmagick->annotateimage( - $text, $position->getX() + $x1 + $xdiff, - $position->getY() + $y2 + $ydiff, $angle, $string - ); - - $pixel = null; - - $text = null; - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Draw text operation failed', $e->getCode(), $e - ); - } - } - - /** - * Gets specifically formatted color string from Color instance - * - * @param Imagine\Image\Color $color - * - * @return string - */ - private function getColor(Color $color) - { - $pixel = new \GmagickPixel((string) $color); - - $pixel->setColorValue( - \Gmagick::COLOR_OPACITY, - number_format(abs(round($color->getAlpha() / 100, 1)), 1) - ); - - return $pixel; - } - -} diff --git a/app/Plugin/Imagine/lib/Imagine/Gmagick/Font.php b/app/Plugin/Imagine/lib/Imagine/Gmagick/Font.php deleted file mode 100755 index 639ac01..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Gmagick/Font.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gmagick; - -use Imagine\Image\AbstractFont; -use Imagine\Image\Box; -use Imagine\Image\Color; - -final class Font extends AbstractFont -{ - /** - * @var Gmagick - */ - private $gmagick; - - /** - * @param Gmagick $gmagick - * @param string $file - * @param integer $size - * @param Imagine\Image\Color $color - */ - public function __construct(\Gmagick $gmagick, $file, $size, Color $color) - { - $this->gmagick = $gmagick; - - parent::__construct($file, $size, $color); - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\AbstractFont::box() - */ - public function box($string, $angle = 0) - { - $text = new \GmagickDraw(); - - $text->setfont($this->file); - $text->setfontsize($this->size); - $text->setfontstyle(\Gmagick::STYLE_OBLIQUE); - - $info = $this->gmagick->queryfontmetrics($text, $string); - - $box = new Box($info['textWidth'], $info['textHeight']); - - return $box; - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Gmagick/Image.php b/app/Plugin/Imagine/lib/Imagine/Gmagick/Image.php deleted file mode 100755 index bc34511..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Gmagick/Image.php +++ /dev/null @@ -1,469 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gmagick; - -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\Fill\FillInterface; -use Imagine\Gmagick\Imagine; -use Imagine\ImageInterface; -use Imagine\Image\Box; -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; - -class Image implements ImageInterface -{ - /** - * @var Gmagick - */ - private $gmagick; - - /** - * Constructs Image with Gmagick and Imagine instances - * - * @param Gmagick $gmagick - */ - public function __construct(\Gmagick $gmagick) - { - $this->gmagick = $gmagick; - } - - /** - * Destroys allocated gmagick resources - */ - public function __destruct() - { - if (null !== $this->gmagick && $this->gmagick instanceof \Gmagick) { - $this->gmagick->clear(); - $this->gmagick->destroy(); - } - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::copy() - */ - public function copy() - { - return new self(clone $this->gmagick); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::crop() - */ - public function crop(PointInterface $start, BoxInterface $size) - { - if (!$start->in($size)) { - throw new OutOfBoundsException( - 'Crop coordinates must start at minimum 0, 0 position from '. - 'top left corner, crop height and width must be positive '. - 'integers and must not exceed the current image borders' - ); - } - - try { - $this->gmagick->cropimage( - $size->getWidth(), - $size->getHeight(), - $start->getX(), - $start->getY() - ); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Crop operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::flipHorizontally() - */ - public function flipHorizontally() - { - try { - $this->gmagick->flopimage(); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Horizontal flip operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::flipVertically() - */ - public function flipVertically() - { - try { - $this->gmagick->flipimage(); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Vertical flip operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::paste() - */ - public function paste(ImageInterface $image, PointInterface $start) - { - if (!$image instanceof self) { - throw new InvalidArgumentException(sprintf( - 'Gmagick\Image can only paste() Gmagick\Image instances, '. - '%s given', get_class($image) - )); - } - - if (!$this->getSize()->contains($image->getSize(), $start)) { - throw new OutOfBoundsException( - 'Cannot paste image of the given size at the specified '. - 'position, as it moves outside of the current image\'s box' - ); - } - - try { - $this->gmagick->compositeimage( - $image->gmagick, - \Gmagick::COMPOSITE_DEFAULT, - $start->getX(), - $start->getY() - ); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Paste operation failed', $e->getCode(), $e - ); - } - - /** - * @see http://pecl.php.net/bugs/bug.php?id=22435 - */ - if (method_exists($this->gmagick, 'flattenImages')) { - try { - $this->gmagick->flattenImages(); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Paste operation failed', $e->getCode(), $e - ); - } - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::resize() - */ - public function resize(BoxInterface $size) - { - try { - $this->gmagick->resizeimage( - $size->getWidth(), - $size->getHeight(), - \Gmagick::FILTER_UNDEFINED, - 1 - ); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Resize operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::rotate() - */ - public function rotate($angle, Color $background = null) - { - try { - $pixel = $this->getColor($background); - - $this->gmagick->rotateimage($pixel, $angle); - - $pixel = null; - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Rotate operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::save() - */ - public function save($path, array $options = array()) - { - try { - $this->gmagick->writeimage($path); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Save operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::show() - */ - public function show($format, array $options = array()) - { - echo $this->get($format, $options); - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::get() - */ - public function get($format, array $options = array()) - { - try { - $this->gmagick->setimageformat($format); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Show operation failed', $e->getCode(), $e - ); - } - - return (string) $this->gmagick; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::__toString() - */ - public function __toString() - { - return $this->get('png'); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::thumbnail() - */ - public function thumbnail(BoxInterface $size, $mode = ImageInterface::THUMBNAIL_INSET) - { - if ($mode !== ImageInterface::THUMBNAIL_INSET && - $mode !== ImageInterface::THUMBNAIL_OUTBOUND) { - throw new InvalidArgumentException('Invalid mode specified'); - } - - $thumbnail = $this->copy(); - - try { - if ($mode === ImageInterface::THUMBNAIL_INSET) { - $thumbnail->gmagick->thumbnailimage( - $size->getWidth(), - $size->getHeight(), - true - ); - } elseif ($mode === ImageInterface::THUMBNAIL_OUTBOUND) { - $thumbnail->gmagick->cropthumbnailimage( - $size->getWidth(), - $size->getHeight() - ); - } - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Thumbnail operation failed', $e->getCode(), $e - ); - } - - return $thumbnail; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::draw() - */ - public function draw() - { - return new Drawer($this->gmagick); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::getSize() - */ - public function getSize() - { - try { - $width = $this->gmagick->getimagewidth(); - $height = $this->gmagick->getimageheight(); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Get size operation failed', $e->getCode(), $e - ); - } - return new Box($width, $height); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::applyMask() - */ - public function applyMask(ImageInterface $mask) - { - if (!$mask instanceof self) { - throw new InvalidArgumentException( - 'Can only apply instances of Imagine\Gmagick\Image as masks' - ); - } - - $size = $this->getSize(); - $maskSize = $mask->getSize(); - - if ($size != $maskSize) { - throw new InvalidArgumentException(sprintf( - 'The given mask doesn\'t match current image\'s sise, current '. - 'mask\'s dimensions are %s, while image\'s dimensions are %s', - $maskSize, $size - )); - } - - try { - $mask = $mask->copy(); - - $this->gmagick->compositeimage( - $mask->gmagick, - \Gmagick::COMPOSITE_DEFAULT, - 0, 0 - ); - } catch (\Exception $e) { - throw new RuntimeException( - 'Apply mask operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::mask() - */ - public function mask() - { - $mask = $this->copy(); - - try { - $mask->gmagick->modulateimage(100, 0, 100); - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Mask operation failed', $e->getCode(), $e - ); - } - - return $mask; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::fill() - */ - public function fill(FillInterface $fill) - { - try { - $draw = new \GmagickDraw(); - $size = $this->getSize(); - - for ($x = 0; $x <= $size->getWidth(); $x++) { - for ($y = 0; $y <= $size->getHeight(); $y++) { - $pixel = $this->getColor($fill->getColor(new Point($x, $y))); - - $draw->setfillcolor($pixel); - $draw->point($x, $y); - - $pixel = null; - } - } - - $this->gmagick->drawimage($draw); - - $draw = null; - } catch (\GmagickException $e) { - throw new RuntimeException( - 'Fill operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::histogram() - */ - public function histogram() - { - $pixels = $this->gmagick->getimagehistogram(); - - return array_map( - function(\GmagickPixel $pixel) - { - $info = $pixel->getColor(); - return new Color( - array( - $info['r'], - $info['g'], - $info['b'], - ), - (int) round($info['a'] * 100) - ); - }, - $pixels - ); - } - - /** - * Gets specifically formatted color string from Color instance - * - * @param Imagine\Image\Color $color - * - * @return string - */ - private function getColor(Color $color) - { - $pixel = new \GmagickPixel((string) $color); - - $pixel->setColorValue( - \Gmagick::COLOR_OPACITY, - number_format(abs(round($color->getAlpha() / 100, 1)), 1) - ); - - return $pixel; - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Gmagick/Imagine.php b/app/Plugin/Imagine/lib/Imagine/Gmagick/Imagine.php deleted file mode 100755 index 77cfc0d..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Gmagick/Imagine.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Gmagick; - -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\ImagineInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; - -class Imagine implements ImagineInterface -{ - /** - * @throws Imagine\Exception\RuntimeException - */ - public function __construct() - { - if (!class_exists('Gmagick')) { - throw new RuntimeException('Gmagick not installed'); - } - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::open() - */ - public function open($path) - { - if (!is_file($path)) { - throw new InvalidArgumentException(sprintf( - 'File %s doesn\'t exist', $path - )); - } - - return new Image(new \Gmagick($path)); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::create() - */ - public function create(BoxInterface $size, Color $color = null) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - $color = null !== $color ? $color : new Color('fff'); - $gmagick = new \Gmagick(); - $pixel = new \GmagickPixel((string) $color); - - if ($color->getAlpha() > 0) { - // TODO: implement support for transparent background - throw new RuntimeException('alpha transparency not implemented'); - } - - $gmagick->newimage($width, $height, $pixel->getcolor(false)); - $gmagick->setimagecolorspace(\Gmagick::COLORSPACE_TRANSPARENT); - // this is needed to propagate transparency - $gmagick->setimagebackgroundcolor($pixel); - - return new Image($gmagick); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::load() - */ - public function load($string) - { - $gmagick = new \Gmagick(); - $gmagick->readimageblob($string); - return new Image($gmagick); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::font() - */ - public function font($file, $size, Color $color) - { - $gmagick = new \Gmagick(); - - $gmagick->newimage(1, 1, 'transparent'); - - return new Font($gmagick, $file, $size, $color); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Image/AbstractFont.php b/app/Plugin/Imagine/lib/Imagine/Image/AbstractFont.php deleted file mode 100755 index 2f27bda..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Image/AbstractFont.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -abstract class AbstractFont implements FontInterface -{ - /** - * @var string - */ - protected $file; - - /** - * @var integer - */ - protected $size; - - /** - * @var Imagine\Image\Color - */ - protected $color; - - /** - * Constructs a font with specified $file, $size and $color - * - * The font size is to be specified in points (e.g. 10pt means 10) - * - * @param string $file - * @param integer $size - * @param Imagine\Image\Color $color - */ - public function __construct($file, $size, Color $color) - { - $this->file = $file; - $this->size = $size; - $this->color = $color; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\FontInterface::getFile() - */ - public function getFile() - { - return $this->file; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\FontInterface::getSize() - */ - public function getSize() - { - return $this->size; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\FontInterface::getColor() - */ - public function getColor() - { - return $this->color; - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Image/Box.php b/app/Plugin/Imagine/lib/Imagine/Image/Box.php deleted file mode 100755 index 6e04242..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Image/Box.php +++ /dev/null @@ -1,115 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Exception\InvalidArgumentException; - -final class Box implements BoxInterface -{ - /** - * @var integer - */ - private $width; - - /** - * @var integer - */ - private $height; - - /** - * Constructs the Size with given width and height - * - * @param integer $width - * @param integer $height - * - * @throws InvalidArgumentException - */ - public function __construct($width, $height) - { - if ($height < 1 || $width < 1) { - throw new InvalidArgumentException(sprintf( - 'Length of either side cannot be 0 or negative, current size '. - 'is %sx%s', $width, $height - )); - } - - $this->width = (int) $width; - $this->height = (int) $height; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\BoxInterface::getWidth() - */ - public function getWidth() - { - return $this->width; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\BoxInterface::getHeight() - */ - public function getHeight() - { - return $this->height; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\BoxInterface::scale() - */ - public function scale($ratio) - { - return new Box(round($ratio * $this->width), round($ratio * $this->height)); - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\BoxInterface::increase() - */ - public function increase($size) - { - return new Box((int) $size + $this->width, (int) $size + $this->height); - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\BoxInterface::contains() - */ - public function contains(BoxInterface $box, PointInterface $start = null) - { - $start = $start ? $start : new Point(0, 0); - - return $start->in($this) && - $this->width >= $box->getWidth() + $start->getX() && - $this->height >= $box->getHeight() + $start->getY(); - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\BoxInterface::square() - */ - public function square() - { - return $this->width * $this->height; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\BoxInterface::__toString() - */ - public function __toString() - { - return sprintf('%dx%d px', $this->width, $this->height); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Image/BoxInterface.php b/app/Plugin/Imagine/lib/Imagine/Image/BoxInterface.php deleted file mode 100755 index 96d42f6..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Image/BoxInterface.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -interface BoxInterface -{ - /** - * Gets current image height - * - * @return integer - */ - function getHeight(); - - /** - * Gets current image width - * - * @return integer - */ - function getWidth(); - - /** - * Creates new BoxInterface instance with ratios applied to both sides - * - * @param integer $ratio - * - * @return Imagine\Image\BoxInterface - */ - function scale($ratio); - - /** - * Creats new BoxInterface, adding given size to both sides - * - * @param integer $size - */ - function increase($size); - - /** - * Checks whether curret box can fit given box at a given start position, - * start position defaults to top left corner xy(0,0) - * - * @param Imagine\Image\BoxInterface $box - * @param Imagine\Image\PointInterface $start - * - * @return Boolean - */ - function contains(BoxInterface $box, PointInterface $start = null); - - /** - * Gets current box square, useful for getting total number of pixels in a - * given box - * - * @return integer - */ - function square(); - - /** - * Returns a string representation of the current box - * - * @return string - */ - function __toString(); -} diff --git a/app/Plugin/Imagine/lib/Imagine/Image/Color.php b/app/Plugin/Imagine/lib/Imagine/Image/Color.php deleted file mode 100755 index 358a11c..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Image/Color.php +++ /dev/null @@ -1,222 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Exception\InvalidArgumentException; - -final class Color -{ - /** - * @var integer - */ - private $r; - - /** - * @var integer - */ - private $g; - - /** - * @var integer - */ - private $b; - - /** - * @var integer - */ - private $alpha; - - /** - * Constructs image color, e.g.: - * - new Color('fff') - will produce non-transparent white color - * - new Color('ffffff', 50) - will product 50% transparent white - * - new Color(array(255, 255, 255)) - another way of getting white - * - * @param array|string $color - * @param integer $alpha - */ - public function __construct($color, $alpha = 0) - { - $this->setColor($color); - $this->setAlpha($alpha); - } - - /** - * Returns RED value of the color - * - * @return integer - */ - public function getRed() - { - return $this->r; - } - - /** - * Returns GREEN value of the color - * - * @return integer - */ - public function getGreen() - { - return $this->g; - } - - /** - * Returns BLUE value of the color - * - * @return integer - */ - public function getBlue() - { - return $this->b; - } - - /** - * Returns percentage of transparency of the color - * - * @return integer - */ - public function getAlpha() - { - return $this->alpha; - } - - /** - * Returns a copy of current color, incrementing the alpha channel by the - * given amount - * - * @param integer $alpha - * - * @return Imagine\Image\Color - */ - public function dissolve($alpha) - { - return new Color((string) $this, $this->alpha + $alpha); - } - - /** - * Returns a copy of the current color, lightened by the specified number - * of shades - * - * @param integer $shade - * - * @retun Imagine\Image\Color - */ - public function lighten($shade) - { - return new Color( - array( - min(255, $this->r + $shade), - min(255, $this->g + $shade), - min(255, $this->b + $shade), - ), - $this->alpha - ); - } - - /** - * Returns a copy of the current color, darkened by the specified number of - * shades - * - * @param integer $shade - * - * @retun Imagine\Image\Color - */ - public function darken($shade) - { - return new Color( - array( - max(0, $this->r - $shade), - max(0, $this->g - $shade), - max(0, $this->b - $shade), - ), - $this->alpha - ); - } - - /** - * Internal - * - * Performs checks for validity of given alpha value and sets it - * - * @param integer $alpha - * - * @throws InvalidArgumentException - */ - private function setAlpha($alpha) - { - if (!is_int($alpha) || $alpha < 0 || $alpha > 100) { - throw new InvalidArgumentException( - 'Alpha must be an integer between 0 and 100' - ); - } - - $this->alpha = $alpha; - } - - /** - * Internal - * - * Performs checks for color validity (hex or array of array(R, G, B)) - * - * @param string|array $color - * - * @throws InvalidArgumentException - */ - private function setColor($color) - { - if (!is_string($color) && !is_array($color)) { - throw new InvalidArgumentException(sprintf( - 'Color must be specified as a hexadecimal string or array, '. - '%s given', gettype($color) - )); - } - if (is_array($color) && count($color) !== 3) { - throw new InvalidArgumentException( - 'Color argument if array, must look like array(R, G, B), '. - 'where R, G, B are the integer values between 0 and 255 for '. - 'red, green and blue color indexes accordingly' - ); - } - - if (is_string($color)) { - $color = ltrim($color, '#'); - - if (strlen($color) !== 3 && strlen($color) !== 6) { - throw new InvalidArgumentException(sprintf( - 'Color must be a hex value in regular (6 characters) or '. - 'short (3 charatcters) notation, "%s" given', $color - )); - } - - if (strlen($color) === 3) { - $color = $color[0].$color[0]. - $color[1].$color[1]. - $color[2].$color[2]; - } - - $color = array_map('hexdec', str_split($color, 2)); - } - - list($this->r, $this->g, $this->b) = array_values($color); - } - - /** - * Returns hex representation of the color - * - * @return string - */ - public function __toString() - { - return sprintf('#%02x%02x%02x', $this->r, $this->g, $this->b); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Image/FontInterface.php b/app/Plugin/Imagine/lib/Imagine/Image/FontInterface.php deleted file mode 100755 index ae2bdca..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Image/FontInterface.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -interface FontInterface -{ - /** - * Gets the fontfile for current font - * - * @return string - */ - function getFile(); - - /** - * Gets font's integer point size - * - * @return integer - */ - function getSize(); - - /** - * Gets font's color - * - * @return Imagine\Image\Color - */ - function getColor(); - - /** - * Gets BoxInterface of font size on the image based on string and angle - * - * @param string $string - * @param integer $angle - * - * @return Imagine\Image\BoxInterface - */ - function box($string, $angle = 0); -} diff --git a/app/Plugin/Imagine/lib/Imagine/Image/Histogram/Bucket.php b/app/Plugin/Imagine/lib/Imagine/Image/Histogram/Bucket.php deleted file mode 100755 index 045972c..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Image/Histogram/Bucket.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Histogram; - -final class Bucket implements \Countable -{ - /** - * @var Imagine\Image\Histogram\Range - */ - private $range; - - /** - * @var integer - */ - private $count; - - /** - * @param Imagine\Image\Histogram\Range $range - * @param integer $count - */ - public function __construct(Range $range, $count = 0) - { - $this->range = $range; - $this->count = $count; - } - - /** - * @param integer $value - */ - public function add($value) - { - if ($this->range->contains($value)) { - $this->count++; - } - } - - /** - * (non-PHPdoc) - * @see Countable::count() - */ - public function count() - { - return $this->count; - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Image/Histogram/Range.php b/app/Plugin/Imagine/lib/Imagine/Image/Histogram/Range.php deleted file mode 100755 index 5501868..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Image/Histogram/Range.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Histogram; - -use Imagine\Exception\OutOfBoundsException; - -final class Range -{ - /** - * @var integer - */ - private $start; - - /** - * @var integer - */ - private $end; - - /** - * @param integer $start - * @param integer $end - * - * @throws Imagine\Exception\OutOfBoundsException - */ - public function __construct($start, $end) - { - if ($end <= $start) { - throw new OutOfBoundsException(sprintf( - 'Range end cannot be bigger than start, %d %d given '. - 'accordingly', $this->start, $this->end - )); - } - - $this->start = $start; - $this->end = $end; - } - - /** - * @param integer $value - * - * @return Boolean - */ - public function contains($value) - { - return $value >= $this->start && $value < $this->end; - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Image/Point.php b/app/Plugin/Imagine/lib/Imagine/Image/Point.php deleted file mode 100755 index c6bbdc7..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Image/Point.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -use Imagine\Exception\InvalidArgumentException; - -final class Point implements PointInterface -{ - /** - * @var integer - */ - private $x; - - /** - * @var integer - */ - private $y; - - /** - * Constructs a point of coordinates - * - * @param integer $x - * @param integer $y - * - * @throws Imagine\Exception\InvalidArgumentException - */ - public function __construct($x, $y) - { - if ($x < 0 || $y < 0) { - throw new InvalidArgumentException( - 'A coordinate cannot be positioned ouside of a bounding box' - ); - } - - $this->x = $x; - $this->y = $y; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\PointInterface::getX() - */ - public function getX() - { - return $this->x; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\PointInterface::getY() - */ - public function getY() - { - return $this->y; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\PointInterface::in() - */ - public function in(BoxInterface $box) - { - return $this->x < $box->getWidth() && $this->y < $box->getHeight(); - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\PointInterface::__toString() - */ - public function __toString() - { - return sprintf('(%d, %d)', $this->x, $this->y); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Image/Point/Center.php b/app/Plugin/Imagine/lib/Imagine/Image/Point/Center.php deleted file mode 100755 index ed38bf1..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Image/Point/Center.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image\Point; - -use Imagine\Image\BoxInterface; -use Imagine\Image\PointInterface; - -final class Center implements PointInterface -{ - /** - * @var Imagine\Image\BoxInterface - */ - private $box; - - /** - * Constructs coordinate with size instantce, it needs to be relative to - * - * @param Imagine\Image\BoxInterface $size - */ - public function __construct(BoxInterface $box) - { - $this->box = $box; - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\PointInterface::getX() - */ - public function getX() - { - return ceil($this->box->getWidth() / 2); - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\PointInterface::getY() - */ - public function getY() - { - return ceil($this->box->getHeight() / 2); - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\PointInterface::in() - */ - public function in(BoxInterface $box) - { - return $this->getX() < $box->getWidth() && $this->getY() < $box->getHeight(); - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\PointInterface::__toString() - */ - public function __toString() - { - return sprintf('(%d, %d)', $this->getX(), $this->getY()); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Image/PointInterface.php b/app/Plugin/Imagine/lib/Imagine/Image/PointInterface.php deleted file mode 100755 index ad7a495..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Image/PointInterface.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Image; - -interface PointInterface -{ - /** - * Gets points x coordinate - * - * @return integer - */ - function getX(); - - /** - * Gets points y coordinate - * - * @return integer - */ - function getY(); - - /** - * Checks if current coordinate is inside a given bo - * - * @param Imagine\Image\BoxInterface $box - * - * @return Boolean - */ - function in(BoxInterface $box); - - /** - * Gets a string representation for the current point - * - * @return string - */ - function __toString(); -} diff --git a/app/Plugin/Imagine/lib/Imagine/ImageInterface.php b/app/Plugin/Imagine/lib/Imagine/ImageInterface.php deleted file mode 100755 index 6078f63..0000000 --- a/app/Plugin/Imagine/lib/Imagine/ImageInterface.php +++ /dev/null @@ -1,223 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine; - -use Imagine\Draw\DrawerInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\RuntimeException; -use Imagine\Fill\FillInterface; -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\Image\PointInterface; - -interface ImageInterface -{ - const THUMBNAIL_INSET = 'inset'; - const THUMBNAIL_OUTBOUND = 'outbound'; - - /** - * Copies current source image into a new ImageInterface instance - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function copy(); - - /** - * Crops a specified box out of the source image (modifies the source image) - * Returns cropped self - * - * @param Imagine\Image\PointInterface $start - * @param Imagine\Image\BoxInterface $size - * - * @throws Imagine\Exception\OutOfBoundsException - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function crop(PointInterface $start, BoxInterface $size); - - /** - * Resizes current image and returns self - * - * @param Imagine\Image\BoxInterface $size - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function resize(BoxInterface $size); - - /** - * Rotates an image at the given angle. - * Optional $background can be used to specify the fill color of the empty - * area of rotated image. - * - * @param integer $angle - * @param Imagine\Image\Color $background - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function rotate($angle, Color $background = null); - - /** - * Pastes an image into a parent image - * Throws exceptions if image exceeds parent image borders or if paste - * operation fails - * - * Returns source image - * - * @param Imagine\ImageInterface $image - * @param Imagine\Image\PointInterface $start - * - * @throws Imagine\Exception\InvalidArgumentException - * @throws Imagine\Exception\OutOfBoundsException - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function paste(ImageInterface $image, PointInterface $start); - - /** - * Saves the image at a specified path, the target file extension is used - * to determine file format, only jpg, jpeg, gif, png, wbmp and xbm are - * supported - * - * @param string $path - * @param array $options - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function save($path, array $options = array()); - - /** - * Outputs the image content - * - * @param string $format - * @param array $options - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function show($format, array $options = array()); - - /** - * Returns the image content as a binary string - * - * @param string $format - * @param array $options - * - * @throws Imagine\Exception\RuntimeException - * - * @return string binary - */ - function get($format, array $options = array()); - - /** - * Returns the image content as a PNG binary string - * - * @param string $format - * @param array $options - * - * @throws Imagine\Exception\RuntimeException - * - * @return string binary - */ - function __toString(); - - /** - * Flips current image using horizontal axis - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function flipHorizontally(); - - /** - * Flips current image using vertical axis - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function flipVertically(); - - /** - * Generates a thumbnail from a current image - * Returns it as a new image, doesn't modify the current image - * - * @param Imagine\Image\BoxInterface $size - * @param string $mode - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function thumbnail(BoxInterface $size, $mode = self::THUMBNAIL_INSET); - - /** - * Instantiates and returns a DrawerInterface instance for image drawing - * - * @return Imagine\Draw\DrawerInterface - */ - function draw(); - - /** - * Returns current image size - * - * @return Imagine\Image\BoxInterface - */ - function getSize(); - - /** - * Applies a given mask to current image's alpha channel - * - * @param Imagine\ImageInterface $mask - * - * @return Imagine\ImageInterface - */ - function applyMask(ImageInterface $mask); - - /** - * Transforms creates a grayscale mask from current image, returns a new - * image, while keeping the existing image unmodified - * - * @return Imagine\ImageInterface - */ - function mask(); - - /** - * Fills image with provided filling, by replacing each pixel's color in - * the current image with corresponding color from FillInterface, and - * returns modified image - * - * @param Imagine\Fill\FillInterface $fill - * - * @return Imagine\ImageInterface - */ - function fill(FillInterface $fill); - - /** - * Returns array of image colors as Imagine\Image\Color instances - * - * @return array - */ - function histogram(); -} diff --git a/app/Plugin/Imagine/lib/Imagine/Imagick/Drawer.php b/app/Plugin/Imagine/lib/Imagine/Imagick/Drawer.php deleted file mode 100755 index ab54d32..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Imagick/Drawer.php +++ /dev/null @@ -1,399 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Imagick; - -use Imagine\Draw\DrawerInterface; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\AbstractFont; -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; - -final class Drawer implements DrawerInterface -{ - /** - * @var Imagick - */ - private $imagick; - - /** - * @param Imagick $imagick - */ - public function __construct(\Imagick $imagick) - { - $this->imagick = $imagick; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::arc() - */ - public function arc(PointInterface $center, BoxInterface $size, $start, $end, Color $color) - { - $x = $center->getX(); - $y = $center->getY(); - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $arc = new \ImagickDraw(); - - $arc->setStrokeColor($pixel); - $arc->setStrokeWidth(1); - $arc->setFillColor('transparent'); - $arc->arc($x - $width / 2, $y - $height / 2, $x + $width / 2, $y + $height / 2, $start, $end); - - $this->imagick->drawImage($arc); - - $pixel->clear(); - $pixel->destroy(); - - $arc->clear(); - $arc->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Draw arc operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::chord() - */ - public function chord(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false) - { - $x = $center->getX(); - $y = $center->getY(); - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $chord = new \ImagickDraw(); - - $chord->setStrokeColor($pixel); - $chord->setStrokeWidth(1); - - if ($fill) { - $chord->setFillColor($pixel); - } else { - $this->line( - new Point( - round($x + $width / 2 * cos(deg2rad($start))), - round($y + $height / 2 * sin(deg2rad($start))) - ), - new Point( - round($x + $width / 2 * cos(deg2rad($end))), - round($y + $height / 2 * sin(deg2rad($end))) - ), - $color - ); - - $chord->setFillColor('transparent'); - } - - $chord->arc( - $x - $width / 2, - $y - $height / 2, - $x + $width / 2, - $y + $height / 2, - $start, - $end - ); - - $this->imagick->drawImage($chord); - - $pixel->clear(); - $pixel->destroy(); - - $chord->clear(); - $chord->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Draw chord operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::ellipse() - */ - public function ellipse(PointInterface $center, BoxInterface $size, Color $color, $fill = false) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - try { - $pixel = $this->getColor($color); - $ellipse = new \ImagickDraw(); - - $ellipse->setStrokeColor($pixel); - $ellipse->setStrokeWidth(1); - - if ($fill) { - $ellipse->setFillColor($pixel); - } else { - $ellipse->setFillColor('transparent'); - } - - $ellipse->ellipse( - $center->getX(), - $center->getY(), - $width / 2, - $height / 2, - 0, 360 - ); - - if (false === $this->imagick->drawImage($ellipse)) { - throw new RuntimeException('Ellipse operation failed'); - } - - $pixel->clear(); - $pixel->destroy(); - - $ellipse->clear(); - $ellipse->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Draw ellipse operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::line() - */ - public function line(PointInterface $start, PointInterface $end, Color $color) - { - try { - $pixel = $this->getColor($color); - $line = new \ImagickDraw(); - - $line->setStrokeColor($pixel); - $line->setStrokeWidth(1); - $line->line( - $start->getX(), - $start->getY(), - $end->getX(), - $end->getY() - ); - - $this->imagick->drawImage($line); - - $pixel->clear(); - $pixel->destroy(); - - $line->clear(); - $line->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Draw line operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::pieSlice() - */ - public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - $x1 = round($center->getX() + $width / 2 * cos(deg2rad($start))); - $y1 = round($center->getY() + $height / 2 * sin(deg2rad($start))); - $x2 = round($center->getX() + $width / 2 * cos(deg2rad($end))); - $y2 = round($center->getY() + $height / 2 * sin(deg2rad($end))); - - if ($fill) { - $this->chord($center, $size, $start, $end, $color, true); - $this->polygon( - array( - $center, - new Point($x1, $y1), - new Point($x2, $y2), - ), - $color, - true - ); - } else { - $this->arc($center, $size, $start, $end, $color); - $this->line($center, new Point($x1, $y1), $color); - $this->line($center, new Point($x2, $y2), $color); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::dot() - */ - public function dot(PointInterface $position, Color $color) - { - $x = $position->getX(); - $y = $position->getY(); - - try { - $pixel = $this->getColor($color); - $point = new \ImagickDraw(); - - $point->setFillColor($pixel); - $point->point($x, $y); - - $this->imagick->drawimage($point); - - $pixel->clear(); - $pixel->destroy(); - - $point->clear(); - $point->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Draw point operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::polygon() - */ - public function polygon(array $coordinates, Color $color, $fill = false) - { - if (count($coordinates) < 3) { - throw new InvalidArgumentException(sprintf( - 'Polygon must consist of at least 3 coordinates, %d given', - count($coordinates) - )); - } - - $points = array_map( - function(PointInterface $p) - { - return array('x' => $p->getX(), 'y' => $p->getY()); - }, - $coordinates - ); - - try { - $pixel = $this->getColor($color); - $polygon = new \ImagickDraw(); - - $polygon->setStrokeColor($pixel); - $polygon->setStrokeWidth(1); - - if ($fill) { - $polygon->setFillColor($pixel); - } else { - $polygon->setFillColor('transparent'); - } - - $polygon->polygon($points); - - $this->imagick->drawImage($polygon); - - $pixel->clear(); - $pixel->destroy(); - - $polygon->clear(); - $polygon->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Draw polygon operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\Draw\DrawerInterface::text() - */ - public function text($string, AbstractFont $font, PointInterface $position, $angle = 0) - { - try { - $pixel = $this->getColor($font->getColor()); - $text = new \ImagickDraw(); - - $text->setFont($font->getFile()); - $text->setFontSize($font->getSize()); - $text->setFillColor($pixel); - $text->setTextAntialias(true); - - $info = $this->imagick->queryFontMetrics($text, $string); - $rad = deg2rad($angle); - $cos = cos($rad); - $sin = sin($rad); - - $x1 = round(0 * $cos - 0 * $sin); - $x2 = round($info['textWidth'] * $cos - $info['textHeight'] * $sin); - $y1 = round(0 * $sin + 0 * $cos); - $y2 = round($info['textWidth'] * $sin + $info['textHeight'] * $cos); - - $xdiff = 0 - min($x1, $x2); - $ydiff = 0 - min($y1, $y2); - - $this->imagick->annotateImage( - $text, $position->getX() + $x1 + $xdiff, - $position->getY() + $y2 + $ydiff, $angle, $string - ); - - $pixel->clear(); - $pixel->destroy(); - - $text->clear(); - $text->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Draw text operation failed', $e->getCode(), $e - ); - } - } - - /** - * Gets specifically formatted color string from Color instance - * - * @param Imagine\Image\Color $color - * - * @return string - */ - private function getColor(Color $color) - { - $pixel = new \ImagickPixel((string) $color); - - $pixel->setColorValue( - \Imagick::COLOR_OPACITY, - number_format(abs(round($color->getAlpha() / 100, 1)), 1) - ); - - return $pixel; - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Imagick/Font.php b/app/Plugin/Imagine/lib/Imagine/Imagick/Font.php deleted file mode 100755 index e46b73a..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Imagick/Font.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Imagick; - -use Imagine\Image\AbstractFont; -use Imagine\Image\Box; -use Imagine\Image\Color; - -final class Font extends AbstractFont -{ - /** - * @var Imagick - */ - private $imagick; - - /** - * @param Imagick $imagick - * @param string $file - * @param integer $size - * @param Imagine\Image\Color $color - */ - public function __construct(\Imagick $imagick, $file, $size, Color $color) - { - $this->imagick = $imagick; - - parent::__construct($file, $size, $color); - } - - /** - * (non-PHPdoc) - * @see Imagine\Image\AbstractFont::box() - */ - public function box($string, $angle = 0) - { - $text = new \ImagickDraw(); - - $text->setFont($this->file); - $text->setFontSize($this->size); - $text->setFontStyle(\Imagick::STYLE_OBLIQUE); - - $info = $this->imagick->queryFontMetrics($text, $string); - - $box = new Box($info['textWidth'], $info['textHeight']); - - return $box; - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Imagick/Image.php b/app/Plugin/Imagine/lib/Imagine/Imagick/Image.php deleted file mode 100755 index 061365e..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Imagick/Image.php +++ /dev/null @@ -1,494 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Imagick; - -use Imagine\Exception\OutOfBoundsException; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\Fill\FillInterface; -use Imagine\Image\Box; -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\Image\Point; -use Imagine\Image\PointInterface; -use Imagine\ImageInterface; -use Imagine\Mask\MaskInterface; - -final class Image implements ImageInterface -{ - /** - * @var Imagick - */ - private $imagick; - - /** - * Constructs Image with Imagick and Imagine instances - * - * @param Imagick $imagick - */ - public function __construct(\Imagick $imagick) - { - $this->imagick = $imagick; - } - - /** - * Destroys allocated imagick resources - */ - public function __destruct() - { - if (null !== $this->imagick && $this->imagick instanceof \Imagick) { - $this->imagick->clear(); - $this->imagick->destroy(); - } - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::copy() - */ - public function copy() - { - try { - $clone = $this->imagick->clone(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Copy operation failed', $e->getCode(), $e - ); - } - return new self($clone); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::crop() - */ - public function crop(PointInterface $start, BoxInterface $size) - { - if (!$start->in($size)) { - throw new OutOfBoundsException('Crop coordinates must start at '. - 'minimum 0, 0 position from top left corner, crop height and '. - 'width must be positive integers and must not exceed the '. - 'current image borders'); - } - - try { - $this->imagick->cropImage( - $size->getWidth(), - $size->getHeight(), - $start->getX(), - $start->getY() - ); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Crop operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::flipHorizontally() - */ - public function flipHorizontally() - { - try { - $this->imagick->flopImage(); - } - catch (\ImagickException $e) { - throw new RuntimeException( - 'Horizontal Flip operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::flipVertically() - */ - public function flipVertically() - { - try { - $this->imagick->flipImage(); - } - catch (\ImagickException $e) { - throw new RuntimeException( - 'Vertical flip operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::paste() - */ - public function paste(ImageInterface $image, PointInterface $start) - { - if (!$image instanceof self) { - throw new InvalidArgumentException(sprintf('Imagick\Image can '. - 'only paste() Imagick\Image instances, %s given', - get_class($image) - )); - } - - if (!$this->getSize()->contains($image->getSize(), $start)) { - throw new OutOfBoundsException( - 'Cannot paste image of the given size at the specified '. - 'position, as it moves outside of the current image\'s box' - ); - } - - try { - - $this->imagick->compositeImage( - $image->imagick, \Imagick::COMPOSITE_DEFAULT, - $start->getX(), - $start->getY() - ); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Paste operation failed', $e->getCode(), $e - ); - } - - try { - $this->imagick->flattenImages(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Paste operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::resize() - */ - public function resize(BoxInterface $size) - { - try { - $this->imagick->adaptiveResizeImage($size->getWidth(), $size->getHeight()); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Resize operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::rotate() - */ - public function rotate($angle, Color $background = null) - { - $color = $background ? $background : new Color('fff'); - - try { - $pixel = $this->getColor($color); - - $this->imagick->rotateimage($pixel, $angle); - - $pixel->clear(); - $pixel->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Rotate operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::save() - */ - public function save($path, array $options = array()) - { - try { - $this->applyImageOptions($this->imagick, $options); - $this->imagick->writeImage($path); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Save operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::show() - */ - public function show($format, array $options = array()) - { - echo $this->get($format, $options); - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::get() - */ - public function get($format, array $options = array()) - { - try { - $this->applyImageOptions($this->imagick, $options); - $this->imagick->setImageFormat($format); - } catch (\ImagickException $e) { - throw new InvalidArgumentException( - 'Show operation failed', $e->getCode(), $e - ); - } - - return (string) $this->imagick; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::__toString() - */ - public function __toString() - { - return $this->get('png'); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::thumbnail() - */ - public function thumbnail(BoxInterface $size, $mode = ImageInterface::THUMBNAIL_INSET) - { - if ($mode !== ImageInterface::THUMBNAIL_INSET && - $mode !== ImageInterface::THUMBNAIL_OUTBOUND) { - throw new InvalidArgumentException('Invalid mode specified'); - } - - $width = $size->getWidth(); - $height = $size->getHeight(); - $thumbnail = $this->copy(); - - try { - if ($mode === ImageInterface::THUMBNAIL_INSET) { - $thumbnail->imagick->thumbnailImage( - $width, - $height, - true - ); - } else if ($mode === ImageInterface::THUMBNAIL_OUTBOUND) { - $thumbnail->imagick->cropThumbnailImage( - $width, - $height - ); - } - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Thumbnail operation failed', $e->getCode(), $e - ); - } - - return $thumbnail; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::draw() - */ - public function draw() - { - return new Drawer($this->imagick); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::getSize() - */ - public function getSize() - { - try { - $width = $this->imagick->getImageWidth(); - $height = $this->imagick->getImageHeight(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Could not get size', $e->getCode(), $e - ); - } - - return new Box($width, $height); - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::applyMask() - */ - public function applyMask(ImageInterface $mask) - { - if (!$mask instanceof self) { - throw new InvalidArgumentException( - 'Can only apply instances of Imagine\Imagick\Image as masks' - ); - } - - $size = $this->getSize(); - $maskSize = $mask->getSize(); - - if ($size != $maskSize) { - throw new InvalidArgumentException(sprintf( - 'The given mask doesn\'t match current image\'s sise, Current '. - 'mask\'s dimensions are %s, while image\'s dimensions are %s', - $maskSize, $size - )); - } - - $mask = $mask->mask(); - - $mask->imagick->negateImage(true); - - try { - $this->imagick->compositeImage( - $mask->imagick, - \Imagick::COMPOSITE_COPYOPACITY, - 0, 0 - ); - - $mask->imagick->clear(); - $mask->imagick->destroy(); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Apply mask operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::mask() - */ - public function mask() - { - $mask = $this->copy(); - - try { - $mask->imagick->modulateImage(100, 0, 100); - $mask->imagick->setImageMatte(false); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Mask operation failed', $e->getCode(), $e - ); - } - - return $mask; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::fill() - */ - public function fill(FillInterface $fill) - { - try { - $iterator = $this->imagick->getPixelIterator(); - - foreach ($iterator as $y => $pixels) { - foreach ($pixels as $x => $pixel) { - $color = $fill->getColor(new Point($x, $y)); - - $pixel->setColor((string) $color); - $pixel->setColorValue( - \Imagick::COLOR_OPACITY, - number_format(abs(round($color->getAlpha() / 100, 1)), 1) - ); - } - - $iterator->syncIterator(); - } - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Fill operation failed', $e->getCode(), $e - ); - } - - return $this; - } - - /** - * (non-PHPdoc) - * @see Imagine\ImageInterface::histogram() - */ - public function histogram() - { - $pixels = $this->imagick->getImageHistogram(); - - return array_map( - function(\ImagickPixel $pixel) - { - $info = $pixel->getColor(); - return new Color( - array( - $info['r'], - $info['g'], - $info['b'], - ), - (int) round($info['a'] * 100) - ); - }, - $pixels - ); - } - - /** - * Internal - * - * Applies options before save or output - * - * @param \Imagick $image - * @param array $options - */ - private function applyImageOptions(\Imagick $image, array $options) - { - if (isset($options['quality'])) { - $image->setImageCompressionQuality($options['quality']); - } - } - - /** - * Gets specifically formatted color string from Color instance - * - * @param Imagine\Image\Color $color - * - * @return string - */ - private function getColor(Color $color) - { - $pixel = new \ImagickPixel((string) $color); - - $pixel->setColorValue( - \Imagick::COLOR_OPACITY, - number_format(abs(round($color->getAlpha() / 100, 1)), 1) - ); - - return $pixel; - } -} \ No newline at end of file diff --git a/app/Plugin/Imagine/lib/Imagine/Imagick/Imagine.php b/app/Plugin/Imagine/lib/Imagine/Imagick/Imagine.php deleted file mode 100755 index a565486..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Imagick/Imagine.php +++ /dev/null @@ -1,120 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Imagick; - -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\ImageInterface; -use Imagine\ImagineInterface; - -final class Imagine implements ImagineInterface -{ - /** - * @throws Imagine\Exception\RuntimeException - */ - public function __construct() - { - if (!class_exists('Imagick')) { - throw new RuntimeException('Imagick not installed'); - } - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::open() - */ - public function open($path) - { - if (!is_file($path)) { - throw new InvalidArgumentException(sprintf( - 'File %s doesn\'t exist', $path - )); - } - - try { - $imagick = new \Imagick($path); - - $imagick->setImageMatte(true); - - return new Image($imagick); - } catch (\ImagickException $e) { - throw new RuntimeException( - sprintf('Could not open path "%s"', $path), $e->getCode(), $e - ); - } - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::create() - */ - public function create(BoxInterface $size, Color $color = null) - { - $width = $size->getWidth(); - $height = $size->getHeight(); - - $color = null !== $color ? $color : new Color('fff'); - - try { - $pixel = new \ImagickPixel((string) $color); - $pixel->setColorValue( - \Imagick::COLOR_OPACITY, - number_format(abs(round($color->getAlpha() / 100, 1)), 1) - ); - - $imagick = new \Imagick(); - $imagick->newImage($width, $height, $pixel); - $imagick->setImageMatte(true); - $imagick->setImageBackgroundColor($pixel); - - $pixel->clear(); - $pixel->destroy(); - - return new Image($imagick); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Could not create empty image', $e->getCode(), $e - ); - } - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::load() - */ - public function load($string) - { - try { - $imagick = new \Imagick(); - - $imagick->readImageBlob($string); - $imagick->setImageMatte(true); - - return new Image($imagick); - } catch (\ImagickException $e) { - throw new RuntimeException( - 'Could not load image from string', $e->getCode(), $e - ); - } - } - - /** - * (non-PHPdoc) - * @see Imagine\ImagineInterface::font() - */ - public function font($file, $size, Color $color) - { - return new Font(new \Imagick(), $file, $size, $color); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/ImagineInterface.php b/app/Plugin/Imagine/lib/Imagine/ImagineInterface.php deleted file mode 100755 index 257b927..0000000 --- a/app/Plugin/Imagine/lib/Imagine/ImagineInterface.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine; - -use Imagine\Exception\InvalidArgumentException; -use Imagine\Exception\RuntimeException; -use Imagine\Image\BoxInterface; -use Imagine\Image\Color; - -interface ImagineInterface -{ - /** - * Creates a new empty image with an optional background color - * - * @param Imagine\Image\BoxInterface $size - * @param Imagine\Image\Color $color - * - * @throws Imagine\Exception\InvalidArgumentException - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function create(BoxInterface $size, Color $color = null); - - /** - * Opens an existing image from $path - * - * @param string $path - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function open($path); - - /** - * Loads an image from a binary $string - * - * @param string $string - * - * @throws Imagine\Exception\RuntimeException - * - * @return Imagine\ImageInterface - */ - function load($string); - - /** - * Constructs a font with specified $file, $size and $color - * - * The font size is to be specified in points (e.g. 10pt means 10) - * - * @param string $file - * @param integer $size - * @param Imagine\Image\Color $color - * - * @return Imagine\Image\AbstractFont - */ - function font($file, $size, Color $color); -} diff --git a/app/Plugin/Imagine/lib/Imagine/Test/Constraint/IsImageEqual.php b/app/Plugin/Imagine/lib/Imagine/Test/Constraint/IsImageEqual.php deleted file mode 100755 index 2f1cc1a..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Test/Constraint/IsImageEqual.php +++ /dev/null @@ -1,158 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Test\Constraint; - -use Imagine\ImageInterface; -use Imagine\Image\Histogram\Bucket; -use Imagine\Image\Histogram\Range; - -class IsImageEqual extends \PHPUnit_Framework_Constraint -{ - /** - * @var Imagine\ImageInterface - */ - private $value; - - /** - * @var float - */ - private $delta; - - /** - * @var integer - */ - private $buckets; - - /** - * @param Imagine\ImageInterface $value - * @param float $delta - * @param integer $buckets - * - * @throws InvalidArgumentException - */ - public function __construct($value, $delta = 0.1, $buckets = 4) - { - if (!$value instanceof ImageInterface) { - throw \PHPUnit_Util_InvalidArgumentHelper::factory(1, 'Imagine\ImageInterface'); - } - - if (!is_numeric($delta)) { - throw \PHPUnit_Util_InvalidArgumentHelper::factory(2, 'numeric'); - } - - if (!is_integer($buckets) || $buckets <= 0) { - throw \PHPUnit_Util_InvalidArgumentHelper::factory(3, 'integer'); - } - - $this->value = $value; - $this->delta = $delta; - $this->buckets = $buckets; - } - - /** - * (non-PHPdoc) - * @see PHPUnit_Framework_Constraint::evaluate() - */ - public function evaluate($other) - { - if (!$other instanceof ImageInterface) { - throw \PHPUnit_Util_InvalidArgumentHelper::factory(1, 'Imagine\ImageInterface'); - } - - list($currentRed, $currentGreen, $currentBlue, $currentAlpha) = $this->normalize($this->value); - list($otherRed, $otherGreen, $otherBlue, $otherAlpha) = $this->normalize($other); - - $total = 0; - - foreach ($currentRed as $bucket => $count) { - $total += abs($count - $otherRed[$bucket]); - } - - foreach ($currentGreen as $bucket => $count) { - $total += abs($count - $otherGreen[$bucket]); - } - - foreach ($currentBlue as $bucket => $count) { - $total += abs($count - $otherBlue[$bucket]); - } - - foreach ($currentAlpha as $bucket => $count) { - $total += abs($count - $otherAlpha[$bucket]); - } - - return $total <= $this->delta; - } - - /** - * (non-PHPdoc) - * @see PHPUnit_Framework_SelfDescribing::toString() - */ - public function toString() - { - return sprintf('contains color histogram identical to expected %s', \PHPUnit_Util_Type::toString($this->value)); - } - - /** - * @param Imagine\ImageInterface $image - * - * @return array - */ - private function normalize(ImageInterface $image) - { - $step = (int) round(255 / $this->buckets); - - $red = - $green = - $blue = - $alpha = array(); - - for ($i = 1; $i <= $this->buckets; $i++) { - $range = new Range(($i - 1) * $step, $i * $step); - $red[] = new Bucket($range); - $green[] = new Bucket($range); - $blue[] = new Bucket($range); - $alpha[] = new Bucket($range); - } - - foreach ($image->histogram() as $color) { - foreach ($red as $bucket) { - $bucket->add($color->getRed()); - } - - foreach ($green as $bucket) { - $bucket->add($color->getGreen()); - } - - foreach ($blue as $bucket) { - $bucket->add($color->getBlue()); - } - - foreach ($alpha as $bucket) { - $bucket->add($color->getAlpha()); - } - } - - $total = $image->getSize()->square(); - - $callback = function (Bucket $bucket) use ($total) - { - return count($bucket) / $total; - }; - - return array( - array_map($callback, $red), - array_map($callback, $green), - array_map($callback, $blue), - array_map($callback, $alpha), - ); - } -} diff --git a/app/Plugin/Imagine/lib/Imagine/Test/ImagineTestCase.php b/app/Plugin/Imagine/lib/Imagine/Test/ImagineTestCase.php deleted file mode 100755 index 4e21b72..0000000 --- a/app/Plugin/Imagine/lib/Imagine/Test/ImagineTestCase.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Imagine\Test; - -use Imagine\ImageInterface; -use Imagine\Test\Constraint\IsImageEqual; - -class ImagineTestCase extends \PHPUnit_Framework_TestCase -{ - /** - * Asserts that two images are equal using color histogram comparison method - * - * @param Imagine\ImageInterface $expected - * @param Imagine\ImageInterface $actual - * @param string $message - * @param float $delta - * @param integer $buckets - */ - public static function assertImageEquals($expected, $actual, $message = '', $delta = 0.1, $buckets = 4) - { - $constraint = new IsImageEqual($expected, $delta, $buckets); - - self::assertThat($actual, $constraint, $message); - } -} From d618f4fb6191ad162b6c9a82fdd2e2aab86d5d4c Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 4 Jan 2012 16:01:27 -0600 Subject: [PATCH 30/50] Filebrowser update for new Imagine lib --- app/Module/Filebrowser/Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Module/Filebrowser/Controller.php b/app/Module/Filebrowser/Controller.php index c02d918..215f911 100755 --- a/app/Module/Filebrowser/Controller.php +++ b/app/Module/Filebrowser/Controller.php @@ -115,7 +115,7 @@ public function imageSizeAction(Alloy\Request $request) // Resize to requested width/height $image = $kernel->imagine() ->open($imagePath . '/' . $image) - ->thumbnail(new Imagine\Image\Box($width, $height), Imagine\ImageInterface::THUMBNAIL_INSET) + ->thumbnail(new Imagine\Image\Box($width, $height), Imagine\Image\ImageInterface::THUMBNAIL_INSET) ->save($resizeDir . $image); // Send image content to browser From 4e29a799f9e4176f74703c401d3cb4e2c9685f8a Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 4 Jan 2012 17:29:24 -0600 Subject: [PATCH 31/50] Updated Filebrowser to redirect after upload * Fixed blank page after upload issue * Fixed URL ampersand encoding --- alloy/lib/Alloy/Kernel.php | 2 +- app/Module/Filebrowser/Controller.php | 2 ++ app/Plugin/Imagine/Plugin.php | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/alloy/lib/Alloy/Kernel.php b/alloy/lib/Alloy/Kernel.php index 3dc07bc..ed1aa8d 100644 --- a/alloy/lib/Alloy/Kernel.php +++ b/alloy/lib/Alloy/Kernel.php @@ -547,7 +547,7 @@ public function url($params = array(), $routeName = null, $queryParams = array() } if(count($queryParams) > 0) { // Build query string from array $qsData - $queryString = http_build_query($queryParams, '', '&'); + $queryString = http_build_query($queryParams, '', '&'); } else { $queryString = false; } diff --git a/app/Module/Filebrowser/Controller.php b/app/Module/Filebrowser/Controller.php index 215f911..dbb11d7 100755 --- a/app/Module/Filebrowser/Controller.php +++ b/app/Module/Filebrowser/Controller.php @@ -207,6 +207,7 @@ public function postMethod(Alloy\Request $request) $err = ''; // CKEditor relies on receiving this custom callback after successful upload + /* return ' '; + */ } // Redirect to images or files diff --git a/app/Plugin/Imagine/Plugin.php b/app/Plugin/Imagine/Plugin.php index 236887d..bba787f 100644 --- a/app/Plugin/Imagine/Plugin.php +++ b/app/Plugin/Imagine/Plugin.php @@ -6,7 +6,7 @@ * Imagine Plugin * Adds Imagine lib to your Alloy project * - * @version 0.1.4 + * @version 0.2.8 * @see https://github.com/avalanche123/Imagine */ class Plugin From 33d4c9173014a36264c8b58416dc03e2558943b5 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Wed, 4 Jan 2012 18:08:08 -0600 Subject: [PATCH 32/50] Added request variable dump to debug output --- app/www/index.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/www/index.php b/app/www/index.php index eb7e058..f73c83f 100644 --- a/app/www/index.php +++ b/app/www/index.php @@ -177,9 +177,14 @@ // Debugging on? if($kernel->config('app.debug')) { + // Request Data + echo "

    Request Data

    "; + echo $kernel->dump($kernel->request()->params()); + echo "
    "; echo "

    Event Trace

    "; echo $kernel->dump($kernel->trace()); + } // Notify that response has been sent @@ -192,4 +197,4 @@ header("HTTP/1.0 500 Internal Server Error"); echo "

    Internal Server Error

    "; echo $content; -} \ No newline at end of file +} From 3ce4bb50e5efa1f75420b6fe2176c344ee07bb30 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Fri, 6 Jan 2012 10:17:07 -0600 Subject: [PATCH 33/50] Filebrowser: Removed commented out code --- app/Module/Filebrowser/Controller.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/app/Module/Filebrowser/Controller.php b/app/Module/Filebrowser/Controller.php index dbb11d7..5ef9f5a 100755 --- a/app/Module/Filebrowser/Controller.php +++ b/app/Module/Filebrowser/Controller.php @@ -199,25 +199,6 @@ public function postMethod(Alloy\Request $request) // =========================================================================== if($saveResult) { - // CKEditor custom response - // @see http://docs.cksource.com/CKEditor_3.x/Developers_Guide/File_Browser_(Uploader)/Custom_File_Browser - if($request->get('CKEditor')) { - $callback = $request->get('CKEditorFuncNum'); - $url = $kernel->config('cms.url.files') . $subDir . '/' . $fileName; - $err = ''; - - // CKEditor relies on receiving this custom callback after successful upload - /* - return ' - - '; - */ - } - // Redirect to images or files if($subDir = 'images') { return $kernel->redirect($kernel->url(array('action' => 'images'), 'filebrowser', $request->query())); From 2279b25e402d61fba08f56c0eb51cdde0d864dc2 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Mon, 9 Jan 2012 11:03:02 -0600 Subject: [PATCH 34/50] Blog now sorts by date_created DESCENDING --- app/www/content/Module/Blog/Post/Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/www/content/Module/Blog/Post/Controller.php b/app/www/content/Module/Blog/Post/Controller.php index 420f021..4113549 100644 --- a/app/www/content/Module/Blog/Post/Controller.php +++ b/app/www/content/Module/Blog/Post/Controller.php @@ -23,7 +23,7 @@ public function indexAction(Request $request, Page $page, Module $module) 'module_id' => $module->id, 'status' => Entity::STATUS_PUBLISHED )) - ->order('date_created'); + ->order(array('date_created' => 'DESC')); // Return only content for HTML if($request->format == 'html') { From 58d0e803c82905321560a7d30bc6b697948b3725 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Thu, 19 Jan 2012 11:12:01 -0600 Subject: [PATCH 35/50] Defined local $kernel var in 'regionModuleFormat' --- app/Module/Page/Controller.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/Module/Page/Controller.php b/app/Module/Page/Controller.php index 4e9d951..608efd7 100755 --- a/app/Module/Page/Controller.php +++ b/app/Module/Page/Controller.php @@ -498,6 +498,7 @@ protected function formView($entityName = null) */ protected function regionModuleFormat($request, $page, $module, $user, $moduleResponse, $includeControls = true) { + $kernel = \Kernel(); $content = ""; if(false === $moduleResponse) { $content = false; @@ -513,7 +514,7 @@ protected function regionModuleFormat($request, $page, $module, $user, $moduleRe // Alloy Module Response type if($moduleResponse instanceof \Alloy\Module\Response) { // Pass HTTP status code - $response = $this->kernel->response($moduleResponse->status()); + $response = $kernel->response($moduleResponse->status()); // Display errors if($errors = $moduleResponse->errors()): @@ -546,7 +547,7 @@ protected function regionModuleFormat($request, $page, $module, $user, $moduleRe
    ' . $module->name . '
      -
    • Edit
    • '; +
    • Edit
    • '; // Options submenu $content .= ' @@ -557,9 +558,9 @@ protected function regionModuleFormat($request, $page, $module, $user, $moduleRe if($module->id > 0) { // Settings link $content .= ' -
    • Settings
    • '; +
    • Settings
    • '; $content .= ' -
    • Delete
    • '; +
    • Delete
    • '; } $content .= '
    From 572ead4f7bc49e632f276a85826596d87ef52fea Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Tue, 7 Feb 2012 23:51:24 -0600 Subject: [PATCH 36/50] Added caching headers to default response --- app/Module/User/Session/Controller.php | 10 ++++++++++ app/Plugin/Stackbox/Plugin.php | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/app/Module/User/Session/Controller.php b/app/Module/User/Session/Controller.php index 26620f8..5a9f148 100755 --- a/app/Module/User/Session/Controller.php +++ b/app/Module/User/Session/Controller.php @@ -7,6 +7,9 @@ */ class Controller extends Stackbox\Module\ControllerAbstract { + const COOKIE_NAME = '_mcnc'; + + /** * Access control list for controller methods */ @@ -113,6 +116,9 @@ public function deleteMethod($request) unset($_SESSION['user']); session_write_close(); } + + // Delete logged-in cookie + setcookie(static::COOKIE_NAME, '0', time()-28800); // Delete all sessions matched for current user $this->kernel->mapper()->delete('Module\User\Entity', array('user_id' => $user->id)); @@ -134,6 +140,10 @@ public function authenticate($sessionKey = null) list($userId, $userSession) = explode(':', $sessionKey); $userSession = $mapper->first('Module\User\Session\Entity', array('user_id' => $userId, 'session_id' => $userSession)); if($userSession) { + // Set cookie to flag logged in user (useful for cache busting) + setcookie(static::COOKIE_NAME, '1', time()+28800); // 28,800 = 8 hours + + // Return user object return $mapper->get('Module\User\Entity', $userSession->user_id); } } diff --git a/app/Plugin/Stackbox/Plugin.php b/app/Plugin/Stackbox/Plugin.php index 3f96bc0..78f2374 100644 --- a/app/Plugin/Stackbox/Plugin.php +++ b/app/Plugin/Stackbox/Plugin.php @@ -160,6 +160,10 @@ public function layoutOrApiOutput($content) $response = $kernel->response(); $response->contentType('text/html'); + $response->header("Expires", gmdate("D, d M Y H:i:s", strtotime('+2 hours')) . " GMT"); + $response->header("Last-Modified", gmdate( "D, d M Y H:i:s" ) . " GMT"); + $response->header("Cache-Control", "max-age=7200, must-revalidate"); + $response->header("Pragma", "public"); $layoutName = null; if($content instanceof Alloy\View\Template) { From 3689c577454a899d24850c816e957ee92b5c66b6 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 13:02:23 -0600 Subject: [PATCH 37/50] Alloy Framework update to v0.8.0 * Added base PluginAbstract for Plugins to extend * Updated Spot ORM to latest version --- alloy/Module/Generator/Controller.php | 134 +++ .../Module/Generator/scaffold/Controller.php | 241 +++++ alloy/Module/Generator/scaffold/Entity.php | 18 + .../scaffold/views/deleteAction.html.php | 13 + .../scaffold/views/editAction.html.php | 8 + .../scaffold/views/indexAction.html.php | 32 + .../scaffold/views/newAction.html.php | 8 + .../scaffold/views/viewAction.html.php | 9 + alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php | 2 +- .../Spot/lib/Spot/Adapter/PDO/Abstract.php | 38 +- alloy/Plugin/Spot/lib/Spot/Config.php | 190 ++-- alloy/Plugin/Spot/lib/Spot/Entity.php | 64 +- alloy/Plugin/Spot/lib/Spot/Mapper.php | 25 +- .../lib/Spot/Relation/RelationAbstract.php | 6 +- .../Plugin/Spot/lib/Spot/tests/Test/CRUD.php | 2 +- .../Spot/lib/Spot/tests/Test/Config.php | 18 +- .../Spot/lib/Spot/tests/Test/Entity.php | 26 + .../Spot/lib/Spot/tests/Test/Relations.php | 4 +- alloy/lib/Alloy/App.php | 33 - alloy/lib/Alloy/Client.php | 141 --- alloy/lib/Alloy/Kernel.php | 73 +- alloy/lib/Alloy/PluginAbstract.php | 37 + alloy/lib/Alloy/Request.php | 44 + .../View/Generic/templates/datagrid.html.php | 2 +- .../View/Generic/templates/form.html.php | 23 +- .../View/Generic/templates/treeview.html.php | 7 +- alloy/lib/Alloy/View/Template.php | 5 +- alloy/lib/Zend/Console/Getopt.php | 970 ++++++++++++++++++ alloy/lib/Zend/Console/Getopt/Exception.php | 66 ++ alloy/lib/Zend/Exception.php | 96 ++ alloy/tests/AllTests.php | 2 +- app/www/index.php | 4 +- 32 files changed, 2007 insertions(+), 334 deletions(-) create mode 100644 alloy/Module/Generator/Controller.php create mode 100644 alloy/Module/Generator/scaffold/Controller.php create mode 100644 alloy/Module/Generator/scaffold/Entity.php create mode 100644 alloy/Module/Generator/scaffold/views/deleteAction.html.php create mode 100644 alloy/Module/Generator/scaffold/views/editAction.html.php create mode 100644 alloy/Module/Generator/scaffold/views/indexAction.html.php create mode 100644 alloy/Module/Generator/scaffold/views/newAction.html.php create mode 100644 alloy/Module/Generator/scaffold/views/viewAction.html.php delete mode 100644 alloy/lib/Alloy/App.php delete mode 100644 alloy/lib/Alloy/Client.php create mode 100644 alloy/lib/Alloy/PluginAbstract.php create mode 100644 alloy/lib/Zend/Console/Getopt.php create mode 100644 alloy/lib/Zend/Console/Getopt/Exception.php create mode 100644 alloy/lib/Zend/Exception.php diff --git a/alloy/Module/Generator/Controller.php b/alloy/Module/Generator/Controller.php new file mode 100644 index 0000000..734d619 --- /dev/null +++ b/alloy/Module/Generator/Controller.php @@ -0,0 +1,134 @@ +request()->isCli()) { + throw new Alloy\Exception\FileNotFound("Requested file or page not found. Please check the URL and try again."); + } + } + + + /** + * Scaffold module + */ + public function scaffoldAction(Request $request) + { + $kernel = \Kernel(); + + /** + * Variables we expect in CLI request: + * + * $name string Name of module + */ + $name = preg_replace('/[^a-zA-Z0-9_ ]/', '', $kernel->formatUnderscoreWord($request->name)); + $name_table = preg_replace('/\s+/', '_', strtolower($name)); + + // URL-usable name + $name_url = preg_replace('/\s+/', '_', $name); + + // Directory path + $name_dir = preg_replace('/\s+/', '/', $name); + + // Valid PHP namespace + $namespace = preg_replace('/\s+/', '\\', $name); + + // TODO: Make this dynamic and generated (allow user field definitions) + $fields = array( + 'name' => array('type' => 'string', 'required' => true) + ); + $field_string = ""; + foreach($fields as $fieldName => $fieldInfo) { + // Flattens field definitions for writing in Entity.php file + // str_replace calls to remove some of the prettyprinting and odd formats var_export does by default + $field_string .= "'" . $fieldName . "' => " . str_replace( + array("array (", "\n", ",)"), + array("array(", "", ")"), + var_export($fieldInfo, true) + ) . ",\n"; + } + + // Set tag variables + $generatorTagNames = compact('name', 'name_table', 'name_sanitized', 'name_url', 'name_dir', 'namespace', 'fields', 'field_string'); + + echo PHP_EOL; + + // File paths + $scaffoldPath = __DIR__ . '/scaffold/'; + $modulePath = $kernel->config('app.path.root') .'/Module/' . $name_dir . '/'; + + // Output + echo 'Generator Module Info' . PHP_EOL; + echo '-----------------------------------------------------------------------' . PHP_EOL; + echo 'Name = ' . $name . PHP_EOL; + echo 'Namespace = ' . $namespace . PHP_EOL; + echo 'Datasource (Table) = ' . $name_table . PHP_EOL; + echo 'Path = ' . $modulePath . PHP_EOL; + echo '-----------------------------------------------------------------------' . PHP_EOL; + echo PHP_EOL; + + // Variables (in 'generator' namespace): + // * name + // * name_table + // * $fields + // + // Other variables + // * $fields (from Entity::fields()) + + // Build tag replecement set for scaffold-generated files + $generator_tag_start = '{$generator.'; + $generator_tag_end = '}'; + $generatorTags = array(); + foreach($generatorTagNames as $tag => $val) { + if(is_array($val)) { + $val = var_export($val, true); + } + $generatorTags[$generator_tag_start . $tag . $generator_tag_end] = $val; + } + + // Copy files and replace tokens + $scaffoldFiles = array( + 'Controller.php', + 'Entity.php', + 'views/indexAction.html.php', + 'views/newAction.html.php', + 'views/editAction.html.php', + 'views/deleteAction.html.php', + 'views/viewAction.html.php' + ); + foreach($scaffoldFiles as $sFile) { + $tmpl = file_get_contents($scaffoldPath . $sFile); + + // Replace template tags + $tmpl = str_replace(array_keys($generatorTags), array_values($generatorTags), $tmpl); + + // Ensure destination directory exists + $sfDir = dirname($modulePath . $sFile); + if(!is_dir($sfDir)) { + mkdir($sfDir, 0755, true); + } + + // Write file to destination module directory + $result = file_put_contents($modulePath . $sFile, $tmpl); + if($result) { + echo "+ Generated '" . $sFile . "'"; + } else { + echo "[ERROR] Unable to generate '" . $sFile . "'"; + } + echo PHP_EOL; + } + + echo PHP_EOL; + } +} \ No newline at end of file diff --git a/alloy/Module/Generator/scaffold/Controller.php b/alloy/Module/Generator/scaffold/Controller.php new file mode 100644 index 0000000..e09120d --- /dev/null +++ b/alloy/Module/Generator/scaffold/Controller.php @@ -0,0 +1,241 @@ +mapper()->all(self::ENTITY); + $fields = $kernel->mapper()->entityManager()->fields(self::ENTITY); + + // Return 404 if no items + if(!$items) { + return false; + } + + // HTML template + if('html' == $request->format) { + return $this->template(__FUNCTION__) + ->set(compact('items', 'fields')); + } + // Resource object (JSON/XML, etc) + return $kernel->resource($items); + } + + + /** + * View single item + * @method GET + */ + public function viewAction(Request $request) + { + $kernel = \Kernel(); + $mapper = $kernel->mapper(); + + $item = $mapper->get(self::ENTITY, (int) $request->item); + if(!$item) { + return false; + } + + if('html' == $request->format) { + return $this->template(__FUNCTION__) + ->set(compact('item')); + } else { + return $kernel->resource($item); + } + } + + + /** + * Create new item form + * @method GET + */ + public function newAction(Request $request) + { + $kernel = \Kernel(); + $item = $kernel->mapper()->get(self::ENTITY); + + return $this->template(__FUNCTION__) + ->set(array( + 'item' => $item, + 'form' => $this->formView($item)->data($request->query()) + )); + } + + + /** + * Helper to save item + */ + public function saveItem(Entity $item) + { + $kernel = \Kernel(); + $mapper = $kernel->mapper(); + $request = $kernel->request(); + + // Attempt save + if($mapper->save($item)) { + $itemUrl = $kernel->url(array('module' => '{$generator.name_url}', 'item' => $item->id), 'module_item'); + + // HTML + if('html' == $request->format) { + return $kernel->redirect($itemUrl); + // Others (XML, JSON) + } else { + return $kernel->resource($item->data()) + ->created($itemUrl); + } + // Handle errors + } else { + // HTML + if('html' == $request->format) { + // Re-display form + $res = $kernel->spotForm($item); + // Others (XML, JSON) + } else { + $res = $kernel->resource(); + } + + // Set HTTP status and errors + return $res->status(400) + ->errors($item->errors()); + } + } + + + /** + * New item creation + * @method POST + */ + public function postMethod(Request $request) + { + $kernel = \Kernel(); + $mapper = $kernel->mapper(); + + $item = $mapper->get(self::ENTITY); + $item->data($request->post()); + + // Common save functionality + return $this->saveItem($item); + } + + + /** + * Edit form for item + * @method GET + */ + public function editAction(Request $request) + { + $kernel = \Kernel(); + $mapper = $kernel->mapper(); + + $item = $mapper->get(self::ENTITY, (int) $request->item); + if(!$item) { + return false; + } + + return $this->template(__FUNCTION__) + ->set(array( + 'item' => $item, + 'form' => $this->formView($item) + )); + } + + + /** + * Edit existing item + * @method PUT + */ + public function putMethod(Request $request) + { + $kernel = \Kernel(); + $mapper = $kernel->mapper(); + + $item = $mapper->get(self::ENTITY, (int) $request->item); + if(!$item) { + return false; + } + + // Set all POST data that can be set + $item->data($request->post()); + + // Ensure 'id' cannot be modified + $item->id = (int) $request->item; + + // Update 'last modified' date + $item->date_modified = new \DateTime(); + + // Common save functionality + return $this->saveItem($item); + } + + + /** + * Delete confirmation + * @method GET + */ + public function deleteAction(Request $request) + { + $kernel = \Kernel(); + $mapper = $kernel->mapper(); + + $item = $mapper->get(self::ENTITY, (int) $request->item); + if(!$item) { + return false; + } + + return $this->template(__FUNCTION__) + ->set(compact('item')); + } + + + /** + * Delete post + * @method DELETE + */ + public function deleteMethod(Request $request) + { + $kernel = \Kernel(); + $mapper = $kernel->mapper(); + + $item = $mapper->get(self::ENTITY, (int) $request->item); + if(!$item) { + return false; + } + + $mapper->delete($item); + + return $kernel->redirect($kernel->url(array('module' => '{$generator.name_url}'), 'module')); + } + + + /** + * Entity form with Alloy form generic + */ + protected function formView($entity = null) + { + return \Kernel()->spotForm($entity); + } + + + /** + * Automatic install/migrate method + */ + public function install() + { + $mapper = \Kernel()->mapper(); + $mapper->migrate(self::ENTITY); + } +} \ No newline at end of file diff --git a/alloy/Module/Generator/scaffold/Entity.php b/alloy/Module/Generator/scaffold/Entity.php new file mode 100644 index 0000000..83ee402 --- /dev/null +++ b/alloy/Module/Generator/scaffold/Entity.php @@ -0,0 +1,18 @@ + array('type' => 'int', 'primary' => true, 'serial' => true), + {$generator.field_string} + 'date_created' => array('type' => 'datetime', 'default' => new \DateTime()), + 'date_modified' => array('type' => 'datetime', 'default' => new \DateTime()) + ); + } +} \ No newline at end of file diff --git a/alloy/Module/Generator/scaffold/views/deleteAction.html.php b/alloy/Module/Generator/scaffold/views/deleteAction.html.php new file mode 100644 index 0000000..5a5cf14 --- /dev/null +++ b/alloy/Module/Generator/scaffold/views/deleteAction.html.php @@ -0,0 +1,13 @@ +head()->title('Delete Item'); ?> + +

    Really delete this item?

    + +generic('Form'); + +$form->action($kernel->url(array('module' => '{$generator.name_url}', 'item' => $item->id), 'module_item')) + ->method('delete') + ->submit('Delete'); +echo $form->content(); +?> \ No newline at end of file diff --git a/alloy/Module/Generator/scaffold/views/editAction.html.php b/alloy/Module/Generator/scaffold/views/editAction.html.php new file mode 100644 index 0000000..28687f3 --- /dev/null +++ b/alloy/Module/Generator/scaffold/views/editAction.html.php @@ -0,0 +1,8 @@ +head()->title('Edit Item'); ?> + +action($kernel->url(array('module' => '{$generator.name_url}', 'item' => $item->id), 'module_item')) + ->method('put'); +echo $form->content(); +?> \ No newline at end of file diff --git a/alloy/Module/Generator/scaffold/views/indexAction.html.php b/alloy/Module/Generator/scaffold/views/indexAction.html.php new file mode 100644 index 0000000..4420e66 --- /dev/null +++ b/alloy/Module/Generator/scaffold/views/indexAction.html.php @@ -0,0 +1,32 @@ +head()->title('Listing Items'); ?> + +

    New {$generator.name}

    + +generic('datagrid'); + +// Set data collection to use for rows +$table->data($items); + +// Add each column heading and cell output callback +foreach($fields as $field => $info) { + $table->column($kernel->formatUnderscoreWord($field), function($item) use($field, $info) { + return $item->$field; + }); +} + +// Edit/delete links +$table->column('View', function($item) use($view) { + return $view->link('View', array('module' => '{$generator.name_url}', 'item' => $item->id), 'module_item'); +}); +$table->column('Edit', function($item) use($view) { + return $view->link('Edit', array('module' => '{$generator.name_url}', 'item' => $item->id, 'action' => 'edit'), 'module_item_action'); +}); +$table->column('Delete', function($item) use($view) { + return $view->link('Delete', array('module' => '{$generator.name_url}', 'item' => $item->id, 'action' => 'delete'), 'module_item_action'); +}); + +// Output table +echo $table->content(); +?> diff --git a/alloy/Module/Generator/scaffold/views/newAction.html.php b/alloy/Module/Generator/scaffold/views/newAction.html.php new file mode 100644 index 0000000..098b151 --- /dev/null +++ b/alloy/Module/Generator/scaffold/views/newAction.html.php @@ -0,0 +1,8 @@ +head()->title('New Item'); ?> + +action($kernel->url(array('module' => '{$generator.name_url}'), 'module')) + ->method('post'); +echo $form->content(); +?> \ No newline at end of file diff --git a/alloy/Module/Generator/scaffold/views/viewAction.html.php b/alloy/Module/Generator/scaffold/views/viewAction.html.php new file mode 100644 index 0000000..7cd1298 --- /dev/null +++ b/alloy/Module/Generator/scaffold/views/viewAction.html.php @@ -0,0 +1,9 @@ +head()->title('View Item'); ?> + +data() as $field => $value): ?> +

    + : +

    + + +link('< Listing', array('module' => '{$generator.name_url}'), 'module', array('class' => 'btn')); ?> \ No newline at end of file diff --git a/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php b/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php index 8210563..309b4b2 100644 --- a/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php +++ b/alloy/Plugin/Spot/lib/Spot/Adapter/Mysql.php @@ -357,7 +357,7 @@ public function migrateSyntaxTableUpdate($table, array $formattedFields, array $ } // Extra - $syntax .= "\n) ENGINE=" . $options['engine'] . " DEFAULT CHARSET=" . $options['charset'] . " COLLATE=" . $options['collate'] . ";"; + $syntax .= ",\n ENGINE=" . $options['engine'] . " DEFAULT CHARSET=" . $options['charset'] . " COLLATE=" . $options['collate'] . ";"; return $syntax; } diff --git a/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php b/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php index c4a6bf0..d1dc04a 100644 --- a/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php +++ b/alloy/Plugin/Spot/lib/Spot/Adapter/PDO/Abstract.php @@ -180,11 +180,13 @@ public function migrateTableUpdate($table, array $formattedFields, array $option // Run SQL $this->connection()->exec($sql); } catch(\PDOException $e) { - // Table does not exist + // Table does not exist - special Exception case if($e->getCode() == "42S02") { throw new \Spot\Exception_Datasource_Missing("Table '" . $table . "' does not exist"); } - return false; + + // Re-throw exception + throw $e; } } return true; @@ -248,7 +250,9 @@ public function create($datasource, array $data, array $options = array()) if($stmt) { // Execute if($stmt->execute($binds)) { - $result = $this->connection()->lastInsertId(); + // Use 'id' if PK exists, otherwise returns true + $id = $this->connection()->lastInsertId(); + $result = $id ? $id : true; } else { $result = false; } @@ -260,7 +264,9 @@ public function create($datasource, array $data, array $options = array()) if($e->getCode() == "42S02") { throw new \Spot\Exception_Datasource_Missing("Table or datasource '" . $datasource . "' does not exist"); } - return false; + + // Re-throw exception + throw $e; } return $result; @@ -320,11 +326,13 @@ public function read(\Spot\Query $query, array $options = array()) } else { $result = false; } - } catch(PDOException $e) { + } catch(\PDOException $e) { // Table does not exist if($e->getCode() == "42S02") { throw new \Spot\Exception_Datasource_Missing("Table or datasource '" . $query->datasource . "' does not exist"); } + + // Re-throw exception throw $e; } @@ -368,11 +376,13 @@ public function count(\Spot\Query $query, array $options = array()) } else { $result = false; } - } catch(PDOException $e) { + } catch(\PDOException $e) { // Table does not exist if($e->getCode() == "42S02") { throw new \Spot\Exception_Datasource_Missing("Table or datasource '" . $query->datasource . "' does not exist"); } + + // Re-throw exception throw $e; } @@ -426,7 +436,9 @@ public function update($datasource, array $data, array $where = array(), array $ if($e->getCode() == "42S02") { throw new \Spot\Exception_Datasource_Missing("Table or datasource '" . $datasource . "' does not exist"); } - return false; + + // Re-throw exception + throw $e; } } else { $result = false; @@ -470,7 +482,9 @@ public function delete($datasource, array $data, array $options = array()) if($e->getCode() == "42S02") { throw new \Spot\Exception_Datasource_Missing("Table or datasource '" . $datasource . "' does not exist"); } - return false; + + // Re-throw exception + throw $e; } } @@ -492,7 +506,9 @@ public function truncateDatasource($datasource) { if($e->getCode() == "42S02") { throw new \Spot\Exception_Datasource_Missing("Table or datasource '" . $datasource . "' does not exist"); } - return false; + + // Re-throw exception + throw $e; } } @@ -514,7 +530,9 @@ public function dropDatasource($datasource) { if($e->getCode() == "42S02") { throw new \Spot\Exception_Datasource_Missing("Table or datasource '" . $datasource . "' does not exist"); } - return false; + + // Re-throw exception + throw $e; } } diff --git a/alloy/Plugin/Spot/lib/Spot/Config.php b/alloy/Plugin/Spot/lib/Spot/Config.php index 6151a2c..6b1aabe 100644 --- a/alloy/Plugin/Spot/lib/Spot/Config.php +++ b/alloy/Plugin/Spot/lib/Spot/Config.php @@ -5,95 +5,109 @@ * @package Spot * @link http://spot.os.ly */ -class Config +class Config implements \Serializable { - protected $_defaultConnection; - protected $_connections = array(); - - - /** - * Add database connection - * - * @param string $name Unique name for the connection - * @param string $dsn DSN string for this connection - * @param array $options Array of key => value options for adapter - * @param boolean $defaut Use this connection as the default? The first connection added is automatically set as the default, even if this flag is false. - * @return Spot_Adapter_Interface Spot adapter instance - * @throws Spot_Exception - */ - public function addConnection($name, $dsn, array $options = array(), $default = false) - { - // Connection name must be unique - if(isset($this->_connections[$name])) { - throw new Exception("Connection for '" . $name . "' already exists. Connection name must be unique."); - } - - $dsnp = \Spot\Adapter\AdapterAbstract::parseDSN($dsn); - $adapterClass = "\\Spot\\Adapter\\" . ucfirst($dsnp['adapter']); - $adapter = new $adapterClass($dsn, $options); - - // Set as default connection? - if(true === $default || null === $this->_defaultConnection) { - $this->_defaultConnection = $name; - } - - // Store connection and return adapter instance - $this->_connections[$name] = $adapter; - return $adapter; - } - - - /** - * Get connection by name - * - * @param string $name Unique name of the connection to be returned - * @return Spot_Adapter_Interface Spot adapter instance - * @throws Spot_Exception - */ - public function connection($name = null) - { - if(null === $name) { - return $this->defaultConnection(); - } - - // Connection name must be unique - if(!isset($this->_connections[$name])) { - return false; - } - - return $this->_connections[$name]; - } - - - /** - * Get default connection - * - * @return Spot_Adapter_Interface Spot adapter instance - * @throws Spot_Exception - */ - public function defaultConnection() - { - return $this->_connections[$this->_defaultConnection]; - } - - - /** - * Class loader - * - * @param string $className Name of class to load - */ - public static function loadClass($className) - { - $loaded = false; - - // Require Spot namespaced files by assumed folder structure (naming convention) - if(false !== strpos($className, "Spot\\")) { - $classFile = trim(str_replace("\\", "/", str_replace("_", "/", str_replace('Spot\\', '', $className))), '\\'); - $loaded = require_once(__DIR__ . "/" . $classFile . ".php"); - } - - return $loaded; - } + protected $_defaultConnection; + protected $_connections = array(); + + + /** + * Add database connection + * + * @param string $name Unique name for the connection + * @param string $dsn DSN string for this connection + * @param array $options Array of key => value options for adapter + * @param boolean $defaut Use this connection as the default? The first connection added is automatically set as the default, even if this flag is false. + * @return Spot_Adapter_Interface Spot adapter instance + * @throws Spot_Exception + */ + public function addConnection($name, $dsn, array $options = array(), $default = false) + { + // Connection name must be unique + if(isset($this->_connections[$name])) { + throw new Exception("Connection for '" . $name . "' already exists. Connection name must be unique."); + } + + $dsnp = \Spot\Adapter\AdapterAbstract::parseDSN($dsn); + $adapterClass = "\\Spot\\Adapter\\" . ucfirst($dsnp['adapter']); + $adapter = new $adapterClass($dsn, $options); + + // Set as default connection? + if(true === $default || null === $this->_defaultConnection) { + $this->_defaultConnection = $name; + } + + // Store connection and return adapter instance + $this->_connections[$name] = $adapter; + return $adapter; + } + + + /** + * Get connection by name + * + * @param string $name Unique name of the connection to be returned + * @return Spot_Adapter_Interface Spot adapter instance + * @throws Spot_Exception + */ + public function connection($name = null) + { + if(null === $name) { + return $this->defaultConnection(); + } + + // Connection name must be unique + if(!isset($this->_connections[$name])) { + return false; + } + + return $this->_connections[$name]; + } + + + /** + * Get default connection + * + * @return Spot_Adapter_Interface Spot adapter instance + * @throws Spot_Exception + */ + public function defaultConnection() + { + return $this->_connections[$this->_defaultConnection]; + } + + + /** + * Class loader + * + * @param string $className Name of class to load + */ + public static function loadClass($className) + { + $loaded = false; + + // Require Spot namespaced files by assumed folder structure (naming convention) + if(false !== strpos($className, "Spot\\")) { + $classFile = trim(str_replace("\\", "/", str_replace("_", "/", str_replace('Spot\\', '', $className))), '\\'); + $loaded = require_once(__DIR__ . "/" . $classFile . ".php"); + } + + return $loaded; + } + + + /** + * Default serialization behavior is to not attempt to serialize stored + * adapter connections at all (thanks @TheSavior re: Issue #7) + */ + public function serialize() + { + return serialize(array()); + } + + public function unserialize($serialized) + { + } } diff --git a/alloy/Plugin/Spot/lib/Spot/Entity.php b/alloy/Plugin/Spot/lib/Spot/Entity.php index 36c5322..1f39a39 100644 --- a/alloy/Plugin/Spot/lib/Spot/Entity.php +++ b/alloy/Plugin/Spot/lib/Spot/Entity.php @@ -17,6 +17,9 @@ abstract class Entity protected $_data = array(); protected $_dataModified = array(); + // Entity error messages (may be present after save attempt) + protected $_errors = array(); + /** * Constructor - allows setting of object properties with array on construct @@ -62,10 +65,10 @@ public static function datasource($ds = null) /** * Datasource options getter/setter */ - public static function datasourceOptions($ds = null) + public static function datasourceOptions($dsOpts = null) { - if(null !== $ds) { - static::$_datasourceOptions = $ds; + if(null !== $dsOpts) { + static::$_datasourceOptions = $dsOpts; return $this; } return static::$_datasourceOptions; @@ -156,6 +159,61 @@ public function toArray() { return $this->data(); } + + + /** + * Check if any errors exist + * + * @param string $field OPTIONAL field name + * @return boolean + */ + public function hasErrors($field = null) + { + if(null !== $field) { + return isset($this->_errors[$field]) ? count($this->_errors[$field]) > 0 : false; + } + return count($this->_errors) > 0; + } + + + /** + * Error message getter/setter + * + * @param $field string|array String return errors with field key, array sets errors + * @return self|array|boolean Setter return self, getter returns array or boolean if key given and not found + */ + public function errors($msgs = null) + { + // Return errors for given field + if(is_string($msgs)) { + return isset($this->_errors[$msgs]) ? $this->_errors[$msgs] : array(); + + // Set error messages from given array + } elseif(is_array($msgs)) { + $this->_errors = $msgs; + } + return $this->_errors; + } + + + /** + * Add an error to error messages array + * + * @param string $field Field name that error message relates to + * @param mixed $msg Error message text - String or array of messages + */ + public function error($field, $msg) + { + if(is_array($msg)) { + // Add array of error messages about field + foreach($msg as $msgx) { + $this->_errors[$field][] = $msgx; + } + } else { + // Add to error array + $this->_errors[$field][] = $msg; + } + } /** diff --git a/alloy/Plugin/Spot/lib/Spot/Mapper.php b/alloy/Plugin/Spot/lib/Spot/Mapper.php index 1ea7799..8cd3d4f 100644 --- a/alloy/Plugin/Spot/lib/Spot/Mapper.php +++ b/alloy/Plugin/Spot/lib/Spot/Mapper.php @@ -601,18 +601,14 @@ public function validate($entity) foreach($this->fields($entityName) as $field => $fieldAttrs) { if(isset($fieldAttrs['required']) && true === $fieldAttrs['required']) { // Required field - if(empty($entity->$field)) { - $this->error($field, "Required field '" . $field . "' was left blank"); + if($this->isEmpty($entity->$field)) { + $entity->error($field, "Required field '" . $field . "' was left blank"); } } } - // Check for errors - if($this->hasErrors()) { - return false; - } else { - return true; - } + // Return error result + return !$entity->hasErrors(); } @@ -624,18 +620,21 @@ public function validate($entity) */ public function isEmpty($value) { - return (empty($value) && 0 !== $value); + return empty($value) && !is_numeric($value); } /** * Check if any errors exist * + * @deprecated Please use Entity::hasErrors instead * @param string $field OPTIONAL field name * @return boolean */ public function hasErrors($field = null) { + trigger_error('Error checks at the Mapper level have been deprecated in favor of checking error at the Entity level. Please use Entity::hasErrors instead. Mapper error methods will be completely removed in v1.0.', E_DEPRECATED); + if(null !== $field) { return isset($this->_errors[$field]) ? count($this->_errors[$field]) : false; } @@ -646,13 +645,16 @@ public function hasErrors($field = null) /** * Get array of error messages * + * @deprecated Please use Entity::errors instead * @return array */ public function errors($msgs = null) { + trigger_error('Error checks at the Mapper level have been deprecated in favor of checking error at the Entity level. Please use Entity::errors instead. Mapper error methods will be completely removed in v1.0.', E_DEPRECATED); + // Return errors for given field if(is_string($msgs)) { - return isset($this->_errors[$field]) ? $this->_errors[$field] : array(); + return isset($this->_errors[$msgs]) ? $this->_errors[$msgs] : array(); // Set error messages from given array } elseif(is_array($msgs)) { @@ -672,6 +674,9 @@ public function errors($msgs = null) */ public function error($field, $msg) { + // Deprecation warning + trigger_error('Adding errors at the Mapper level have been deprecated in favor of adding errors at the Entity level. Please use Entity::error instead. Mapper error methods will be completely removed in v1.0.', E_DEPRECATED); + if(is_array($msg)) { // Add array of error messages about field foreach($msg as $msgx) { diff --git a/alloy/Plugin/Spot/lib/Spot/Relation/RelationAbstract.php b/alloy/Plugin/Spot/lib/Spot/Relation/RelationAbstract.php index a610ad9..a559053 100644 --- a/alloy/Plugin/Spot/lib/Spot/Relation/RelationAbstract.php +++ b/alloy/Plugin/Spot/lib/Spot/Relation/RelationAbstract.php @@ -38,7 +38,7 @@ public function __construct(\Spot\Mapper $mapper, \Spot\Entity $entity, array $r // Checks ... if(null === $this->_entityName) { - throw new \InvalidArgumentException("Relation description key 'entity' not set."); + throw new \InvalidArgumentException("Relation description key 'entity' must be set to an Entity class name."); } } @@ -119,8 +119,8 @@ public function relationOrder() public function __toString() { // Load related records for current row - $success = $this->findAllRelation(); - return ($success) ? "1" : "0"; + $res = $this->execute(); + return ($res) ? "1" : "0"; } diff --git a/alloy/Plugin/Spot/lib/Spot/tests/Test/CRUD.php b/alloy/Plugin/Spot/lib/Spot/tests/Test/CRUD.php index 0d9814d..029467e 100644 --- a/alloy/Plugin/Spot/lib/Spot/tests/Test/CRUD.php +++ b/alloy/Plugin/Spot/lib/Spot/tests/Test/CRUD.php @@ -77,6 +77,6 @@ public function testSampleNewsDelete() $post = $mapper->first('Entity_Post', array('title' => "Test Post Modified")); $result = $mapper->delete($post); - $this->assertTrue($result); + $this->assertTrue((boolean) $result); } } \ No newline at end of file diff --git a/alloy/Plugin/Spot/lib/Spot/tests/Test/Config.php b/alloy/Plugin/Spot/lib/Spot/tests/Test/Config.php index 204e030..7181f83 100644 --- a/alloy/Plugin/Spot/lib/Spot/tests/Test/Config.php +++ b/alloy/Plugin/Spot/lib/Spot/tests/Test/Config.php @@ -11,6 +11,22 @@ public function testAddConnectionWithDSNString() { $cfg = new \Spot\Config(); $adapter = $cfg->addConnection('test_mysql', 'mysql://test:password@localhost/test'); - $this->assertTrue($adapter instanceof \Spot\Adapter\Mysql); + $this->assertInstanceOf('\Spot\Adapter\Mysql', $adapter); } + + public function testConfigCanSerialize() + { + $cfg = new \Spot\Config(); + $adapter = $cfg->addConnection('test_mysql', 'mysql://test:password@localhost/test'); + + $this->assertInternalType('string', serialize($cfg)); + } + + public function testConfigCanUnserialize() + { + $cfg = new \Spot\Config(); + $adapter = $cfg->addConnection('test_mysql', 'mysql://test:password@localhost/test'); + + $this->assertInstanceOf('\Spot\Config', unserialize(serialize($cfg))); + } } \ No newline at end of file diff --git a/alloy/Plugin/Spot/lib/Spot/tests/Test/Entity.php b/alloy/Plugin/Spot/lib/Spot/tests/Test/Entity.php index 949ce95..0bedb91 100644 --- a/alloy/Plugin/Spot/lib/Spot/tests/Test/Entity.php +++ b/alloy/Plugin/Spot/lib/Spot/tests/Test/Entity.php @@ -54,4 +54,30 @@ public function testEntitySetDataConstruct() $this->assertEquals($testData, $data); } + + public function testEntityErrors() + { + $post = new Entity_Post(array( + 'title' => 'My Awesome Post', + 'body' => '

    Body

    ' + )); + $postErrors = array( + 'title' => array('Title cannot contain the word awesome') + ); + + // Has NO errors + $this->assertTrue(!$post->hasErrors()); + + // Set errors + $post->errors($postErrors); + + // Has errors + $this->assertTrue($post->hasErrors()); + + // Full error array + $this->assertEquals($postErrors, $post->errors()); + + // Errors for one key only + $this->assertEquals($postErrors['title'], $post->errors('title')); + } } \ No newline at end of file diff --git a/alloy/Plugin/Spot/lib/Spot/tests/Test/Relations.php b/alloy/Plugin/Spot/lib/Spot/tests/Test/Relations.php index 95824c7..6f23c2d 100644 --- a/alloy/Plugin/Spot/lib/Spot/tests/Test/Relations.php +++ b/alloy/Plugin/Spot/lib/Spot/tests/Test/Relations.php @@ -54,12 +54,12 @@ public function testBlogCommentsRelationInsertByObject($postId) 'name' => 'Testy McTester', 'email' => 'test@test.com', 'body' => 'This is a test comment. Yay!', - 'date_created' => $mapper->connection('Entity_Post_Comment')->dateTime() + 'date_created' => new \DateTime() )); try { $commentSaved = $mapper->save($comment); if(!$commentSaved) { - print_r($mapper->errors()); + print_r($comment->errors()); $this->fail("Comment NOT saved"); } } catch(Exception $e) { diff --git a/alloy/lib/Alloy/App.php b/alloy/lib/Alloy/App.php deleted file mode 100644 index 29195fe..0000000 --- a/alloy/lib/Alloy/App.php +++ /dev/null @@ -1,33 +0,0 @@ -header('USER_AGENT') == 'Shockwave Flash'); - } -} \ No newline at end of file diff --git a/alloy/lib/Alloy/Client.php b/alloy/lib/Alloy/Client.php deleted file mode 100644 index 6b3c07d..0000000 --- a/alloy/lib/Alloy/Client.php +++ /dev/null @@ -1,141 +0,0 @@ - value parameters to pass - */ - public function get($url, array $params = array()) - { - return $this->_fetch($url, $params, 'GET'); - } - - - /** - * POST - * - * @param string $url URL to perform action on - * @param optional array $params Array of key => value parameters to pass - */ - public function post($url, array $params = array()) - { - return $this->_fetch($url, $params, 'POST'); - } - - - /** - * PUT - * - * @param string $url URL to perform action on - * @param optional array $params Array of key => value parameters to pass - */ - public function put($url, array $params = array()) - { - return $this->_fetch($url, $params, 'PUT'); - } - - - /** - * DELETE - * - * @param string $url URL to perform action on - * @param optional array $params Array of key => value parameters to pass - */ - public function delete($url, array $params = array()) - { - return $this->_fetch($url, $params, 'DELETE'); - } - - - /** - * Fetch a URL with given parameters - */ - protected function _fetch($url, array $params = array(), $method = 'GET') - { - $method = strtoupper($method); - - $urlParts = parse_url($url); - $queryString = http_build_query($params); - - // Append params to URL as query string if not a POST - if(strtoupper($method) != 'POST') { - $url = $url . "?" . $queryString; - } - - //echo $url; - //var_dump("Fetching External URL: [" . $method . "] " . $url, $params); - - // Use cURL - if(function_exists('curl_init')) { - $ch = curl_init($urlParts['host']); - - // METHOD differences - switch($method) { - case 'GET': - curl_setopt($ch, CURLOPT_URL, $url . "?" . $queryString); - break; - case 'POST': - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $queryString); - break; - - case 'PUT': - curl_setopt($ch, CURLOPT_URL, $url); - $putData = file_put_contents("php://memory", $queryString); - curl_setopt($ch, CURLOPT_PUT, true); - curl_setopt($ch, CURLOPT_INFILE, $putData); - curl_setopt($ch, CURLOPT_INFILESIZE, strlen($queryString)); - break; - - case 'DELETE': - curl_setopt($ch, CURLOPT_URL, $url . "?" . $queryString); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); - break; - } - - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Return the data - curl_setopt($ch, CURLOPT_HEADER, false); // Get headers - - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_MAXREDIRS, 5); - - // HTTP digest authentication - if(isset($urlParts['user']) && isset($urlParts['pass'])) { - $authHeaders = array("Authorization: Basic ".base64_encode($urlParts['user'].':'.$urlParts['pass'])); - curl_setopt($ch, CURLOPT_HTTPHEADER, $authHeaders); - } - - $response = curl_exec($ch); - $responseInfo = curl_getinfo($ch); - curl_close($ch); - - // Use sockets... (eventually) - } else { - throw new Exception(__METHOD__ . " Requres the cURL library to work."); - } - - // Only return false on 404 or 500 errors for now - if($responseInfo['http_code'] == 404 || $responseInfo['http_code'] == 500) { - $response = false; - } - - return $response; - } -} \ No newline at end of file diff --git a/alloy/lib/Alloy/Kernel.php b/alloy/lib/Alloy/Kernel.php index ed1aa8d..add7f10 100644 --- a/alloy/lib/Alloy/Kernel.php +++ b/alloy/lib/Alloy/Kernel.php @@ -15,6 +15,8 @@ */ class Kernel { + const VERSION = '0.8.0'; + protected static $self; protected static $cfg = array(); @@ -63,10 +65,22 @@ protected function __construct(array $config = array()) // Save memory starting point static::$traceMemoryStart = memory_get_usage(); static::$traceTimeStart = microtime(true); + // Set last as current starting for good zero-base static::$traceMemoryLast = static::$traceMemoryStart; static::$traceTimeLast = static::$traceTimeStart; } + + + /** + * Return current Alloy version string + * + * @return string Current Alloy version in dot-notation + */ + public function version() + { + return static::VERSION; + } /** @@ -127,7 +141,7 @@ public function config($value = null, $default = false) */ public function factory($className, array $params = array()) { - $instanceHash = md5($className . var_export($params, true)); + $instanceHash = md5($className . serialize($params)); // Return already instantiated object instance if set if(isset($this->instances[$instanceHash])) { @@ -214,15 +228,6 @@ public function request() } - /** - * Get HTTP REST client - */ - public function client() - { - return $this->factory(__NAMESPACE__ . '\Client'); - } - - /** * Send HTTP response header * @@ -397,7 +402,7 @@ public function module($module, $init = true, $dispatchAction = null) * @throws \InvalidArgumentException When plugin is not found by name * @return object */ - public function plugin($plugin, $init = true) + public function plugin($plugin, array $pluginConfig = array(), $init = true) { // Module plugin // ex: 'Module\User' @@ -413,17 +418,42 @@ public function plugin($plugin, $init = true) } // Ensure class exists / can be loaded - if(!class_exists($sPluginClass, (boolean)$init)) { - if ($init) { - throw new \InvalidArgumentException("Unable to load plugin '" . $sPluginClass . "'. Remove from app config or ensure plugin files exist in 'app' or 'alloy' load paths."); + if(!class_exists($sPluginClass, (boolean) $init)) { + if($init) { + throw new \InvalidArgumentException("Unable to load plugin '" . $sPluginClass . "'. Remove from app configuration file or ensure plugin files exist in 'app' or 'alloy' load paths."); } return false; } // Instantiate module class - $sPluginObject = new $sPluginClass($this); - return $sPluginObject; + //$sPluginObject = new $sPluginClass($this); + return $this->factory($sPluginClass, array($this, $pluginConfig)); + } + + + /** + * Load plugins if provided + * + * @param array $plugins Array of plugin names to load or key => config array format + * Example: + * array('plugin1', 'plugin2', 'plugin3' => array('foo' => 'bar', 'bar' => 'baz'), 'plugin4') + */ + public function loadPlugins(array $plugins) + { + // Load plugins + if($plugins) { + foreach($plugins as $pluginName => $pluginConfig) { + // Config name supplied without config array - need to shift params + if(is_numeric($pluginName)) { + $pluginName = $pluginConfig; + $pluginConfig = array(); + } + + // Load plugin + $plugin = $this->plugin($pluginName, $pluginConfig); + } + } } @@ -547,7 +577,7 @@ public function url($params = array(), $routeName = null, $queryParams = array() } if(count($queryParams) > 0) { // Build query string from array $qsData - $queryString = http_build_query($queryParams, '', '&'); + $queryString = http_build_query($queryParams, '', '&'); } else { $queryString = false; } @@ -800,4 +830,13 @@ public function __call($method, $args) throw new \BadMethodCallException("Method '" . __CLASS__ . "::" . $method . "' not found or the command is not a valid callback type."); } } + + + /** + * Prevent PHP from trying to serialize cached object instances on Kernel + */ + public function __sleep() + { + return array(); + } } \ No newline at end of file diff --git a/alloy/lib/Alloy/PluginAbstract.php b/alloy/lib/Alloy/PluginAbstract.php new file mode 100644 index 0000000..810d089 --- /dev/null +++ b/alloy/lib/Alloy/PluginAbstract.php @@ -0,0 +1,37 @@ +kernel = $kernel; + $this->config = $config; + + // Initialize plugin code + $this->init(); + } + + + /** + * Initialization work for plugin + */ + abstract public function init(); +} diff --git a/alloy/lib/Alloy/Request.php b/alloy/lib/Alloy/Request.php index 5069000..afcce9f 100644 --- a/alloy/lib/Alloy/Request.php +++ b/alloy/lib/Alloy/Request.php @@ -397,6 +397,50 @@ public function method() return $sm; } + + + /** + * Request URI, as seen by PHP + * + * @return string request URI from $_SERVER superglobal + */ + public function uri() + { + return $this->server('REQUEST_URI'); + } + + + /** + * Request scheme (http, https, or cli) + * + * @return string 'http', 'https', or 'cli' + */ + public function scheme() + { + return ($this->isCli() ? 'cli' : ($this->isSecure() ? 'https' : 'http' )); + } + + + /** + * Request HTTP_HOST + * + * @return String request host from $_SERVER superglobal + */ + public function host() + { + return $this->server('HTTP_HOST'); + } + + + /** + * Request port + * + * @return integer request port + */ + public function port() + { + return $this->server('SERVER_PORT'); + } /** diff --git a/alloy/lib/Alloy/View/Generic/templates/datagrid.html.php b/alloy/lib/Alloy/View/Generic/templates/datagrid.html.php index b309dfe..bdafa53 100644 --- a/alloy/lib/Alloy/View/Generic/templates/datagrid.html.php +++ b/alloy/lib/Alloy/View/Generic/templates/datagrid.html.php @@ -3,7 +3,7 @@ $ci = 0; ?> - +
    $colOpts): $ci++; ?> diff --git a/alloy/lib/Alloy/View/Generic/templates/form.html.php b/alloy/lib/Alloy/View/Generic/templates/form.html.php index 49430ec..cf01289 100644 --- a/alloy/lib/Alloy/View/Generic/templates/form.html.php +++ b/alloy/lib/Alloy/View/Generic/templates/form.html.php @@ -21,14 +21,13 @@ $fieldData = (isset($fieldOpts['default']) && is_scalar($fieldOpts['default'])) ? $fieldOpts['default'] : null; } ?> -
    +
    +
    ' . $fieldOpts['before'] . '' : ''; - ?> - - ' . $fieldOpts['before'] . '' : ''; + // Adjust field depending on field type switch($fieldType) { case 'text': @@ -65,12 +64,12 @@ default: echo $form->input($fieldType, $fieldName, $fieldData); } - ?> - - ' . $fieldOpts['after'] . '' : ''; + echo isset($fieldOpts['after']) ? '' . $fieldOpts['after'] . '' : ''; + echo isset($fieldOpts['help']) ? '' . $fieldOpts['help'] . '' : ''; ?> +
    @@ -88,13 +87,13 @@ endif; endforeach; ?> - +
    submit()): ?> -
    - +
    +
    diff --git a/alloy/lib/Alloy/View/Generic/templates/treeview.html.php b/alloy/lib/Alloy/View/Generic/templates/treeview.html.php index 2e38e5d..a2c2df5 100644 --- a/alloy/lib/Alloy/View/Generic/templates/treeview.html.php +++ b/alloy/lib/Alloy/View/Generic/templates/treeview.html.php @@ -2,10 +2,10 @@ $currentLevel = $view::$_level; // Check if we're okay to display against min level set -$levelMinCheck = (!$levelMin || $currentLevel > $levelMin); +$levelMinCheck = (!$levelMin || $currentLevel >= $levelMin); // Check if we're okay to display against max level set -$levelMaxCheck = (!$levelMax || $currentLevel < $levelMax); +$levelMaxCheck = (!$levelMax || $currentLevel <= $levelMax); if($levelMaxCheck): @@ -38,8 +38,7 @@ // Ensure we can go to next level // Don't show children if current level is equal to max - // DO show full tree if max is set to '0' - if($levelMaxCheck && ($levelMax == 0 || $currentLevel != $levelMax)): + if($levelMaxCheck && $currentLevel != $levelMax): // Item children (hierarchy) if(isset($itemChildrenCallback)): $children = $itemChildrenCallback($item); diff --git a/alloy/lib/Alloy/View/Template.php b/alloy/lib/Alloy/View/Template.php index 4089980..66e07e7 100644 --- a/alloy/lib/Alloy/View/Template.php +++ b/alloy/lib/Alloy/View/Template.php @@ -120,13 +120,12 @@ public function block($name, $closure = null) * @return mixed value if the key is found * @return null if key is not found */ - public function get($var) + public function get($var, $default = null) { if(isset($this->_vars[$var])) { return $this->_vars[$var]; - } else { - return null; } + return $default; } diff --git a/alloy/lib/Zend/Console/Getopt.php b/alloy/lib/Zend/Console/Getopt.php new file mode 100644 index 0000000..d603566 --- /dev/null +++ b/alloy/lib/Zend/Console/Getopt.php @@ -0,0 +1,970 @@ + self::MODE_ZEND, + self::CONFIG_DASHDASH => true, + self::CONFIG_IGNORECASE => false, + self::CONFIG_PARSEALL => true, + ); + + /** + * Stores the command-line arguments for the calling applicaion. + * + * @var array + */ + protected $_argv = array(); + + /** + * Stores the name of the calling applicaion. + * + * @var string + */ + protected $_progname = ''; + + /** + * Stores the list of legal options for this application. + * + * @var array + */ + protected $_rules = array(); + + /** + * Stores alternate spellings of legal options. + * + * @var array + */ + protected $_ruleMap = array(); + + /** + * Stores options given by the user in the current invocation + * of the application, as well as parameters given in options. + * + * @var array + */ + protected $_options = array(); + + /** + * Stores the command-line arguments other than options. + * + * @var array + */ + protected $_remainingArgs = array(); + + /** + * State of the options: parsed or not yet parsed? + * + * @var boolean + */ + protected $_parsed = false; + + /** + * The constructor takes one to three parameters. + * + * The first parameter is $rules, which may be a string for + * gnu-style format, or a structured array for Zend-style format. + * + * The second parameter is $argv, and it is optional. If not + * specified, $argv is inferred from the global argv. + * + * The third parameter is an array of configuration parameters + * to control the behavior of this instance of Getopt; it is optional. + * + * @param array $rules + * @param array $argv + * @param array $getoptConfig + * @return void + */ + public function __construct($rules, $argv = null, $getoptConfig = array()) + { + if (!isset($_SERVER['argv'])) { + require_once 'Zend/Console/Getopt/Exception.php'; + if (ini_get('register_argc_argv') == false) { + throw new Zend_Console_Getopt_Exception( + "argv is not available, because ini option 'register_argc_argv' is set Off" + ); + } else { + throw new Zend_Console_Getopt_Exception( + '$_SERVER["argv"] is not set, but Zend_Console_Getopt cannot work without this information.' + ); + } + } + + $this->_progname = $_SERVER['argv'][0]; + $this->setOptions($getoptConfig); + $this->addRules($rules); + if (!is_array($argv)) { + $argv = array_slice($_SERVER['argv'], 1); + } + if (isset($argv)) { + $this->addArguments((array)$argv); + } + } + + /** + * Return the state of the option seen on the command line of the + * current application invocation. This function returns true, or the + * parameter to the option, if any. If the option was not given, + * this function returns null. + * + * The magic __get method works in the context of naming the option + * as a virtual member of this class. + * + * @param string $key + * @return string + */ + public function __get($key) + { + return $this->getOption($key); + } + + /** + * Test whether a given option has been seen. + * + * @param string $key + * @return boolean + */ + public function __isset($key) + { + $this->parse(); + if (isset($this->_ruleMap[$key])) { + $key = $this->_ruleMap[$key]; + return isset($this->_options[$key]); + } + return false; + } + + /** + * Set the value for a given option. + * + * @param string $key + * @param string $value + * @return void + */ + public function __set($key, $value) + { + $this->parse(); + if (isset($this->_ruleMap[$key])) { + $key = $this->_ruleMap[$key]; + $this->_options[$key] = $value; + } + } + + /** + * Return the current set of options and parameters seen as a string. + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Unset an option. + * + * @param string $key + * @return void + */ + public function __unset($key) + { + $this->parse(); + if (isset($this->_ruleMap[$key])) { + $key = $this->_ruleMap[$key]; + unset($this->_options[$key]); + } + } + + /** + * Define additional command-line arguments. + * These are appended to those defined when the constructor was called. + * + * @param array $argv + * @throws Zend_Console_Getopt_Exception When not given an array as parameter + * @return Zend_Console_Getopt Provides a fluent interface + */ + public function addArguments($argv) + { + if(!is_array($argv)) { + require_once 'Zend/Console/Getopt/Exception.php'; + throw new Zend_Console_Getopt_Exception( + "Parameter #1 to addArguments should be an array"); + } + $this->_argv = array_merge($this->_argv, $argv); + $this->_parsed = false; + return $this; + } + + /** + * Define full set of command-line arguments. + * These replace any currently defined. + * + * @param array $argv + * @throws Zend_Console_Getopt_Exception When not given an array as parameter + * @return Zend_Console_Getopt Provides a fluent interface + */ + public function setArguments($argv) + { + if(!is_array($argv)) { + require_once 'Zend/Console/Getopt/Exception.php'; + throw new Zend_Console_Getopt_Exception( + "Parameter #1 to setArguments should be an array"); + } + $this->_argv = $argv; + $this->_parsed = false; + return $this; + } + + /** + * Define multiple configuration options from an associative array. + * These are not program options, but properties to configure + * the behavior of Zend_Console_Getopt. + * + * @param array $getoptConfig + * @return Zend_Console_Getopt Provides a fluent interface + */ + public function setOptions($getoptConfig) + { + if (isset($getoptConfig)) { + foreach ($getoptConfig as $key => $value) { + $this->setOption($key, $value); + } + } + return $this; + } + + /** + * Define one configuration option as a key/value pair. + * These are not program options, but properties to configure + * the behavior of Zend_Console_Getopt. + * + * @param string $configKey + * @param string $configValue + * @return Zend_Console_Getopt Provides a fluent interface + */ + public function setOption($configKey, $configValue) + { + if ($configKey !== null) { + $this->_getoptConfig[$configKey] = $configValue; + } + return $this; + } + + /** + * Define additional option rules. + * These are appended to the rules defined when the constructor was called. + * + * @param array $rules + * @return Zend_Console_Getopt Provides a fluent interface + */ + public function addRules($rules) + { + $ruleMode = $this->_getoptConfig['ruleMode']; + switch ($this->_getoptConfig['ruleMode']) { + case self::MODE_ZEND: + if (is_array($rules)) { + $this->_addRulesModeZend($rules); + break; + } + // intentional fallthrough + case self::MODE_GNU: + $this->_addRulesModeGnu($rules); + break; + default: + /** + * Call addRulesModeFoo() for ruleMode 'foo'. + * The developer should subclass Getopt and + * provide this method. + */ + $method = '_addRulesMode' . ucfirst($ruleMode); + $this->$method($rules); + } + $this->_parsed = false; + return $this; + } + + /** + * Return the current set of options and parameters seen as a string. + * + * @return string + */ + public function toString() + { + $this->parse(); + $s = array(); + foreach ($this->_options as $flag => $value) { + $s[] = $flag . '=' . ($value === true ? 'true' : $value); + } + return implode(' ', $s); + } + + /** + * Return the current set of options and parameters seen + * as an array of canonical options and parameters. + * + * Clusters have been expanded, and option aliases + * have been mapped to their primary option names. + * + * @return array + */ + public function toArray() + { + $this->parse(); + $s = array(); + foreach ($this->_options as $flag => $value) { + $s[] = $flag; + if ($value !== true) { + $s[] = $value; + } + } + return $s; + } + + /** + * Return the current set of options and parameters seen in Json format. + * + * @return string + */ + public function toJson() + { + $this->parse(); + $j = array(); + foreach ($this->_options as $flag => $value) { + $j['options'][] = array( + 'option' => array( + 'flag' => $flag, + 'parameter' => $value + ) + ); + } + + /** + * @see Zend_Json + */ + require_once 'Zend/Json.php'; + $json = Zend_Json::encode($j); + + return $json; + } + + /** + * Return the current set of options and parameters seen in XML format. + * + * @return string + */ + public function toXml() + { + $this->parse(); + $doc = new DomDocument('1.0', 'utf-8'); + $optionsNode = $doc->createElement('options'); + $doc->appendChild($optionsNode); + foreach ($this->_options as $flag => $value) { + $optionNode = $doc->createElement('option'); + $optionNode->setAttribute('flag', utf8_encode($flag)); + if ($value !== true) { + $optionNode->setAttribute('parameter', utf8_encode($value)); + } + $optionsNode->appendChild($optionNode); + } + $xml = $doc->saveXML(); + return $xml; + } + + /** + * Return a list of options that have been seen in the current argv. + * + * @return array + */ + public function getOptions() + { + $this->parse(); + return array_keys($this->_options); + } + + /** + * Return the state of the option seen on the command line of the + * current application invocation. + * + * This function returns true, or the parameter value to the option, if any. + * If the option was not given, this function returns false. + * + * @param string $flag + * @return mixed + */ + public function getOption($flag) + { + $this->parse(); + if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) { + $flag = strtolower($flag); + } + if (isset($this->_ruleMap[$flag])) { + $flag = $this->_ruleMap[$flag]; + if (isset($this->_options[$flag])) { + return $this->_options[$flag]; + } + } + return null; + } + + /** + * Return the arguments from the command-line following all options found. + * + * @return array + */ + public function getRemainingArgs() + { + $this->parse(); + return $this->_remainingArgs; + } + + /** + * Return a useful option reference, formatted for display in an + * error message. + * + * Note that this usage information is provided in most Exceptions + * generated by this class. + * + * @return string + */ + public function getUsageMessage() + { + $usage = "Usage: {$this->_progname} [ options ]\n"; + $maxLen = 20; + $lines = array(); + foreach ($this->_rules as $rule) { + $flags = array(); + if (is_array($rule['alias'])) { + foreach ($rule['alias'] as $flag) { + $flags[] = (strlen($flag) == 1 ? '-' : '--') . $flag; + } + } + $linepart['name'] = implode('|', $flags); + if (isset($rule['param']) && $rule['param'] != 'none') { + $linepart['name'] .= ' '; + switch ($rule['param']) { + case 'optional': + $linepart['name'] .= "[ <{$rule['paramType']}> ]"; + break; + case 'required': + $linepart['name'] .= "<{$rule['paramType']}>"; + break; + } + } + if (strlen($linepart['name']) > $maxLen) { + $maxLen = strlen($linepart['name']); + } + $linepart['help'] = ''; + if (isset($rule['help'])) { + $linepart['help'] .= $rule['help']; + } + $lines[] = $linepart; + } + foreach ($lines as $linepart) { + $usage .= sprintf("%s %s\n", + str_pad($linepart['name'], $maxLen), + $linepart['help']); + } + return $usage; + } + + /** + * Define aliases for options. + * + * The parameter $aliasMap is an associative array + * mapping option name (short or long) to an alias. + * + * @param array $aliasMap + * @throws Zend_Console_Getopt_Exception + * @return Zend_Console_Getopt Provides a fluent interface + */ + public function setAliases($aliasMap) + { + foreach ($aliasMap as $flag => $alias) + { + if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) { + $flag = strtolower($flag); + $alias = strtolower($alias); + } + if (!isset($this->_ruleMap[$flag])) { + continue; + } + $flag = $this->_ruleMap[$flag]; + if (isset($this->_rules[$alias]) || isset($this->_ruleMap[$alias])) { + $o = (strlen($alias) == 1 ? '-' : '--') . $alias; + require_once 'Zend/Console/Getopt/Exception.php'; + throw new Zend_Console_Getopt_Exception( + "Option \"$o\" is being defined more than once."); + } + $this->_rules[$flag]['alias'][] = $alias; + $this->_ruleMap[$alias] = $flag; + } + return $this; + } + + /** + * Define help messages for options. + * + * The parameter $help_map is an associative array + * mapping option name (short or long) to the help string. + * + * @param array $helpMap + * @return Zend_Console_Getopt Provides a fluent interface + */ + public function setHelp($helpMap) + { + foreach ($helpMap as $flag => $help) + { + if (!isset($this->_ruleMap[$flag])) { + continue; + } + $flag = $this->_ruleMap[$flag]; + $this->_rules[$flag]['help'] = $help; + } + return $this; + } + + /** + * Parse command-line arguments and find both long and short + * options. + * + * Also find option parameters, and remaining arguments after + * all options have been parsed. + * + * @return Zend_Console_Getopt|null Provides a fluent interface + */ + public function parse() + { + if ($this->_parsed === true) { + return; + } + $argv = $this->_argv; + $this->_options = array(); + $this->_remainingArgs = array(); + while (count($argv) > 0) { + if ($argv[0] == '--') { + array_shift($argv); + if ($this->_getoptConfig[self::CONFIG_DASHDASH]) { + $this->_remainingArgs = array_merge($this->_remainingArgs, $argv); + break; + } + } + if (substr($argv[0], 0, 2) == '--') { + $this->_parseLongOption($argv); + } else if (substr($argv[0], 0, 1) == '-' && ('-' != $argv[0] || count($argv) >1)) { + $this->_parseShortOptionCluster($argv); + } else if($this->_getoptConfig[self::CONFIG_PARSEALL]) { + $this->_remainingArgs[] = array_shift($argv); + } else { + /* + * We should put all other arguments in _remainingArgs and stop parsing + * since CONFIG_PARSEALL is false. + */ + $this->_remainingArgs = array_merge($this->_remainingArgs, $argv); + break; + } + } + $this->_parsed = true; + return $this; + } + + /** + * Parse command-line arguments for a single long option. + * A long option is preceded by a double '--' character. + * Long options may not be clustered. + * + * @param mixed &$argv + * @return void + */ + protected function _parseLongOption(&$argv) + { + $optionWithParam = ltrim(array_shift($argv), '-'); + $l = explode('=', $optionWithParam, 2); + $flag = array_shift($l); + $param = array_shift($l); + if (isset($param)) { + array_unshift($argv, $param); + } + $this->_parseSingleOption($flag, $argv); + } + + /** + * Parse command-line arguments for short options. + * Short options are those preceded by a single '-' character. + * Short options may be clustered. + * + * @param mixed &$argv + * @return void + */ + protected function _parseShortOptionCluster(&$argv) + { + $flagCluster = ltrim(array_shift($argv), '-'); + foreach (str_split($flagCluster) as $flag) { + $this->_parseSingleOption($flag, $argv); + } + } + + /** + * Parse command-line arguments for a single option. + * + * @param string $flag + * @param mixed $argv + * @throws Zend_Console_Getopt_Exception + * @return void + */ + protected function _parseSingleOption($flag, &$argv) + { + if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) { + $flag = strtolower($flag); + } + if (!isset($this->_ruleMap[$flag])) { + require_once 'Zend/Console/Getopt/Exception.php'; + throw new Zend_Console_Getopt_Exception( + "Option \"$flag\" is not recognized.", + $this->getUsageMessage()); + } + $realFlag = $this->_ruleMap[$flag]; + switch ($this->_rules[$realFlag]['param']) { + case 'required': + if (count($argv) > 0) { + $param = array_shift($argv); + $this->_checkParameterType($realFlag, $param); + } else { + require_once 'Zend/Console/Getopt/Exception.php'; + throw new Zend_Console_Getopt_Exception( + "Option \"$flag\" requires a parameter.", + $this->getUsageMessage()); + } + break; + case 'optional': + if (count($argv) > 0 && substr($argv[0], 0, 1) != '-') { + $param = array_shift($argv); + $this->_checkParameterType($realFlag, $param); + } else { + $param = true; + } + break; + default: + $param = true; + } + $this->_options[$realFlag] = $param; + } + + /** + * Return true if the parameter is in a valid format for + * the option $flag. + * Throw an exception in most other cases. + * + * @param string $flag + * @param string $param + * @throws Zend_Console_Getopt_Exception + * @return bool + */ + protected function _checkParameterType($flag, $param) + { + $type = 'string'; + if (isset($this->_rules[$flag]['paramType'])) { + $type = $this->_rules[$flag]['paramType']; + } + switch ($type) { + case 'word': + if (preg_match('/\W/', $param)) { + require_once 'Zend/Console/Getopt/Exception.php'; + throw new Zend_Console_Getopt_Exception( + "Option \"$flag\" requires a single-word parameter, but was given \"$param\".", + $this->getUsageMessage()); + } + break; + case 'integer': + if (preg_match('/\D/', $param)) { + require_once 'Zend/Console/Getopt/Exception.php'; + throw new Zend_Console_Getopt_Exception( + "Option \"$flag\" requires an integer parameter, but was given \"$param\".", + $this->getUsageMessage()); + } + break; + case 'string': + default: + break; + } + return true; + } + + /** + * Define legal options using the gnu-style format. + * + * @param string $rules + * @return void + */ + protected function _addRulesModeGnu($rules) + { + $ruleArray = array(); + + /** + * Options may be single alphanumeric characters. + * Options may have a ':' which indicates a required string parameter. + * No long options or option aliases are supported in GNU style. + */ + preg_match_all('/([a-zA-Z0-9]:?)/', $rules, $ruleArray); + foreach ($ruleArray[1] as $rule) { + $r = array(); + $flag = substr($rule, 0, 1); + if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) { + $flag = strtolower($flag); + } + $r['alias'][] = $flag; + if (substr($rule, 1, 1) == ':') { + $r['param'] = 'required'; + $r['paramType'] = 'string'; + } else { + $r['param'] = 'none'; + } + $this->_rules[$flag] = $r; + $this->_ruleMap[$flag] = $flag; + } + } + + /** + * Define legal options using the Zend-style format. + * + * @param array $rules + * @throws Zend_Console_Getopt_Exception + * @return void + */ + protected function _addRulesModeZend($rules) + { + foreach ($rules as $ruleCode => $helpMessage) + { + // this may have to translate the long parm type if there + // are any complaints that =string will not work (even though that use + // case is not documented) + if (in_array(substr($ruleCode, -2, 1), array('-', '='))) { + $flagList = substr($ruleCode, 0, -2); + $delimiter = substr($ruleCode, -2, 1); + $paramType = substr($ruleCode, -1); + } else { + $flagList = $ruleCode; + $delimiter = $paramType = null; + } + if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) { + $flagList = strtolower($flagList); + } + $flags = explode('|', $flagList); + $rule = array(); + $mainFlag = $flags[0]; + foreach ($flags as $flag) { + if (empty($flag)) { + require_once 'Zend/Console/Getopt/Exception.php'; + throw new Zend_Console_Getopt_Exception( + "Blank flag not allowed in rule \"$ruleCode\"."); + } + if (strlen($flag) == 1) { + if (isset($this->_ruleMap[$flag])) { + require_once 'Zend/Console/Getopt/Exception.php'; + throw new Zend_Console_Getopt_Exception( + "Option \"-$flag\" is being defined more than once."); + } + $this->_ruleMap[$flag] = $mainFlag; + $rule['alias'][] = $flag; + } else { + if (isset($this->_rules[$flag]) || isset($this->_ruleMap[$flag])) { + require_once 'Zend/Console/Getopt/Exception.php'; + throw new Zend_Console_Getopt_Exception( + "Option \"--$flag\" is being defined more than once."); + } + $this->_ruleMap[$flag] = $mainFlag; + $rule['alias'][] = $flag; + } + } + if (isset($delimiter)) { + switch ($delimiter) { + case self::PARAM_REQUIRED: + $rule['param'] = 'required'; + break; + case self::PARAM_OPTIONAL: + default: + $rule['param'] = 'optional'; + } + switch (substr($paramType, 0, 1)) { + case self::TYPE_WORD: + $rule['paramType'] = 'word'; + break; + case self::TYPE_INTEGER: + $rule['paramType'] = 'integer'; + break; + case self::TYPE_STRING: + default: + $rule['paramType'] = 'string'; + } + } else { + $rule['param'] = 'none'; + } + $rule['help'] = $helpMessage; + $this->_rules[$mainFlag] = $rule; + } + } + +} diff --git a/alloy/lib/Zend/Console/Getopt/Exception.php b/alloy/lib/Zend/Console/Getopt/Exception.php new file mode 100644 index 0000000..cabb406 --- /dev/null +++ b/alloy/lib/Zend/Console/Getopt/Exception.php @@ -0,0 +1,66 @@ +usage = $usage; + parent::__construct($message); + } + + /** + * Returns the usage + * + * @return string + */ + public function getUsageMessage() + { + return $this->usage; + } +} diff --git a/alloy/lib/Zend/Exception.php b/alloy/lib/Zend/Exception.php new file mode 100644 index 0000000..e838f51 --- /dev/null +++ b/alloy/lib/Zend/Exception.php @@ -0,0 +1,96 @@ +_previous = $previous; + } else { + parent::__construct($msg, (int) $code, $previous); + } + } + + /** + * Overloading + * + * For PHP < 5.3.0, provides access to the getPrevious() method. + * + * @param string $method + * @param array $args + * @return mixed + */ + public function __call($method, array $args) + { + if ('getprevious' == strtolower($method)) { + return $this->_getPrevious(); + } + return null; + } + + /** + * String representation of the exception + * + * @return string + */ + public function __toString() + { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + if (null !== ($e = $this->getPrevious())) { + return $e->__toString() + . "\n\nNext " + . parent::__toString(); + } + } + return parent::__toString(); + } + + /** + * Returns previous Exception + * + * @return Exception|null + */ + protected function _getPrevious() + { + return $this->_previous; + } +} diff --git a/alloy/tests/AllTests.php b/alloy/tests/AllTests.php index b56acda..46490b4 100644 --- a/alloy/tests/AllTests.php +++ b/alloy/tests/AllTests.php @@ -34,4 +34,4 @@ public static function suite() } return $suite; } -} +} \ No newline at end of file diff --git a/app/www/index.php b/app/www/index.php index f73c83f..f6bf630 100644 --- a/app/www/index.php +++ b/app/www/index.php @@ -22,9 +22,7 @@ throw new \InvalidArgumentException("Plugin configuration from app config must be an array. Given (" . gettype($plugins) . ")."); } - foreach($plugins as $pluginName) { - $plugin = $kernel->plugin($pluginName); - } + $kernel->loadPlugins($plugins); } $kernel->events()->trigger('boot_start'); From 0f98d07049a38b175378c887ea3f771686b4339b Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 13:03:55 -0600 Subject: [PATCH 38/50] Added PageCache plugin (disabled by default) * Caches full page output to Memcache after render * Dumps cache on authenticated non-GET or HEAD request --- app/Plugin/PageCache/Plugin.php | 64 ++++++ .../lib/Doctrine/Common/Cache/ApcCache.php | 97 +++++++++ .../lib/Doctrine/Common/Cache/ArrayCache.php | 96 +++++++++ .../lib/Doctrine/Common/Cache/Cache.php | 102 ++++++++++ .../Doctrine/Common/Cache/CacheProvider.php | 188 ++++++++++++++++++ .../Doctrine/Common/Cache/MemcacheCache.php | 118 +++++++++++ .../Doctrine/Common/Cache/MemcachedCache.php | 121 +++++++++++ .../Doctrine/Common/Cache/WinCacheCache.php | 92 +++++++++ .../lib/Doctrine/Common/Cache/XcacheCache.php | 110 ++++++++++ .../Doctrine/Common/Cache/ZendDataCache.php | 84 ++++++++ app/Plugin/Stackbox/Plugin.php | 2 + 11 files changed, 1074 insertions(+) create mode 100644 app/Plugin/PageCache/Plugin.php create mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/ApcCache.php create mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/ArrayCache.php create mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/Cache.php create mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/CacheProvider.php create mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcacheCache.php create mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcachedCache.php create mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/WinCacheCache.php create mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/XcacheCache.php create mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/ZendDataCache.php diff --git a/app/Plugin/PageCache/Plugin.php b/app/Plugin/PageCache/Plugin.php new file mode 100644 index 0000000..5d0bd94 --- /dev/null +++ b/app/Plugin/PageCache/Plugin.php @@ -0,0 +1,64 @@ +kernel->loader() + ->registerNamespace('Doctrine\Common\Cache', __DIR__ . '/lib/Doctrine/Common/Cache'); + + // Load Memcache connection + $memcache = new \Memcache(); + $memcache->connect($this->config['host'], $this->config['port']); + + $cache = new \Doctrine\Common\Cache\MemcacheCache(); + $cache->setMemcache($memcache); + + // Save cache instance on object + $this->cache = $cache; + + // Layout / API output + $this->kernel->events()->bind('response_sent', 'pagecache_cache', array($this, 'cacheOutput')); + } + + + /** + * Set cache key with full page output + */ + protected function cacheOutput($output) + { + $user = $this->kernel->user(); + $request = $this->kernel->request(); + + // Only cache if user is not logged in, and request is GET + if(!$user->isLoggedIn() && $request->isGet()) { + // Build key with current request info + // Key Format: '$scheme$host$request_method$request_uri' + $cacheKey = $request->scheme() + . $request->host() + . $request->method() + . $request->uri(); + + // Cache output content + $this->cache->set($cacheKey, (string) $response->content()); + } + } +} + +// Ensure Memcache extension is loaded +if(!class_exists('Memcache', false)) { + throw new \Exception("The 'memcache' extension must be installed and enabled to using " . __NAMESPACE__ . "."); +} diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ApcCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ApcCache.php new file mode 100755 index 0000000..a59296f --- /dev/null +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ApcCache.php @@ -0,0 +1,97 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * APC cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class ApcCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return apc_fetch($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $found = false; + + apc_fetch($id, $found); + + return $found; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return (bool) apc_store($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return apc_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return apc_clear_cache() && apc_clear_cache('user'); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = apc_cache_info(); + $sma = apc_sma_info(); + + return array( + Cache::STATS_HITS => $info['num_hits'], + Cache::STATS_MISSES => $info['num_misses'], + Cache::STATS_UPTIME => $info['start_time'], + Cache::STATS_MEMORY_USAGE => $info['mem_size'], + Cache::STATS_MEMORY_AVAILIABLE => $sma['avail_mem'], + ); + } +} \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ArrayCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ArrayCache.php new file mode 100755 index 0000000..8a0b982 --- /dev/null +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ArrayCache.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Array cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class ArrayCache extends CacheProvider +{ + /** + * @var array $data + */ + private $data = array(); + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return (isset($this->data[$id])) ? $this->data[$id] : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return isset($this->data[$id]); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $this->data[$id] = $data; + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + unset($this->data[$id]); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->data = array(); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return null; + } +} \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/Cache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/Cache.php new file mode 100755 index 0000000..d303bde --- /dev/null +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/Cache.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache drivers. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Fabio B. Silva + */ +interface Cache +{ + const STATS_HITS = 'hits'; + const STATS_MISSES = 'misses'; + const STATS_UPTIME = 'uptime'; + const STATS_MEMORY_USAGE = 'memory_usage'; + const STATS_MEMORY_AVAILIABLE = 'memory_available'; + + /** + * Fetches an entry from the cache. + * + * @param string $id cache id The id of the cache entry to fetch. + * @return string The cached data or FALSE, if no cache entry exists for the given id. + */ + function fetch($id); + + /** + * Test if an entry exists in the cache. + * + * @param string $id cache id The cache id of the entry to check for. + * @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + function contains($id); + + /** + * Puts data into the cache. + * + * @param string $id The cache id. + * @param string $data The cache entry/data. + * @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime). + * @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + function save($id, $data, $lifeTime = 0); + + /** + * Deletes a cache entry. + * + * @param string $id cache id + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + function delete($id); + + /** + * Retrieves cached information from data store + * + * The server's statistics array has the following values: + * + * - hits + * Number of keys that have been requested and found present. + * + * - misses + * Number of items that have been requested and not found. + * + * - uptime + * Time that the server is running. + * + * - memory_usage + * Memory used by this server to store items. + * + * - memory_available + * Memory allowed to use for storage. + * + * @since 2.2 + * @var array Associative array with server's statistics if available, NULL otherwise. + */ + function getStats(); +} diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/CacheProvider.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/CacheProvider.php new file mode 100755 index 0000000..fa11fc2 --- /dev/null +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/CacheProvider.php @@ -0,0 +1,188 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Base class for cache provider implementations. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Fabio B. Silva + */ +abstract class CacheProvider implements Cache +{ + const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]'; + + /** + * @var string The namespace to prefix all cache ids with + */ + private $namespace = ''; + + /** + * Set the namespace to prefix all cache ids with. + * + * @param string $namespace + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = (string) $namespace; + } + + /** + * Retrieve the namespace that prefixes all cache ids. + * + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * {@inheritdoc} + */ + public function fetch($id) + { + return $this->doFetch($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function contains($id) + { + return $this->doContains($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function save($id, $data, $lifeTime = 0) + { + return $this->doSave($this->getNamespacedId($id), $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + public function delete($id) + { + return $this->doDelete($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function getStats() + { + return $this->doGetStats(); + } + + /** + * Deletes all cache entries. + * + * @return boolean TRUE if the cache entries were successfully flushed, FALSE otherwise. + */ + public function flushAll() + { + return $this->doFlush(); + } + + /** + * Delete all cache entries. + * + * @return boolean TRUE if the cache entries were successfully deleted, FALSE otherwise. + */ + public function deleteAll() + { + $namespaceCacheKey = sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); + $namespaceVersion = ($this->doContains($namespaceCacheKey)) ? $this->doFetch($namespaceCacheKey) : 1; + + return $this->doSave($namespaceCacheKey, $namespaceVersion + 1); + } + + /** + * Prefix the passed id with the configured namespace value + * + * @param string $id The id to namespace + * @return string $id The namespaced id + */ + private function getNamespacedId($id) + { + $namespaceCacheKey = sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); + $namespaceVersion = ($this->doContains($namespaceCacheKey)) ? $this->doFetch($namespaceCacheKey) : 1; + + return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); + } + + /** + * Fetches an entry from the cache. + * + * @param string $id cache id The id of the cache entry to fetch. + * @return string The cached data or FALSE, if no cache entry exists for the given id. + */ + abstract protected function doFetch($id); + + /** + * Test if an entry exists in the cache. + * + * @param string $id cache id The cache id of the entry to check for. + * @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + abstract protected function doContains($id); + + /** + * Puts data into the cache. + * + * @param string $id The cache id. + * @param string $data The cache entry/data. + * @param int $lifeTime The lifetime. If != false, sets a specific lifetime for this cache entry (null => infinite lifeTime). + * @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + abstract protected function doSave($id, $data, $lifeTime = false); + + /** + * Deletes a cache entry. + * + * @param string $id cache id + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doDelete($id); + + /** + * Deletes all cache entries. + * + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doFlush(); + + /** + * Retrieves cached information from data store + * + * @since 2.2 + * @return array An associative array with server's statistics if available, NULL otherwise. + */ + abstract protected function doGetStats(); +} diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcacheCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcacheCache.php new file mode 100755 index 0000000..aacbfd3 --- /dev/null +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcacheCache.php @@ -0,0 +1,118 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcache; + +/** + * Memcache cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class MemcacheCache extends CacheProvider +{ + /** + * @var Memcache + */ + private $memcache; + + /** + * Sets the memcache instance to use. + * + * @param Memcache $memcache + */ + public function setMemcache(Memcache $memcache) + { + $this->memcache = $memcache; + } + + /** + * Gets the memcache instance used by the cache. + * + * @return Memcache + */ + public function getMemcache() + { + return $this->memcache; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcache->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (bool) $this->memcache->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return $this->memcache->set($id, $data, 0, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->memcache->delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->memcache->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->memcache->getStats(); + return array( + Cache::STATS_HITS => $stats['get_hits'], + Cache::STATS_MISSES => $stats['get_misses'], + Cache::STATS_UPTIME => $stats['uptime'], + Cache::STATS_MEMORY_USAGE => $stats['bytes'], + Cache::STATS_MEMORY_AVAILIABLE => $stats['limit_maxbytes'], + ); + } +} \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcachedCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcachedCache.php new file mode 100755 index 0000000..3f7f8e8 --- /dev/null +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcachedCache.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcached; + +/** + * Memcached cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class MemcachedCache extends CacheProvider +{ + /** + * @var Memcached + */ + private $memcached; + + /** + * Sets the memcache instance to use. + * + * @param Memcached $memcached + */ + public function setMemcached(Memcached $memcached) + { + $this->memcached = $memcached; + } + + /** + * Gets the memcached instance used by the cache. + * + * @return Memcached + */ + public function getMemcached() + { + return $this->memcached; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcached->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (false !== $this->memcached->get($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return $this->memcached->set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->memcached->delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->memcached->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->memcached->getStats(); + $servers = $this->memcached->getServerList(); + $key = $servers[0]['host'] . ':' . $servers[0]['port']; + $stats = $stats[$key]; + return array( + Cache::STATS_HITS => $stats['get_hits'], + Cache::STATS_MISSES => $stats['get_misses'], + Cache::STATS_UPTIME => $stats['uptime'], + Cache::STATS_MEMORY_USAGE => $stats['bytes'], + Cache::STATS_MEMORY_AVAILIABLE => $stats['limit_maxbytes'], + ); + } +} \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/WinCacheCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/WinCacheCache.php new file mode 100755 index 0000000..ed8ca74 --- /dev/null +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/WinCacheCache.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * WinCache cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class WincacheCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return wincache_ucache_get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return wincache_ucache_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return (bool) wincache_ucache_set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return wincache_ucache_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return wincache_ucache_clear(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = wincache_ucache_info(); + $meminfo= wincache_ucache_meminfo(); + return array( + Cache::STATS_HITS => $info['total_hit_count'], + Cache::STATS_MISSES => $info['total_miss_count'], + Cache::STATS_UPTIME => $info['total_cache_uptime'], + Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'], + Cache::STATS_MEMORY_AVAILIABLE => $meminfo['memory_free'], + ); + } +} \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/XcacheCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/XcacheCache.php new file mode 100755 index 0000000..6e22d26 --- /dev/null +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/XcacheCache.php @@ -0,0 +1,110 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Xcache cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class XcacheCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->doContains($id) ? unserialize(xcache_get($id)) : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return xcache_isset($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return xcache_set($id, serialize($data), (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return xcache_unset($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->checkAuthorization(); + + xcache_clear_cache(XC_TYPE_VAR, 0); + + return true; + } + + /** + * Checks that xcache.admin.enable_auth is Off + * + * @throws \BadMethodCallException When xcache.admin.enable_auth is On + * @return void + */ + protected function checkAuthorization() + { + if (ini_get('xcache.admin.enable_auth')) { + throw new \BadMethodCallException('To use all features of \Doctrine\Common\Cache\XcacheCache, you must set "xcache.admin.enable_auth" to "Off" in your php.ini.'); + } + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $this->checkAuthorization(); + + $info = xcache_info(XC_TYPE_VAR, 0); + return array( + Cache::STATS_HITS => $info['hits'], + Cache::STATS_MISSES => $info['misses'], + Cache::STATS_UPTIME => null, + Cache::STATS_MEMORY_USAGE => $info['size'], + Cache::STATS_MEMORY_AVAILIABLE => $info['avail'], + ); + } +} \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ZendDataCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ZendDataCache.php new file mode 100755 index 0000000..4e4dabe --- /dev/null +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ZendDataCache.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Zend Data Cache cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Ralph Schindler + * @author Guilherme Blanco + */ +class ZendDataCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return zend_shm_cache_fetch($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (false !== zend_shm_cache_fetch($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return zend_shm_cache_store($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return zend_shm_cache_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $namespace = $this->getNamespace(); + if (empty($namespace)) { + return zend_shm_cache_clear(); + } + return zend_shm_cache_clear($namespace); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return null; + } +} diff --git a/app/Plugin/Stackbox/Plugin.php b/app/Plugin/Stackbox/Plugin.php index 78f2374..21ac76e 100644 --- a/app/Plugin/Stackbox/Plugin.php +++ b/app/Plugin/Stackbox/Plugin.php @@ -160,6 +160,8 @@ public function layoutOrApiOutput($content) $response = $kernel->response(); $response->contentType('text/html'); + + // Default cache settings for frontend/proxy caches like nginx and Varnish $response->header("Expires", gmdate("D, d M Y H:i:s", strtotime('+2 hours')) . " GMT"); $response->header("Last-Modified", gmdate( "D, d M Y H:i:s" ) . " GMT"); $response->header("Cache-Control", "max-age=7200, must-revalidate"); From 78aa3e9abcaf30590678a3a2b3c329ae681dd669 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 14:12:43 -0600 Subject: [PATCH 39/50] PageCache PLugin * Updated to not namespace by site * Purges cache by prefixed scheme and host now --- app/Plugin/PageCache/Plugin.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/Plugin/PageCache/Plugin.php b/app/Plugin/PageCache/Plugin.php index 5d0bd94..adb39f6 100644 --- a/app/Plugin/PageCache/Plugin.php +++ b/app/Plugin/PageCache/Plugin.php @@ -17,16 +17,19 @@ class Plugin extends PluginAbstract public function init() { // Add Doctrine\Common\Cache to load path - $this->kernel->loader() - ->registerNamespace('Doctrine\Common\Cache', __DIR__ . '/lib/Doctrine/Common/Cache'); + $this->kernel->loader()->registerNamespace('Doctrine', __DIR__ . '/lib'); // Load Memcache connection $memcache = new \Memcache(); $memcache->connect($this->config['host'], $this->config['port']); + // Initiate cache $cache = new \Doctrine\Common\Cache\MemcacheCache(); $cache->setMemcache($memcache); + // Required for advanced deletion options like regex, pattern, and prefix + $cache->setManageCacheIds(true); + // Save cache instance on object $this->cache = $cache; @@ -38,7 +41,7 @@ public function init() /** * Set cache key with full page output */ - protected function cacheOutput($output) + public function cacheOutput($output) { $user = $this->kernel->user(); $request = $this->kernel->request(); @@ -55,6 +58,14 @@ protected function cacheOutput($output) // Cache output content $this->cache->set($cacheKey, (string) $response->content()); } + + // Delete all cache entries on request when user logged in and it is NOT a GET or HEAD + // Really overkill, but it's the only safe way to not run into cache invalidation issues + if($user->isLoggedIn() && (!$request->isGet() || !$request->isHead())) { + // Delete all cache entries prefixed with current site + $sitePrefix = $request->scheme() . $request->host(); + $this->cache->deleteByPrefix($sitePrefix); + } } } From 3afe89c376651434a20f64a932b73927facbfe1a Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 14:46:26 -0600 Subject: [PATCH 40/50] Switched version of Doctrine\Common\Cache to 2.0.x --- .../{CacheProvider.php => AbstractCache.php} | 156 +++++++++++------- .../lib/Doctrine/Common/Cache/ApcCache.php | 57 +++---- .../lib/Doctrine/Common/Cache/ArrayCache.php | 39 ++--- .../lib/Doctrine/Common/Cache/Cache.php | 39 +---- .../Doctrine/Common/Cache/MemcacheCache.php | 65 ++++---- .../Doctrine/Common/Cache/MemcachedCache.php | 121 -------------- .../Doctrine/Common/Cache/WinCacheCache.php | 92 ----------- .../lib/Doctrine/Common/Cache/XcacheCache.php | 63 ++++--- .../Doctrine/Common/Cache/ZendDataCache.php | 84 ---------- 9 files changed, 207 insertions(+), 509 deletions(-) rename app/Plugin/PageCache/lib/Doctrine/Common/Cache/{CacheProvider.php => AbstractCache.php} (52%) delete mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcachedCache.php delete mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/WinCacheCache.php delete mode 100755 app/Plugin/PageCache/lib/Doctrine/Common/Cache/ZendDataCache.php diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/CacheProvider.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/AbstractCache.php similarity index 52% rename from app/Plugin/PageCache/lib/Doctrine/Common/Cache/CacheProvider.php rename to app/Plugin/PageCache/lib/Doctrine/Common/Cache/AbstractCache.php index fa11fc2..9bba8f6 100755 --- a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/CacheProvider.php +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/AbstractCache.php @@ -1,5 +1,4 @@ * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel - * @author Fabio B. Silva */ -abstract class CacheProvider implements Cache +abstract class AbstractCache implements Cache { - const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]'; + /** @var string The cache id to store the index of cache ids under */ + private $_cacheIdsIndexId = 'doctrine_cache_ids'; - /** - * @var string The namespace to prefix all cache ids with - */ - private $namespace = ''; + /** @var string The namespace to prefix all cache ids with */ + private $_namespace = null; /** * Set the namespace to prefix all cache ids with. @@ -47,17 +44,7 @@ abstract class CacheProvider implements Cache */ public function setNamespace($namespace) { - $this->namespace = (string) $namespace; - } - - /** - * Retrieve the namespace that prefixes all cache ids. - * - * @return string - */ - public function getNamespace() - { - return $this->namespace; + $this->_namespace = $namespace; } /** @@ -65,7 +52,7 @@ public function getNamespace() */ public function fetch($id) { - return $this->doFetch($this->getNamespacedId($id)); + return $this->_doFetch($this->_getNamespacedId($id)); } /** @@ -73,7 +60,7 @@ public function fetch($id) */ public function contains($id) { - return $this->doContains($this->getNamespacedId($id)); + return $this->_doContains($this->_getNamespacedId($id)); } /** @@ -81,7 +68,7 @@ public function contains($id) */ public function save($id, $data, $lifeTime = 0) { - return $this->doSave($this->getNamespacedId($id), $data, $lifeTime); + return $this->_doSave($this->_getNamespacedId($id), $data, $lifeTime); } /** @@ -89,38 +76,96 @@ public function save($id, $data, $lifeTime = 0) */ public function delete($id) { - return $this->doDelete($this->getNamespacedId($id)); + $id = $this->_getNamespacedId($id); + + if (strpos($id, '*') !== false) { + return $this->deleteByRegex('/' . str_replace('*', '.*', $id) . '/'); + } + + return $this->_doDelete($id); } /** - * {@inheritdoc} + * Delete all cache entries. + * + * @return array $deleted Array of the deleted cache ids + */ + public function deleteAll() + { + $ids = $this->getIds(); + + foreach ($ids as $id) { + $this->delete($id); + } + + return $ids; + } + + /** + * Delete cache entries where the id matches a PHP regular expressions + * + * @param string $regex + * @return array $deleted Array of the deleted cache ids */ - public function getStats() + public function deleteByRegex($regex) { - return $this->doGetStats(); + $deleted = array(); + + $ids = $this->getIds(); + + foreach ($ids as $id) { + if (preg_match($regex, $id)) { + $this->delete($id); + $deleted[] = $id; + } + } + + return $deleted; } /** - * Deletes all cache entries. + * Delete cache entries where the id has the passed prefix * - * @return boolean TRUE if the cache entries were successfully flushed, FALSE otherwise. + * @param string $prefix + * @return array $deleted Array of the deleted cache ids */ - public function flushAll() + public function deleteByPrefix($prefix) { - return $this->doFlush(); + $deleted = array(); + + $prefix = $this->_getNamespacedId($prefix); + $ids = $this->getIds(); + + foreach ($ids as $id) { + if (strpos($id, $prefix) === 0) { + $this->delete($id); + $deleted[] = $id; + } + } + + return $deleted; } /** - * Delete all cache entries. + * Delete cache entries where the id has the passed suffix * - * @return boolean TRUE if the cache entries were successfully deleted, FALSE otherwise. + * @param string $suffix + * @return array $deleted Array of the deleted cache ids */ - public function deleteAll() + public function deleteBySuffix($suffix) { - $namespaceCacheKey = sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); - $namespaceVersion = ($this->doContains($namespaceCacheKey)) ? $this->doFetch($namespaceCacheKey) : 1; + $deleted = array(); + + $ids = $this->getIds(); - return $this->doSave($namespaceCacheKey, $namespaceVersion + 1); + foreach ($ids as $id) { + if (substr($id, -1 * strlen($suffix)) === $suffix) { + $this->delete($id); + $deleted[] = $id; + } + } + + return $deleted; } /** @@ -129,12 +174,13 @@ public function deleteAll() * @param string $id The id to namespace * @return string $id The namespaced id */ - private function getNamespacedId($id) + private function _getNamespacedId($id) { - $namespaceCacheKey = sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); - $namespaceVersion = ($this->doContains($namespaceCacheKey)) ? $this->doFetch($namespaceCacheKey) : 1; - - return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); + if ( ! $this->_namespace || strpos($id, $this->_namespace) === 0) { + return $id; + } else { + return $this->_namespace . $id; + } } /** @@ -143,7 +189,7 @@ private function getNamespacedId($id) * @param string $id cache id The id of the cache entry to fetch. * @return string The cached data or FALSE, if no cache entry exists for the given id. */ - abstract protected function doFetch($id); + abstract protected function _doFetch($id); /** * Test if an entry exists in the cache. @@ -151,7 +197,7 @@ abstract protected function doFetch($id); * @param string $id cache id The cache id of the entry to check for. * @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. */ - abstract protected function doContains($id); + abstract protected function _doContains($id); /** * Puts data into the cache. @@ -161,7 +207,7 @@ abstract protected function doContains($id); * @param int $lifeTime The lifetime. If != false, sets a specific lifetime for this cache entry (null => infinite lifeTime). * @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. */ - abstract protected function doSave($id, $data, $lifeTime = false); + abstract protected function _doSave($id, $data, $lifeTime = false); /** * Deletes a cache entry. @@ -169,20 +215,12 @@ abstract protected function doSave($id, $data, $lifeTime = false); * @param string $id cache id * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. */ - abstract protected function doDelete($id); + abstract protected function _doDelete($id); /** - * Deletes all cache entries. - * - * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. - */ - abstract protected function doFlush(); - - /** - * Retrieves cached information from data store + * Get an array of all the cache ids stored * - * @since 2.2 - * @return array An associative array with server's statistics if available, NULL otherwise. + * @return array $ids */ - abstract protected function doGetStats(); -} + abstract public function getIds(); +} \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ApcCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ApcCache.php index a59296f..f3a9a95 100755 --- a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ApcCache.php +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ApcCache.php @@ -1,6 +1,7 @@ * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel * @author David Abdemoulaie + * @todo Rename: APCCache */ -class ApcCache extends CacheProvider +class ApcCache extends AbstractCache { /** * {@inheritdoc} */ - protected function doFetch($id) - { - return apc_fetch($id); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) + public function getIds() { - $found = false; + $ci = apc_cache_info('user'); + $keys = array(); - apc_fetch($id, $found); + foreach ($ci['cache_list'] as $entry) { + $keys[] = $entry['info']; + } - return $found; + return $keys; } /** * {@inheritdoc} */ - protected function doSave($id, $data, $lifeTime = 0) + protected function _doFetch($id) { - return (bool) apc_store($id, $data, (int) $lifeTime); + return apc_fetch($id); } /** * {@inheritdoc} */ - protected function doDelete($id) + protected function _doContains($id) { - return apc_delete($id); + $found = false; + + apc_fetch($id, $found); + + return $found; } /** * {@inheritdoc} */ - protected function doFlush() + protected function _doSave($id, $data, $lifeTime = 0) { - return apc_clear_cache() && apc_clear_cache('user'); + return (bool) apc_store($id, $data, (int) $lifeTime); } /** * {@inheritdoc} */ - protected function doGetStats() + protected function _doDelete($id) { - $info = apc_cache_info(); - $sma = apc_sma_info(); - - return array( - Cache::STATS_HITS => $info['num_hits'], - Cache::STATS_MISSES => $info['num_misses'], - Cache::STATS_UPTIME => $info['start_time'], - Cache::STATS_MEMORY_USAGE => $info['mem_size'], - Cache::STATS_MEMORY_AVAILIABLE => $sma['avail_mem'], - ); + return apc_delete($id); } } \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ArrayCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ArrayCache.php index 8a0b982..ada233b 100755 --- a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ArrayCache.php +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ArrayCache.php @@ -27,13 +27,14 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.doctrine-project.org * @since 2.0 + * @version $Revision: 3938 $ * @author Benjamin Eberlei * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel * @author David Abdemoulaie */ -class ArrayCache extends CacheProvider +class ArrayCache extends AbstractCache { /** * @var array $data @@ -43,45 +44,37 @@ class ArrayCache extends CacheProvider /** * {@inheritdoc} */ - protected function doFetch($id) + public function getIds() { - return (isset($this->data[$id])) ? $this->data[$id] : false; + return array_keys($this->data); } /** * {@inheritdoc} */ - protected function doContains($id) + protected function _doFetch($id) { - return isset($this->data[$id]); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - $this->data[$id] = $data; + if (isset($this->data[$id])) { + return $this->data[$id]; + } - return true; + return false; } /** * {@inheritdoc} */ - protected function doDelete($id) + protected function _doContains($id) { - unset($this->data[$id]); - - return true; + return isset($this->data[$id]); } /** * {@inheritdoc} */ - protected function doFlush() + protected function _doSave($id, $data, $lifeTime = 0) { - $this->data = array(); + $this->data[$id] = $data; return true; } @@ -89,8 +82,10 @@ protected function doFlush() /** * {@inheritdoc} */ - protected function doGetStats() + protected function _doDelete($id) { - return null; + unset($this->data[$id]); + + return true; } } \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/Cache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/Cache.php index d303bde..e4cb1c0 100755 --- a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/Cache.php +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/Cache.php @@ -27,23 +27,17 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.doctrine-project.org * @since 2.0 + * @version $Revision: 3938 $ * @author Benjamin Eberlei * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel - * @author Fabio B. Silva */ interface Cache { - const STATS_HITS = 'hits'; - const STATS_MISSES = 'misses'; - const STATS_UPTIME = 'uptime'; - const STATS_MEMORY_USAGE = 'memory_usage'; - const STATS_MEMORY_AVAILIABLE = 'memory_available'; - /** * Fetches an entry from the cache. - * + * * @param string $id cache id The id of the cache entry to fetch. * @return string The cached data or FALSE, if no cache entry exists for the given id. */ @@ -69,34 +63,9 @@ function save($id, $data, $lifeTime = 0); /** * Deletes a cache entry. - * + * * @param string $id cache id * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. */ function delete($id); - - /** - * Retrieves cached information from data store - * - * The server's statistics array has the following values: - * - * - hits - * Number of keys that have been requested and found present. - * - * - misses - * Number of items that have been requested and not found. - * - * - uptime - * Time that the server is running. - * - * - memory_usage - * Memory used by this server to store items. - * - * - memory_available - * Memory allowed to use for storage. - * - * @since 2.2 - * @var array Associative array with server's statistics if available, NULL otherwise. - */ - function getStats(); -} +} \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcacheCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcacheCache.php index aacbfd3..a76bd17 100755 --- a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcacheCache.php +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcacheCache.php @@ -1,6 +1,7 @@ * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel * @author David Abdemoulaie */ -class MemcacheCache extends CacheProvider +class MemcacheCache extends AbstractCache { /** * @var Memcache */ - private $memcache; + private $_memcache; /** * Sets the memcache instance to use. @@ -48,7 +50,7 @@ class MemcacheCache extends CacheProvider */ public function setMemcache(Memcache $memcache) { - $this->memcache = $memcache; + $this->_memcache = $memcache; } /** @@ -58,61 +60,64 @@ public function setMemcache(Memcache $memcache) */ public function getMemcache() { - return $this->memcache; + return $this->_memcache; } /** * {@inheritdoc} */ - protected function doFetch($id) + public function getIds() { - return $this->memcache->get($id); - } + $keys = array(); + $allSlabs = $this->_memcache->getExtendedStats('slabs'); - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return (bool) $this->memcache->get($id); + foreach ($allSlabs as $server => $slabs) { + if (is_array($slabs)) { + foreach (array_keys($slabs) as $slabId) { + $dump = $this->_memcache->getExtendedStats('cachedump', (int) $slabId); + + if ($dump) { + foreach ($dump as $entries) { + if ($entries) { + $keys = array_merge($keys, array_keys($entries)); + } + } + } + } + } + } + return $keys; } /** * {@inheritdoc} */ - protected function doSave($id, $data, $lifeTime = 0) + protected function _doFetch($id) { - return $this->memcache->set($id, $data, 0, (int) $lifeTime); + return $this->_memcache->get($id); } /** * {@inheritdoc} */ - protected function doDelete($id) + protected function _doContains($id) { - return $this->memcache->delete($id); + return (bool) $this->_memcache->get($id); } /** * {@inheritdoc} */ - protected function doFlush() + protected function _doSave($id, $data, $lifeTime = 0) { - return $this->memcache->flush(); + return $this->_memcache->set($id, $data, 0, (int) $lifeTime); } /** * {@inheritdoc} */ - protected function doGetStats() + protected function _doDelete($id) { - $stats = $this->memcache->getStats(); - return array( - Cache::STATS_HITS => $stats['get_hits'], - Cache::STATS_MISSES => $stats['get_misses'], - Cache::STATS_UPTIME => $stats['uptime'], - Cache::STATS_MEMORY_USAGE => $stats['bytes'], - Cache::STATS_MEMORY_AVAILIABLE => $stats['limit_maxbytes'], - ); + return $this->_memcache->delete($id); } } \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcachedCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcachedCache.php deleted file mode 100755 index 3f7f8e8..0000000 --- a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/MemcachedCache.php +++ /dev/null @@ -1,121 +0,0 @@ -. - */ - -namespace Doctrine\Common\Cache; - -use \Memcached; - -/** - * Memcached cache provider. - * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.doctrine-project.org - * @since 2.2 - * @author Benjamin Eberlei - * @author Guilherme Blanco - * @author Jonathan Wage - * @author Roman Borschel - * @author David Abdemoulaie - */ -class MemcachedCache extends CacheProvider -{ - /** - * @var Memcached - */ - private $memcached; - - /** - * Sets the memcache instance to use. - * - * @param Memcached $memcached - */ - public function setMemcached(Memcached $memcached) - { - $this->memcached = $memcached; - } - - /** - * Gets the memcached instance used by the cache. - * - * @return Memcached - */ - public function getMemcached() - { - return $this->memcached; - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return $this->memcached->get($id); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return (false !== $this->memcached->get($id)); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - return $this->memcached->set($id, $data, (int) $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return $this->memcached->delete($id); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return $this->memcached->flush(); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $stats = $this->memcached->getStats(); - $servers = $this->memcached->getServerList(); - $key = $servers[0]['host'] . ':' . $servers[0]['port']; - $stats = $stats[$key]; - return array( - Cache::STATS_HITS => $stats['get_hits'], - Cache::STATS_MISSES => $stats['get_misses'], - Cache::STATS_UPTIME => $stats['uptime'], - Cache::STATS_MEMORY_USAGE => $stats['bytes'], - Cache::STATS_MEMORY_AVAILIABLE => $stats['limit_maxbytes'], - ); - } -} \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/WinCacheCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/WinCacheCache.php deleted file mode 100755 index ed8ca74..0000000 --- a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/WinCacheCache.php +++ /dev/null @@ -1,92 +0,0 @@ -. - */ - -namespace Doctrine\Common\Cache; - -/** - * WinCache cache provider. - * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.doctrine-project.org - * @since 2.2 - * @author Benjamin Eberlei - * @author Guilherme Blanco - * @author Jonathan Wage - * @author Roman Borschel - * @author David Abdemoulaie - */ -class WincacheCache extends CacheProvider -{ - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return wincache_ucache_get($id); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return wincache_ucache_exists($id); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - return (bool) wincache_ucache_set($id, $data, (int) $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return wincache_ucache_delete($id); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - return wincache_ucache_clear(); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $info = wincache_ucache_info(); - $meminfo= wincache_ucache_meminfo(); - return array( - Cache::STATS_HITS => $info['total_hit_count'], - Cache::STATS_MISSES => $info['total_miss_count'], - Cache::STATS_UPTIME => $info['total_cache_uptime'], - Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'], - Cache::STATS_MEMORY_AVAILIABLE => $meminfo['memory_free'], - ); - } -} \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/XcacheCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/XcacheCache.php index 6e22d26..d180730 100755 --- a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/XcacheCache.php +++ b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/XcacheCache.php @@ -1,6 +1,7 @@ * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel * @author David Abdemoulaie */ -class XcacheCache extends CacheProvider +class XcacheCache extends AbstractCache { /** * {@inheritdoc} */ - protected function doFetch($id) + public function getIds() { - return $this->doContains($id) ? unserialize(xcache_get($id)) : false; + $this->_checkAuth(); + $keys = array(); + + for ($i = 0, $count = xcache_count(XC_TYPE_VAR); $i < $count; $i++) { + $entries = xcache_list(XC_TYPE_VAR, $i); + + if (is_array($entries['cache_list'])) { + foreach ($entries['cache_list'] as $entry) { + $keys[] = $entry['name']; + } + } + } + + return $keys; } /** * {@inheritdoc} */ - protected function doContains($id) + protected function _doFetch($id) { - return xcache_isset($id); + return $this->_doContains($id) ? unserialize(xcache_get($id)) : false; } /** * {@inheritdoc} */ - protected function doSave($id, $data, $lifeTime = 0) + protected function _doContains($id) { - return xcache_set($id, serialize($data), (int) $lifeTime); + return xcache_isset($id); } /** * {@inheritdoc} */ - protected function doDelete($id) + protected function _doSave($id, $data, $lifeTime = 0) { - return xcache_unset($id); + return xcache_set($id, serialize($data), (int) $lifeTime); } /** * {@inheritdoc} */ - protected function doFlush() + protected function _doDelete($id) { - $this->checkAuthorization(); - - xcache_clear_cache(XC_TYPE_VAR, 0); - - return true; + return xcache_unset($id); } + /** * Checks that xcache.admin.enable_auth is Off * * @throws \BadMethodCallException When xcache.admin.enable_auth is On * @return void */ - protected function checkAuthorization() + protected function _checkAuth() { if (ini_get('xcache.admin.enable_auth')) { throw new \BadMethodCallException('To use all features of \Doctrine\Common\Cache\XcacheCache, you must set "xcache.admin.enable_auth" to "Off" in your php.ini.'); } } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - $this->checkAuthorization(); - - $info = xcache_info(XC_TYPE_VAR, 0); - return array( - Cache::STATS_HITS => $info['hits'], - Cache::STATS_MISSES => $info['misses'], - Cache::STATS_UPTIME => null, - Cache::STATS_MEMORY_USAGE => $info['size'], - Cache::STATS_MEMORY_AVAILIABLE => $info['avail'], - ); - } } \ No newline at end of file diff --git a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ZendDataCache.php b/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ZendDataCache.php deleted file mode 100755 index 4e4dabe..0000000 --- a/app/Plugin/PageCache/lib/Doctrine/Common/Cache/ZendDataCache.php +++ /dev/null @@ -1,84 +0,0 @@ -. - */ - -namespace Doctrine\Common\Cache; - -/** - * Zend Data Cache cache driver. - * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.doctrine-project.org - * @since 2.0 - * @author Ralph Schindler - * @author Guilherme Blanco - */ -class ZendDataCache extends CacheProvider -{ - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - return zend_shm_cache_fetch($id); - } - - /** - * {@inheritdoc} - */ - protected function doContains($id) - { - return (false !== zend_shm_cache_fetch($id)); - } - - /** - * {@inheritdoc} - */ - protected function doSave($id, $data, $lifeTime = 0) - { - return zend_shm_cache_store($id, $data, $lifeTime); - } - - /** - * {@inheritdoc} - */ - protected function doDelete($id) - { - return zend_shm_cache_delete($id); - } - - /** - * {@inheritdoc} - */ - protected function doFlush() - { - $namespace = $this->getNamespace(); - if (empty($namespace)) { - return zend_shm_cache_clear(); - } - return zend_shm_cache_clear($namespace); - } - - /** - * {@inheritdoc} - */ - protected function doGetStats() - { - return null; - } -} From 9bdfa5d08f7df8175331fab1eabf9fdd5913c095 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 14:53:48 -0600 Subject: [PATCH 41/50] Removed non-existent method call (stupid docs) --- app/Plugin/PageCache/Plugin.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/Plugin/PageCache/Plugin.php b/app/Plugin/PageCache/Plugin.php index adb39f6..75f2847 100644 --- a/app/Plugin/PageCache/Plugin.php +++ b/app/Plugin/PageCache/Plugin.php @@ -27,9 +27,6 @@ public function init() $cache = new \Doctrine\Common\Cache\MemcacheCache(); $cache->setMemcache($memcache); - // Required for advanced deletion options like regex, pattern, and prefix - $cache->setManageCacheIds(true); - // Save cache instance on object $this->cache = $cache; From 71c6618108b46031208beee36249b51f32d8c9f2 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 15:26:06 -0600 Subject: [PATCH 42/50] Added HTML comment to Cache output --- app/Plugin/PageCache/Plugin.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/Plugin/PageCache/Plugin.php b/app/Plugin/PageCache/Plugin.php index 75f2847..7ce8d24 100644 --- a/app/Plugin/PageCache/Plugin.php +++ b/app/Plugin/PageCache/Plugin.php @@ -52,8 +52,10 @@ public function cacheOutput($output) . $request->method() . $request->uri(); + $cacheContent = (string) $response->content() . "\n"; + // Cache output content - $this->cache->set($cacheKey, (string) $response->content()); + $this->cache->save($cacheKey, $cacheContent); } // Delete all cache entries on request when user logged in and it is NOT a GET or HEAD From 1864d2556cbd73f28e2e5690cf24eb30a668d334 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 15:28:28 -0600 Subject: [PATCH 43/50] PageCache param not needed (not filter) --- app/Plugin/PageCache/Plugin.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Plugin/PageCache/Plugin.php b/app/Plugin/PageCache/Plugin.php index 7ce8d24..deae029 100644 --- a/app/Plugin/PageCache/Plugin.php +++ b/app/Plugin/PageCache/Plugin.php @@ -38,7 +38,7 @@ public function init() /** * Set cache key with full page output */ - public function cacheOutput($output) + public function cacheOutput() { $user = $this->kernel->user(); $request = $this->kernel->request(); @@ -52,7 +52,12 @@ public function cacheOutput($output) . $request->method() . $request->uri(); - $cacheContent = (string) $response->content() . "\n"; + $cacheContent = (string) $response->content(); + + // Append cache comment if HTML + if('html' === $request->format) { + $cacheContent .= "\n"; + } // Cache output content $this->cache->save($cacheKey, $cacheContent); From 86005ff1ef724b57e18fded670c6a794da6c8f35 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 15:46:52 -0600 Subject: [PATCH 44/50] Added flag to delete all cache --- app/Plugin/PageCache/Plugin.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/Plugin/PageCache/Plugin.php b/app/Plugin/PageCache/Plugin.php index deae029..2b0084f 100644 --- a/app/Plugin/PageCache/Plugin.php +++ b/app/Plugin/PageCache/Plugin.php @@ -42,6 +42,7 @@ public function cacheOutput() { $user = $this->kernel->user(); $request = $this->kernel->request(); + $response = $this->kernel->response(); // Only cache if user is not logged in, and request is GET if(!$user->isLoggedIn() && $request->isGet()) { @@ -65,10 +66,15 @@ public function cacheOutput() // Delete all cache entries on request when user logged in and it is NOT a GET or HEAD // Really overkill, but it's the only safe way to not run into cache invalidation issues - if($user->isLoggedIn() && (!$request->isGet() || !$request->isHead())) { - // Delete all cache entries prefixed with current site - $sitePrefix = $request->scheme() . $request->host(); - $this->cache->deleteByPrefix($sitePrefix); + if($user->isLoggedIn() && (!$request->isGet() && !$request->isHead())) { + + if($request->clear_cache) { + $this->cache->deleteAll(); + } else { + // Delete all cache entries prefixed with current site + $sitePrefix = $request->scheme() . $request->host(); + @$this->cache->deleteByPrefix($sitePrefix); + } } } } From 72b245276362533a0ea8db415c0f22ffb08a14ed Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 15:51:35 -0600 Subject: [PATCH 45/50] Alloy\Response::content() now returns content --- alloy/lib/Alloy/Response.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/alloy/lib/Alloy/Response.php b/alloy/lib/Alloy/Response.php index 025b7e1..7249adb 100644 --- a/alloy/lib/Alloy/Response.php +++ b/alloy/lib/Alloy/Response.php @@ -93,6 +93,9 @@ public function encoding($encoding = null) */ public function content($content = null) { + if(null === $content) { + return (string) $this->_content; + } $this->_content = $content; } public function appendContent($content) From dd4d167d42f5d1a83f87fada780732497496507c Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 15:56:43 -0600 Subject: [PATCH 46/50] Modified PageCache purge parameters --- app/Plugin/PageCache/Plugin.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/Plugin/PageCache/Plugin.php b/app/Plugin/PageCache/Plugin.php index 2b0084f..e2975c2 100644 --- a/app/Plugin/PageCache/Plugin.php +++ b/app/Plugin/PageCache/Plugin.php @@ -53,7 +53,7 @@ public function cacheOutput() . $request->method() . $request->uri(); - $cacheContent = (string) $response->content(); + $cacheContent = $response->content(); // Append cache comment if HTML if('html' === $request->format) { @@ -66,12 +66,14 @@ public function cacheOutput() // Delete all cache entries on request when user logged in and it is NOT a GET or HEAD // Really overkill, but it's the only safe way to not run into cache invalidation issues - if($user->isLoggedIn() && (!$request->isGet() && !$request->isHead())) { + if($user->isLoggedIn()) { - if($request->clear_cache) { + // Delete all cache entries with flag + if($request->isGet() && $request->clear_cache) { $this->cache->deleteAll(); - } else { - // Delete all cache entries prefixed with current site + + // Delete all cache entries prefixed with current site + } elseif(!$request->isGet() && !$request->isHead()) { $sitePrefix = $request->scheme() . $request->host(); @$this->cache->deleteByPrefix($sitePrefix); } From 4e971503fd7ceacc449860da85476a244aa04a7d Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Sat, 11 Feb 2012 16:00:17 -0600 Subject: [PATCH 47/50] Updated Memcache flush --- app/Plugin/PageCache/Plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Plugin/PageCache/Plugin.php b/app/Plugin/PageCache/Plugin.php index e2975c2..e72395e 100644 --- a/app/Plugin/PageCache/Plugin.php +++ b/app/Plugin/PageCache/Plugin.php @@ -70,7 +70,7 @@ public function cacheOutput() // Delete all cache entries with flag if($request->isGet() && $request->clear_cache) { - $this->cache->deleteAll(); + $this->cache->getMemcache()->flush(); // Delete all cache entries prefixed with current site } elseif(!$request->isGet() && !$request->isHead()) { From eb3d3f30ee0799d84dea5e00b6aa971f57828c28 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Thu, 23 Feb 2012 20:34:45 -0600 Subject: [PATCH 48/50] Fixed error about 'user_id' column on logout --- app/Module/User/Session/Controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Module/User/Session/Controller.php b/app/Module/User/Session/Controller.php index 5a9f148..a9e2c63 100755 --- a/app/Module/User/Session/Controller.php +++ b/app/Module/User/Session/Controller.php @@ -121,7 +121,6 @@ public function deleteMethod($request) setcookie(static::COOKIE_NAME, '0', time()-28800); // Delete all sessions matched for current user - $this->kernel->mapper()->delete('Module\User\Entity', array('user_id' => $user->id)); $this->kernel->mapper()->delete('Module\User\Session\Entity', array('user_id' => $user->id)); return $this->kernel->redirect($this->kernel->url(array('page' => '/'), 'page')); } From ab70b520e08d1cb47d2cd8c687dd950d57f42ad0 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Fri, 29 Jun 2012 11:04:11 -0500 Subject: [PATCH 49/50] Ensuring jQuery does not cache, fixing Text module * Set 'cache' flag to false on admin jQuery calls * Added proper type-hinting to Text module --- app/www/assets/admin/scripts/cms_admin.js | 3 ++- app/www/content/Module/Blog/Post/Controller.php | 1 - app/www/content/Module/Text/Controller.php | 16 ++++++++++------ app/www/content/Module/Text/Mapper.php | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/www/assets/admin/scripts/cms_admin.js b/app/www/assets/admin/scripts/cms_admin.js index ea1fa93..262a10a 100755 --- a/app/www/assets/admin/scripts/cms_admin.js +++ b/app/www/assets/admin/scripts/cms_admin.js @@ -193,6 +193,7 @@ cms.modal = (function (cms, $) { $.ajax({ type: "GET", url: a.attr('href'), + cache: false, success: function(data, textStatus, req) { cms.modal.content(data); }, @@ -744,4 +745,4 @@ $.extend($.ui.dialog.overlay, { create: function(dialog){ $('body').tabs('ul[data-tabs] li > a, ul[data-pills] > li > a') }) -}( window.jQuery || window.ender ); \ No newline at end of file +}( window.jQuery || window.ender ); diff --git a/app/www/content/Module/Blog/Post/Controller.php b/app/www/content/Module/Blog/Post/Controller.php index 4113549..3631863 100644 --- a/app/www/content/Module/Blog/Post/Controller.php +++ b/app/www/content/Module/Blog/Post/Controller.php @@ -6,7 +6,6 @@ use Module\Page\Entity as Page; use Module\Page\Module\Entity as Module; - /** * Blog Module * Blog Posts Controller diff --git a/app/www/content/Module/Text/Controller.php b/app/www/content/Module/Text/Controller.php index 078888b..edb698e 100755 --- a/app/www/content/Module/Text/Controller.php +++ b/app/www/content/Module/Text/Controller.php @@ -1,6 +1,10 @@ kernel->mapper('Module\Text\Mapper')->currentEntity($module); if(!$item) { @@ -31,7 +35,7 @@ public function indexAction($request, $page, $module) /** * @method GET */ - public function newAction($request, $page, $module) + public function newAction(Request $request, Page $page, Module $module) { $form = $this->formView() ->method('post') @@ -44,7 +48,7 @@ public function newAction($request, $page, $module) /** * @method GET */ - public function editlistAction($request, $page, $module) + public function editlistAction(Request $request, Page $page, Module $module) { $form = $this->formView() ->action($this->kernel->url(array('page' => $page->url, 'module_name' => $this->name(), 'module_id' => $module->id), 'module'), 'module') @@ -71,7 +75,7 @@ public function editlistAction($request, $page, $module) * Create a new resource with the given parameters * @method POST */ - public function postMethod($request, $page, $module) + public function postMethod(Request $request, Page $page, Module $module) { $mapper = $this->kernel->mapper('Module\Text\Mapper'); $item = $mapper->get('Module\Text\Entity')->data($request->post()); @@ -98,7 +102,7 @@ public function postMethod($request, $page, $module) * Save over existing entry (from edit) * @method PUT */ - public function putMethod($request, $page, $module) + public function putMethod(Request $request, Page $page, Module $module) { $mapper = $this->kernel->mapper('Module\Text\Mapper'); $item = $mapper->currentEntity($module); @@ -129,7 +133,7 @@ public function putMethod($request, $page, $module) /** * @method DELETE */ - public function deleteMethod($request, $page, $module) + public function deleteMethod(Request $request, Page $page, Module $module) { $mapper = $this->kernel->mapper('Module\Text\Mapper'); $item = $mapper->get('Module\Text\Entity', $request->module_item); diff --git a/app/www/content/Module/Text/Mapper.php b/app/www/content/Module/Text/Mapper.php index c400db7..4c055d3 100755 --- a/app/www/content/Module/Text/Mapper.php +++ b/app/www/content/Module/Text/Mapper.php @@ -18,4 +18,4 @@ public function currentEntity(\Module\Page\Module\Entity $module) } return $item; } -} \ No newline at end of file +} From 0db65095cff2f5174599a0d9048ffe1ff29114ee Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Fri, 28 Jun 2013 12:06:48 -0500 Subject: [PATCH 50/50] Add auth checks to Pages controller * Whitespace cleanup --- app/Module/Page/Controller.php | 147 ++++++++++-------- .../Stackbox/Module/ControllerAbstract.php | 36 ++--- .../content/Module/Navigation/Controller.php | 10 +- 3 files changed, 108 insertions(+), 85 deletions(-) diff --git a/app/Module/Page/Controller.php b/app/Module/Page/Controller.php index 608efd7..3d7ebd2 100755 --- a/app/Module/Page/Controller.php +++ b/app/Module/Page/Controller.php @@ -9,7 +9,7 @@ class Controller extends Stackbox\Module\ControllerAbstract { protected $_path = __DIR__; - + /** * @method GET */ @@ -19,8 +19,8 @@ public function indexAction(Request $request) return $this->viewUrl($request->page); } - - + + /** * View page by URL */ @@ -30,7 +30,7 @@ public function viewUrl($pageUrl) $request = $kernel->request(); $user = $kernel->user(); $site = $kernel->site(); - + // Ensure page exists $mapper = $kernel->mapper(); $pageMapper = $kernel->mapper('Module\Page\Mapper'); @@ -69,13 +69,13 @@ public function viewUrl($pageUrl) $moduleId = (int) $request->module_id; $moduleName = $request->module_name; $moduleAction = $request->module_action; - + $module = false; if($moduleName != 'page' && $moduleName != 'site' && $moduleId) { // Get module by ID $module = $mapper->first('Module\Page\Module\Entity', array('id' => $moduleId)); } - + // Setup dummy module object if there is none loaded // @todo Possibly restrict callable action with ID of '0' to 'new', etc. because other functions may depend on saved and valid module record if(false === $module) { @@ -83,7 +83,7 @@ public function viewUrl($pageUrl) $module->id = $moduleId; $module->name = $moduleName; } - + // Load requested module // Try 'Site' module first $moduleObject = $kernel->module('Site_' . $moduleName); @@ -100,13 +100,13 @@ public function viewUrl($pageUrl) if(!$moduleObject->userCanExecute($user, $moduleAction)) { throw new Alloy\Exception\Auth("User does not have sufficient permissions to execute requested action. Please login and try again."); } - + // Emulate REST for browsers $requestMethod = $request->method(); if($request->isPost() && $request->post('_method')) { $requestMethod = $request->post('_method'); } - + // Append 'Action' or 'Method' depending on HTTP method if(strtolower($requestMethod) == strtolower($moduleAction)) { $moduleAction = $moduleAction . (false === strpos($moduleAction, 'Method') ? 'Method' : ''); @@ -118,13 +118,13 @@ public function viewUrl($pageUrl) if(!is_callable(array($moduleObject, $moduleAction))) { throw new \BadMethodCallException("Module '" . $moduleName ."' does not have a callable method '" . $moduleAction . "'"); } + try { $moduleResponse = $kernel->dispatch($moduleObject, $moduleAction, array($request, $page, $module)); } catch(\Exception $e) { // Catch module exeptions and pass them through filter $moduleResponse = $kernel->events('cms')->filter('module_dispatch_exception', $e); } - // Set content as main content (detail view) $mainContent = $this->regionModuleFormat($request, $page, $module, $user, $moduleResponse); @@ -133,14 +133,14 @@ public function viewUrl($pageUrl) return $mainContent; } } - + // Load page template $activeTheme = ($page->theme) ? $page->theme : $kernel->config('cms.site.theme', $kernel->config('cms.default.theme')); // Default template or page template if(!$page->template) { $activeTemplate = $activeTheme . '/' . $kernel->config('cms.default.theme_template'); } else { - $activeTemplate = $page->template; + $activeTemplate = $page->template; } $activeTheme = current(explode('/', $activeTemplate)); $template = new Template($activeTemplate); @@ -167,7 +167,7 @@ public function viewUrl($pageUrl) // Add jQuery as the first item $templateHead = $template->head(); $templateHead->script($kernel->config('cms.url.assets') . 'jquery.min.js'); - + // Template Region Defaults $regionModules = array(); $mainRegion = $template->regionMain(); @@ -175,11 +175,11 @@ public function viewUrl($pageUrl) foreach($template->regions() as $regionName => $regionData) { $regionModules[$regionName] = $regionData['content']; } - + // Modules $modules = $page->modules; $unusedModules = array(); - + // Also include modules in global template regions if global regions are present if($template->regionsType('global')) { $modules->orWhere(array('site_id' => $kernel->config('cms.site.id'), 'region' => $template->regionsType('global'))); @@ -204,7 +204,7 @@ public function viewUrl($pageUrl) if(!isset($regionModules[$module->region]) || !is_array($regionModules[$module->region])) { $regionModules[$module->region] = array(); } - + // If we have a 'main' module render, don't dispatch/render other content in main region if(false !== $mainContent) { // If module goes in 'main' region, skip it @@ -221,7 +221,7 @@ public function viewUrl($pageUrl) if(false !== $mainContent) { $regionModules[$mainRegionName] = array($mainContent); } - + // Replace region content $regionModules = $kernel->events('cms')->filter('module_page_template_regions', $regionModules); foreach($regionModules as $region => $modules) { @@ -234,17 +234,17 @@ public function viewUrl($pageUrl) } $template->regionContent($region, $regionContent); } - + // Replace template tags $tags = $mapper->data($page); $tags = $kernel->events('cms')->filter('module_page_template_data', $tags); foreach($tags as $tagName => $tagValue) { $template->replaceTag($tagName, $tagValue); } - + // Template string content $template->clean(); // Remove all unmatched tokens - + // Admin stuff for HTML format if($template->format() == 'html') { @@ -252,7 +252,7 @@ public function viewUrl($pageUrl) if($user && $user->isAdmin()) { // Admin toolbar, javascript, styles, etc. $templateHead->script($kernel->config('cms.url.assets') . 'jquery-ui.min.js'); - + // Setup javascript variables for use $templateHead->prepend('