Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Media on Articles in Blog #70

Merged
merged 5 commits into from
Apr 22, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions app/Http/Controllers/Admin/Blog/ArticleController.php
Original file line number Diff line number Diff line change
@@ -73,6 +73,22 @@ public function create(Request $request): RedirectResponse
$article->content = $content;
$article->save();

// Default banner for the article.
$banner = resource_path('assets/images/articles/default_banner.jpg');

if (!is_null($request->file('banner'))) {
$banner = $request->file('banner');
}

$article->clearMediaCollection('article');
$article->addMedia($banner)
->preservingOriginal()
->setName(substr(md5($article->slug), 0, 10))
->setFileName(
substr(md5($article->slug), 0, 10) . '.' . (is_string($banner) ? 'jpg' : $banner->extension())
)
->toMediaCollection('article');

return redirect()
->route('admin.blog.article.index')
->with('success', 'Your article has been created successfully !');
@@ -133,6 +149,26 @@ public function update(Request $request, int $id): RedirectResponse
$article->content = $content;
$article->save();

if (!is_null($request->file('banner')) || $article->article_banner == '/images/articles/default_banner.jpg') {
// Default banner for the article.
$banner = resource_path('assets/images/articles/default_banner.jpg');

if (!is_null($request->file('banner'))) {
$banner = $request->file('banner');
}

$article->clearMediaCollection('article');
$article->addMedia($banner)
->preservingOriginal()
->setName(substr(md5($article->slug), 0, 10))
->setFileName(
substr(md5($article->slug), 0, 10) . '.' . (is_string($banner) ? 'jpg' : $banner->extension())
)
->toMediaCollection('article');
}



return redirect()
->route('admin.blog.article.index')
->with('success', 'Your article has been updated successfully !');
8 changes: 1 addition & 7 deletions app/Http/Controllers/Blog/CommentController.php
Original file line number Diff line number Diff line change
@@ -24,13 +24,7 @@ class CommentController extends Controller
*/
public function create(Request $request): RedirectResponse
{
$article = Article::findOrFail($request->article_id);

if ($article->is_display == false) {
return back()
->withInput()
->with('danger', 'This article doesn\'t exist or you can not reply to it !');
}
Article::findOrFail($request->article_id);

if (Comment::isFlooding('xetaravel.flood.blog.comment')) {
return back()
1 change: 1 addition & 0 deletions app/Http/Kernel.php
Original file line number Diff line number Diff line change
@@ -70,6 +70,7 @@ class Kernel extends HttpKernel
// Applications
'permission' => \Xetaravel\Http\Middleware\VerifyPermission::class,
'discuss.maintenance' => \Xetaravel\Http\Middleware\DiscussMaintenance::class,
'display' => \Xetaravel\Http\Middleware\EnableDisplayScopeMiddleware::class,

// Extension
'role' => \Ultraware\Roles\Middleware\VerifyRole::class,
26 changes: 26 additions & 0 deletions app/Http/Middleware/EnableDisplayScopeMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Xetaravel\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Xetaravel\Models\Article;
use Xetaravel\Models\Scopes\DisplayScope;

class EnableDisplayScopeMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
*
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
Article::addGlobalScope(new DisplayScope);

return $next($request);
}
}
2 changes: 1 addition & 1 deletion app/Http/Middleware/VerifyPermission.php
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ public function handle($request, Closure $next, ...$permission)
if (!$this->auth->check() && in_array('allowGuest', $permission)) {
return $next($request);
}

if ($this->auth->check() && $this->auth->user()->hasPermission($permission[0])) {
return $next($request);
}
41 changes: 26 additions & 15 deletions app/Models/Article.php
Original file line number Diff line number Diff line change
@@ -3,21 +3,22 @@

use Eloquence\Behaviours\CountCache\Countable;
use Eloquence\Behaviours\Sluggable;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Xetaio\Mentions\Models\Traits\HasMentionsTrait;
use Xetaravel\Models\Category;
use Xetaravel\Models\Presenters\ArticlePresenter;
use Xetaravel\Models\Scopes\DisplayScope;
use Xetaravel\Models\User;

class Article extends Model
class Article extends Model implements HasMedia
{
use Countable,
Sluggable,
ArticlePresenter,
HasMentionsTrait;
HasMentionsTrait,
InteractsWithMedia;

/**
* The attributes that are mass assignable.
@@ -38,7 +39,10 @@ class Article extends Model
* @var array
*/
protected $appends = [
'article_url'
'article_url',

// Media Model
'article_banner'
];

/**
@@ -50,15 +54,6 @@ protected static function boot()
{
parent::boot();

// The Route::getPrefix() is undefined in the testing environment for mysterious reasons.
if (App::environment() !== 'testing') {
// Don't apply the scope to the admin part.
$result = strpos(Route::getFacadeRoot()->current()->getPrefix(), 'admin');
if ($result === false) {
static::addGlobalScope(new DisplayScope);
}
}

// Generated the slug before updating.
static::updating(function ($model) {
$model->generateSlug();
@@ -93,6 +88,22 @@ public function countCaches(): array
];
}

/**
* Register the related to the Model.
*
* @return void
*/
public function registerMediaConversions(Media $media = null): void
{
$this->addMediaConversion('article.banner')
->width(825)
->height(250)
->keepOriginalImageFormat();

$this->addMediaConversion('original')
->keepOriginalImageFormat();
}

/**
* Get the category that owns the article.
*
33 changes: 33 additions & 0 deletions app/Models/Presenters/ArticlePresenter.php
Original file line number Diff line number Diff line change
@@ -3,6 +3,13 @@

trait ArticlePresenter
{
/**
* The default banner used when there is no banner for the article.
*
* @var string
*/
protected $defaultBanner = '/images/articles/default_banner.jpg';

/**
* Get the article url.
*
@@ -16,4 +23,30 @@ public function getArticleUrlAttribute(): string

return route('blog.article.show', ['slug' => $this->slug, 'id' => $this->getKey()]);
}

/**
* Get the small avatar.
*
* @return string
*/
public function getArticleBannerAttribute(): string
{
return $this->parseMedia('article.banner');
}

/**
* Parse a media and return it if isset or return the default banner.
*
* @param string $type The type of the media to get.
*
* @return string
*/
protected function parseMedia(string $type): string
{
if (isset($this->getMedia('article')[0])) {
return $this->getMedia('article')[0]->getUrl($type);
}

return $this->defaultBanner;
}
}
2 changes: 1 addition & 1 deletion app/Models/Presenters/UserPresenter.php
Original file line number Diff line number Diff line change
@@ -231,7 +231,7 @@ public function getOnlineAttribute(): string
}

/**
* Parse a mdedia and return it if isset or return the default avatar.
* Parse a media and return it if isset or return the default avatar.
*
* @param string $type The type of the media to get.
*
10 changes: 10 additions & 0 deletions database/seeders/ArticlesTableSeed.php
Original file line number Diff line number Diff line change
@@ -114,5 +114,15 @@ public function article()
];

DB::table('articles')->insert($articles);

// Update avatars
foreach ($articles as $article) {
$model = \Xetaravel\Models\Article::where('slug', $article['slug'])->first();
$model->addMedia(resource_path('assets/images/articles/default_banner.jpg'))
->preservingOriginal()
->setName(substr(md5($article['slug']), 0, 10))
->setFileName(substr(md5($article['slug']), 0, 10) . '.jpg')
->toMediaCollection('article');
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
27 changes: 18 additions & 9 deletions resources/assets/sass/_blog.scss
Original file line number Diff line number Diff line change
@@ -28,15 +28,24 @@
* Blog posts
*/

.blog-post {
margin-bottom: 4rem;
}
.blog-post-meta {
margin-bottom: 1.25rem;
color: #999;
}
.blog-footer {
margin-top: 3rem;
.blog {
&-post {
margin-bottom: 4rem;

&-meta {
margin-bottom: 1.25rem;
color: #999;
}

&-banner {
object-fit: fill;
max-width: 100%;
}
}

&-footer {
margin-top: 3rem;
}
}

/**
17 changes: 17 additions & 0 deletions resources/assets/sass/admin/_article.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.card {
.fileinput {
&-new,
&-exists {
max-width: 100%;
max-height: 250px;

& img {
max-width: 100%;
max-height: 250px;
border: 1px solid $brand-primary;
padding: 4px;
border-radius: 0.18rem;
}
}
}
}
1 change: 1 addition & 0 deletions resources/assets/sass/admin/xetaravel.admin.scss
Original file line number Diff line number Diff line change
@@ -32,3 +32,4 @@ $selection-color: #fff !default;
@import "interface";
@import "widget";
@import "profile";
@import "article";
26 changes: 25 additions & 1 deletion resources/views/Admin/Blog/article/create.blade.php
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@
Create an article
</h5>
<div class="card-block">
{!! Form::open(['route' => 'admin.blog.article.create', 'method' => 'post']) !!}
{!! Form::open(['route' => 'admin.blog.article.create', 'files'=>'true', 'method' => 'post']) !!}

{!! Form::bsText(
'title',
@@ -67,6 +67,30 @@
]
) !!}

<div class="form-group mb-2 {{ $errors->has('banner') ? 'has-danger' : '' }}">
<div class="fileinput fileinput-exists" data-provides="fileinput">
<div class="fileinput-new">
<img src="{{ asset('images/articles/default_banner.jpg') }}" alt="Default Banner">
</div>
<div class="fileinput-preview fileinput-exists">
<img src="{{ asset('images/articles/default_banner.jpg') }}" alt="Article Banner">
</div>
<div class="mt-1">
<span class="btn btn-outline-primary btn-file">
<i class="fa fa-refresh"></i>
<span class="fileinput-exists">Change</span>
{!! Form::file('banner') !!}
</span>
<span class="text-muted">Recommended size : 825x250px<span>
</div>
</div>
@if ($errors->has('banner'))
<div class="form-control-feedback">
{{ $errors->first('banner') }}
</div>
@endif
</div>

{!! Form::bsTextarea(
'content',
'Content',
26 changes: 25 additions & 1 deletion resources/views/Admin/Blog/article/update.blade.php
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@
Update : {{ Str::limit($article->title, 60) }}
</h5>
<div class="card-block">
{!! Form::model($article, ['route' => ['admin.blog.article.update', $article->id], 'method' => 'put']) !!}
{!! Form::model($article, ['route' => ['admin.blog.article.update', $article->id], 'files'=>'true', 'method' => 'put']) !!}

{!! Form::bsText(
'title',
@@ -62,6 +62,30 @@
]
) !!}

<div class="form-group mb-2 {{ $errors->has('banner') ? 'has-danger' : '' }}">
<div class="fileinput fileinput-exists" data-provides="fileinput">
<div class="fileinput-new">
<img src="{{ $article->article_banner }}" alt="Default Banner">
</div>
<div class="fileinput-preview fileinput-exists">
<img src="{{ $article->article_banner }}" alt="Article Banner">
</div>
<div class="mt-1">
<span class="btn btn-outline-primary btn-file">
<i class="fa fa-refresh"></i>
<span class="fileinput-exists">Change</span>
{!! Form::file('banner') !!}
</span>
<span class="text-muted">Recommended size : 825x250px<span>
</div>
</div>
@if ($errors->has('banner'))
<div class="form-control-feedback">
{{ $errors->first('banner') }}
</div>
@endif
</div>

{!! Form::bsTextarea(
'content',
'Content',
1 change: 1 addition & 0 deletions resources/views/Blog/article/show.blade.php
Original file line number Diff line number Diff line change
@@ -72,6 +72,7 @@
</li>
</ul>
</div>
<img class="blog-post-banner" src="{{ $article->article_banner }}" alt="Article image">
<div>
{!! Markdown::convertToHtml($article->content) !!}
</div>
4 changes: 2 additions & 2 deletions resources/views/page/index.blade.php
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@

@forelse ($articles as $article)
<div class="col-md-4 mb-1">
<div class="card card-outline-primary text-xs-center" style="height: 100%; margin-bottom: 60px;">
<div class="card card-outline-primary text-xs-center">

<div class="card-block">
<h4 class="card-title text-truncate" data-toggle="tooltip" title="{{ $article->title }}">
@@ -126,7 +126,7 @@
</div>
</div>

<div class="card-footer" style="position: absolute; bottom: 0; width: 100%;">
<div class="card-footer">
<a href="{{ $article->article_url }}" class="card-link btn btn-outline-primary">
<i aria-hidden="true" class="fa fa-newspaper-o"></i> Read More
</a>
2 changes: 2 additions & 0 deletions resources/views/partials/blog/_articles.blade.php
Original file line number Diff line number Diff line change
@@ -26,6 +26,8 @@
</ul>
</div>

<img class="blog-post-banner" src="{{ $article->article_banner }}" alt="Article image">

<div>
{!! Markdown::convertToHtml(Str::limit($article->content, 650)) !!}
</div>
4 changes: 2 additions & 2 deletions routes/web.php
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
| Regular Routes
|--------------------------------------------------------------------------
*/
Route::group(['middleware' => ['permission:access.site,allowGuest']], function () {
Route::group(['middleware' => ['permission:access.site,allowGuest', 'display']], function () {
Route::get('/', 'PageController@index')->name('page.index');

Route::get('terms', 'PageController@terms')->name('page.terms');
@@ -114,7 +114,7 @@
Route::group([
'namespace' => 'Blog',
'prefix' => 'blog',
'middleware' => ['permission:access.site,allowGuest']
'middleware' => ['permission:access.site,allowGuest', 'display']
], function () {

// Article Routes
74 changes: 73 additions & 1 deletion tests/Http/Controllers/Admin/Blog/ArticleControllerTest.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<?php
namespace Tests\Http\Controllers\Admin\Blog;

use Illuminate\Http\UploadedFile;
use Tests\TestCase;
use Xetaravel\Models\Article;
use Xetaravel\Models\User;
use Tests\TestCase;

class ArticleControllerTest extends TestCase
{
@@ -67,6 +68,41 @@ public function testCreateSuccess()
$this->assertSame($data['category_id'], $article->category_id);
}

/**
* testCreateWithBannerSuccess method
*
* @return void
*/
public function testCreateWithBannerSuccess()
{
$file = new UploadedFile(
resource_path('assets/images/articles/default_banner.jpg'),
'default_banner.jpg',
'image/jpg',
null,
true
);

$data = [
'title' => 'My article',
'category_id' => 1,
'is_display' => 'on',
'content' => 'My awesome content.',
'banner' => $file
];

$response = $this->post('/admin/blog/article/create', $data);
$response->assertStatus(302);
$response->assertSessionHas('success');

$article = Article::where('title', $data['title'])->first();
$this->assertSame($data['title'], $article->title);
$this->assertSame('my-article', (string) $article->slug);
$this->assertSame(1, $article->user_id);
$this->assertSame($data['category_id'], $article->category_id);
$this->assertStringContainsString('-article.banner.jpg', $article->article_banner);
}

/**
* testShowUpdateFormSuccess method
*
@@ -116,6 +152,42 @@ public function testUpdateSuccess()
$this->assertSame($data['category_id'], $article->category_id);
}

/**
* testUpdateSuccess method
*
* @return void
*/
public function testUpdateWithBannerSuccess()
{
$file = new UploadedFile(
resource_path('assets/images/articles/default_banner.jpg'),
'default_banner.jpg',
'image/jpg',
null,
true
);

$data = [
'title' => 'My article',
'category_id' => 2,
'content' => 'My awesome content.',
'banner' => $file
];

$response = $this->put('/admin/blog/article/update/1', $data);
$response->assertStatus(302);
$response->assertSessionHas('success');

$article = Article::find(1);
$this->assertSame($data['title'], $article->title);
$this->assertSame('my-article', (string) $article->slug);
$this->assertSame(false, (bool) $article->is_display);
$this->assertSame(1, $article->user_id);
$this->assertSame($data['content'], $article->content);
$this->assertSame($data['category_id'], $article->category_id);
$this->assertStringContainsString('-article.banner.jpg', $article->article_banner);
}

/**
* testDeleteSuccess method
*
5 changes: 3 additions & 2 deletions tests/Http/Controllers/Blog/CommentControllerTest.php
Original file line number Diff line number Diff line change
@@ -44,8 +44,9 @@ public function testCreateArticleNotDisplay()
$article->save();

$response = $this->post('/blog/comment/create', ['article_id' => 1]);
$response->assertSessionHas('danger');
$response->assertStatus(302);
// The article must not be found since is_display is false and the global scope apply.
$response->assertSeeText('Not found');
$response->assertStatus(404);
}

/**