Skip to content
This repository has been archived by the owner on Nov 16, 2022. It is now read-only.

Commit

Permalink
Merge pull request #86 from davidyell/develop
Browse files Browse the repository at this point in the history
Release 0.3.0
  • Loading branch information
davidyell committed Mar 17, 2015
2 parents d72b88c + d81c0e8 commit d4776a6
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 14 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ All the documentation can be found in the [docs](docs) folder.
* [Customisation](docs/customisation.md)
* [Shell tasks](docs/shell.md)
* [Examples](docs/examples.md)
* [FAQ](docs/faq.md)

##Contribution
Please open a pull request or submit an issue if there is anything you would like to contribute. Please write a test for
Expand Down
20 changes: 15 additions & 5 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ $this->addBehavior('Proffer.Proffer', [
'photo' => [ // The name of your upload field
'root' => WWW_DIR . 'files', // Customise the root upload folder here, or omit to use the default
'dir' => 'photo_dir', // The name of the field to store the folder
'thumbnailSizes' => [
'thumbnailSizes' => [ // Declare your thumbnails
'square' => ['w' => 200, 'h' => 200], // Define the size and prefix of your thumbnails
'portrait' => ['w' => 100, 'h' => 300, 'crop' => true], // Crop will crop the image as well as resize it
],
Expand Down Expand Up @@ -53,14 +53,24 @@ There are a number of configuration options you can pass into the behaviour when
The database field which will store the name of the folder in which the files are uploaded.

###thumbnailSizes
**required** `array`
**optional** `array`
An array of sizes to create thumbnails of an uploaded image. The format is that the image prefix will be the array key and the sizes are the value as an array.
Eg, `'square' => ['w' => 200, 'h' => 200]` would create a thumbnail prefixed with `square_` and would be 100px x 100px.
If you do not specify the `thumbnailSizes` configuration option, no thumbnails will be created.

###root
**default:** `WWW_DIR . 'files'`
**optional:** defaults to, `WWW_DIR . 'files'`
Allows you to customise the root folder in which all the file upload folders and files will be created.

###thumbnailMethod
**default:** `gd`
Which Imagine engine to use to convert the images. Defaults to PHP's GD library. Can also be `imagick` and `gmagick`.
**optional:** defaults to, `gd`
Which Imagine engine to use to convert the images. Defaults to PHP's GD library. Can also be `imagick` and `gmagick`.

## Configuring your templates
You will need to make sure that your forms are using the file type so that the files can be uploaded.

```php
echo $this->Form->create($entity, ['type' => 'file']);
echo $this->Form->input('photo', ['type' => 'file']);
// etc
```
4 changes: 3 additions & 1 deletion docs/customisation.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ creating any thumbnails.
##Customising upload file names and paths
Using the `Proffer.afterPath` event you can hook into all the details about the file upload before it is processed. Using
this event you can change the name of the file and the upload path to match whatever convention you want. I have created
an example listener which is [available as an example](examples/UploadFilenameListener.md).
an example listener which is [available as an example](examples/UploadFilenameListener.md).

You would attach this listener in the same way as above, but there is no need to remove the existing listeners first.
4 changes: 4 additions & 0 deletions docs/examples/UploadFilenameListener.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class UploadFilenameListener implements EventListenerInterface
// Create a new filename using the id and the name of the entity
$newFilename = $event->subject()->get('id') . '_' . Inflector::slug($event->subject()->get('name')) . $ext;

// This would set the containing upload folder to `webroot/files/user_profile_pictures/<field>/<seed>/<file>`
// for every file uploaded through the table this listener was attached to.
$path->setTable('user_profile_pictures');

// If a seed is set in the data already, we'll use that rather than make a new one each time we upload
if (empty($event->subject()->get('image_dir'))) {
$path->setSeed(date('Y-m-d-His'));
Expand Down
62 changes: 62 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#Frequently asked questions
This manual page collects together all the frequent questions about the plugin, it's functionality and some of the more
common errors people might experience.

## Proffers scope
The scope of the plugin is the limit of the functionality it will provide.

First and foremost it is an upload plugin. This means it's core responsibility is to copy files from once place to
another. Which, in most cases, will be from a client machine to a server.

Additional functionality to this is the generation of various sizes of thumbnail and some associated tools. In this
capacity there are some events which process images and create the thumbnails. There are also some related shell tasks
to make thumbnail generation easier.

Some things which the plugin does not do are provide methods for linking images in the front-end of your website, such
as a helper. It's up to the developer to place the uploaded content in the front-end of the website. Nor will the plugin
interact with your admin to display uploaded images or anything like that.

Proffer will also not manage your file system for you. It can only upload images, and doesn't version them or anything
similar. This kind of functionality would need to be developed by the developer.

The provided thumbnail generation is basic. If you want to expand upon this, such as creating new types of thumbnail or
creating watermarked images you are encouraged to hook the events in the plugin and create your own code for generating
your customised thumbnails.

## Errors
If you are experiencing any of these errors, here are your solutions.

### Unknown type "proffer.file"
This has two primary causes.

The first is that in your `config/boostrap.php` you might have forgotten to include the
`'bootstrap' => true` when loading the plugin, which means the datatype isn't loaded.

```php
// config/bootstrap.php
Plugin::load('Proffer', ['bootstrap' => true]);
```

The second thing is that you might have forgotten to include the `_initializeSchema` method in your table class. This
method bind the data type class to the field.

```php
// src/Model/Table/Examples.php
protected function _initializeSchema(\Cake\Database\Schema\Table $table) {
$table->columnType('file','proffer.file');
return $table;
}
```

### File name is written to the database as "Array"
The thing to check is your form is using the file type, and your input is also a file type.

```php
echo $this->Form->input($entity, ['type' => 'file']);
echo $this->Form->input('file_upload', ['type' => 'file']);
// etc
```

## Still having trouble?
If you're still having trouble, head to `#cakephp` on Freenode.net and ask for help. A web chat client is available
on [the Freenode website](http://webchat.freenode.net/).
8 changes: 6 additions & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ It's always advised to lock your dependencies to a specific version number. You
or [read more about versioning on Composer.org](https://getcomposer.org/doc/01-basic-usage.md#package-versions).

## CakePHP
Then you'll need to load the plugin in your `config/bootstrap.php` file. `Plugin::load('Proffer', ['bootstrap' => true]);`.
Then you'll need to load the plugin in your `config/bootstrap.php` file.

```php
Plugin::load('Proffer', ['bootstrap' => true]);
```

## Database
Next you need to add the fields to your table. You'll want to add your file upload field, this will store the name of the
Expand All @@ -23,4 +27,4 @@ An example query to add columns might look like this for MySQL.
ALTER TABLE `teams`
ADD COLUMN `photo` VARCHAR(255),
ADD COLUMN `photo_dir` VARCHAR(255)
```
```
8 changes: 6 additions & 2 deletions src/Lib/ProfferPath.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ public function __construct(Table $table, Entity $entity, $field, $settings)
$this->setTable($table->alias());
$this->setField($field);
$this->setSeed($this->generateSeed($entity->get($settings['dir'])));
$this->setPrefixes($settings['thumbnailSizes']);

if (isset($settings['thumbnailSizes'])) {
$this->setPrefixes($settings['thumbnailSizes']);
}

$this->setFilename($entity->get($field));
}

Expand Down Expand Up @@ -88,7 +92,7 @@ public function getTable()
* @param string $table The name of the table the behaviour is dealing with.
* @return void
*/
protected function setTable($table)
public function setTable($table)
{
$this->table = strtolower($table);
}
Expand Down
14 changes: 10 additions & 4 deletions src/Model/Behavior/ProfferBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public function beforeSave(Event $event, Entity $entity, ArrayObject $options, P
$entity->set($settings['dir'], $path->getSeed());

// Don't generate thumbnails for non-images
if (getimagesize($path->fullPath()) !== false) {
if (getimagesize($path->fullPath()) !== false && isset($settings['thumbnailSizes'])) {
$this->makeThumbs($field, $path);
}
} else {
Expand Down Expand Up @@ -118,13 +118,19 @@ public function afterDelete(Event $event, Entity $entity, ArrayObject $options,

foreach ($settings['thumbnailSizes'] as $prefix => $dimensions) {
$filename = $path->fullPath($prefix);
unlink($filename);
if (file_exists($filename)) {
unlink($filename);
}
}

$filename = $path->fullPath();
unlink($filename);
if (file_exists($filename)) {
unlink($filename);
}

rmdir($path->getFolder());
if (file_exists($path->getFolder())) {
rmdir($path->getFolder());
}
}
}

Expand Down
160 changes: 160 additions & 0 deletions tests/TestCase/Model/Behavior/ProfferBehaviorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,43 @@ public function testAfterDelete()
$this->assertFileNotExists($testUploadPath . 'portrait_image_640x480.jpg');
}

public function testAfterDeleteWithMissingFiles()
{
$table = $this->getMock('Cake\ORM\Table', ['alias']);
$table->method('alias')
->willReturn('ProfferTest');

$Proffer = new ProfferBehavior($table, $this->config);

$entity = new Entity([
'photo' => 'image_640x480.jpg',
'photo_dir' => 'proffer_test'
]);

$path = $this->getProfferPathMock($table, $entity, 'photo');
$testUploadPath = $path->getFolder();

if (!file_exists($testUploadPath)) {
mkdir($testUploadPath, 0777, true);
}

copy(
Plugin::path('Proffer') . 'tests' . DS . 'Fixture' . DS . 'image_640x480.jpg',
$testUploadPath . 'image_640x480.jpg'
);

$Proffer->afterDelete(
$this->getMock('Cake\Event\Event', null, ['afterDelete']),
$entity,
new ArrayObject(),
$path
);

$this->assertFileNotExists($testUploadPath . 'image_640x480.jpg');
$this->assertFileNotExists($testUploadPath . 'square_image_640x480.jpg');
$this->assertFileNotExists($testUploadPath . 'portrait_image_640x480.jpg');
}

public function testBeforePathEvent()
{
$entityData = [
Expand Down Expand Up @@ -528,4 +565,127 @@ function ($param) use ($entity, $path) {
$path
);
}

public function testThumbsNotCreatedWhenNoSizes()
{
$table = $this->getMock('Cake\ORM\Table', ['alias']);
$table->method('alias')
->willReturn('ProfferTest');

$config = $this->config;
unset($config['photo']['thumbnailSizes']);

$entityData = [
'photo' => [
'name' => 'image_640x480.jpg',
'tmp_name' => Plugin::path('Proffer') . 'tests' . DS . 'Fixture' . DS . 'image_640x480.jpg',
'size' => 33000,
'error' => UPLOAD_ERR_OK
],
'photo_dir' => 'proffer_test'
];
$entity = new Entity($entityData);
$path = $this->getProfferPathMock($table, $entity, 'photo');

$Proffer = $this->getMockBuilder('Proffer\Model\Behavior\ProfferBehavior')
->setConstructorArgs([$table, $config])
->setMethods(['moveUploadedFile'])
->getMock();

$Proffer->expects($this->once())
->method('moveUploadedFile')
->willReturnCallback(function ($source, $destination) {
if (!file_exists(pathinfo($destination, PATHINFO_DIRNAME))) {
mkdir(pathinfo($destination, PATHINFO_DIRNAME), 0777, true);
}
return copy($source, $destination);
});

$Proffer->beforeSave(
$this->getMock('Cake\Event\Event', null, ['beforeSave']),
$entity,
new ArrayObject(),
$path
);

$this->assertEquals('image_640x480.jpg', $entity->get('photo'));
$this->assertEquals('proffer_test', $entity->get('photo_dir'));

$testUploadPath = $path->getFolder();

$this->assertFileExists($testUploadPath . 'image_640x480.jpg');
$this->assertFileNotExists($testUploadPath . 'portrait_image_640x480.jpg');
$this->assertFileNotExists($testUploadPath . 'square_image_640x480.jpg');
}

public function testChangingThePathUsingEvents()
{
$table = $this->getMock('Cake\ORM\Table', ['alias']);
$table->method('alias')
->willReturn('ProfferTest');

$listener = $this->getMockBuilder('Cake\Event\EventListenerInterface')
->setMethods(['implementedEvents', 'filename'])
->getMock();

$listener->expects($this->once())
->method('implementedEvents')
->willReturn(['Proffer.afterPath' => 'filename']);

$listener->expects($this->once())
->method('filename')
->willReturnCallback(function ($event, $path) {
$path->setTable('proffer_path_event_test');
$path->setSeed('proffer_event_test');
$path->setFilename('event_image_640x480.jpg');

$event->subject()['photo']['name'] = 'event_image_640x480.jpg';

return $path;
});

$table->eventManager()->on($listener);

$entityData = [
'photo' => [
'name' => 'image_640x480.jpg',
'tmp_name' => Plugin::path('Proffer') . 'tests' . DS . 'Fixture' . DS . 'image_640x480.jpg',
'size' => 33000,
'error' => UPLOAD_ERR_OK
],
'photo_dir' => 'proffer_test'
];
$entity = new Entity($entityData);
$path = $this->getProfferPathMock($table, $entity, 'photo');

$Proffer = $this->getMockBuilder('Proffer\Model\Behavior\ProfferBehavior')
->setConstructorArgs([$table, $this->config])
->setMethods(['moveUploadedFile'])
->getMock();

$Proffer->expects($this->once())
->method('moveUploadedFile')
->willReturnCallback(function ($source, $destination) {
if (!file_exists(pathinfo($destination, PATHINFO_DIRNAME))) {
mkdir(pathinfo($destination, PATHINFO_DIRNAME), 0777, true);
}
return copy($source, $destination);
});

$Proffer->beforeSave(
$this->getMock('Cake\Event\Event', null, ['beforeSave']),
$entity,
new ArrayObject(),
$path
);

$this->assertEquals('event_image_640x480.jpg', $entity->get('photo'));
$this->assertEquals('proffer_event_test', $entity->get('photo_dir'));

$testUploadPath = $path->getFolder();

$this->assertFileExists($testUploadPath . 'event_image_640x480.jpg');
$this->assertFileExists($testUploadPath . 'portrait_event_image_640x480.jpg');
$this->assertFileExists($testUploadPath . 'square_event_image_640x480.jpg');
}
}

0 comments on commit d4776a6

Please sign in to comment.