To participate you must create an account on apostrophenow.org. If you have already done so, click Login.

Show
Ignore:
Timestamp:
08/08/10 12:18:34 (22 months ago)
Author:
tboutell
Message:

Merged the cropping branch back to trunk:

Cropping is now available in the media repository. Every time you select an image for use on the site you have the opportunity to crop it. The image size constraints of the slot are taken into account. This means that you can satisfy a given set of constraints by cropping a wide variety of images that would not have been eligible for selection before.

Crops of existing images appear in the database as separate aMediaItem objects but do not duplicate the original image file. This means that many crops of a single source image can exist without significant overhead.

The slug field of a cropped version of an image contains cropping parameters. When rendering the image, Symfony routes spot these and crop the original file as needed.

By approaching the problem this way we have avoided the need for code that takes advantage of the media repository's image selection capabilities to change in any way in order to take advantage of cropping.

The single-image-select behavior of Apostrophe has changed to accommodate the extra cropping step. Single select for PDFs and videos still uses the selectSingle partial because they cannot be cropped.

Thanks to Spike Broehm for his contributions to the cropping project.

Fixes #227

Location:
plugins/apostrophePlugin/trunk
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • plugins/apostrophePlugin/trunk

  • plugins/apostrophePlugin/trunk/lib/model/doctrine/PluginaMediaItem.class.php

    r1701 r1917  
    2020    } 
    2121    // Let the culture be the user's culture 
    22     return aZendSearch::saveInDoctrineAndLucene($this, null, $conn); 
     22    $result = aZendSearch::saveInDoctrineAndLucene($this, null, $conn); 
     23    $crops = $this->getCrops(); 
     24    foreach ($crops as $crop) 
     25    { 
     26      $crop->setTitle($this->getTitle()); 
     27      $crop->setDescription($this->getDescription()); 
     28      $crop->setCredit($this->getCredit()); 
     29      $crop->save(); 
     30    } 
     31    return $result; 
    2332  } 
    2433 
     
    3342    $ret = aZendSearch::deleteFromDoctrineAndLucene($this, null, $conn); 
    3443    $this->clearImageCache(); 
     44     
     45    $this->deleteCrops(); 
     46     
    3547    // Don't even think about trashing the original until we know 
    3648    // it's gone from the db and so forth 
     
    132144    $path = $this->getOriginalPath($this->getFormat()); 
    133145    $result = copy($file, $path); 
     146    // Crops are invalid if you replace the original image 
     147    $this->deleteCrops(); 
    134148    return $result; 
    135149  } 
     
    249263    $options = aDimensions::constrain($this->getWidth(), $this->getHeight(), $this->getFormat(), $options); 
    250264 
    251     return "aMediaBackend/image?" . http_build_query( 
    252       array("slug" => $this->slug, "width" => $options['width'], "height" => $options['height'],  
    253         "resizeType" => $options['resizeType'], "format" => $options['format'])); 
     265    $params = array("slug" => $this->slug, "width" => $options['width'], "height" => $options['height'],  
     266      "resizeType" => $options['resizeType'], "format" => $options['format']); 
     267 
     268    // check for null because 0 is valid 
     269    if (!is_null($options['cropLeft']) && !is_null($options['cropTop']) && !is_null($options['cropWidth']) && !is_null($options['cropHeight'])) 
     270    {       
     271      $params = array_merge( 
     272        $params, 
     273        array("cropLeft" => $options['cropLeft'], "cropTop" => $options['cropTop'], 
     274          "cropWidth" => $options['cropWidth'], "cropHeight" => $options['cropHeight']) 
     275      ); 
     276    } 
     277    return "aMediaBackend/image?" . http_build_query($params); 
     278  } 
     279   
     280  public function getCropThumbnailUrl() 
     281  {     
     282    $selectedConstraints = aMediaTools::getOption('selected_constraints'); 
     283     
     284    if ($aspectRatio = aMediaTools::getAspectRatio()) // this returns 0 if aspect-width and aspect-height were not set 
     285    { 
     286      $selectedConstraints = array_merge( 
     287        $selectedConstraints,  
     288        array('height' => floor($selectedConstraints['width'] / $aspectRatio)) 
     289      ); 
     290    } 
     291     
     292     
     293    $imageInfo = aMediaTools::getAttribute('imageInfo'); 
     294    if (isset($imageInfo[$this->id]['cropLeft']) && 
     295        isset($imageInfo[$this->id]['cropTop']) && isset($imageInfo[$this->id]['cropWidth']) && isset($imageInfo[$this->id]['cropHeight'])) 
     296    { 
     297      $selectedConstraints = array_merge( 
     298        $selectedConstraints,  
     299        array( 
     300          'cropLeft' => $imageInfo[$this->id]['cropLeft'], 
     301          'cropTop' => $imageInfo[$this->id]['cropTop'], 
     302          'cropWidth' => $imageInfo[$this->id]['cropWidth'], 
     303          'cropHeight' => $imageInfo[$this->id]['cropHeight'] 
     304        ) 
     305      ); 
     306    } 
     307       
     308    return $this->getScaledUrl($selectedConstraints); 
     309  } 
     310   
     311  // Crops of other images have periods in the slug. Real slugs are always [\w_]+ (well, the i18n equivalent) 
     312  public function isCrop() 
     313  { 
     314    return (strpos($this->slug, '.') !== false); 
     315  } 
     316   
     317  public function getCrops() 
     318  { 
     319    // This should perform well because there is an index on the slug and 
     320    // indexes are great with prefix queries 
     321    return $this->getTable()->createQuery('m')->where('m.slug LIKE ?', array($this->slug . '.%'))->execute(); 
     322     
     323  } 
     324   
     325  public function deleteCrops() 
     326  { 
     327    $crops = $this->getCrops(); 
     328    // Let's make darn sure the PHP stuff gets called rather than using a delete all trick of some sort 
     329    foreach ($crops as $crop) 
     330    { 
     331      $crop->delete(); 
     332    } 
     333  } 
     334   
     335  public function findOrCreateCrop($info) 
     336  { 
     337    $slug = $this->slug . '.' . $info['cropLeft'] . '.' . $info['cropTop'] . '.' . $info['cropWidth'] . '.' . $info['cropHeight']; 
     338    $crop = $this->getTable()->findOneBySlug($slug); 
     339    if (!$crop) 
     340    { 
     341      $crop = $this->copy(false); 
     342      $crop->slug = $slug; 
     343      $crop->width = $info['cropWidth']; 
     344      $crop->height = $info['cropHeight']; 
     345    } 
     346    return $crop; 
     347  } 
     348   
     349  public function getCroppingInfo() 
     350  { 
     351    $p = preg_split('/\./', $this->slug); 
     352    if (count($p) == 5) 
     353    { 
     354      return array('cropLeft' => $p[1], 'cropTop' => $p[2], 'cropWidth' => $p[3], 'cropHeight' => $p[4]); 
     355    } 
     356    else 
     357    { 
     358      return array(); 
     359    } 
     360  } 
     361   
     362  public function getCropOriginal() 
     363  { 
     364    if (!$this->isCrop()) 
     365    { 
     366      return $this; 
     367    } 
     368    $p = preg_split('/\./', $this->slug); 
     369    return $this->getTable()->findOneBySlug($p[0]); 
    254370  } 
    255371}