Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save emersonthis/b5149b57de940943fcc1 to your computer and use it in GitHub Desktop.
Save emersonthis/b5149b57de940943fcc1 to your computer and use it in GitHub Desktop.
Questions about CakePHP FileStorage plugin

##1. Implementing delete I've updated ListingPhoto.php below with my code to delete files.

    public function afterDelete() {
        StorageManager::adapter($this->record['Photo']['adapter'])->delete($this->record['Photo']['path']);
    }

I copied this directly from the docs here but when I run it I get the following error: Unable to remove the "images/.../.../...jpg" key.

I checked and the path to the file is correct. And I don't think it's a permissions problem because PHP wrote the files to that location already.

Update 1: I think the root of the problem is that my code is telling Gaufrette to do this:

    public function delete($key)
    {
        if ($this->isDirectory($key)) {
            return rmdir($this->computePath($key));
        }
        return unlink($this->computePath($key));
    }

I'm creating multiple versions of images, and rmdir doesn't work when the directory isn't empty.

##2. Validation now working You can see in my ListingPhoto model that I am trying to restrict which files can be uploaded...

    public $actsAs = array(
        'FileStorage.UploadValidator' => array(
            'allowedExtensions' => array(
                'jpg',
                'png'

            )
        )
    );

...but when I upload a GIF file it works fine.

Update1: After further testing, I'm pretty confident that this is a config problem, not a bug in the validation. I hacked the code to log out the settings it is using, they are definitely defaults (or something other than what I'm trying to set):

(
    [localFile] => 1
    [validate] =>
    [allowedExtensions] => Array
        (
            [0] => jpg
            [1] => jpeg
            [2] => png
            [3] => gif
        )

)

Update2 I still don't know why it's happening, but I'm pretty sure that the settings are getting inherited from the parent class ImageStorage, which has the following:

	public $actsAs = array(
		'Imagine.Imagine',
		'FileStorage.UploadValidator' => array(
			'localFile' => true,
			'validate' => false,
			'allowedExtensions' => array('jpg', 'jpeg', 'png', 'gif')
		),
	);

It would seem like I'm overriding this in my ListingPhoto class, but I don't know enough about how inheritance interacts with behaviors in CakePHP.

<?php foreach ($listing['Photo'] as $key=>$photo) : ?>
<?php echo $this->Image->display($photo, 'small');
//This is looking in: http://example.dev/images/ListingPhoto/58/16/04/5487c0b38d2040a199df426fa9fe6d6a/5487c0b38d2040a199df426fa9fe6d6a.19e760eb.jpg
//But my file are currently appearing in app/tmp/imgages/ListingPhoto
//I tried creating a symlink but instead of 404 errors I just get 403 errors
?>
<?php endforeach; ?>
<form action="/listings/upload_photos.json" class="dropzone" id="my-awesome-dropzone">
<input type="hidden" name="data[Photo][listing_id]" value="<?php echo $listing['Listing']['ID']; ?>" />
</form>
<button class="submit-btn" id="dz-submit"><?php echo __('Upload photos'); ?></button>
<script>
/* Here we configure the form that Dropzone.js will generate for sending the file */
Dropzone.options.myAwesomeDropzone = { // The camelized version of the ID of the form element
// The configuration we've talked about above
autoProcessQueue: false,
uploadMultiple: true,
parallelUploads: 100,
maxFiles: 100,
paramName: 'data[Photo][file]', //this is name attribute for the file field
... //callbacks, etc...
</script>
/* Setup for FileStorage */
App::uses('CakeEventManager', 'Event');
App::uses('FileStorageUtils', 'FileStorage.Lib/Utility');
App::uses('StorageManager', 'FileStorage.Lib');
//App::uses('LocalImageProcessingListener', 'FileStorage.Event');
App::uses('ImageProcessingListener', 'FileStorage.Event');
App::uses('LocalFileStorageListener', 'FileStorage.Event');
App::uses('FileStorageListener', 'FileStorage.Event');
// Only required if you're *NOT* using composer or another autoloader!
spl_autoload_register(__NAMESPACE__ .'\FileStorageUtils::gaufretteLoader');
$listener = new LocalFileStorageListener();
CakeEventManager::instance()->attach($listener);
// For automated image processing you'll have to attach this listener as well
$listener = new ImageProcessingListener();
CakeEventManager::instance()->attach($listener);
Configure::write('Media', array(
// Configure the `basePath` for the Local adapter, not needed when not using it
'basePath' => APP . 'FileStorage' . DS,
// Configure image versions on a per model base
'imageSizes' => array(
'ListingPhoto' => array(
'large' => array(
'thumbnail' => array(
'mode' => 'inbound',
'width' => 800,
'height' => 800)),
'medium' => array(
'thumbnail' => array(
'mode' => 'inbound',
'width' => 200,
'height' => 200
)
),
'small' => array(
'thumbnail' => array(
'mode' => 'inbound',
'width' => 80,
'height' => 80
)
)
)
)
));
// This is very important! The hashes are needed to calculate the image versions!
App::uses('ClassRegistry', 'Utility');
ClassRegistry::init('FileStorage.ImageStorage')->generateHashes();
StorageManager::config('Local', array(
'adapterOptions' => array(TMP, true),
'adapterClass' => '\Gaufrette\Adapter\Local',
'class' => '\Gaufrette\Filesystem')
);
public $hasMany = array(
'Photo' => array(
'className' => 'ListingPhoto',
'foreignKey' => 'foreign_key'
)
);
public function delete_photo( $photo_id = null) {
$errorMessages = array();
// #only accept post requests
// if ( !$this->request->is('post') )
// return;
$photo = $this->Listing->Photo->findById($photo_id);
//TODO: Check if current user owns this listing
//DELETE THE PHOTO
if ( $this->Listing->Photo->delete($photo['Photo']['id']) ) {
$this->set('status', 'OK');
} else {
$this->set('status', 'ERROR');
$this->set('errorMessage', 'Problem deleting photo');
}
$this->set('_serialize', array('status', 'errorMessages'));
}
public function upload_photos( $listing_id = null) {
$this->log($this->request->data);
#only accept post requests
if ( !$this->request->is('post') )
return;
//TODO: Check if current user owns this listing, etc
foreach ($this->request->data['Photo']['file'] as $file) {
$this->Listing->Photo->upload($listing_id, array('Photo'=>array('file'=>$file) ) );
}
//TODO: Return error messages
$listing = $this->Listing->findById( $listing_id );
$this->log($listing);
$this->set('listing', $listing);
$this->set('_serialize', array('listing'));
}
App::uses('ImageStorage', 'FileStorage.Model');
class ListingPhoto extends ImageStorage {
public $actsAs = array(
'FileStorage.UploadValidator' => array(
'allowedExtensions' => array(
'jpg',
'png'
)
)
);
public function upload($listing_id, $data) {
$data[$this->alias]['adapter'] = 'Local';
$data[$this->alias]['model'] = 'ListingPhoto';
$data[$this->alias]['foreign_key'] = $listing_id;
$this->create();
return $this->save($data);
}
public function afterDelete() {
StorageManager::adapter($this->record['Photo']['adapter'])->delete($this->record['Photo']['path']);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment