Added avatar image upload
This commit is contained in:
parent
e71f37238c
commit
820c929ab3
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
use Kanboard\Core\ObjectStorage\ObjectStorageException;
|
||||
use Kanboard\Core\Thumbnail;
|
||||
|
||||
/**
|
||||
* Avatar File Controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AvatarFile extends Base
|
||||
{
|
||||
/**
|
||||
* Show Avatar image and send aggressive caching headers
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
$user_id = $this->request->getIntegerParam('user_id');
|
||||
$size = $this->request->getStringParam('size', 48);
|
||||
$filename = $this->avatarFile->getFilename($user_id);
|
||||
$etag = md5($filename.$size);
|
||||
|
||||
$this->response->cache(365 * 86400, $etag);
|
||||
$this->response->contentType('image/jpeg');
|
||||
|
||||
if ($this->request->getHeader('If-None-Match') !== '"'.$etag.'"') {
|
||||
$this->render($filename, $size);
|
||||
} else {
|
||||
$this->response->status(304);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render thumbnail from object storage
|
||||
*
|
||||
* @access private
|
||||
* @param string $filename
|
||||
* @param integer $size
|
||||
*/
|
||||
private function render($filename, $size)
|
||||
{
|
||||
try {
|
||||
$blob = $this->objectStorage->get($filename);
|
||||
|
||||
Thumbnail::createFromString($blob)
|
||||
->resize($size, $size)
|
||||
->toOutput();
|
||||
} catch (ObjectStorageException $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -405,4 +405,41 @@ class User extends Base
|
|||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display avatar page
|
||||
*/
|
||||
public function avatar()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->helper->layout->user('user/avatar', array(
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload Avatar
|
||||
*/
|
||||
public function uploadAvatar()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
if (! $this->avatarFile->uploadFile($user['id'], $this->request->getFileInfo('avatar'))) {
|
||||
$this->flash->failure(t('Unable to upload the file.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('user', 'avatar', array('user_id' => $user['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Avatar image
|
||||
*/
|
||||
public function removeAvatar()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$user = $this->getUser();
|
||||
$this->avatarFile->remove($user['id']);
|
||||
$this->response->redirect($this->helper->url->to('user', 'avatar', array('user_id' => $user['id'])));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ use Pimple\Container;
|
|||
* @property \Kanboard\Formatter\GroupAutoCompleteFormatter $groupAutoCompleteFormatter
|
||||
* @property \Kanboard\Model\Action $action
|
||||
* @property \Kanboard\Model\ActionParameter $actionParameter
|
||||
* @property \Kanboard\Model\AvatarFile $avatarFile
|
||||
* @property \Kanboard\Model\Board $board
|
||||
* @property \Kanboard\Model\Category $category
|
||||
* @property \Kanboard\Model\Color $color
|
||||
|
|
|
|||
|
|
@ -13,6 +13,24 @@ use Kanboard\Core\Csv;
|
|||
*/
|
||||
class Response extends Base
|
||||
{
|
||||
/**
|
||||
* Send headers to cache a resource
|
||||
*
|
||||
* @access public
|
||||
* @param integer $duration
|
||||
* @param string $etag
|
||||
*/
|
||||
public function cache($duration, $etag = '')
|
||||
{
|
||||
header('Pragma: cache');
|
||||
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $duration) . ' GMT');
|
||||
header('Cache-Control: public, max-age=' . $duration);
|
||||
|
||||
if ($etag) {
|
||||
header('ETag: "' . $etag . '"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send no cache headers
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Core;
|
||||
|
||||
/**
|
||||
* Thumbnail Generator
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Thumbnail
|
||||
{
|
||||
protected $metadata = array();
|
||||
protected $srcImage;
|
||||
protected $dstImage;
|
||||
|
||||
/**
|
||||
* Create a thumbnail from a local file
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $filename
|
||||
* @return Thumbnail
|
||||
*/
|
||||
public static function createFromFile($filename)
|
||||
{
|
||||
$self = new static();
|
||||
$self->fromFile($filename);
|
||||
return $self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a thumbnail from a string
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $blob
|
||||
* @return Thumbnail
|
||||
*/
|
||||
public static function createFromString($blob)
|
||||
{
|
||||
$self = new static();
|
||||
$self->fromString($blob);
|
||||
return $self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the local image file in memory with GD
|
||||
*
|
||||
* @access public
|
||||
* @param string $filename
|
||||
* @return Thumbnail
|
||||
*/
|
||||
public function fromFile($filename)
|
||||
{
|
||||
$this->metadata = getimagesize($filename);
|
||||
$this->srcImage = imagecreatefromstring(file_get_contents($filename));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the image blob in memory with GD
|
||||
*
|
||||
* @access public
|
||||
* @param string $blob
|
||||
* @return Thumbnail
|
||||
*/
|
||||
public function fromString($blob)
|
||||
{
|
||||
if (!function_exists('getimagesizefromstring')) {
|
||||
$uri = 'data://application/octet-stream;base64,' . base64_encode($blob);
|
||||
$this->metadata = getimagesize($uri);
|
||||
} else {
|
||||
$this->metadata = getimagesizefromstring($blob);
|
||||
}
|
||||
|
||||
$this->srcImage = imagecreatefromstring($blob);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize the image
|
||||
*
|
||||
* @access public
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return Thumbnail
|
||||
*/
|
||||
public function resize($width = 250, $height = 100)
|
||||
{
|
||||
$srcWidth = $this->metadata[0];
|
||||
$srcHeight = $this->metadata[1];
|
||||
$dstX = 0;
|
||||
$dstY = 0;
|
||||
|
||||
if ($width == 0 && $height == 0) {
|
||||
$width = 100;
|
||||
$height = 100;
|
||||
}
|
||||
|
||||
if ($width > 0 && $height == 0) {
|
||||
$dstWidth = $width;
|
||||
$dstHeight = floor($srcHeight * ($width / $srcWidth));
|
||||
$this->dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
|
||||
} elseif ($width == 0 && $height > 0) {
|
||||
$dstWidth = floor($srcWidth * ($height / $srcHeight));
|
||||
$dstHeight = $height;
|
||||
$this->dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
|
||||
} else {
|
||||
$srcRatio = $srcWidth / $srcHeight;
|
||||
$resizeRatio = $width / $height;
|
||||
|
||||
if ($srcRatio <= $resizeRatio) {
|
||||
$dstWidth = $width;
|
||||
$dstHeight = floor($srcHeight * ($width / $srcWidth));
|
||||
$dstY = ($dstHeight - $height) / 2 * (-1);
|
||||
} else {
|
||||
$dstWidth = floor($srcWidth * ($height / $srcHeight));
|
||||
$dstHeight = $height;
|
||||
$dstX = ($dstWidth - $width) / 2 * (-1);
|
||||
}
|
||||
|
||||
$this->dstImage = imagecreatetruecolor($width, $height);
|
||||
}
|
||||
|
||||
imagecopyresampled($this->dstImage, $this->srcImage, $dstX, $dstY, 0, 0, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the thumbnail to a local file
|
||||
*
|
||||
* @access public
|
||||
* @param string $filename
|
||||
* @return Thumbnail
|
||||
*/
|
||||
public function toFile($filename)
|
||||
{
|
||||
imagejpeg($this->dstImage, $filename);
|
||||
imagedestroy($this->dstImage);
|
||||
imagedestroy($this->srcImage);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the thumbnail as a string
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function toString()
|
||||
{
|
||||
ob_start();
|
||||
imagejpeg($this->dstImage, null);
|
||||
imagedestroy($this->dstImage);
|
||||
imagedestroy($this->srcImage);
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the thumbnail directly to the browser or stdout
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function toOutput()
|
||||
{
|
||||
imagejpeg($this->dstImage, null);
|
||||
imagedestroy($this->dstImage);
|
||||
imagedestroy($this->srcImage);
|
||||
}
|
||||
}
|
||||
|
|
@ -75,78 +75,4 @@ class Tool
|
|||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a jpeg thumbnail from an image
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $src_file Source file image
|
||||
* @param string $dst_file Destination file image
|
||||
* @param integer $resize_width Desired image width
|
||||
* @param integer $resize_height Desired image height
|
||||
*/
|
||||
public static function generateThumbnail($src_file, $dst_file, $resize_width = 250, $resize_height = 100)
|
||||
{
|
||||
$metadata = getimagesize($src_file);
|
||||
$src_width = $metadata[0];
|
||||
$src_height = $metadata[1];
|
||||
$dst_y = 0;
|
||||
$dst_x = 0;
|
||||
|
||||
if (empty($metadata['mime'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($resize_width == 0 && $resize_height == 0) {
|
||||
$resize_width = 100;
|
||||
$resize_height = 100;
|
||||
}
|
||||
|
||||
if ($resize_width > 0 && $resize_height == 0) {
|
||||
$dst_width = $resize_width;
|
||||
$dst_height = floor($src_height * ($resize_width / $src_width));
|
||||
$dst_image = imagecreatetruecolor($dst_width, $dst_height);
|
||||
} elseif ($resize_width == 0 && $resize_height > 0) {
|
||||
$dst_width = floor($src_width * ($resize_height / $src_height));
|
||||
$dst_height = $resize_height;
|
||||
$dst_image = imagecreatetruecolor($dst_width, $dst_height);
|
||||
} else {
|
||||
$src_ratio = $src_width / $src_height;
|
||||
$resize_ratio = $resize_width / $resize_height;
|
||||
|
||||
if ($src_ratio <= $resize_ratio) {
|
||||
$dst_width = $resize_width;
|
||||
$dst_height = floor($src_height * ($resize_width / $src_width));
|
||||
|
||||
$dst_y = ($dst_height - $resize_height) / 2 * (-1);
|
||||
} else {
|
||||
$dst_width = floor($src_width * ($resize_height / $src_height));
|
||||
$dst_height = $resize_height;
|
||||
|
||||
$dst_x = ($dst_width - $resize_width) / 2 * (-1);
|
||||
}
|
||||
|
||||
$dst_image = imagecreatetruecolor($resize_width, $resize_height);
|
||||
}
|
||||
|
||||
switch ($metadata['mime']) {
|
||||
case 'image/jpeg':
|
||||
case 'image/jpg':
|
||||
$src_image = imagecreatefromjpeg($src_file);
|
||||
break;
|
||||
case 'image/png':
|
||||
$src_image = imagecreatefrompng($src_file);
|
||||
break;
|
||||
case 'image/gif':
|
||||
$src_image = imagecreatefromgif($src_file);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
|
||||
imagejpeg($dst_image, $dst_file);
|
||||
imagedestroy($dst_image);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,23 +32,25 @@ class AvatarManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Render avatar html element
|
||||
* Render avatar HTML element
|
||||
*
|
||||
* @access public
|
||||
* @param string $user_id
|
||||
* @param string $username
|
||||
* @param string $name
|
||||
* @param string $email
|
||||
* @param string $avatar_path
|
||||
* @param int $size
|
||||
* @return string
|
||||
*/
|
||||
public function render($user_id, $username, $name, $email, $size)
|
||||
public function render($user_id, $username, $name, $email, $avatar_path, $size)
|
||||
{
|
||||
$user = array(
|
||||
'id' => $user_id,
|
||||
'username' => $username,
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
'avatar_path' => $avatar_path,
|
||||
);
|
||||
|
||||
krsort($this->providers);
|
||||
|
|
@ -80,6 +82,7 @@ class AvatarManager
|
|||
'username' => '',
|
||||
'name' => '?',
|
||||
'email' => '',
|
||||
'avatar_path' => '',
|
||||
);
|
||||
|
||||
return $provider->render($user, $size);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,19 @@ use Kanboard\Core\Security\Role;
|
|||
*/
|
||||
class UserSession extends Base
|
||||
{
|
||||
/**
|
||||
* Refresh current session if necessary
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
*/
|
||||
public function refresh($user_id)
|
||||
{
|
||||
if ($this->getId() == $user_id) {
|
||||
$this->initialize($this->user->getById($user_id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user session
|
||||
*
|
||||
|
|
|
|||
|
|
@ -20,16 +20,17 @@ class AvatarHelper extends Base
|
|||
* @param string $username
|
||||
* @param string $name
|
||||
* @param string $email
|
||||
* @param string $avatar_path
|
||||
* @param string $css
|
||||
* @param int $size
|
||||
* @return string
|
||||
*/
|
||||
public function render($user_id, $username, $name, $email, $css = 'avatar-left', $size = 48)
|
||||
public function render($user_id, $username, $name, $email, $avatar_path, $css = 'avatar-left', $size = 48)
|
||||
{
|
||||
if (empty($user_id) && empty($username)) {
|
||||
$html = $this->avatarManager->renderDefault($size);
|
||||
} else {
|
||||
$html = $this->avatarManager->render($user_id, $username, $name, $email, $size);
|
||||
$html = $this->avatarManager->render($user_id, $username, $name, $email, $avatar_path, $size);
|
||||
}
|
||||
|
||||
return '<div class="avatar avatar-'.$size.' '.$css.'">'.$html.'</div>';
|
||||
|
|
@ -39,26 +40,29 @@ class AvatarHelper extends Base
|
|||
* Render small user avatar
|
||||
*
|
||||
* @access public
|
||||
* @param string $user_id
|
||||
* @param string $username
|
||||
* @param string $name
|
||||
* @param string $email
|
||||
* @param string $user_id
|
||||
* @param string $username
|
||||
* @param string $name
|
||||
* @param string $email
|
||||
* @param string $avatar_path
|
||||
* @param string $css
|
||||
* @return string
|
||||
*/
|
||||
public function small($user_id, $username, $name, $email, $css = '')
|
||||
public function small($user_id, $username, $name, $email, $avatar_path, $css = '')
|
||||
{
|
||||
return $this->render($user_id, $username, $name, $email, $css, 20);
|
||||
return $this->render($user_id, $username, $name, $email, $avatar_path, $css, 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a small avatar for the current user
|
||||
*
|
||||
* @access public
|
||||
* @param string $css
|
||||
* @return string
|
||||
*/
|
||||
public function currentUserSmall($css = '')
|
||||
{
|
||||
$user = $this->userSession->getAll();
|
||||
return $this->small($user['id'], $user['username'], $user['name'], $user['email'], $css);
|
||||
return $this->small($user['id'], $user['username'], $user['name'], $user['email'], $user['avatar_path'], $css);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Avatar File
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AvatarFile extends Base
|
||||
{
|
||||
/**
|
||||
* Path prefix
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PATH_PREFIX = 'avatars';
|
||||
|
||||
/**
|
||||
* Get image filename
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @return string
|
||||
*/
|
||||
public function getFilename($user_id)
|
||||
{
|
||||
return $this->db->table(User::TABLE)->eq('id', $user_id)->findOneColumn('avatar_path');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add avatar in the user profile
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id Foreign key
|
||||
* @param string $path Path on the disk
|
||||
* @return bool
|
||||
*/
|
||||
public function create($user_id, $path)
|
||||
{
|
||||
$result = $this->db->table(User::TABLE)->eq('id', $user_id)->update(array(
|
||||
'avatar_path' => $path,
|
||||
));
|
||||
|
||||
$this->userSession->refresh($user_id);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove avatar from the user profile
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id Foreign key
|
||||
* @return bool
|
||||
*/
|
||||
public function remove($user_id)
|
||||
{
|
||||
try {
|
||||
$this->objectStorage->remove($this->getFilename($user_id));
|
||||
$result = $this->db->table(User::TABLE)->eq('id', $user_id)->update(array('avatar_path' => ''));
|
||||
$this->userSession->refresh($user_id);
|
||||
return $result;
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload avatar image
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param array $file
|
||||
*/
|
||||
public function uploadFile($user_id, array $file)
|
||||
{
|
||||
try {
|
||||
if ($file['error'] == UPLOAD_ERR_OK && $file['size'] > 0) {
|
||||
$destination_filename = $this->generatePath($user_id, $file['name']);
|
||||
$this->objectStorage->moveUploadedFile($file['tmp_name'], $destination_filename);
|
||||
$this->create($user_id, $destination_filename);
|
||||
} else {
|
||||
throw new Exception('File not uploaded: '.var_export($file['error'], true));
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the path for a new filename
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
public function generatePath($user_id, $filename)
|
||||
{
|
||||
return implode(DIRECTORY_SEPARATOR, array(self::PATH_PREFIX, $user_id, hash('sha1', $filename.time())));
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,8 @@ class Comment extends Base
|
|||
self::TABLE.'.comment',
|
||||
User::TABLE.'.username',
|
||||
User::TABLE.'.name',
|
||||
User::TABLE.'.email'
|
||||
User::TABLE.'.email',
|
||||
User::TABLE.'.avatar_path'
|
||||
)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->orderBy(self::TABLE.'.date_creation', $sorting)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace Kanboard\Model;
|
||||
|
||||
use Exception;
|
||||
use Kanboard\Core\Thumbnail;
|
||||
use Kanboard\Event\FileEvent;
|
||||
use Kanboard\Core\Tool;
|
||||
use Kanboard\Core\ObjectStorage\ObjectStorageException;
|
||||
|
|
@ -315,15 +316,15 @@ abstract class File extends Base
|
|||
*/
|
||||
public function generateThumbnailFromData($destination_filename, &$data)
|
||||
{
|
||||
$temp_filename = tempnam(sys_get_temp_dir(), 'datafile');
|
||||
$blob = Thumbnail::createFromString($data)
|
||||
->resize()
|
||||
->toString();
|
||||
|
||||
file_put_contents($temp_filename, $data);
|
||||
$this->generateThumbnailFromFile($temp_filename, $destination_filename);
|
||||
unlink($temp_filename);
|
||||
$this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate thumbnail from a blob
|
||||
* Generate thumbnail from a local file
|
||||
*
|
||||
* @access public
|
||||
* @param string $uploaded_filename
|
||||
|
|
@ -331,8 +332,10 @@ abstract class File extends Base
|
|||
*/
|
||||
public function generateThumbnailFromFile($uploaded_filename, $destination_filename)
|
||||
{
|
||||
$thumbnail_filename = tempnam(sys_get_temp_dir(), 'thumbnail');
|
||||
Tool::generateThumbnail($uploaded_filename, $thumbnail_filename);
|
||||
$this->objectStorage->moveFile($thumbnail_filename, $this->getThumbnailPath($destination_filename));
|
||||
$blob = Thumbnail::createFromFile($uploaded_filename)
|
||||
->resize()
|
||||
->toString();
|
||||
|
||||
$this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,8 @@ class ProjectActivity extends Base
|
|||
self::TABLE.'.*',
|
||||
User::TABLE.'.username AS author_username',
|
||||
User::TABLE.'.name AS author_name',
|
||||
User::TABLE.'.email'
|
||||
User::TABLE.'.email',
|
||||
User::TABLE.'.avatar_path'
|
||||
)
|
||||
->in('project_id', $project_ids)
|
||||
->join(User::TABLE, 'id', 'creator_id')
|
||||
|
|
@ -117,7 +118,8 @@ class ProjectActivity extends Base
|
|||
self::TABLE.'.*',
|
||||
User::TABLE.'.username AS author_username',
|
||||
User::TABLE.'.name AS author_name',
|
||||
User::TABLE.'.email'
|
||||
User::TABLE.'.email',
|
||||
User::TABLE.'.avatar_path'
|
||||
)
|
||||
->eq('task_id', $task_id)
|
||||
->join(User::TABLE, 'id', 'creator_id')
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ class TaskFinder extends Base
|
|||
User::TABLE.'.username AS assignee_username',
|
||||
User::TABLE.'.name AS assignee_name',
|
||||
User::TABLE.'.email AS assignee_email',
|
||||
User::TABLE.'.avatar_path AS assignee_avatar_path',
|
||||
Category::TABLE.'.name AS category_name',
|
||||
Category::TABLE.'.description AS category_description',
|
||||
Column::TABLE.'.title AS column_name',
|
||||
|
|
|
|||
|
|
@ -283,12 +283,7 @@ class User extends Base
|
|||
{
|
||||
$this->prepare($values);
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values);
|
||||
|
||||
// If the user is connected refresh his session
|
||||
if ($this->userSession->getId() == $values['id']) {
|
||||
$this->userSession->initialize($this->getById($this->userSession->getId()));
|
||||
}
|
||||
|
||||
$this->userSession->refresh($values['id']);
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
@ -327,6 +322,9 @@ class User extends Base
|
|||
{
|
||||
return $this->db->transaction(function (Database $db) use ($user_id) {
|
||||
|
||||
// Remove Avatar
|
||||
$this->avatarFile->remove($user_id);
|
||||
|
||||
// All assigned tasks are now unassigned (no foreign key)
|
||||
if (! $db->table(Task::TABLE)->eq('owner_id', $user_id)->update(array('owner_id' => 0))) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,12 @@ use PDO;
|
|||
use Kanboard\Core\Security\Token;
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
const VERSION = 108;
|
||||
const VERSION = 109;
|
||||
|
||||
function version_109(PDO $pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN avatar_path VARCHAR(255)");
|
||||
}
|
||||
|
||||
function version_108(PDO $pdo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,12 @@ use PDO;
|
|||
use Kanboard\Core\Security\Token;
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
const VERSION = 88;
|
||||
const VERSION = 89;
|
||||
|
||||
function version_89(PDO $pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN avatar_path VARCHAR(255)");
|
||||
}
|
||||
|
||||
function version_88(PDO $pdo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,12 @@ use Kanboard\Core\Security\Token;
|
|||
use Kanboard\Core\Security\Role;
|
||||
use PDO;
|
||||
|
||||
const VERSION = 100;
|
||||
const VERSION = 101;
|
||||
|
||||
function version_101(PDO $pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN avatar_path TEXT");
|
||||
}
|
||||
|
||||
function version_100(PDO $pdo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ class AuthenticationProvider implements ServiceProviderInterface
|
|||
$acl->add('Board', 'readonly', Role::APP_PUBLIC);
|
||||
$acl->add('Ical', '*', Role::APP_PUBLIC);
|
||||
$acl->add('Feed', '*', Role::APP_PUBLIC);
|
||||
$acl->add('AvatarFile', 'show', Role::APP_PUBLIC);
|
||||
|
||||
$acl->add('Config', '*', Role::APP_ADMIN);
|
||||
$acl->add('Currency', '*', Role::APP_ADMIN);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use Pimple\Container;
|
|||
use Pimple\ServiceProviderInterface;
|
||||
use Kanboard\Core\User\Avatar\AvatarManager;
|
||||
use Kanboard\User\Avatar\GravatarProvider;
|
||||
use Kanboard\User\Avatar\AvatarFileProvider;
|
||||
use Kanboard\User\Avatar\LetterAvatarProvider;
|
||||
|
||||
/**
|
||||
|
|
@ -28,6 +29,7 @@ class AvatarProvider implements ServiceProviderInterface
|
|||
$container['avatarManager'] = new AvatarManager;
|
||||
$container['avatarManager']->register(new LetterAvatarProvider($container));
|
||||
$container['avatarManager']->register(new GravatarProvider($container));
|
||||
$container['avatarManager']->register(new AvatarFileProvider($container));
|
||||
return $container;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class ClassProvider implements ServiceProviderInterface
|
|||
'Model' => array(
|
||||
'Action',
|
||||
'ActionParameter',
|
||||
'AvatarFile',
|
||||
'Board',
|
||||
'Category',
|
||||
'Color',
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
$task['assignee_username'],
|
||||
$task['assignee_name'],
|
||||
$task['assignee_email'],
|
||||
$task['assignee_avatar_path'],
|
||||
'avatar-inline'
|
||||
) ?>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div class="comment <?= isset($preview) ? 'comment-preview' : '' ?>" id="comment-<?= $comment['id'] ?>">
|
||||
|
||||
<?= $this->avatar->render($comment['user_id'], $comment['username'], $comment['name'], $comment['email']) ?>
|
||||
<?= $this->avatar->render($comment['user_id'], $comment['username'], $comment['name'], $comment['email'], $comment['avatar_path']) ?>
|
||||
|
||||
<div class="comment-title">
|
||||
<?php if (! empty($comment['username'])): ?>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
$event['creator_id'],
|
||||
$event['author_username'],
|
||||
$event['author_name'],
|
||||
$event['email']
|
||||
$event['email'],
|
||||
$event['avatar_path']
|
||||
) ?>
|
||||
|
||||
<div class="activity-content">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Avatar') ?></h2>
|
||||
</div>
|
||||
|
||||
<?= $this->avatar->render($user['id'], $user['username'], $user['name'], $user['email'], $user['avatar_path'], '') ?>
|
||||
|
||||
<form method="post" enctype="multipart/form-data" action="<?= $this->url->href('user', 'uploadAvatar', array('user_id' => $user['id'])) ?>">
|
||||
<?= $this->form->csrf() ?>
|
||||
<?= $this->form->label(t('Upload my avatar image'), 'avatar') ?>
|
||||
<?= $this->form->file('avatar') ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<?php if (! empty($user['avatar_path'])): ?>
|
||||
<?= $this->url->link(t('Remove my image'), 'User', 'removeAvatar', array('user_id' => $user['id']), true, 'btn btn-red') ?>
|
||||
<?php endif ?>
|
||||
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
|
||||
<?= t('or') ?>
|
||||
<?= $this->url->link(t('cancel'), 'user', 'show', array('user_id' => $user['id'])) ?>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -37,6 +37,9 @@
|
|||
<li <?= $this->app->checkMenuSelection('user', 'edit') ?>>
|
||||
<?= $this->url->link(t('Edit profile'), 'user', 'edit', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li <?= $this->app->checkMenuSelection('user', 'avatar') ?>>
|
||||
<?= $this->url->link(t('Avatar'), 'user', 'avatar', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($user['is_ldap_user'] == 0): ?>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\User\Avatar;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Core\User\Avatar\AvatarProviderInterface;
|
||||
|
||||
/**
|
||||
* Avatar Local Image File Provider
|
||||
*
|
||||
* @package avatar
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AvatarFileProvider extends Base implements AvatarProviderInterface
|
||||
{
|
||||
/**
|
||||
* Render avatar html
|
||||
*
|
||||
* @access public
|
||||
* @param array $user
|
||||
* @param int $size
|
||||
* @return string
|
||||
*/
|
||||
public function render(array $user, $size)
|
||||
{
|
||||
$url = $this->helper->url->href('AvatarFile', 'show', array('user_id' => $user['id'], 'size' => $size));
|
||||
$title = $this->helper->text->e($user['name'] ?: $user['username']);
|
||||
return '<img src="' . $url . '" alt="' . $title . '" title="' . $title . '">';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the provider is active
|
||||
*
|
||||
* @access public
|
||||
* @param array $user
|
||||
* @return boolean
|
||||
*/
|
||||
public function isActive(array $user)
|
||||
{
|
||||
return !empty($user['avatar_path']);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,8 +17,9 @@ class GravatarProvider extends Base implements AvatarProviderInterface
|
|||
* Render avatar html
|
||||
*
|
||||
* @access public
|
||||
* @param array $user
|
||||
* @param int $size
|
||||
* @param array $user
|
||||
* @param int $size
|
||||
* @return string
|
||||
*/
|
||||
public function render(array $user, $size)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@ class LetterAvatarProvider extends Base implements AvatarProviderInterface
|
|||
* Render avatar html
|
||||
*
|
||||
* @access public
|
||||
* @param array $user
|
||||
* @param int $size
|
||||
* @param array $user
|
||||
* @param int $size
|
||||
* @return string
|
||||
*/
|
||||
public function render(array $user, $size)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ class ProjectFileTest extends Base
|
|||
$fileModel = $this
|
||||
->getMockBuilder('\Kanboard\Model\ProjectFile')
|
||||
->setConstructorArgs(array($this->container))
|
||||
->setMethods(array('generateThumbnailFromFile'))
|
||||
->setMethods(array('generateThumbnailFromData'))
|
||||
->getMock();
|
||||
|
||||
$projectModel = new Project($this->container);
|
||||
|
|
@ -288,7 +288,7 @@ class ProjectFileTest extends Base
|
|||
|
||||
$fileModel
|
||||
->expects($this->once())
|
||||
->method('generateThumbnailFromFile');
|
||||
->method('generateThumbnailFromData');
|
||||
|
||||
$this->container['objectStorage']
|
||||
->expects($this->once())
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ class TaskFileTest extends Base
|
|||
$fileModel = $this
|
||||
->getMockBuilder('\Kanboard\Model\TaskFile')
|
||||
->setConstructorArgs(array($this->container))
|
||||
->setMethods(array('generateThumbnailFromFile'))
|
||||
->setMethods(array('generateThumbnailFromData'))
|
||||
->getMock();
|
||||
|
||||
$projectModel = new Project($this->container);
|
||||
|
|
@ -343,7 +343,7 @@ class TaskFileTest extends Base
|
|||
|
||||
$fileModel
|
||||
->expects($this->once())
|
||||
->method('generateThumbnailFromFile');
|
||||
->method('generateThumbnailFromData');
|
||||
|
||||
$this->container['objectStorage']
|
||||
->expects($this->once())
|
||||
|
|
|
|||
Loading…
Reference in New Issue