Author: uncleringo
Date: 2010-01-20 18:19:32 +0100 (Wed, 20 Jan 2010)
New Revision: 26951

Added:
   plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/
   
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/LICENSE
   
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/README
   plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/
   
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/doctrine/
   
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/doctrine/JCroppable.php
   
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/doctrine/Listener/
   
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/doctrine/Listener/JCroppable.php
   
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/widget/
   
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/widget/sfWidgetFormInputImageJCroppable.class.php
Log:
Tagging the 1.0.7 release

Copied: 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/LICENSE
 (from rev 24100, plugins/sfDoctrineJCroppablePlugin/trunk/LICENSE)
===================================================================
--- 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/LICENSE
                            (rev 0)
+++ 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/LICENSE
    2010-01-20 17:19:32 UTC (rev 26951)
@@ -0,0 +1,19 @@
+Copyright (c) 2008 Rich Birch
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file

Copied: 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/README 
(from rev 24100, plugins/sfDoctrineJCroppablePlugin/trunk/README)
===================================================================
--- 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/README 
                            (rev 0)
+++ 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/README 
    2010-01-20 17:19:32 UTC (rev 26951)
@@ -0,0 +1,189 @@
+sfDoctrineJCroppablePlugin
+==========================
+
+This plugin allows you to effortlessly add image editing capabilities to your
+admin generator backend. You'll be able to add images to your models and be
+presented with the awesome Jcrop (http://deepliquid.com/content/Jcrop.html)
+jQuery plugin.
+
+
+Features
+--------
+
+The plugin already has a few very useful features
+
+* Create as many different sized crops of each of your image fields as you like
+* Add any amount of coloured padding around the croppable image
+* Fix the aspect ratio of the cropper to add consistency to your images'
+  dimensions
+* Change the directory that uploaded images are stored in
+* Use the getImageSrc() or getImageTag() method in your frontend app to easily 
display your
+  neatly cropped images
+
+Installation
+------------
+
+To install the plugin for a symfony project, the usual process is to use the
+symfony command line:
+
+    php symfony plugin:install sfDoctrineJCroppablePlugin
+
+sfDoctrineJCroppablePlugin depends on the excellent sfImageTransformPlugin, but
+until I can figure out how to specify that dependency in the package then 
you'll
+have to install it yourself (it's getting late!)
+
+    php symfony plugin:install sfImageTransformPlugin
+
+If the installation of either package fails (because you're running symfony 1.3
+for example) then you can manually download the tgz files from
+
+    
http://plugins.symfony-project.org/get/sfImageTransformPlugin/sfImageTransformPlugin-1.2.0.tgz
+    
http://plugins.symfony-project.org/get/sfDoctrineJCroppablePlugin/sfDoctrineJCroppablePlugin-1.0.2.tgz
+
+and then ask symfony to install the downloaded files
+
+    php symfony plugin:install sfImageTransformPlugin-1.2.0.tgz
+    php symfony plugin:install sfDoctrineJCroppablePlugin-1.0.2.tgz
+
+Enable the plugins if necessary by editing 
config/ProjectConfiguration.class.php:
+
+    $this->enablePlugins(array('sfDoctrinePlugin', 'sfImageTransformPlugin', 
'sfDoctrineJCroppablePlugin'));
+
+
+Symfony 1.2 & Doctrine 1.0
+--------------------------
+
+If you're using symfony 1.2 which comes with doctrine 1.0 this plugin will not
+be able to delete the old versions of its images when it creates new ones. This
+is because we've not been able to find a way of getting the old filename.
+Upgrading to doctrine 1.1 fixes this and is a reasonably straightforward
+exorcise. There is a good guide here:
+
+    http://www.brandonturner.net/blog/2009/05/doctrine11-with-symfony12/
+
+Symfony 1.3 comes with doctrine 1.2 and so this is not an issue.
+
+
+Usage
+-----
+
+You'll first want to edit config/doctrine/schema.yml to add the behaviour to 
one
+of your models:
+
+    Person:
+      actAs:
+        JCroppable:
+          images: [ mugshot ]
+      columns:
+        first_name:  { type: string(128), notnull: true }
+        last_name: { type: string(128), notnull: false }
+
+Here we have told the plugin we want one jcroppable image field called mugshot.
+The plugin will take care of the creation of the relevant fields in the person
+table.
+
+Now we've updated/created our schema, we can tell symfony to rebuild our
+database.
+
+Caution! This step will erase any data in the database that is not included in 
any
+fixtures:
+
+    php symfony doctrine:build-all-reload
+
+
+Next let's configure sfImageTransform to use the gd library and to auto-detect
+images' mime-types (apps/backend/config/app.yml)
+
+    all:
+      sfImageTransformPlugin:
+        default_adapter: GD
+        mime_type:
+          auto_detect: true
+          library: gd_mime_type
+
+If you haven't aleady done so let's create an admin module for our person 
model:
+
+    php symfony doctrine:generate-admin backend Person --module=person
+
+The plugin also depends on the jquery & jcrop libraries, so you must download
+them (http://deepliquid.com/content/Jcrop.html) and place the two js files
+(jquery.Jcrop.min.js & jquery.min.js) in web/js, and the css file
+(jquery.Jcrop.css) in web/css:
+
+    web/js/jquery.min.js
+    web/js/jquery.Jcrop.min.js
+    web/css/jquery.Jcrop.css
+
+Now we need to tell our module's view to include them. We can do this
+specifically for our module by editing
+apps/backend/modules/person/config/view.yml, or for the whole application by
+editing apps/backend/config/view.yml:
+
+    default:
+      javascripts:    [jquery.min.js, jquery.Jcrop.min.js]
+      stylesheets:    [jquery.Jcrop.css]
+
+We're almost finished now! Just two things left to do. First we have to
+configure the widget & validator for our jcroppable image field. Add the
+following calls to configure the widgets & validators to the form's configure
+function (lib/form/doctrine/PersonForm.class.php):
+
+    public function configure()
+    {
+      $this->getObject()->configureJCropWidgets($this);
+      $this->getObject()->configureJCropValidators($this);
+    }
+
+And finally create the upload directory for the images to be stored in and make
+them writeable:
+
+    mkdir -p web/uploads/images/Person
+    chmod 777 web/uploads/images/Person
+
+That's it! Now if you followed the instructions carefully then you should be
+able to aim your browser at the backend app's person module and add a few
+people and their mug shots.
+
+To show the images in your frontend templates, simply pass a Person object, and
+call the getImageSrc() or the getImageTag() function, passing the desired image
+& size, eg
+
+    <img src="<?php echo $person->getImageSrc('mugshot', 'thumb') ?>" alt="Mug 
shot!" />
+
+or
+
+    <?php echo $person->getImageTag('mugshot', 'thumb') ?>
+
+Configuration
+-------------
+
+Now the above example has used a load of default values which you'll almost
+definitely want to configure. Here's an example of some config you can place in
+your config/app.yml:
+
+    all:
+      sfDoctrineJCroppablePlugin:
+        models:
+          Person:
+            directory:    people
+            images:
+              mugshot:
+                padding:    { percent: 35, color: #ffffff }
+                ratio:      1.5
+                sizes:
+                  thumb:    { width: 450 }
+                  main:     { width: 675 }
+
+Let's run through these options:
+
+* directory - overrides the default web/uploads/images/Person to instead be
+    web/uploads/people
+* padding - adds padding to the image, of the specified color, using either a
+    'percent' or 'pixels' value
+* ratio - fixes the aspect ratio of the cropper and therefore the generated
+    cropped images
+* sizes - allows you to specify any number of different sized images to be
+    created. You specify the width and the height will be calculated using the
+    ratio if there is one, otherwise it will be variable according to the
+    selected crop. The default sizes are thumb (width 120px) &
+    main (width 360px)

Copied: 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/doctrine/JCroppable.php
 (from rev 24100, 
plugins/sfDoctrineJCroppablePlugin/trunk/lib/doctrine/JCroppable.php)
===================================================================
--- 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/doctrine/JCroppable.php
                                (rev 0)
+++ 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/doctrine/JCroppable.php
        2010-01-20 17:19:32 UTC (rev 26951)
@@ -0,0 +1,526 @@
+<?php
+
+class Doctrine_Template_JCroppable extends Doctrine_Template
+{
+  /**
+  * Array of ageable options
+  *
+  * @var string
+  */
+  protected $_options = array();
+  
+  private $editableImages = array();
+  private $originalImages = array();
+  
+  /**
+  * __construct
+  *
+  * @param string $array
+  * @return void
+  */
+  public function __construct(array $options = array())
+  { 
+    $this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options);
+  }
+
+  /**
+   * Set table definition for JCroppable behavior
+   *
+   * @return void
+   */
+  public function setTableDefinition()
+  {
+    if (empty($this->_options['images']))
+    {
+      return false;
+    }
+    
+    foreach ($this->_options['images'] as $fieldName) {
+      $this->hasColumn($fieldName, 'string', 255, array('type' => 'string', 
'length' => '255'));
+      
+      foreach (array('x1', 'y1', 'x2', 'y2') as $suffix) {
+        $this->hasColumn($fieldName . '_' . $suffix, 'integer', null, 
array('type' => 'integer'));
+      }
+    } 
+    
+    $this->addListener(new 
Doctrine_Template_Listener_JCroppable($this->_options));
+
+    $this->createChildFormClass();
+  }
+
+  private function getTableNameCamelCase() {
+    return preg_replace('/(?:^|_)(.?)/e',"strtoupper('$1')",
+      $this->getInvoker()->getTable()->getTableName());
+  }
+  
+  /**
+   * Performs the following operations for a given image:
+   *  1) removes any old files if the image has been re-uploaded
+   *  2) creates a scaled down version for editing in the cropper if the image 
has been (re)uploaded
+   *  3) creates the cropped versions of the image
+   * 
+   * This method is called from the listener if the image has been edited in 
any way
+   *  
+   * @param string $fieldName
+   */
+  public function updateImage($fieldName) {
+//    die($fieldName);
+    $oldValues = $this->getInvoker()->getModified(true);
+    
+    if (!empty($oldValues[$fieldName]) && $oldValues[$fieldName] != 
$this->getInvoker()->$fieldName) {
+      
+      $this->removeImages($fieldName, $oldValues[$fieldName]);
+      
+    }
+
+    if (in_array($fieldName, array_keys($this->getInvoker()->getModified()))) {
+
+      $this->createEditableImage($fieldName);
+      
+    }
+    
+    $this->createCrops($fieldName);
+  }
+  
+  /**
+   * Takes a form and configures each image's widget.
+   * 
+   * This is one of only 2 methods the user needs to call manually (the other 
being configureJCropValidators)
+   * Should be called from the form's configure() method
+   * 
+   * @param sfForm $form
+   */
+  public function configureJCropWidgets(sfForm $form, $formOptions = array()) {
+    
+    foreach ($this->_options['images'] as $fieldName) {
+      if (!$imageConfig = $this->getImageConfig($fieldName))
+      {
+        continue;
+      }
+      
+      $form->setWidget($fieldName, 
+        new sfWidgetFormInputFileInputImageJCroppable(array(
+          'invoker' => $this->getInvoker(),
+          'image_field' => $fieldName,
+          'image_ratio' => isset($imageConfig['ratio']) ? 
$imageConfig['ratio'] : false,
+          'with_delete' => true,
+          'file_src' => $this->getImageSrc($fieldName, 'editable'),
+          'template'  => '%file%<br />%input%<br />%delete% %delete_label%',
+          'form' => $form
+        ))
+      );
+      
+      foreach (array('x1', 'y1', 'x2', 'y2') as $suffix) {
+        $form->setWidget($fieldName . '_' . $suffix, new 
sfWidgetFormInputHidden());
+      }
+    }
+  }
+  
+  /**
+   * Takes a form and configures each image's widget.
+   * 
+   * This is one of only 2 methods the user needs to call manually (the other 
being configureJCropWidgets)
+   * Should be called from the form's configure() method
+   * 
+   * @param sfForm $form
+   */
+  public function configureJCropValidators($form) {
+    
+    foreach ($this->_options['images'] as $fieldName) {
+      
+      $form->setValidator($fieldName . '_delete',  new sfValidatorPass());
+      
+      $form->setValidator($fieldName,
+        new sfValidatorFile(array(
+            'required'   => false,
+            'path'       => $this->getImageDir(),
+            'mime_types' => 'web_images',
+          ),
+          array('mime_types' => 'Unsupported image type (%mime_type%)')
+        )
+      );
+    }
+  }
+  
+  /**
+   * Get the directory to store the images in by looking in the following 
places:
+   * 
+   *  1) table specific config
+   *  2) global plugin config
+   *  3) default location
+   * 
+   * @return string
+   */
+  private function getImageDir() {
+    $config = sfConfig::get('app_sfDoctrineJCroppablePlugin_models');
+    
+    $basePath = sfConfig::get('sf_upload_dir');
+    
+    $tableName = $this->getTableNameCamelCase();
+
+    if (!empty($config[$tableName]['directory'])) {
+      
+      $relativePath = $config[$tableName]['directory'];
+      
+    } else {
+      
+      $relativePath = $this->getImageDirDefault();
+      
+    }
+    
+    return $basePath . DIRECTORY_SEPARATOR . $relativePath;
+  }
+  
+  /**
+   * Generate's the default directory to store the model's images in (relative 
to uploads/)
+   * 
+   * @return string
+   */
+  private function getImageDirDefault() {
+    return 'images' . DIRECTORY_SEPARATOR . $this->getTableNameCamelCase();
+  }
+  
+  /**
+   * Gets the model's image directory relative to the web root (sf_web_dir)
+   * 
+   * @return string
+   */
+  private function getImageDirWeb() {
+       $webDir = str_replace('\\', '/', sfConfig::get('sf_web_dir'));
+       $imageDir = str_replace('\\', '/', $this->getImageDir());
+       
+    return (string)str_replace($webDir . '/', '', $imageDir);
+  }
+  
+  /**
+   * Gets the given field's absolute editable image path, and warns if the 
directory
+   *  doesn't exist or is not writable
+   * 
+   * @param string $fieldName
+   * @return string
+   */
+  public function getImageSrc($fieldName, $size = 'thumb') {
+    $fileDir = $this->getImageDirWeb();
+    
+    if (!file_exists($fileDir)) {
+      print("image upload directory <strong>$fileDir</strong> doesn't exist");
+    }
+    if (!is_writable($fileDir)) {
+      print("image upload directory <strong>$fileDir</strong> is not 
writable");
+    }
+    
+    $fileSrc = '/' . $fileDir . '/' . $this->getImageFromName($fieldName, 
$size);
+    
+    return $fileSrc;
+  }
+
+  /**
+   * Returns an img tag for the specified image field & size (default thumb)
+   *
+   * @param string $fieldName
+   * @param string $size
+   * @param array $attributes
+   * @return string
+   */
+  public function getImageTag($fieldName, $size = 'thumb', $attributes = 
array())
+  {
+    return tag(
+      'img',
+      array_merge(
+        $attributes,
+        array('src' => $this->getImageSrc($fieldName, $size))
+      )
+    );
+  }
+  
+  /**
+   * Takes the original image, adds and padding to it and creates an editable 
version
+   *  for use in the cropper
+   *  
+   * @param string $fieldName
+   */
+  private function createEditableImage($fieldName) {
+    $imageConfig = $this->getImageConfig($fieldName);
+    /**
+     * Get the filenames for the editoable and original versions of the image
+     */
+    $original = $this->getImageFromName($fieldName, 'original');
+    $editable = $this->getImageFromName($fieldName, 'editable');
+
+    if (empty($original) || empty($editable))
+    {
+      return false;
+    }
+    
+    $dir = $this->getImageDir();
+    
+    /**
+     * Move the new image to be named as the original
+     */
+    rename($dir . DIRECTORY_SEPARATOR . $editable, $dir . DIRECTORY_SEPARATOR 
. $original);
+    //print("mv $editable $original<br/>");exit;
+    /**
+     * Load the original and resize it for the editable version
+     */
+    $img = new sfImage($dir . DIRECTORY_SEPARATOR . $original);
+    
+    if (isset($imageConfig['padding'])) {
+      $img = $this->addPadding($img, $imageConfig['padding']);
+      
+      $img->saveAs($dir . DIRECTORY_SEPARATOR . $original);
+    }
+    
+    $img->resize(400, null);
+    $img->saveAs($dir . DIRECTORY_SEPARATOR . $editable);
+    
+    $this->getInvoker()->{$fieldName . '_x1'} = 0;
+    $this->getInvoker()->{$fieldName . '_y1'} = 0;
+    $this->getInvoker()->{$fieldName . '_x2'} = $img->getWidth();
+    $this->getInvoker()->{$fieldName . '_y2'} = $img->getHeight();
+  }
+  
+  /**
+   * Adds any padding to the given image using the supplied padding config
+   * 
+   * @param $img
+   * @param array $padding
+   * @return $img
+   */
+  private function addPadding($img, $padding) {
+    if (!$padding) {
+      return $img;
+    }
+    
+    if (isset($padding['percent']) && is_numeric($padding['percent'])) {
+      
+      $width = $img->getWidth() * (1 + ($padding['percent'] / 100));
+      $height = $img->getHeight() * (1 + ($padding['percent'] / 100));
+      
+    } else if (isset($padding['pixels']) && is_numeric($padding['pixels'])) {
+      
+      $width = $img->getWidth() + $padding['pixels'];
+      $height = $img->getHeight() + $padding['pixels'];
+      
+    } else {
+      
+      return $img;
+      
+    }
+    
+    $canvas = new sfImage();
+    $canvas
+      ->fill(0, 0, isset($padding['color']) ? $padding['color'] : '#ffffff')
+      ->resize($width, $height)
+      ->overlay($img, 'center');
+    
+    return $canvas;
+  }
+  
+  /**
+   * Gets the filename for the given image field and size. Uses the current 
field value, 
+   *  but can be overriden by passing a different value as the 3rd parameter
+   *  
+   * @param $fieldName
+   * @param $size
+   * @param $editable
+   * @return $image
+   */
+  private function getImageFromName($fieldName, $size = 'editable', $editable 
= null) {
+    if (!$imageConfig = $this->getImageConfig($fieldName)) {
+      return false;
+    }
+    
+    if ($editable == null) {
+      $editable = $this->getInvoker()->$fieldName;
+    }
+    
+    if ($size == 'editable' || (!isset($imageConfig['sizes'][$size]) && $size 
!= 'original')) {
+      return $editable;
+    }
+    
+    $extensionPosition = strrpos($editable, '.');
+    $stub = substr($editable, 0, $extensionPosition);
+    
+    $image = str_replace($stub, $stub . '_' . $size, $editable);
+    
+    return $image;
+  }
+  
+  /**
+   * Creates the cropped version of the given field's images
+   * 
+   * @param string $fieldName
+   * @return bool
+   */
+  private function createCrops($fieldName) {
+    if (!$imageConfig = $this->getImageConfig($fieldName)) {
+      return false;
+    }
+    
+    $this->loadImage($fieldName, 'editable');
+    $this->loadImage($fieldName, 'original');
+    
+    foreach ($imageConfig['sizes'] as $size => $dims) {
+      
+      $this->createCropForSize($fieldName, $size);
+      
+    }
+    
+    return true;
+  }
+  
+  /**
+   * Loads either the editable or original version of the given image field
+   * 
+   * @param string $fieldName
+   * @param string $version - editable or original 
+   * @param $force - try to load the image even if there's no config for image
+   */
+  private function loadImage($fieldName, $version, $force = false) {
+    $imageConfig = $this->getImageConfig($fieldName);
+    
+    if (!$this->getInvoker()->$fieldName || (!$imageConfig && !$force)) {
+      return;
+    }
+    
+    $this->{$version . 'Images'}[$fieldName] =
+      new sfImage($this->getImageDir() . DIRECTORY_SEPARATOR . 
$this->getImageFromName($fieldName, $version));
+  }
+  
+  /**
+   * Creates the crop of the given field's image at the specified size
+   * 
+   * @param $fieldName
+   * @param $size
+   */
+  private function createCropForSize($fieldName, $size) {
+    if (!$imageConfig = $this->getImageConfig($fieldName)) {
+      return false;
+    }
+    
+    $this->loadImage($fieldName, 'original');
+    $this->loadImage($fieldName, 'editable');
+
+    if (empty($this->originalImages[$fieldName]) || 
empty($this->editableImages[$fieldName]))
+    {
+      return false;
+    }
+    
+    $ratio = $this->originalImages[$fieldName]->getWidth() /
+      $this->editableImages[$fieldName]->getWidth();
+    
+    $dims['x'] = (int)$this->getInvoker()->{$fieldName . '_x1'} * $ratio;
+    $dims['y'] = (int)$this->getInvoker()->{$fieldName . '_y1'} * $ratio;
+    $dims['w'] = (int)($this->getInvoker()->{$fieldName . '_x2'} * $ratio) - 
$dims['x'];
+    $dims['h'] = (int)($this->getInvoker()->{$fieldName . '_y2'} * $ratio) - 
$dims['y'];
+    
+    $origCrop = $this->originalImages[$fieldName]
+      ->crop($dims['x'], $dims['y'], $dims['w'], $dims['h']);
+      
+    $finalCrop = $origCrop->resize(
+      $imageConfig['sizes'][$size]['width'],
+      empty($imageConfig['ratio']) ?
+        null :
+        $imageConfig['sizes'][$size]['width'] / $imageConfig['ratio']);
+    
+    $fullPath = $this->getImageDir() . DIRECTORY_SEPARATOR . 
$this->getImageFromName($fieldName, $size);
+    
+    $finalCrop->saveAs($fullPath);
+  }
+  
+  /**
+   * Removes all existing images for the given field, and the field's value
+   *  can be overridden using the second parameter
+   *  
+   * @param $fieldName
+   * @param $editable
+   */
+  private function removeImages($fieldName, $editable) {
+    if (!$imageConfig = $this->getImageConfig($fieldName)) {
+      return;
+    }
+    
+    /**
+     * Remove the editable & original images
+     */
+    foreach (array('editable', 'original') as $type) {
+      $fullPath = $this->getImageDir() . DIRECTORY_SEPARATOR
+        . $this->getImageFromName($fieldName, $type, $editable);
+      
+      if (file_exists($fullPath)) {
+        unlink($fullPath);
+      }
+    }
+    
+    /**
+     * Loop through the sizes and remove them
+     */
+    foreach ($imageConfig['sizes'] as $size => $dims) {
+      
+      $filename = $this->getImageFromName($fieldName, $size, $editable);
+      
+      $fullPath = $this->getImageDir() . DIRECTORY_SEPARATOR . $filename;
+      
+      if (file_exists($fullPath)) {
+        unlink($fullPath);
+      }
+      
+    }
+  }
+  
+  /**
+   * Get's the config for the given field's image
+   * 
+   * @param $fieldName
+   * @return array
+   */
+  private function getImageConfig($fieldName) {
+    $config = sfConfig::get('app_sfDoctrineJCroppablePlugin_models');
+    
+    if (!isset($config[$this->getTableNameCamelCase()]['images'][$fieldName])) 
{
+      return array('sizes' => array(
+        'thumb' => array('width' => 120),
+        'main' => array('width' => 360)
+      ));
+    }
+    
+    return $config[$this->getTableNameCamelCase()]['images'][$fieldName];
+  }
+  
+  /**
+   * Get's the table name of the invoking model
+   * 
+   * @return string
+   */
+  private function getTableName() {
+    return $this->getInvoker()->getTable()->getTableName();
+  }
+
+  private function createChildFormClass()
+  {
+    $tableName = $this->getTableNameCamelCase();
+
+    $baseForm = $tableName . 'Form';
+    $extendedForm = 'JCroppable' . $baseForm;
+    
+    if (!class_exists($tableName . 'Form') || class_exists($extendedForm))
+    {
+      return false;
+    }
+
+    $class = '
+class ' . $extendedForm . ' extends ' . $baseForm . '
+{
+
+  public function configure()
+  {
+    $this->getObject()->configureJCropWidgets($this, $this->options);
+    $this->getObject()->configureJCropValidators($this);
+
+    parent::configure();
+  }
+}';
+    
+    eval($class);
+  }
+}

Copied: 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/doctrine/Listener/JCroppable.php
 (from rev 24100, 
plugins/sfDoctrineJCroppablePlugin/trunk/lib/doctrine/Listener/JCroppable.php)
===================================================================
--- 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/doctrine/Listener/JCroppable.php
                               (rev 0)
+++ 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/doctrine/Listener/JCroppable.php
       2010-01-20 17:19:32 UTC (rev 26951)
@@ -0,0 +1,57 @@
+<?php
+
+class Doctrine_Template_Listener_JCroppable extends Doctrine_Record_Listener
+{
+  /**
+  * Array of ageable options
+  *
+  * @var array
+  */
+  protected $_options = array();
+
+  /**
+  * __construct
+  *
+  * @param array $options
+  * @return void
+  */
+  public function __construct(array $options)
+  {
+    $this->_options = $options;
+  }
+  
+  public function preInsert(Doctrine_Event $event)
+  {
+    $this->checkImages($event);
+  }
+
+  public function preUpdate(Doctrine_Event $event)
+  {
+    $this->checkImages($event);
+  }
+
+  private function checkImages(Doctrine_Event $event) {
+    $invoker = $event->getInvoker();
+
+    $modifiedFields = array_keys($invoker->getModified());
+    $imageFieldSuffixes = array('', '_x1', '_y1', '_x2', '_y2');
+//    print("<pre>");var_dump($modifiedFields);exit;
+    foreach ($this->_options['images'] as $imageName) {
+
+      $needsUpdate = false;
+
+      foreach ($imageFieldSuffixes as $suff) {
+
+        if (in_array($imageName . $suff, $modifiedFields)) {
+          $needsUpdate = true;
+          break;
+        }
+
+      }
+
+      if ($needsUpdate) {
+        $invoker->updateImage($imageName);
+      }
+    }
+  }
+}
\ No newline at end of file

Copied: 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/widget/sfWidgetFormInputImageJCroppable.class.php
 (from rev 24100, 
plugins/sfDoctrineJCroppablePlugin/trunk/lib/widget/sfWidgetFormInputImageJCroppable.class.php)
===================================================================
--- 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/widget/sfWidgetFormInputImageJCroppable.class.php
                              (rev 0)
+++ 
plugins/sfDoctrineJCroppablePlugin/tags/sfDoctrineJCroppablePlugin-1.0.7/lib/widget/sfWidgetFormInputImageJCroppable.class.php
      2010-01-20 17:19:32 UTC (rev 26951)
@@ -0,0 +1,184 @@
+<?php
+
+/**
+ * sfWidgetFormInputFileInputImageJCroppable represents an upload HTML input 
tag which will
+ * also display the uploaded image with the JCrop functionality.
+ *
+ * @author     Rich Birch <[email protected]>
+ */
+class sfWidgetFormInputFileInputImageJCroppable extends sfWidgetFormInputFile
+{
+  /**
+   * Constructor.
+   *
+   * Available options:
+   *
+   *  * file_src:     The current image web source path (required)
+   *  * with_delete:  Whether to add a delete checkbox or not
+   *  * delete_label: The delete label used by the template
+   *  * image_field:
+   *  * image_ratio:
+   *  * invoker:
+   *  * template:     The HTML template to use to render this widget
+   *                  The available placeholders are:
+   *                    * input (the image upload widget)
+   *                    * delete (the delete checkbox)
+   *                    * delete_label (the delete label text)
+   *                    * file (the file tag)
+   *  * form:
+   *
+   * In edit mode, this widget renders an additional widget named after the
+   * file upload widget with a "_delete" suffix. So, when creating a form,
+   * don't forget to add a validator for this additional field.
+   *
+   * @param array $options     An array of options
+   * @param array $attributes  An array of default HTML attributes
+   *
+   * @see sfWidgetFormInputFile
+   */
+  protected function configure($options = array(), $attributes = array())
+  {
+    parent::configure($options, $attributes);
+
+    $this->setOption('type', 'file');
+    $this->setOption('needs_multipart', true);
+    
+    $this->addRequiredOption('file_src');
+    $this->addOption('with_delete', true);
+    $this->addOption('delete_label', 'remove the current file');
+    $this->addOption('image_field', null);
+    $this->addOption('image_ratio', null);
+    $this->addOption('invoker', null);
+    $this->addOption('template', '%file%<br />%input%<br />%delete% 
%delete_label%');
+    $this->addOption('form', null);
+  }
+
+  /**
+   * @param  string $name        The element name
+   * @param  string $value       The value displayed in this widget
+   * @param  array  $attributes  An array of HTML attributes to be merged with 
the default HTML attributes
+   * @param  array  $errors      An array of errors for the field
+   *
+   * @return string An HTML tag string
+   *
+   * @see sfWidgetForm
+   */
+  public function render($name, $value = null, $attributes = array(), $errors 
= array())
+  {
+    $input = parent::render($name, $value, $attributes, $errors);
+    
+    $object = $this->getOption('invoker');
+    
+    if (!$object->exists())
+    {
+      return $input;
+    }
+
+    if (class_exists('sfJSLibManager'))
+    {
+      sfJSLibManager::addLib('jcrop');
+    }
+
+    if ($this->getOption('with_delete'))
+    {
+      $deleteName = ']' == substr($name, -1) ? substr($name, 0, -1).'_delete]' 
: $name.'_delete';
+      $form = $this->getOption('form');
+
+      if ($form->getOption('embedded', false) && 
$form->getOption('parent_model', false))
+      {
+        $delete = $form->getOption('parent_model')->getDeleteLinkFor($object);
+        $deleteLabel = '';
+      }
+      else
+      {
+        $delete = $this->renderTag('input', array_merge(array('type' => 
'checkbox', 'name' => $deleteName), $attributes));
+        $deleteLabel = $this->renderContentTag('label', 
$this->getOption('delete_label'), array_merge(array('for' => 
$this->generateId($deleteName))));
+      }
+    }
+    else
+    {
+      $delete = '';
+      $deleteLabel = '';
+    }
+
+    return strtr($this->getOption('template') . $this->getJCropJS(), 
array('%input%' => $input, '%delete%' => $delete, '%delete_label%' => 
$deleteLabel, '%file%' => $this->getFileAsTag($attributes)));
+  }
+
+  protected function getFileAsTag($attributes)
+  {
+    $id = $this->getIdStub() . '_img';
+    
+    return false !== $this->getOption('file_src')
+      ?
+        $this->renderTag(
+          'img',
+          array_merge(
+            array(
+              'src' => $this->getOption('file_src'),
+              'id' => $id
+            ),
+            $attributes
+          )
+        )
+      :
+        '';
+    
+  }
+  
+  private function getJCropJS() {
+    $idStub = $this->getIdStub();
+    $ratio = $this->getOption('image_ratio') ? 'aspectRatio: ' . 
$this->getOption('image_ratio') . ',' : '';
+    
+    $js = "
+<script language=\"Javascript\">
+  jQuery(document).ready(function(){
+
+    jQuery('#{$idStub}_img').Jcrop({
+      $ratio
+      setSelect: [document.getElementById('{$idStub}_x1').value,
+                  document.getElementById('{$idStub}_y1').value,
+                  document.getElementById('{$idStub}_x2').value,
+                  document.getElementById('{$idStub}_y2').value
+                  ],
+      onChange: _jCropUpdateCoords" . ucfirst($idStub) . ",
+      onSelect: _jCropUpdateCoords" . ucfirst($idStub) . "
+    });
+
+  });
+  
+  function _jCropUpdateCoords" . ucfirst($idStub) . "(c) {
+    jQuery('#{$idStub}_x1').val(c.x);
+    jQuery('#{$idStub}_y1').val(c.y);
+    jQuery('#{$idStub}_x2').val(c.x2);
+    jQuery('#{$idStub}_y2').val(c.y2);
+  }
+</script>
+  ";
+    
+    return $js;
+  }
+
+  private function getIdStub() {
+    $form = $this->getOption('form');
+    $separator = '';
+
+    $imageName = $this->getOption('image_field');
+    $tableName = str_replace("[%s]", '', 
$form->getWidgetSchema()->getNameFormat());
+
+    if ($form->getOption('embedded'))
+    {
+      $parentTableName = 
$form->getOption('parent_model')->getTable()->getTableName();
+      
+      $separator = 'embedded_' . $tableName . '_' . $tableName .
+        '_' . $this->getOption('invoker')->getId();
+
+      $idStub = $parentTableName . '_' . $separator . '_' . $imageName;
+    }
+    else
+    {
+      $idStub = $tableName . '_' . $imageName;
+    }
+
+    return $idStub;
+  }
+}

-- 
You received this message because you are subscribed to the Google Groups 
"symfony SVN" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/symfony-svn?hl=en.


Reply via email to