1
0
mirror of https://github.com/owncloudarchive/contacts.git synced 2025-01-19 08:52:22 +01:00

Mostly done refactoring Photo controller

This commit is contained in:
Thomas Tanghus 2014-03-20 01:55:48 +01:00
parent d31547b12a
commit 05acb6e87a
9 changed files with 432 additions and 147 deletions

View File

@ -243,7 +243,7 @@ $this->create('contacts_contact_photo', 'addressbook/{backend}/{addressBookId}/c
->requirements(array('backend', 'addressbook', 'contactId')); ->requirements(array('backend', 'addressbook', 'contactId'));
$this->create('contacts_upload_contact_photo', 'addressbook/{backend}/{addressBookId}/contact/{contactId}/photo') $this->create('contacts_upload_contact_photo', 'addressbook/{backend}/{addressBookId}/contact/{contactId}/photo')
->post() ->put()
->action( ->action(
function($params) { function($params) {
session_write_close(); session_write_close();

View File

@ -765,29 +765,26 @@ OC.Contacts = OC.Contacts || {
} }
});*/ });*/
$('#contactphoto_fileupload').on('click', function(event, metadata) { $('#contactphoto_fileupload').on('click', function(event, metadata) {
var form = $('#file_upload_form');
var url = OC.generateUrl( var url = OC.generateUrl(
'apps/contacts/addressbook/{backend}/{addressBookId}/contact/{contactId}/photo', 'apps/contacts/addressbook/{backend}/{addressBookId}/contact/{contactId}/photo',
{backend: metadata.backend, addressBookId: metadata.addressBookId, contactId: metadata.contactId} {backend: metadata.backend, addressBookId: metadata.addressBookId, contactId: metadata.contactId}
); );
form.attr('action', url); $(this).fileupload('option', 'url', url);
}).on('change', function() { }).fileupload({
console.log('#contactphoto_fileupload, change'); singleFileUploads: true,
self.uploadPhoto(this.files); multipart: false,
}); dataType: 'json',
type: 'PUT',
var target = $('#file_upload_target'); done: function (e, data) {
target.load(function() { console.log('Upload done:', data);
var response = $.parseJSON(target.contents().text());
if(response && response.status === 'success') {
console.log('response', response);
self.editPhoto( self.editPhoto(
response.data.metadata, data.result.metadata,
response.data.tmp data.result.tmp
); );
//alert('File: ' + file.tmp + ' ' + file.name + ' ' + file.mime); },
} else if(response) { fail: function(e, data) {
$(document).trigger('status.contacts.error', response); console.log('fail', data);
OC.notify({message:data.errorThrown + ': ' + data.textStatus});
} }
}); });
@ -1491,34 +1488,14 @@ OC.Contacts = OC.Contacts || {
update: function() { update: function() {
console.log('update'); console.log('update');
}, },
uploadPhoto:function(filelist) {
console.log('uploadPhoto');
if(!filelist) {
$(document).trigger('status.contacts.error', {message:t('contacts','No files selected for upload.')});
return;
}
var file = filelist[0];
var form = $('#file_upload_form');
if(file.size > $('#max_upload').val()) {
$(document).trigger('status.contacts.error', {
message:t(
'contacts',
'The file you are trying to upload exceed the maximum size for file uploads on this server.')
});
return;
} else {
form.submit();
}
},
cloudPhotoSelected:function(metadata, path) { cloudPhotoSelected:function(metadata, path) {
var self = this; var self = this;
console.log('cloudPhotoSelected', metadata); console.log('cloudPhotoSelected', metadata);
var url = OC.generateUrl( var url = OC.generateUrl(
'apps/contacts/addressbook/{backend}/{addressBookId}/contact/{contactId}/photo/cacheFS', 'apps/contacts/addressbook/{backend}/{addressBookId}/contact/{contactId}/photo/cacheFS',
{backend: metadata.backend, addressBookId: metadata.addressBookId, contactId: metadata.contactId, path: path} {backend: metadata.backend, addressBookId: metadata.addressBookId, contactId: metadata.contactId}
); );
var jqXHR = $.getJSON(url, function(response) { var jqXHR = $.getJSON(url, {path: path}, function(response) {
console.log('response', response); console.log('response', response);
response = self.storage.formatResponse(response, jqXHR); response = self.storage.formatResponse(response, jqXHR);
if(!response.error) { if(!response.error) {

View File

@ -385,6 +385,21 @@ class Contact extends VObject\VCard implements IPIMObject {
return true; return true;
} }
/**
* Get the PHOTO or LOGO
*
* @return \OCP\Image|null
*/
public function getPhoto() {
$image = new \OCP\Image();
if (isset($this->PHOTO) && $image->loadFromBase64((string)$this->PHOTO)) {
return $image;
} elseif (isset($this->LOGO) && $image->loadFromBase64((string)$this->LOGO)) {
return $image;
}
}
/** /**
* Get a property index in the contact by the checksum of its serialized value * Get a property index in the contact by the checksum of its serialized value
* *

View File

@ -13,6 +13,7 @@ use OCA\Contacts\App,
OCA\Contacts\JSONResponse, OCA\Contacts\JSONResponse,
OCA\Contacts\ImageResponse, OCA\Contacts\ImageResponse,
OCA\Contacts\Utils\Properties, OCA\Contacts\Utils\Properties,
OCA\Contacts\Utils\TemporaryPhoto,
OCA\Contacts\Controller; OCA\Contacts\Controller;
/** /**
@ -79,73 +80,24 @@ class ContactPhotoController extends Controller {
*/ */
public function uploadPhoto() { public function uploadPhoto() {
$params = $this->request->urlParams; $params = $this->request->urlParams;
$maxSize = isset($this->request->post['maxSize']) ? $this->request->post['maxSize'] : 400;
$tempPhoto = TemporaryPhoto::get(
$this->server,
TemporaryPhoto::PHOTO_UPLOADED,
$this->request
);
$response = new JSONResponse(); $response = new JSONResponse();
if (!isset($this->request->files['imagefile'])) { return $response->setParams(array(
$response->bailOut(App::$l10n->t('No file was uploaded. Unknown error')); 'tmp'=>$tempPhoto->getKey(),
return $response; 'metadata' => array(
} 'contactId'=> $params['contactId'],
'addressBookId'=> $params['addressBookId'],
$file = $this->request->files['imagefile']; 'backend'=> $params['backend'],
$error = $file['error']; ),
if($error !== UPLOAD_ERR_OK) {
$errors = array(
0=>App::$l10n->t("There is no error, the file uploaded with success"),
1=>App::$l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'),
2=>App::$l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"),
3=>App::$l10n->t("The uploaded file was only partially uploaded"),
4=>App::$l10n->t("No file was uploaded"),
6=>App::$l10n->t("Missing a temporary folder")
);
$response->bailOut($errors[$error]);
return $response;
}
if(!file_exists($file['tmp_name'])) {
$response->bailOut('Temporary file: \''.$file['tmp_name'].'\' has gone AWOL?');
return $response;
}
$tmpkey = 'contact-photo-'.md5(basename($file['tmp_name']));
$image = new \OCP\Image();
if(!$image->loadFromFile($file['tmp_name'])) {
$response->bailOut(App::$l10n->t('Couldn\'t load temporary image: ').$file['tmp_name']);
return $response;
}
if(!$image->fixOrientation()) { // No fatal error so we don't bail out.
$response->debug('Couldn\'t save correct image orientation: '.$tmpkey);
}
if($image->valid()) {
if($image->height() > $maxSize || $image->width() > $maxSize) {
$image->resize($maxSize);
}
} else {
$response->bailOut(App::$l10n->t('Uploaded image is invalid'));
}
if(!$this->server->getCache()->set($tmpkey, $image->data(), 600)) {
$response->bailOut(App::$l10n->t('Couldn\'t save temporary image: ').$tmpkey);
return $response;
}
$response->setData(array(
'status' => 'success',
'data' => array(
'tmp'=>$tmpkey,
'metadata' => array(
'contactId'=> $params['contactId'],
'addressBookId'=> $params['addressBookId'],
'backend'=> $params['backend'],
),
)
)); ));
return $response;
} }
/** /**
@ -158,32 +110,24 @@ class ContactPhotoController extends Controller {
public function cacheCurrentPhoto() { public function cacheCurrentPhoto() {
$params = $this->request->urlParams; $params = $this->request->urlParams;
$response = new JSONResponse(); $response = new JSONResponse();
$maxSize = isset($this->request->get['maxSize']) ? $this->request->get['maxSize'] : 400;
$photoResponse = $this->getPhoto($maxSize); $addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
$contact = $addressBook->getChild($params['contactId']);
if(!$photoResponse instanceof ImageResponse) { $tempPhoto = TemporaryPhoto::get(
return $photoResponse; $this->server,
} TemporaryPhoto::PHOTO_CURRENT,
$contact
);
$data = $photoResponse->render(); return $response->setParams(array(
$tmpkey = 'contact-photo-' . $params['contactId']; 'tmp'=>$tempPhoto->getKey(),
if(!$this->server->getCache()->set($tmpkey, $data, 600)) {
$response->bailOut(App::$l10n->t('Couldn\'t save temporary image: ').$tmpkey);
return $response;
}
$response->setParams(array(
'tmp'=>$tmpkey,
'metadata' => array( 'metadata' => array(
'contactId'=> $params['contactId'], 'contactId'=> $params['contactId'],
'addressBookId'=> $params['addressBookId'], 'addressBookId'=> $params['addressBookId'],
'backend'=> $params['backend'], 'backend'=> $params['backend'],
), ),
)); ));
return $response;
} }
/** /**
@ -202,39 +146,20 @@ class ContactPhotoController extends Controller {
$response->bailOut(App::$l10n->t('No photo path was submitted.')); $response->bailOut(App::$l10n->t('No photo path was submitted.'));
} }
$localpath = \OC\Files\Filesystem::getLocalFile($this->request->get['path']); $tempPhoto = TemporaryPhoto::get(
$tmpkey = 'contact-photo-' . $params['contactId']; $this->server,
TemporaryPhoto::PHOTO_FILESYSTEM,
$this->request->get['path']
);
if(!file_exists($localpath)) { return $response->setParams(array(
return $response->bailOut(App::$l10n->t('File doesn\'t exist:').$localpath); 'tmp'=>$tempPhoto->getKey(),
}
$image = new \OCP\Image();
if(!$image) {
return $response->bailOut(App::$l10n->t('Error loading image.'));
}
if(!$image->loadFromFile($localpath)) {
return $response->bailOut(App::$l10n->t('Error loading image.'));
}
if($image->width() > $maxSize || $image->height() > $maxSize) {
$image->resize($maxSize); // Prettier resizing than with browser and saves bandwidth.
}
if(!$image->fixOrientation()) { // No fatal error so we don't bail out.
$response->debug('Couldn\'t save correct image orientation: '.$localpath);
}
if(!$this->server->getCache()->set($tmpkey, $image->data(), 600)) {
return $response->bailOut('Couldn\'t save temporary image: '.$tmpkey);
}
return $response->setData(array(
'tmp'=>$tmpkey,
'metadata' => array( 'metadata' => array(
'contactId'=> $params['contactId'], 'contactId'=> $params['contactId'],
'addressBookId'=> $params['addressBookId'], 'addressBookId'=> $params['addressBookId'],
'backend'=> $params['backend'], 'backend'=> $params['backend'],
), ),
)); ));
} }
/** /**

View File

@ -0,0 +1,180 @@
<?php
/**
* ownCloud - TemporaryPhoto
*
* @author Thomas Tanghus
* @copyright 2014 Thomas Tanghus (thomas@tanghus.net)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Contacts\Utils;
/**
* This class is used for getting a contact photo for cropping.
*/
abstract class TemporaryPhoto {
const MAX_SIZE = 400;
const PHOTO_CURRENT = 0;
const PHOTO_FILESYSTEM = 1;
const PHOTO_UPLOADED = 2;
/**
* @var \OCP\IServerContainer
*/
protected $server;
/**
* @var OCP\Image
*/
protected $image;
/**
* Cache key for temporary storage.
*
* @var string
*/
protected $key;
/**
* Whether normalizePhoto() has already been called.
*
* @var bool
*/
protected $normalized;
/**
* Whether the photo is cached.
*
* @var bool
*/
protected $cached;
/**
* Photo loader classes.
*
* @var array
*/
static public $classMap = array(
'OCA\\Contacts\\Utils\\TemporaryPhoto\\Contact',
'OCA\\Contacts\\Utils\\TemporaryPhoto\\FileSystem',
'OCA\\Contacts\\Utils\\TemporaryPhoto\\Uploaded',
);
/**
* Always call parents ctor:
* parent::__construct($server);
*/
public function __construct(\OCP\IServerContainer $server) {
$this->server = $server;
}
/**
* Returns an instance of a subclass of this class
*
* @param \OCP\IServerContainer $server
* @param int $type One of the pre-defined types.
* @param mixed $data Whatever data is needed to load the photo.
*/
public static function get(\OCP\IServerContainer $server, $type, $data) {
if (isset(self::$classMap[$type])) {
return new self::$classMap[$type]($server, $data);
} else {
// TODO: Return a "null object"
return new self($data);
}
}
/**
* Do what's needed to get the image from storage
* depending on the type.
* After this method is called $this->image must hold an
* instance of \OCP\Image.
*/
protected abstract function processImage();
/**
* Whether this image is valied
*
* @return bool
*/
public function isValid() {
return (($this->image instanceof \OCP\Image) && $this->image->valid());
}
/**
* Get the key to the cache image.
*
* @return string
*/
public function getKey() {
$this->cachePhoto();
return $this->key;
}
/**
* Get normalized image.
*
* @return \OCP\Image
*/
public function getPhoto() {
$this->normalizePhoto();
return $this->image;
}
/**
* Save image data to cache and return the key
*
* @return string
*/
private function cachePhoto() {
if ($this->cached) {
return;
}
if (!$this->image instanceof \OCP\Image) {
$this->processImage();
}
$this->normalizePhoto();
$data = $this->image->data();
$this->key = uniqid('photo-');
$this->server->getCache()->set($this->key, $data, 600);
}
/**
* Resize and rotate the image if needed.
*/
private function normalizePhoto() {
if($this->normalized) {
return;
}
$this->image->fixOrientation();
if ($this->image->height() > self::MAX_SIZE
|| $this->image->width() > self::MAX_SIZE
) {
$this->image->resize(self::MAX_SIZE);
}
$this->normalized = true;
}
}

View File

@ -0,0 +1,61 @@
<?php
/**
* ownCloud - Load contact PHOTO or LOGO
*
* @author Thomas Tanghus
* @copyright 2014 Thomas Tanghus (thomas@tanghus.net)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Contacts\Utils\TemporaryPhoto;
use OCA\Contacts\Contact as ContactObject,
OCA\Contacts\Utils\TemporaryPhoto as AbstractTemporaryPhoto;
/**
* This class loads the PHOTO or LOGO property from a contact.
*/
class Contact extends AbstractTemporaryPhoto {
/**
* The Contact object to load the image from
*
* @var OCA\Contacts\Contact
*/
protected $contact;
public function __construct(\OCP\IServerContainer $server, $contact) {
if (!$contact instanceof ContactObject) {
throw new \Exception(
__METHOD__
. ' Second argument must be an instance of OCA\\Contacts\\Contact'
);
}
parent::__construct($server);
$this->contact = $contact;
$this->processImage();
}
/**
* Do what's needed to get the image from storage
* depending on the type.
*/
protected function processImage() {
$this->image = $this->contact->getPhoto();
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* ownCloud - Load an image from the virtual file system.
*
* @author Thomas Tanghus
* @copyright 2014 Thomas Tanghus (thomas@tanghus.net)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Contacts\Utils\TemporaryPhoto;
use OCA\Contacts\Contact as ContactObject,
OCA\Contacts\Utils\TemporaryPhoto as AbstractTemporaryPhoto;
/**
* This class loads an image from the virtual file system.
*/
class FileSystem extends AbstractTemporaryPhoto {
/**
* The virtual file system path to load the image from
*
* @var string
*/
protected $path;
public function __construct(\OCP\IServerContainer $server, $path) {
\OCP\Util::writeLog('contacts', __METHOD__.' path: ' . $path, \OCP\Util::DEBUG);
if (!is_string($path)) {
throw new \Exception(
__METHOD__ . ' Second argument must a string'
);
}
parent::__construct($server);
$this->path = $path;
$this->processImage();
}
/**
* Load the image.
*/
protected function processImage() {
$localpath = \OC\Files\Filesystem::getLocalFile($this->path);
$this->image = new \OCP\Image();
$this->image->loadFromFile($localpath);
}
}

View File

@ -0,0 +1,64 @@
<?php
/**
* ownCloud - Load an uploaded image from system.
*
* @author Thomas Tanghus
* @copyright 2014 Thomas Tanghus (thomas@tanghus.net)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Contacts\Utils\TemporaryPhoto;
use OCA\Contacts\Contact as ContactObject,
OCA\Contacts\Utils\TemporaryPhoto as AbstractTemporaryPhoto;
/**
* This class loads an image from the virtual file system.
*/
class Uploaded extends AbstractTemporaryPhoto {
/**
* The request to read the data from
*
* @var \OCP\IRequest
*/
protected $input;
public function __construct(\OCP\IServerContainer $server, \OCP\IRequest $request) {
\OCP\Util::writeLog('contacts', __METHOD__, \OCP\Util::DEBUG);
if (!$request instanceOf \OCP\IRequest) {
throw new \Exception(
__METHOD__ . ' Second argument must be an instance of \\OCP\\IRequest'
);
}
parent::__construct($server);
$this->request = $request;
$this->processImage();
}
/**
* Load the image.
*/
protected function processImage() {
$this->image = new \OCP\Image();
\OCP\Util::writeLog('contacts', __METHOD__ . ', Content-Type: ' . $this->request->getHeader('Content-Type'), \OCP\Util::DEBUG);
\OCP\Util::writeLog('contacts', __METHOD__ . ', Content-Length: ' . $this->request->getHeader('Content-Length'), \OCP\Util::DEBUG);
$this->image->loadFromFileHandle($this->request->put);
}
}

View File

@ -117,7 +117,7 @@
</div> </div>
</div> </div>
</div> </div>
<form class="float" id="file_upload_form" action="<?php print_unescaped(OCP\Util::linkTo('contacts', 'ajax/uploadphoto.php')); ?>" method="post" enctype="multipart/form-data" target="file_upload_target"> <form class="float" id="file_upload_form" action="<?php print_unescaped(OCP\Util::linkTo('contacts', 'ajax/uploadphoto.php')); ?>" method="put" target="file_upload_target">
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>"> <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>">
<input type="hidden" name="MAX_FILE_SIZE" value="<?php p($_['uploadMaxFilesize']) ?>" id="max_upload"> <input type="hidden" name="MAX_FILE_SIZE" value="<?php p($_['uploadMaxFilesize']) ?>" id="max_upload">
<input type="hidden" class="max_human_file_size" value="(max <?php p($_['uploadMaxHumanFilesize']); ?>)"> <input type="hidden" class="max_human_file_size" value="(max <?php p($_['uploadMaxHumanFilesize']); ?>)">