Ajout de l'E2E #10

Merged
Shikiryu merged 3 commits from e2e into master 2020-03-16 17:06:09 +01:00
28 changed files with 960 additions and 178 deletions
Showing only changes of commit 7293fec3ca - Show all commits

4
.gitignore vendored
View File

@ -11,3 +11,7 @@ Homestead.yaml
npm-debug.log npm-debug.log
yarn-error.log yarn-error.log
.idea .idea
/public/css/
/public/fonts/
/public/js/
/public/images/

View File

@ -11,3 +11,5 @@
éditer le fichier `.env` éditer le fichier `.env`
`php artisan migrate` `php artisan migrate`
`php artisan db:seed --class=TagsTableSeeder`

View File

@ -0,0 +1,175 @@
<?php
namespace App\Http\Controllers;
use App\Post;
use App\PostsTag;
use App\Services\ImageService;
use App\Services\TagDetectorService;
use App\Tag;
use DateTime;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
class DashboardController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
* @throws \Exception
*/
public function index()
{
setlocale(LC_TIME, 'fr_FR.utf8');
$today = new DateTime();
$user_id = Auth::user()->getAuthIdentifier();
$user = \App\User::find($user_id);
$all_counts = Post::where('user_id', $user_id)->count();
$year_counts = Post::where('user_id', $user_id)->whereYear('date_post', $today->format('Y'))->count();
$month_counts = Post::where('user_id', $user_id)
->whereYear('date_post', $today->format('Y'))
->whereMonth('date_post', $today->format('m'))->count();
$image_counts = Post::where('user_id', Auth::user()->getAuthIdentifier())
->where('image', '!=', 'null')
->count();
$posts = Post::where('user_id', $user_id)->orderBy('date_post', 'DESC')->limit(9)->get();
$today_post = Post::whereDate('date_post', '=', $today->format('Y-m-d'))
->where('user_id', $user_id)
->count();
$already = false;
if ($today_post > 0) {
$already = true;
}
return view('home', [
'already' => $already,
'must_encrypt' => $user->encrypt_messages,
'all_counts' => $all_counts,
'year_counts' => $year_counts,
'month_counts' => $month_counts,
'image_counts' => $image_counts,
'today' => $today,
'posts' => $posts,
]);
}
/**
* Edit today's entry
*/
public function edit(TagDetectorService $tag_detector)
{
/** @var Post $today_post */
$today_post = Post::whereDate('date_post', '=', (new DateTime())->format('Y-m-d'))
->where('user_id', Auth::user()->getAuthIdentifier())
->firstOrFail();
$data = request()->validate([
'message' => 'required'
]);
$today_post->deleteTags();
$tags = $tag_detector->detectFrom($data['message']);
$today_post->content = $data['message'];
$today_post->save();
$all_tags = Tag::all();
$all_tags_names = [];
foreach ($all_tags as $tag) {
$all_tags_names[$tag->id] = $tag->name;
}
foreach ($tags as $tag) {
if (in_array($tag, $all_tags_names)) {
PostsTag::create([
'post_id' => $today_post->id,
'tag_id' => array_search($tag, $all_tags_names),
]);
}
}
return Redirect::to('home')->withSuccess('Great! Form successfully submit with validation.');
}
/**
* @return \Illuminate\Http\RedirectResponse
* @throws \Exception
*/
public function delete()
{
try {
$today_post = Post::whereDate('date_post', '=', (new DateTime())->format('Y-m-d'))
->where('user_id', Auth::user()->getAuthIdentifier())
->firstOrFail();
/** @var Post $today_post */
$today_post->deleteTags();
$today_post->delete();
return Redirect::to('home')->withSuccess('Great! Your today\'s post is now deleted. You can make a new one!');
} catch (\ErrorException $e) {
return Redirect::to('home')->withErrors('Oh no! We could\'t find your today\'s post.');
}
}
/**
* @param TagDetectorService $tag_detector
* @return mixed
* @throws \Exception
*/
public function store(TagDetectorService $tag_detector, ImageService $image_service)
{
$user_id = Auth::user()->getAuthIdentifier();
$validated_data = request()->validate([
'message' => 'required',
'file' => 'file|mimes:jpeg,png,jpg,gif,svg',
]);
$data = [
'user_id' => $user_id,
'date_post' => new DateTime(),
'content' => $validated_data['message'],
];
if (!empty($validated_data['file'])) {
/** @var UploadedFile $uploaded_document */
$uploaded_document = $validated_data['file'];
$file_name = $uploaded_document->getClientOriginalName();
$uploaded_document->storeAs(Auth::user()->getFolder(), $file_name);
$image_service->makeThumbnail($uploaded_document->getRealPath(), ['width' => 300]);
$data['image'] = $file_name;
}
$tags = $tag_detector->detectFrom($data['content']);
$check = Post::create($data);
$all_tags = Tag::all();
$all_tags_names = [];
foreach ($all_tags as $tag) {
$all_tags_names[$tag->id] = $tag->name;
}
foreach ($tags as $tag) {
if (in_array($tag, $all_tags_names)) {
PostsTag::create([
'post_id' => $check->id,
'tag_id' => array_search($tag, $all_tags_names),
]);
}
}
return Redirect::to('home')->withSuccess('Great! Form successfully submit with validation.');
}
}

View File

@ -1,99 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Post;
use App\PostsTag;
use App\Services\TagDetectorService;
use App\Tag;
use DateTime;
use http\Client\Curl\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
setlocale(LC_TIME, 'fr_FR.utf8');
$today = new DateTime();
$user_id = Auth::user()->getAuthIdentifier();
$user = \App\User::find($user_id);
$all_counts = Post::where('user_id', $user_id)->count();
$year_counts = Post::where('user_id', $user_id)->whereYear('date_post', $today->format('Y'))->count();
$month_counts = Post::where('user_id', $user_id)
->whereYear('date_post', $today->format('Y'))
->whereMonth('date_post', $today->format('m'))->count();
$image_counts = 0; // TODO #4
$posts = Post::where('user_id', $user_id)->orderBy('date_post', 'DESC')->limit(9)->get();
$today_post = Post::whereDate('date_post', '=', $today->format('Y-m-d'))
->where('user_id', $user_id)
->count();
$already = false;
if ($today_post > 0) {
$already = true;
}
return view('home', [
'already' => $already,
'must_encrypt' => $user->encrypt_messages,
'all_counts' => $all_counts,
'year_counts' => $year_counts,
'month_counts' => $month_counts,
'image_counts' => $image_counts,
'today' => $today,
'posts' => $posts,
]);
}
public function store(TagDetectorService $tag_detector)
{
$today = new DateTime();
$data = request()->validate([
'message' => 'required'
]);
$data = [
'user_id' => Auth::user()->getAuthIdentifier(),
'date_post' => new DateTime(), // Take back the date from the form ?
'content' => $data['message'],
];
$tags = $tag_detector->detectFrom($data['content']);
$check = Post::create($data);
$all_tags = Tag::all();
$all_tags_names = [];
foreach ($all_tags as $tag) {
$all_tags_names[$tag->id] = $tag->name;
}
foreach ($tags as $tag) {
if (in_array($tag, $all_tags_names)) {
PostsTag::create([
'post_id' => $check->id,
'tag_id' => array_search($tag, $all_tags_names),
]);
}
}
return Redirect::to('home')->withSuccess('Great! Form successfully submit with validation.');
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Http\Controllers;
use App\Post;
use App\Services\ImageService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\File;
use Intervention\Image\Constraint;
use Intervention\Image\Facades\Image;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class ImageController extends Controller
{
/**
* @param int $post_id
* @param string|array $options
* @param string $image
* @param ImageService $image_service
*
* @return mixed
*/
public function display($post_id, $options = 'o:full', $image, ImageService $image_service)
{
if (Auth::guest()) {
throw new UnauthorizedHttpException('Vous devez être connecté pour voir cette image.');
}
$post = Post::find($post_id);
if (Auth::user()->getAuthIdentifier() !== (int)$post->user_id) {
throw new UnauthorizedHttpException('Cette image ne vous appartient pas.');
}
$original = sprintf('%s/%s/%s', storage_path('app'), Auth::user()->getFolder(), $image);
if ('o:full' === $options) {
$img = $image_service->makeThumbnail($original);
} else {
$img = $image_service->makeThumbnail($original, ['width' => 300]);
}
return $img->response(File::extension($original));
}
/**
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function gallery()
{
$posts_with_image = Post::where('user_id', Auth::user()->getAuthIdentifier())
->where('image', '!=', 'null')
->orderBy('created_at', 'desc')
->paginate(30);
return view('gallery.my', [
'posts' => $posts_with_image,
]);
}
}

View File

@ -3,13 +3,22 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class WelcomeController extends Controller class WelcomeController extends Controller
{ {
/**
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
* @throws \Exception
*/
public function index() public function index()
{ {
return view('welcome', [ if (Auth::guest()) {
'today' => new \DateTime(), return view('welcome', [
]); 'today' => new \DateTime(),
]);
}
return redirect(route('dashboard'));
} }
} }

View File

@ -2,7 +2,28 @@
namespace App\Http; namespace App\Http;
use App\Http\Middleware\Authenticate;
use App\Http\Middleware\CheckForMaintenanceMode;
use App\Http\Middleware\EncryptCookies;
use App\Http\Middleware\GenerateMenus;
use App\Http\Middleware\RedirectIfAuthenticated;
use App\Http\Middleware\TrimStrings;
use App\Http\Middleware\TrustProxies;
use App\Http\Middleware\VerifyCsrfToken;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Foundation\Http\Kernel as HttpKernel; use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Http\Middleware\SetCacheHeaders;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Routing\Middleware\ValidateSignature;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class Kernel extends HttpKernel class Kernel extends HttpKernel
{ {
@ -14,11 +35,11 @@ class Kernel extends HttpKernel
* @var array * @var array
*/ */
protected $middleware = [ protected $middleware = [
\App\Http\Middleware\TrustProxies::class, TrustProxies::class,
\App\Http\Middleware\CheckForMaintenanceMode::class, CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class, TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, ConvertEmptyStringsToNull::class,
]; ];
/** /**
@ -28,13 +49,14 @@ class Kernel extends HttpKernel
*/ */
protected $middlewareGroups = [ protected $middlewareGroups = [
'web' => [ 'web' => [
\App\Http\Middleware\EncryptCookies::class, EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class, StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class, ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class, VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class, SubstituteBindings::class,
GenerateMenus::class,
], ],
'api' => [ 'api' => [
@ -51,15 +73,15 @@ class Kernel extends HttpKernel
* @var array * @var array
*/ */
protected $routeMiddleware = [ protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class, 'auth' => Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'auth.basic' => AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'bindings' => SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'cache.headers' => SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class, 'can' => Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'guest' => RedirectIfAuthenticated::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'signed' => ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'throttle' => ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'verified' => EnsureEmailIsVerified::class,
]; ];
/** /**
@ -70,12 +92,12 @@ class Kernel extends HttpKernel
* @var array * @var array
*/ */
protected $middlewarePriority = [ protected $middlewarePriority = [
\Illuminate\Session\Middleware\StartSession::class, StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class, ShareErrorsFromSession::class,
\App\Http\Middleware\Authenticate::class, Authenticate::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class, ThrottleRequests::class,
\Illuminate\Session\Middleware\AuthenticateSession::class, AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class, SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class, Authorize::class,
]; ];
} }

View File

@ -0,0 +1,40 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Lavary\Menu\Builder;
use Lavary\Menu\Menu;
class GenerateMenus
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
(new Menu)->make('menu', function (Builder $menu) {
$accueil = $menu->add('Accueil', ['route' => 'dashboard', 'class' => 'nav-item'])->nickname('home');
$accueil->link->attr(['class' => 'nav-link']);
$accueil->prepend('<i class="fe fe-home"></i> ');
$accueil->checkActivationStatus();
$stats = $menu->add('Mes statistiques', ['route' => 'stats', 'class' => 'nav-item'])->nickname('stats');
$stats->link->attr(['class' => 'nav-link']);
$stats->prepend('<i class="fe fe-trending-up"></i> ');
$stats->checkActivationStatus();
$gallery = $menu->add('Ma gallerie', ['route' => 'gallery', 'class' => 'nav-item'])->nickname('gallery');
$gallery->link->attr(['class' => 'nav-link']);
$gallery->prepend('<i class="fe fe-image"></i> ');
$gallery->checkActivationStatus();
});
return $next($request);
}
}

View File

@ -14,16 +14,16 @@ class Post extends Model
protected $fillable = [ protected $fillable = [
'content', 'image', 'date_post', 'user_id' 'content', 'image', 'date_post', 'user_id'
]; ];
/** /**
* The attributes that should be hidden for arrays. * The attributes that should be hidden for arrays.
* *
* @var array * @var array
*/ */
protected $hidden = [ protected $hidden = [
]; ];
/** /**
* The attributes that should be cast to native types. * The attributes that should be cast to native types.
* *
@ -32,4 +32,15 @@ class Post extends Model
protected $casts = [ protected $casts = [
'date_post' => 'datetime', 'date_post' => 'datetime',
]; ];
/**
* Delete this posts tags
*/
public function deleteTags()
{
$tags_to_delete = PostsTag::where('post_id', $this->id)->get();
foreach ($tags_to_delete as $tag_to_delete) {
$tag_to_delete->delete();
}
}
} }

View File

@ -0,0 +1,29 @@
<?php
namespace App\Providers;
use App\Services\ImageService;
use Illuminate\Support\ServiceProvider;
class ImageServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
$this->app->bind(ImageService::class);
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace App\Services;
use Illuminate\Support\Facades\Auth;
use Intervention\Image\Constraint;
use Intervention\Image\Facades\Image;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class ImageService
{
/**
* @param string $image path to image
* @param array $options option to make thumbnail
*
* @return \Intervention\Image\Image
*/
public function makeThumbnail($image, $options = [])
{
if (!is_readable($image)) {
throw new NotFoundHttpException();
}
$img = Image::make($image);
if (empty($options)) {
return $img;
}
$folder = dirname($img->basePath());
$file_name = basename($image);
$width = null;
if (array_key_exists('width', $options)) {
$width = $options['width'];
$file_name = sprintf('w%u-%s', $width, $file_name);
}
$height = null;
if (array_key_exists('$height', $options)) {
$height = $options['$height'];
$file_name = sprintf('h%u-%s', $height, $file_name);
}
$full_path = sprintf('%s/%s', $folder, $file_name);
if (!array_key_exists('force', $options) && is_readable($full_path)) {
return Image::make($full_path);
}
//http://image.intervention.io/getting_started/
$img->resize($width, $height, static function (Constraint $constraint) {
$constraint->aspectRatio();
})->save(sprintf('%s/%s', $folder, $file_name));
return $img;
}
}

View File

@ -36,4 +36,10 @@ class User extends Authenticatable
protected $casts = [ protected $casts = [
'email_verified_at' => 'datetime', 'email_verified_at' => 'datetime',
]; ];
public function getFolder()
{
$arrayHash = str_split(strtolower(md5($this->id)));
return sprintf('%s/%s', $arrayHash[0], $arrayHash[1]);
}
} }

View File

@ -10,8 +10,10 @@
"require": { "require": {
"php": "^7.2", "php": "^7.2",
"fideloper/proxy": "^4.0", "fideloper/proxy": "^4.0",
"intervention/image": "^2.5",
"laravel/framework": "^6.0", "laravel/framework": "^6.0",
"laravel/tinker": "^1.0" "laravel/tinker": "^1.0",
"lavary/laravel-menu": "^1.7"
}, },
"require-dev": { "require-dev": {
"facade/ignition": "^1.4", "facade/ignition": "^1.4",

285
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "3f43a645b4cb3d85ae04049046b0845a", "content-hash": "a8999b4d9f518e7a9608ff59c6ba40e9",
"packages": [ "packages": [
{ {
"name": "dnoegel/php-xdg-base-dir", "name": "dnoegel/php-xdg-base-dir",
@ -380,6 +380,147 @@
], ],
"time": "2019-09-03T16:45:42+00:00" "time": "2019-09-03T16:45:42+00:00"
}, },
{
"name": "guzzlehttp/psr7",
"version": "1.6.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "239400de7a173fe9901b9ac7c06497751f00727a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
"reference": "239400de7a173fe9901b9ac7c06497751f00727a",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0",
"ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"ext-zlib": "*",
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
},
"suggest": {
"zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Schultze",
"homepage": "https://github.com/Tobion"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
],
"time": "2019-07-01T23:21:34+00:00"
},
{
"name": "intervention/image",
"version": "2.5.1",
"source": {
"type": "git",
"url": "https://github.com/Intervention/image.git",
"reference": "abbf18d5ab8367f96b3205ca3c89fb2fa598c69e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Intervention/image/zipball/abbf18d5ab8367f96b3205ca3c89fb2fa598c69e",
"reference": "abbf18d5ab8367f96b3205ca3c89fb2fa598c69e",
"shasum": ""
},
"require": {
"ext-fileinfo": "*",
"guzzlehttp/psr7": "~1.1",
"php": ">=5.4.0"
},
"require-dev": {
"mockery/mockery": "~0.9.2",
"phpunit/phpunit": "^4.8 || ^5.7"
},
"suggest": {
"ext-gd": "to use GD library based image processing.",
"ext-imagick": "to use Imagick based image processing.",
"intervention/imagecache": "Caching extension for the Intervention Image library"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
},
"laravel": {
"providers": [
"Intervention\\Image\\ImageServiceProvider"
],
"aliases": {
"Image": "Intervention\\Image\\Facades\\Image"
}
}
},
"autoload": {
"psr-4": {
"Intervention\\Image\\": "src/Intervention/Image"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Oliver Vogel",
"email": "oliver@olivervogel.com",
"homepage": "http://olivervogel.com/"
}
],
"description": "Image handling and manipulation library with support for Laravel integration",
"homepage": "http://image.intervention.io/",
"keywords": [
"gd",
"image",
"imagick",
"laravel",
"thumbnail",
"watermark"
],
"time": "2019-11-02T09:15:47+00:00"
},
{ {
"name": "jakub-onderka/php-console-color", "name": "jakub-onderka/php-console-color",
"version": "v0.2", "version": "v0.2",
@ -676,6 +817,58 @@
], ],
"time": "2019-08-07T15:10:45+00:00" "time": "2019-08-07T15:10:45+00:00"
}, },
{
"name": "lavary/laravel-menu",
"version": "v1.7.7",
"source": {
"type": "git",
"url": "https://github.com/lavary/laravel-menu.git",
"reference": "5592778b3193ae561614ecb107467469b694ab11"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lavary/laravel-menu/zipball/5592778b3193ae561614ecb107467469b694ab11",
"reference": "5592778b3193ae561614ecb107467469b694ab11",
"shasum": ""
},
"require": {
"illuminate/support": ">=5.0",
"illuminate/view": ">=5.0",
"php": ">=5.4.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Lavary\\Menu\\ServiceProvider"
],
"aliases": {
"Menu": "Lavary\\Menu\\Facade"
}
}
},
"autoload": {
"psr-0": {
"Lavary\\Menu\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Lavary",
"email": "mrl.8081@gmail.com"
}
],
"description": "A quick way to create menus in Laravel 5",
"homepage": "https://github.com/lavary/laravel-menu",
"keywords": [
"laravel"
],
"time": "2019-09-06T16:28:16+00:00"
},
{ {
"name": "league/flysystem", "name": "league/flysystem",
"version": "1.0.55", "version": "1.0.55",
@ -1164,6 +1357,56 @@
], ],
"time": "2017-02-14T16:28:37+00:00" "time": "2017-02-14T16:28:37+00:00"
}, },
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2016-08-06T14:39:51+00:00"
},
{ {
"name": "psr/log", "name": "psr/log",
"version": "1.1.0", "version": "1.1.0",
@ -1333,6 +1576,46 @@
], ],
"time": "2018-10-13T15:16:03+00:00" "time": "2018-10-13T15:16:03+00:00"
}, },
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/ralouphie/getallheaders.git",
"reference": "120b605dfeb996808c31b6477290a714d356e822"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
"reference": "120b605dfeb996808c31b6477290a714d356e822",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5 || ^6.5"
},
"type": "library",
"autoload": {
"files": [
"src/getallheaders.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ralph Khattar",
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders.",
"time": "2019-03-08T08:55:37+00:00"
},
{ {
"name": "ramsey/uuid", "name": "ramsey/uuid",
"version": "3.8.0", "version": "3.8.0",

View File

@ -13,7 +13,7 @@ return [
| |
*/ */
'name' => env('APP_NAME', 'Laravel'), 'name' => env('APP_NAME', 'Journal Intime'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -67,7 +67,7 @@ return [
| |
*/ */
'timezone' => 'UTC', 'timezone' => 'Europe/Paris',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -176,7 +176,8 @@ return [
App\Providers\RouteServiceProvider::class, App\Providers\RouteServiceProvider::class,
\App\Providers\TagDetectorProvider::class, \App\Providers\TagDetectorProvider::class,
\App\Providers\ImageServiceProvider::class,
Intervention\Image\ImageServiceProvider::class,
], ],
/* /*
@ -209,6 +210,7 @@ return [
'File' => Illuminate\Support\Facades\File::class, 'File' => Illuminate\Support\Facades\File::class,
'Gate' => Illuminate\Support\Facades\Gate::class, 'Gate' => Illuminate\Support\Facades\Gate::class,
'Hash' => Illuminate\Support\Facades\Hash::class, 'Hash' => Illuminate\Support\Facades\Hash::class,
'Image' => Intervention\Image\Facades\Image::class,
'Lang' => Illuminate\Support\Facades\Lang::class, 'Lang' => Illuminate\Support\Facades\Lang::class,
'Log' => Illuminate\Support\Facades\Log::class, 'Log' => Illuminate\Support\Facades\Log::class,
'Mail' => Illuminate\Support\Facades\Mail::class, 'Mail' => Illuminate\Support\Facades\Mail::class,

20
config/image.php Normal file
View File

@ -0,0 +1,20 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Image Driver
|--------------------------------------------------------------------------
|
| Intervention Image supports "GD Library" and "Imagick" to process images
| internally. You may choose one of them according to your PHP
| configuration. By default PHP's "GD Library" implementation is used.
|
| Supported: "gd", "imagick"
|
*/
'driver' => 'gd'
];

View File

@ -0,0 +1,13 @@
<?php
return [
'default' => [
'auto_activate' => true,
'activate_parents' => true,
'active_class' => 'active',
'restful' => false,
'cascade_data' => true,
'rest_base' => '', // string|array
'active_element' => 'link', // item|link
],
];

View File

@ -0,0 +1,5 @@
<?php
return [
'bootstrap-items' => 'laravel-menu::bootstrap-navbar-items',
];

38
resources/js/app.js vendored
View File

@ -29,8 +29,8 @@ window.Vue = require('vue');
import Vue from 'vue'; import Vue from 'vue';
import Autocomplete from './components/Autocomplete.vue'; import Autocomplete from './components/Autocomplete.vue';
import { Endcrypt } from 'endcrypt'; import {Endcrypt} from 'endcrypt';
import { Vue2Storage } from 'vue2-storage' import {Vue2Storage} from 'vue2-storage';
const e = new Endcrypt(); const e = new Endcrypt();
@ -49,6 +49,14 @@ Vue.component(
'messageform', { 'messageform', {
name: 'MessageForm', name: 'MessageForm',
props: { props: {
'imageLabel': {
type: String,
default: "Choisir une image"
},
'uploadClass': {
type: String,
default: "fe fe-upload"
},
'mustencrypt': { 'mustencrypt': {
type: Boolean, type: Boolean,
default: true default: true
@ -64,11 +72,24 @@ Vue.component(
if (this.mustencrypt) { if (this.mustencrypt) {
// https://www.npmjs.com/package/endcrypt // https://www.npmjs.com/package/endcrypt
let plain = document.querySelector("[name=message]").value; let plain = document.querySelector("[name=message]").value;
let encryptedMessage = JSON.stringify(e.encryptWithKey(plain, this.$storage.get('passphrase'))); document.querySelector("[name=message]").value = JSON.stringify(e.encryptWithKey(plain, this.$storage.get('passphrase')));
document.querySelector("[name=message]").value = encryptedMessage;
} }
document.getElementById('messageForm').submit(); document.getElementById('messageForm').submit();
return false; return false;
},
fileNameChanged: function(inputName, inputFiles) {
let file = inputFiles[0];
let imageName = file["name"];
if (imageName !== "") {
imageName = imageName.split('\\').pop();
}
if (imageName !== "") {
this.uploadClass = "fe fe-check";
this.imageLabel = imageName;
} else {
this.uploadClass = "fe fe-upload";
this.imageLabel = "Choisir une image";
}
} }
}, },
beforeMount: function() { beforeMount: function() {
@ -86,7 +107,6 @@ Vue.component(
cardBody.innerHTML = e.decryptWithKey(cardBodyDecrypted, $this.$storage.get('passphrase')); cardBody.innerHTML = e.decryptWithKey(cardBodyDecrypted, $this.$storage.get('passphrase'));
} catch (e) { } catch (e) {
console.log('can\'t decode '+ cardBody.innerHTML); console.log('can\'t decode '+ cardBody.innerHTML);
console.log(e);
} }
card.addEventListener('click', function(evt) { card.addEventListener('click', function(evt) {
let $card = this.parentElement.parentElement.parentElement; let $card = this.parentElement.parentElement.parentElement;
@ -103,6 +123,8 @@ Vue.component(
} }
); );
new Vue({ if (document.getElementById("store")) {
el: '#store' new Vue({
}); el: '#store'
});
}

View File

@ -51,6 +51,13 @@
} }
} }
}); });
let collapse = document.getElementById("collapse");
let already = collapse && document.getElementById("collapse").classList.contains("collapse");
if (already) {
let post = document.querySelector(".already").innerText;
document.querySelector("textarea").innerHTML = post;
this.inputValue = post;
}
}, },
computed: { computed: {
listToSearch() { listToSearch() {

View File

@ -1,3 +1,47 @@
h1, h2, h3, h4 { h1, h2, h3, h4 {
font-family: $font-family-base; font-family: $font-family-base;
} }
.card-options .btn-link {
color: #9aa0ac;
text-decoration: none;
min-width: 0;
padding: 0;
margin-left: 5px;
&:hover {
color: #6e7687;
}
}
.btn-tertiary {
color: #555;
padding: 0;
line-height: 40px;
width: 300px;
margin: auto;
display: block;
border: 2px solid #555;
&:hover,
&:focus {
color: lighten(#555, 20%);
border-color: lighten(#555, 20%);
}
}
/* input file style */
.input-file {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
+ .js-labelFile {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 10px;
cursor: pointer;
}
}

View File

@ -11,7 +11,7 @@
} }
// Bootstrap // Bootstrap
//@import '~bootstrap/scss/bootstrap'; @import '~bootstrap/scss/bootstrap';
@import '~tabler-ui/src/assets/scss/bundle.scss'; @import '~tabler-ui/src/assets/scss/bundle.scss';
// Variables // Variables

View File

@ -1,12 +1,19 @@
<MessageForm :mustencrypt="{{ $mustencrypt ? 'true' : 'false' }}" :possible="{{ $possible ? 'true' : 'false' }}" inline-template> <MessageForm :mustencrypt="{{ $mustencrypt ? 'true' : 'false' }}" :possible="{{ $possible ? 'true' : 'false' }}" inline-template>
<form action="{{ url('store') }}" method="post" @submit="encrypt" id="messageForm" v-if="possible"> <form action="{{ url('store') }}" method="post" @submit="encrypt" id="messageForm" v-if="possible" enctype="multipart/form-data">
{{ $crsf }} {{ $crsf }}
<div class="contact-form"> <div class="contact-form">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-5" for="message">Quoi de neuf aujourd'hui ?</label> <label class="control-label col-sm-5" for="message">Quoi de neuf aujourd'hui ?</label>
<div class="col-sm-10"> <div class="col-sm-10">
<Autocomplete hasLabel="false" id="message" name="message" rows="5" placeholder="Que s'est-il passé aujourd'hui ?" textarea="true" /> <Autocomplete hasLabel="false" id="message" name="message" rows="5" placeholder="Que s'est-il passé aujourd'hui ?" textarea="true" />
</div>
<div class="col-sm-10">
<span class="text-danger"></span> <span class="text-danger"></span>
<input type="file" name="file" id="file" class="input-file" v-on:change="fileNameChanged($event.target.name, $event.target.files)">
<label for="file" class="btn btn-tertiary js-labelFile">
<i v-bind:class="uploadClass"></i>
<span class="js-fileName" v-html:imageLabel="imageLabel"></span>
</label>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@ -0,0 +1,33 @@
@extends('layouts.connected')
@section('content')
<div class="container">
<div class="page-header">
<h1 class="page-title">
{{ __('My gallery') }}
</h1>
</div>
<div class="row flex-fill">
@foreach($posts as $post)
<div class="col-md-6 col-xl-4">
<div class="card">
<div class="card-status bg-blue"></div>
<div class="card-header">
<h3 class="card-title">{{ ucfirst(\Carbon\Carbon::instance($post->date_post)->diffForHumans()) }}</h3>
<div class="card-options">
<a href="#" class="card-options-collapse" data-toggle="card-collapse"><i class="fe fe-chevron-up"></i></a>
</div>
</div>
<div class="card-body">
<p>
<a href="{{ route('display_image', ['post_id' => $post->id, 'options' => 'o:full', 'image_name' => $post->image]) }}">
<img src="{{ route('display_image', ['post_id' => $post->id, 'options' => 'w:300', 'image_name' => $post->image]) }}"/>
</a>
</p>
</div>
</div>
</div>
@endforeach
</div>
</div>
@endsection

View File

@ -13,6 +13,16 @@
{{ session('status') }} {{ session('status') }}
</div> </div>
@endif @endif
@if (session('success'))
<div class="alert alert-success" role="alert">
{{ session('success') }}
</div>
@endif
@if (session('errors'))
<div class="alert alert-danger" role="alert">
{{ session('errors') }}
</div>
@endif
</div> </div>
<div class="row flex-fill"> <div class="row flex-fill">
<div class="col-sm-6 col-lg-3"> <div class="col-sm-6 col-lg-3">
@ -80,23 +90,19 @@
@endcomponent @endcomponent
</div> </div>
@else @else
<div class="row"> <div class="card">
<div class="col-sm-12"> <div class="card-header">
<div class="card"> <h3 class="card-title">{{ strftime('%B %G', $today->format('U')) }}</h3>
<div class="card-header"> </div>
<h3 class="card-title">{{ strftime('%B %G', $today->format('U')) }}</h3> <div class="card-body">
</div> <div id="store">
<div class="card-body"> @component('components/app', [
<div id="store"> 'route' => url('store'),
@component('components/app', [ 'crsf' => csrf_field(),
'route' => url('store'), 'mustencrypt' => $must_encrypt,
'crsf' => csrf_field(), 'possible' => !$already,
'mustencrypt' => $must_encrypt, ])
'possible' => !$already, @endcomponent
])
@endcomponent
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -110,11 +116,18 @@
<h3 class="card-title">{{ ucfirst(\Carbon\Carbon::instance($post->date_post)->diffForHumans()) }}</h3> <h3 class="card-title">{{ ucfirst(\Carbon\Carbon::instance($post->date_post)->diffForHumans()) }}</h3>
<div class="card-options"> <div class="card-options">
<a href="#" class="card-options-collapse" data-toggle="card-collapse"><i class="fe fe-chevron-up"></i></a> <a href="#" class="card-options-collapse" data-toggle="card-collapse"><i class="fe fe-chevron-up"></i></a>
<a href="#" class="card-options-remove" data-toggle="card-remove"><i class="fe fe-x"></i></a> @if($today->format('Y-m-d') === \Carbon\Carbon::instance($post->date_post)->format('Y-m-d'))
<button class="btn btn-link" data-toggle="collapse" data-target="#collapse" aria-expanded="true" aria-controls="collapse"><i class="fe fe-edit-2"></i></button>
<form action="{{ url('delete') }}" method="post" accept-charset="utf-8">
@csrf
<button class="btn btn-link" data-toggle="card-remove"><i class="fe fe-x"></i></button>
</form>
@endif
</div> </div>
</div> </div>
<div class="card-body" data-encrypt> <div class="card-body @if($today->format('Y-m-d') === \Carbon\Carbon::instance($post->date_post)->format('Y-m-d')) already @endif ">
{!! $tag_detector->linkTagFrom($post->content) !!} @if($post->image != '')<p><img src="{{ route('display_image', ['post_id' => $post->id, 'options' => 'w:300', 'image_name' => $post->image]) }}"/></p>@endif
<p data-encrypt>{!! $tag_detector->linkTagFrom($post->content) !!}</p>
</div> </div>
<div class="card-footer"> <div class="card-footer">
{{ \Carbon\Carbon::instance($post->date_post)->format('d F Y') }} {{ \Carbon\Carbon::instance($post->date_post)->format('d F Y') }}

View File

@ -101,15 +101,7 @@
</div> </div>
<div class="col-lg order-lg-first"> <div class="col-lg order-lg-first">
<ul class="nav nav-tabs border-0 flex-column flex-lg-row"> <ul class="nav nav-tabs border-0 flex-column flex-lg-row">
<li class="nav-item"> @include(config('laravel-menu.views.bootstrap-items'), ['items' => $menu->roots()])
<a href="./" class="nav-link active"><i class="fe fe-home"></i> {{ __('Accueil') }}</a>
</li>
<li class="nav-item dropdown">
<a href="#" class="nav-link"><i class="fe fe-trending-up"></i> {{ __('Mes statistiques') }}</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link"><i class="fe fe-image"></i> {{ __('Ma gallerie') }}</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -0,0 +1,20 @@
@foreach($items as $item)
<li @lm_attrs($item) @if($item->hasChildren()) class="nav-item dropdown" @endif @lm_endattrs>
@if($item->link) <a @lm_attrs($item->link) @if($item->hasChildren()) class="nav-link dropdown-toggle" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" @else class="nav-link" @endif @lm_endattrs href="{!! $item->url() !!}">
{!! $item->title !!}
@if($item->hasChildren()) <b class="caret"></b> @endif
</a>
@else
<span class="navbar-text">{!! $item->title !!}</span>
@endif
@if($item->hasChildren())
<ul class="dropdown-menu">
@include(config('laravel-menu.views.bootstrap-items'),
array('items' => $item->children()))
</ul>
@endif
</li>
@if($item->divider)
<li{!! Lavary\Menu\Builder::attributes($item->divider) !!}></li>
@endif
@endforeach

View File

@ -14,6 +14,10 @@
Auth::routes(); Auth::routes();
Route::get('/', 'WelcomeController@index')->name('home'); Route::get('/', 'WelcomeController@index')->name('home');
Route::get('/home', 'HomeController@index')->name('dashboard'); Route::get('/home', 'DashboardController@index')->name('dashboard');
Route::post('store', 'HomeController@store')->name('store'); Route::post('store', 'DashboardController@store')->name('store');
Route::post('delete', 'DashboardController@delete')->name('delete');
Route::post('edit', 'DashboardController@edit')->name('edit');
Route::post('stats', 'StatsController@tag')->name('stats'); // TODO make a group for stats Route::post('stats', 'StatsController@tag')->name('stats'); // TODO make a group for stats
Route::get('/display/{post_id}/{options}/{image_name}', 'ImageController@display')->name('display_image');
Route::get('/gallery', 'ImageController@gallery')->name('gallery');