diff --git a/app/Attachment.php b/app/Attachment.php
index 55344cd7d..6749130d9 100644
--- a/app/Attachment.php
+++ b/app/Attachment.php
@@ -31,6 +31,9 @@ class Attachment extends Ownable
*/
public function getUrl()
{
+ if ($this->external && strpos($this->path, 'http') !== 0) {
+ return $this->path;
+ }
return baseUrl('/attachments/' . $this->id);
}
}
diff --git a/app/Console/Commands/CleanupImages.php b/app/Console/Commands/CleanupImages.php
new file mode 100644
index 000000000..e05508d5e
--- /dev/null
+++ b/app/Console/Commands/CleanupImages.php
@@ -0,0 +1,83 @@
+imageService = $imageService;
+ parent::__construct();
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $checkRevisions = $this->option('all') ? false : true;
+ $dryRun = $this->option('force') ? false : true;
+
+ if (!$dryRun) {
+ $proceed = $this->confirm("This operation is destructive and is not guaranteed to be fully accurate.\nEnsure you have a backup of your images.\nAre you sure you want to proceed?");
+ if (!$proceed) {
+ return;
+ }
+ }
+
+ $deleted = $this->imageService->deleteUnusedImages($checkRevisions, $dryRun);
+ $deleteCount = count($deleted);
+
+ if ($dryRun) {
+ $this->comment('Dry run, No images have been deleted');
+ $this->comment($deleteCount . ' images found that would have been deleted');
+ $this->showDeletedImages($deleted);
+ $this->comment('Run with -f or --force to perform deletions');
+ return;
+ }
+
+ $this->showDeletedImages($deleted);
+ $this->comment($deleteCount . ' images deleted');
+ }
+
+ protected function showDeletedImages($paths)
+ {
+ if ($this->getOutput()->getVerbosity() <= OutputInterface::VERBOSITY_NORMAL) return;
+ if (count($paths) > 0) {
+ $this->line('Images to delete:');
+ }
+ foreach ($paths as $path) {
+ $this->line($path);
+ }
+ }
+}
diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
index 4f6e690bc..0eb62dc31 100644
--- a/app/Exceptions/Handler.php
+++ b/app/Exceptions/Handler.php
@@ -4,8 +4,6 @@ namespace BookStack\Exceptions;
use Exception;
use Illuminate\Auth\AuthenticationException;
-use Illuminate\Http\Request;
-use Illuminate\Pipeline\Pipeline;
use Illuminate\Validation\ValidationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -33,6 +31,7 @@ class Handler extends ExceptionHandler
*
* @param \Exception $e
* @return mixed
+ * @throws Exception
*/
public function report(Exception $e)
{
@@ -65,30 +64,12 @@ class Handler extends ExceptionHandler
// Handle 404 errors with a loaded session to enable showing user-specific information
if ($this->isExceptionType($e, NotFoundHttpException::class)) {
- return $this->loadErrorMiddleware($request, function ($request) use ($e) {
- $message = $e->getMessage() ?: trans('errors.404_page_not_found');
- return response()->view('errors/404', ['message' => $message], 404);
- });
+ return \Route::respondWithRoute('fallback');
}
return parent::render($request, $e);
}
- /**
- * Load the middleware required to show state/session-enabled error pages.
- * @param Request $request
- * @param $callback
- * @return mixed
- */
- protected function loadErrorMiddleware(Request $request, $callback)
- {
- $middleware = (\Route::getMiddlewareGroups()['web_errors']);
- return (new Pipeline($this->container))
- ->send($request)
- ->through($middleware)
- ->then($callback);
- }
-
/**
* Check the exception chain to compare against the original exception type.
* @param Exception $e
diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php
index ea41278ae..54e14bfb6 100644
--- a/app/Http/Controllers/AttachmentController.php
+++ b/app/Http/Controllers/AttachmentController.php
@@ -103,7 +103,7 @@ class AttachmentController extends Controller
$this->validate($request, [
'uploaded_to' => 'required|integer|exists:pages,id',
'name' => 'required|string|min:1|max:255',
- 'link' => 'url|min:1|max:255'
+ 'link' => 'string|min:1|max:255'
]);
$pageId = $request->get('uploaded_to');
@@ -131,7 +131,7 @@ class AttachmentController extends Controller
$this->validate($request, [
'uploaded_to' => 'required|integer|exists:pages,id',
'name' => 'required|string|min:1|max:255',
- 'link' => 'required|url|min:1|max:255'
+ 'link' => 'required|string|min:1|max:255'
]);
$pageId = $request->get('uploaded_to');
@@ -184,6 +184,7 @@ class AttachmentController extends Controller
* @param $attachmentId
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Symfony\Component\HttpFoundation\Response
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
+ * @throws NotFoundException
*/
public function get($attachmentId)
{
diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php
index bbe1a8679..2077f6888 100644
--- a/app/Http/Controllers/HomeController.php
+++ b/app/Http/Controllers/HomeController.php
@@ -33,22 +33,41 @@ class HomeController extends Controller
$recents = $this->signedIn ? Views::getUserRecentlyViewed(12*$recentFactor, 0) : $this->entityRepo->getRecentlyCreated('book', 12*$recentFactor);
$recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdated('page', 12);
- // Custom homepage
+
$customHomepage = false;
- $homepageSetting = setting('app-homepage');
- if ($homepageSetting) {
- $id = intval(explode(':', $homepageSetting)[0]);
- $customHomepage = $this->entityRepo->getById('page', $id, false, true);
- $this->entityRepo->renderPage($customHomepage, true);
+ $books = false;
+ $booksViewType = false;
+
+ // Check book homepage
+ $bookHomepageSetting = setting('app-book-homepage');
+ if ($bookHomepageSetting) {
+ $books = $this->entityRepo->getAllPaginated('book', 18);
+ $booksViewType = setting()->getUser($this->currentUser, 'books_view_type', config('app.views.books', 'list'));
+ } else {
+ // Check custom homepage
+ $homepageSetting = setting('app-homepage');
+ if ($homepageSetting) {
+ $id = intval(explode(':', $homepageSetting)[0]);
+ $customHomepage = $this->entityRepo->getById('page', $id, false, true);
+ $this->entityRepo->renderPage($customHomepage, true);
+ }
}
- $view = $customHomepage ? 'home-custom' : 'home';
- return view($view, [
+ $view = 'home';
+ if ($bookHomepageSetting) {
+ $view = 'home-book';
+ } else if ($customHomepage) {
+ $view = 'home-custom';
+ }
+
+ return view('common/' . $view, [
'activity' => $activity,
'recents' => $recents,
'recentlyUpdatedPages' => $recentlyUpdatedPages,
'draftPages' => $draftPages,
- 'customHomepage' => $customHomepage
+ 'customHomepage' => $customHomepage,
+ 'books' => $books,
+ 'booksViewType' => $booksViewType
]);
}
@@ -89,27 +108,6 @@ class HomeController extends Controller
]);
}
- /**
- * Get an icon via image request.
- * Can provide a 'color' parameter with hex value to color the icon.
- * @param $iconName
- * @param Request $request
- * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
- */
- public function getIcon($iconName, Request $request)
- {
- $attrs = [];
- if ($request->filled('color')) {
- $attrs['fill'] = '#' . $request->get('color');
- }
-
- $icon = icon($iconName, $attrs);
- return response($icon, 200, [
- 'Content-Type' => 'image/svg+xml',
- 'Cache-Control' => 'max-age=3600',
- ]);
- }
-
/**
* Get custom head HTML, Used in ajax calls to show in editor.
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
@@ -131,7 +129,15 @@ class HomeController extends Controller
$allowRobots = $sitePublic;
}
return response()
- ->view('robots', ['allowRobots' => $allowRobots])
+ ->view('common/robots', ['allowRobots' => $allowRobots])
->header('Content-Type', 'text/plain');
}
+
+ /**
+ * Show the route for 404 responses.
+ */
+ public function getNotFound()
+ {
+ return response()->view('errors/404', [], 404);
+ }
}
diff --git a/app/Http/Controllers/ImageController.php b/app/Http/Controllers/ImageController.php
index 8437c80d7..eb92ae9a8 100644
--- a/app/Http/Controllers/ImageController.php
+++ b/app/Http/Controllers/ImageController.php
@@ -164,32 +164,6 @@ class ImageController extends Controller
return response()->json($image);
}
- /**
- * Replace the data content of a drawing.
- * @param string $id
- * @param Request $request
- * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
- */
- public function replaceDrawing(string $id, Request $request)
- {
- $this->validate($request, [
- 'image' => 'required|string'
- ]);
- $this->checkPermission('image-create-all');
-
- $imageBase64Data = $request->get('image');
- $image = $this->imageRepo->getById($id);
- $this->checkOwnablePermission('image-update', $image);
-
- try {
- $image = $this->imageRepo->replaceDrawingContent($image, $imageBase64Data);
- } catch (ImageUploadException $e) {
- return response($e->getMessage(), 500);
- }
-
- return response()->json($image);
- }
-
/**
* Get the content of an image based64 encoded.
* @param $id
@@ -245,26 +219,29 @@ class ImageController extends Controller
}
/**
- * Deletes an image and all thumbnail/image files
+ * Show the usage of an image on pages.
* @param EntityRepo $entityRepo
- * @param Request $request
- * @param int $id
+ * @param $id
* @return \Illuminate\Http\JsonResponse
*/
- public function destroy(EntityRepo $entityRepo, Request $request, $id)
+ public function usage(EntityRepo $entityRepo, $id)
+ {
+ $image = $this->imageRepo->getById($id);
+ $pageSearch = $entityRepo->searchForImage($image->url);
+ return response()->json($pageSearch);
+ }
+
+ /**
+ * Deletes an image and all thumbnail/image files
+ * @param int $id
+ * @return \Illuminate\Http\JsonResponse
+ * @throws \Exception
+ */
+ public function destroy($id)
{
$image = $this->imageRepo->getById($id);
$this->checkOwnablePermission('image-delete', $image);
- // Check if this image is used on any pages
- $isForced = in_array($request->get('force', ''), [true, 'true']);
- if (!$isForced) {
- $pageSearch = $entityRepo->searchForImage($image->url);
- if ($pageSearch !== false) {
- return response()->json($pageSearch, 400);
- }
- }
-
$this->imageRepo->destroyImage($image);
return response()->json(trans('components.images_deleted'));
}
diff --git a/app/Http/Controllers/SettingController.php b/app/Http/Controllers/SettingController.php
index e0e351458..d9d66042e 100644
--- a/app/Http/Controllers/SettingController.php
+++ b/app/Http/Controllers/SettingController.php
@@ -1,5 +1,6 @@
checkPermission('settings-manage');
- $this->setPageTitle('Settings');
+ $this->setPageTitle(trans('settings.settings'));
// Get application version
$version = trim(file_get_contents(base_path('version')));
@@ -43,4 +44,48 @@ class SettingController extends Controller
session()->flash('success', trans('settings.settings_save_success'));
return redirect('/settings');
}
+
+ /**
+ * Show the page for application maintenance.
+ * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
+ */
+ public function showMaintenance()
+ {
+ $this->checkPermission('settings-manage');
+ $this->setPageTitle(trans('settings.maint'));
+
+ // Get application version
+ $version = trim(file_get_contents(base_path('version')));
+
+ return view('settings/maintenance', ['version' => $version]);
+ }
+
+ /**
+ * Action to clean-up images in the system.
+ * @param Request $request
+ * @param ImageService $imageService
+ * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+ */
+ public function cleanupImages(Request $request, ImageService $imageService)
+ {
+ $this->checkPermission('settings-manage');
+
+ $checkRevisions = !($request->get('ignore_revisions', 'false') === 'true');
+ $dryRun = !($request->has('confirm'));
+
+ $imagesToDelete = $imageService->deleteUnusedImages($checkRevisions, $dryRun);
+ $deleteCount = count($imagesToDelete);
+ if ($deleteCount === 0) {
+ session()->flash('warning', trans('settings.maint_image_cleanup_nothing_found'));
+ return redirect('/settings/maintenance')->withInput();
+ }
+
+ if ($dryRun) {
+ session()->flash('cleanup-images-warning', trans('settings.maint_image_cleanup_warning', ['count' => $deleteCount]));
+ } else {
+ session()->flash('success', trans('settings.maint_image_cleanup_success', ['count' => $deleteCount]));
+ }
+
+ return redirect('/settings/maintenance#image-cleanup')->withInput();
+ }
}
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index 9d2871bbe..cd894de95 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -33,14 +33,6 @@ class Kernel extends HttpKernel
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\BookStack\Http\Middleware\Localization::class
],
- 'web_errors' => [
- \BookStack\Http\Middleware\EncryptCookies::class,
- \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
- \Illuminate\Session\Middleware\StartSession::class,
- \Illuminate\View\Middleware\ShareErrorsFromSession::class,
- \BookStack\Http\Middleware\VerifyCsrfToken::class,
- \BookStack\Http\Middleware\Localization::class
- ],
'api' => [
'throttle:60,1',
'bindings',
diff --git a/app/Image.php b/app/Image.php
index ad23a077a..412beea90 100644
--- a/app/Image.php
+++ b/app/Image.php
@@ -9,13 +9,15 @@ class Image extends Ownable
/**
* Get a thumbnail for this image.
- * @param int $width
- * @param int $height
+ * @param int $width
+ * @param int $height
* @param bool|false $keepRatio
* @return string
+ * @throws \Exception
*/
public function getThumb($width, $height, $keepRatio = false)
{
return Images::getThumbnail($this, $width, $height, $keepRatio);
}
+
}
diff --git a/app/Providers/CustomFacadeProvider.php b/app/Providers/CustomFacadeProvider.php
index a97512e8c..c81a5529d 100644
--- a/app/Providers/CustomFacadeProvider.php
+++ b/app/Providers/CustomFacadeProvider.php
@@ -3,6 +3,7 @@
namespace BookStack\Providers;
use BookStack\Activity;
+use BookStack\Image;
use BookStack\Services\ImageService;
use BookStack\Services\PermissionService;
use BookStack\Services\ViewService;
@@ -57,6 +58,7 @@ class CustomFacadeProvider extends ServiceProvider
$this->app->bind('images', function () {
return new ImageService(
+ $this->app->make(Image::class),
$this->app->make(ImageManager::class),
$this->app->make(Factory::class),
$this->app->make(Repository::class)
diff --git a/app/Repos/ImageRepo.php b/app/Repos/ImageRepo.php
index 245c0f27b..4ccd719ad 100644
--- a/app/Repos/ImageRepo.php
+++ b/app/Repos/ImageRepo.php
@@ -153,17 +153,6 @@ class ImageRepo
return $image;
}
- /**
- * Replace the image content of a drawing.
- * @param Image $image
- * @param string $base64Uri
- * @return Image
- * @throws \BookStack\Exceptions\ImageUploadException
- */
- public function replaceDrawingContent(Image $image, string $base64Uri)
- {
- return $this->imageService->replaceImageDataFromBase64Uri($image, $base64Uri);
- }
/**
* Update the details of an image via an array of properties.
@@ -183,13 +172,14 @@ class ImageRepo
/**
- * Destroys an Image object along with its files and thumbnails.
+ * Destroys an Image object along with its revisions, files and thumbnails.
* @param Image $image
* @return bool
+ * @throws \Exception
*/
public function destroyImage(Image $image)
{
- $this->imageService->destroyImage($image);
+ $this->imageService->destroy($image);
return true;
}
@@ -200,7 +190,7 @@ class ImageRepo
* @throws \BookStack\Exceptions\ImageUploadException
* @throws \Exception
*/
- private function loadThumbs(Image $image)
+ protected function loadThumbs(Image $image)
{
$image->thumbs = [
'gallery' => $this->getThumbnail($image, 150, 150),
@@ -250,7 +240,7 @@ class ImageRepo
*/
public function isValidType($type)
{
- $validTypes = ['drawing', 'gallery', 'cover', 'system', 'user'];
+ $validTypes = ['gallery', 'cover', 'system', 'user'];
return in_array($type, $validTypes);
}
}
diff --git a/app/Repos/UserRepo.php b/app/Repos/UserRepo.php
index 3cfd61d27..d113b676a 100644
--- a/app/Repos/UserRepo.php
+++ b/app/Repos/UserRepo.php
@@ -166,7 +166,7 @@ class UserRepo
// Delete user profile images
$profileImages = $images = Image::where('type', '=', 'user')->where('created_by', '=', $user->id)->get();
foreach ($profileImages as $image) {
- Images::destroyImage($image);
+ Images::destroy($image);
}
}
diff --git a/app/Services/ImageService.php b/app/Services/ImageService.php
index 06ef3a0f0..73a677ac2 100644
--- a/app/Services/ImageService.php
+++ b/app/Services/ImageService.php
@@ -3,11 +3,11 @@
use BookStack\Exceptions\ImageUploadException;
use BookStack\Image;
use BookStack\User;
+use DB;
use Exception;
use Intervention\Image\Exception\NotSupportedException;
use Intervention\Image\ImageManager;
use Illuminate\Contracts\Filesystem\Factory as FileSystem;
-use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
use Illuminate\Contracts\Cache\Repository as Cache;
use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -17,15 +17,18 @@ class ImageService extends UploadService
protected $imageTool;
protected $cache;
protected $storageUrl;
+ protected $image;
/**
* ImageService constructor.
- * @param $imageTool
- * @param $fileSystem
- * @param $cache
+ * @param Image $image
+ * @param ImageManager $imageTool
+ * @param FileSystem $fileSystem
+ * @param Cache $cache
*/
- public function __construct(ImageManager $imageTool, FileSystem $fileSystem, Cache $cache)
+ public function __construct(Image $image, ImageManager $imageTool, FileSystem $fileSystem, Cache $cache)
{
+ $this->image = $image;
$this->imageTool = $imageTool;
$this->cache = $cache;
parent::__construct($fileSystem);
@@ -82,31 +85,6 @@ class ImageService extends UploadService
return $this->saveNew($name, $data, $type, $uploadedTo);
}
- /**
- * Replace the data for an image via a Base64 encoded string.
- * @param Image $image
- * @param string $base64Uri
- * @return Image
- * @throws ImageUploadException
- */
- public function replaceImageDataFromBase64Uri(Image $image, string $base64Uri)
- {
- $splitData = explode(';base64,', $base64Uri);
- if (count($splitData) < 2) {
- throw new ImageUploadException("Invalid base64 image data provided");
- }
- $data = base64_decode($splitData[1]);
- $storage = $this->getStorage();
-
- try {
- $storage->put($image->path, $data);
- } catch (Exception $e) {
- throw new ImageUploadException(trans('errors.path_not_writable', ['filePath' => $image->path]));
- }
-
- return $image;
- }
-
/**
* Gets an image from url and saves it to the database.
* @param $url
@@ -140,16 +118,16 @@ class ImageService extends UploadService
$secureUploads = setting('app-secure-images');
$imageName = str_replace(' ', '-', $imageName);
- if ($secureUploads) {
- $imageName = str_random(16) . '-' . $imageName;
- }
-
$imagePath = '/uploads/images/' . $type . '/' . Date('Y-m-M') . '/';
while ($storage->exists($imagePath . $imageName)) {
$imageName = str_random(3) . $imageName;
}
+
$fullPath = $imagePath . $imageName;
+ if ($secureUploads) {
+ $fullPath = $imagePath . str_random(16) . '-' . $imageName;
+ }
try {
$storage->put($fullPath, $imageData);
@@ -172,20 +150,11 @@ class ImageService extends UploadService
$imageDetails['updated_by'] = $userId;
}
- $image = (new Image());
+ $image = $this->image->newInstance();
$image->forceFill($imageDetails)->save();
return $image;
}
- /**
- * Get the storage path, Dependant of storage type.
- * @param Image $image
- * @return mixed|string
- */
- protected function getPath(Image $image)
- {
- return $image->path;
- }
/**
* Checks if the image is a gif. Returns true if it is, else false.
@@ -194,7 +163,7 @@ class ImageService extends UploadService
*/
protected function isGif(Image $image)
{
- return strtolower(pathinfo($this->getPath($image), PATHINFO_EXTENSION)) === 'gif';
+ return strtolower(pathinfo($image->path, PATHINFO_EXTENSION)) === 'gif';
}
/**
@@ -212,11 +181,11 @@ class ImageService extends UploadService
public function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false)
{
if ($keepRatio && $this->isGif($image)) {
- return $this->getPublicUrl($this->getPath($image));
+ return $this->getPublicUrl($image->path);
}
$thumbDirName = '/' . ($keepRatio ? 'scaled-' : 'thumbs-') . $width . '-' . $height . '/';
- $imagePath = $this->getPath($image);
+ $imagePath = $image->path;
$thumbFilePath = dirname($imagePath) . $thumbDirName . basename($imagePath);
if ($this->cache->has('images-' . $image->id . '-' . $thumbFilePath) && $this->cache->get('images-' . $thumbFilePath)) {
@@ -262,43 +231,51 @@ class ImageService extends UploadService
*/
public function getImageData(Image $image)
{
- $imagePath = $this->getPath($image);
+ $imagePath = $image->path;
$storage = $this->getStorage();
return $storage->get($imagePath);
}
/**
- * Destroys an Image object along with its files and thumbnails.
+ * Destroy an image along with its revisions, thumbnails and remaining folders.
* @param Image $image
- * @return bool
* @throws Exception
*/
- public function destroyImage(Image $image)
+ public function destroy(Image $image)
+ {
+ $this->destroyImagesFromPath($image->path);
+ $image->delete();
+ }
+
+ /**
+ * Destroys an image at the given path.
+ * Searches for image thumbnails in addition to main provided path..
+ * @param string $path
+ * @return bool
+ */
+ protected function destroyImagesFromPath(string $path)
{
$storage = $this->getStorage();
- $imageFolder = dirname($this->getPath($image));
- $imageFileName = basename($this->getPath($image));
+ $imageFolder = dirname($path);
+ $imageFileName = basename($path);
$allImages = collect($storage->allFiles($imageFolder));
+ // Delete image files
$imagesToDelete = $allImages->filter(function ($imagePath) use ($imageFileName) {
$expectedIndex = strlen($imagePath) - strlen($imageFileName);
return strpos($imagePath, $imageFileName) === $expectedIndex;
});
-
$storage->delete($imagesToDelete->all());
// Cleanup of empty folders
- foreach ($storage->directories($imageFolder) as $directory) {
+ $foldersInvolved = array_merge([$imageFolder], $storage->directories($imageFolder));
+ foreach ($foldersInvolved as $directory) {
if ($this->isFolderEmpty($directory)) {
$storage->deleteDirectory($directory);
}
}
- if ($this->isFolderEmpty($imageFolder)) {
- $storage->deleteDirectory($imageFolder);
- }
- $image->delete();
return true;
}
@@ -321,6 +298,46 @@ class ImageService extends UploadService
return $image;
}
+
+ /**
+ * Delete gallery and drawings that are not within HTML content of pages or page revisions.
+ * Checks based off of only the image name.
+ * Could be much improved to be more specific but kept it generic for now to be safe.
+ *
+ * Returns the path of the images that would be/have been deleted.
+ * @param bool $checkRevisions
+ * @param bool $dryRun
+ * @param array $types
+ * @return array
+ */
+ public function deleteUnusedImages($checkRevisions = true, $dryRun = true, $types = ['gallery', 'drawio'])
+ {
+ $types = array_intersect($types, ['gallery', 'drawio']);
+ $deletedPaths = [];
+
+ $this->image->newQuery()->whereIn('type', $types)
+ ->chunk(1000, function($images) use ($types, $checkRevisions, &$deletedPaths, $dryRun) {
+ foreach ($images as $image) {
+ $searchQuery = '%' . basename($image->path) . '%';
+ $inPage = DB::table('pages')
+ ->where('html', 'like', $searchQuery)->count() > 0;
+ $inRevision = false;
+ if ($checkRevisions) {
+ $inRevision = DB::table('page_revisions')
+ ->where('html', 'like', $searchQuery)->count() > 0;
+ }
+
+ if (!$inPage && !$inRevision) {
+ $deletedPaths[] = $image->path;
+ if (!$dryRun) {
+ $this->destroy($image);
+ }
+ }
+ }
+ });
+ return $deletedPaths;
+ }
+
/**
* Convert a image URI to a Base64 encoded string.
* Attempts to find locally via set storage method first.
diff --git a/composer.json b/composer.json
index 5106ed0bb..3de0cb5f7 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,7 @@
"type": "project",
"require": {
"php": ">=7.0.0",
- "laravel/framework": "5.5.*",
+ "laravel/framework": "~5.5.22",
"fideloper/proxy": "~3.3",
"ext-tidy": "*",
"intervention/image": "^2.4",
diff --git a/composer.lock b/composer.lock
index 9370bebff..6e0a35323 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "ed85d10e69b1071020178cb400a80e48",
+ "content-hash": "3bf33ab103b15b06ca06c85fd8ae3b78",
"packages": [
{
"name": "aws/aws-sdk-php",
- "version": "3.52.6",
+ "version": "3.56.4",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "c9af7657eddc0267cc7ac4f969c10d5c18459992"
+ "reference": "03273bb5c1d8098ff6c23b3fa9ee444c4cc1dcee"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c9af7657eddc0267cc7ac4f969c10d5c18459992",
- "reference": "c9af7657eddc0267cc7ac4f969c10d5c18459992",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/03273bb5c1d8098ff6c23b3fa9ee444c4cc1dcee",
+ "reference": "03273bb5c1d8098ff6c23b3fa9ee444c4cc1dcee",
"shasum": ""
},
"require": {
@@ -84,7 +84,7 @@
"s3",
"sdk"
],
- "time": "2018-02-09T22:53:37+00:00"
+ "time": "2018-05-18T19:53:15+00:00"
},
{
"name": "barryvdh/laravel-dompdf",
@@ -439,16 +439,16 @@
},
{
"name": "egulias/email-validator",
- "version": "2.1.3",
+ "version": "2.1.4",
"source": {
"type": "git",
"url": "https://github.com/egulias/EmailValidator.git",
- "reference": "1bec00a10039b823cc94eef4eddd47dcd3b2ca04"
+ "reference": "8790f594151ca6a2010c6218e09d96df67173ad3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/1bec00a10039b823cc94eef4eddd47dcd3b2ca04",
- "reference": "1bec00a10039b823cc94eef4eddd47dcd3b2ca04",
+ "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/8790f594151ca6a2010c6218e09d96df67173ad3",
+ "reference": "8790f594151ca6a2010c6218e09d96df67173ad3",
"shasum": ""
},
"require": {
@@ -457,7 +457,7 @@
},
"require-dev": {
"dominicsayers/isemail": "dev-master",
- "phpunit/phpunit": "^4.8.35",
+ "phpunit/phpunit": "^4.8.35||^5.7||^6.0",
"satooshi/php-coveralls": "^1.0.1"
},
"suggest": {
@@ -492,23 +492,24 @@
"validation",
"validator"
],
- "time": "2017-11-15T23:40:40+00:00"
+ "time": "2018-04-10T10:11:19+00:00"
},
{
"name": "erusev/parsedown",
- "version": "1.6.4",
+ "version": "1.7.1",
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown.git",
- "reference": "fbe3fe878f4fe69048bb8a52783a09802004f548"
+ "reference": "92e9c27ba0e74b8b028b111d1b6f956a15c01fc1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/erusev/parsedown/zipball/fbe3fe878f4fe69048bb8a52783a09802004f548",
- "reference": "fbe3fe878f4fe69048bb8a52783a09802004f548",
+ "url": "https://api.github.com/repos/erusev/parsedown/zipball/92e9c27ba0e74b8b028b111d1b6f956a15c01fc1",
+ "reference": "92e9c27ba0e74b8b028b111d1b6f956a15c01fc1",
"shasum": ""
},
"require": {
+ "ext-mbstring": "*",
"php": ">=5.3.0"
},
"require-dev": {
@@ -537,7 +538,7 @@
"markdown",
"parser"
],
- "time": "2017-11-14T20:44:03+00:00"
+ "time": "2018-03-08T01:11:30+00:00"
},
{
"name": "fideloper/proxy",
@@ -647,16 +648,16 @@
},
{
"name": "guzzlehttp/guzzle",
- "version": "6.3.0",
+ "version": "6.3.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
- "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699"
+ "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699",
- "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+ "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
"shasum": ""
},
"require": {
@@ -666,7 +667,7 @@
},
"require-dev": {
"ext-curl": "*",
- "phpunit/phpunit": "^4.0 || ^5.0",
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
"psr/log": "^1.0"
},
"suggest": {
@@ -675,7 +676,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "6.2-dev"
+ "dev-master": "6.3-dev"
}
},
"autoload": {
@@ -708,7 +709,7 @@
"rest",
"web service"
],
- "time": "2017-06-22T18:50:49+00:00"
+ "time": "2018-04-22T15:46:56+00:00"
},
{
"name": "guzzlehttp/promises",
@@ -964,27 +965,27 @@
},
{
"name": "laravel/framework",
- "version": "v5.5.34",
+ "version": "v5.5.40",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "1de7c0aec13eadbdddc2d1ba4019b064b2c6b966"
+ "reference": "d724ce0aa61bbd9adf658215eec484f5dd6711d6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/1de7c0aec13eadbdddc2d1ba4019b064b2c6b966",
- "reference": "1de7c0aec13eadbdddc2d1ba4019b064b2c6b966",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/d724ce0aa61bbd9adf658215eec484f5dd6711d6",
+ "reference": "d724ce0aa61bbd9adf658215eec484f5dd6711d6",
"shasum": ""
},
"require": {
"doctrine/inflector": "~1.1",
- "erusev/parsedown": "~1.6",
+ "erusev/parsedown": "~1.7",
"ext-mbstring": "*",
"ext-openssl": "*",
- "league/flysystem": "~1.0",
+ "league/flysystem": "^1.0.8",
"monolog/monolog": "~1.12",
"mtdowling/cron-expression": "~1.0",
- "nesbot/carbon": "~1.20",
+ "nesbot/carbon": "^1.24.1",
"php": ">=7.0",
"psr/container": "~1.0",
"psr/simple-cache": "^1.0",
@@ -1030,7 +1031,7 @@
"illuminate/translation": "self.version",
"illuminate/validation": "self.version",
"illuminate/view": "self.version",
- "tightenco/collect": "self.version"
+ "tightenco/collect": "<5.5.33"
},
"require-dev": {
"aws/aws-sdk-php": "~3.0",
@@ -1094,20 +1095,20 @@
"framework",
"laravel"
],
- "time": "2018-02-06T15:36:55+00:00"
+ "time": "2018-03-30T13:29:30+00:00"
},
{
"name": "laravel/socialite",
- "version": "v3.0.9",
+ "version": "v3.0.11",
"source": {
"type": "git",
"url": "https://github.com/laravel/socialite.git",
- "reference": "fc1c8d415699e502f3e61cbc61e3250d5bd942eb"
+ "reference": "4d29ba66fdb38ec994b778e5e51657555cc10511"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/socialite/zipball/fc1c8d415699e502f3e61cbc61e3250d5bd942eb",
- "reference": "fc1c8d415699e502f3e61cbc61e3250d5bd942eb",
+ "url": "https://api.github.com/repos/laravel/socialite/zipball/4d29ba66fdb38ec994b778e5e51657555cc10511",
+ "reference": "4d29ba66fdb38ec994b778e5e51657555cc10511",
"shasum": ""
},
"require": {
@@ -1156,20 +1157,20 @@
"laravel",
"oauth"
],
- "time": "2017-11-06T16:02:48+00:00"
+ "time": "2018-05-12T17:44:53+00:00"
},
{
"name": "league/flysystem",
- "version": "1.0.42",
+ "version": "1.0.45",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
- "reference": "09eabc54e199950041aef258a85847676496fe8e"
+ "reference": "a99f94e63b512d75f851b181afcdf0ee9ebef7e6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/09eabc54e199950041aef258a85847676496fe8e",
- "reference": "09eabc54e199950041aef258a85847676496fe8e",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a99f94e63b512d75f851b181afcdf0ee9ebef7e6",
+ "reference": "a99f94e63b512d75f851b181afcdf0ee9ebef7e6",
"shasum": ""
},
"require": {
@@ -1240,20 +1241,20 @@
"sftp",
"storage"
],
- "time": "2018-01-27T16:03:56+00:00"
+ "time": "2018-05-07T08:44:23+00:00"
},
{
"name": "league/flysystem-aws-s3-v3",
- "version": "1.0.18",
+ "version": "1.0.19",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
- "reference": "dc09b19f455750663b922ed52dcc0ff215bed284"
+ "reference": "f135691ef6761542af301b7c9880f140fb12dc74"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/dc09b19f455750663b922ed52dcc0ff215bed284",
- "reference": "dc09b19f455750663b922ed52dcc0ff215bed284",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/f135691ef6761542af301b7c9880f140fb12dc74",
+ "reference": "f135691ef6761542af301b7c9880f140fb12dc74",
"shasum": ""
},
"require": {
@@ -1287,7 +1288,7 @@
}
],
"description": "Flysystem adapter for the AWS S3 SDK v3.x",
- "time": "2017-06-30T06:29:25+00:00"
+ "time": "2018-03-27T20:33:59+00:00"
},
{
"name": "league/oauth1-client",
@@ -1531,35 +1532,30 @@
},
{
"name": "nesbot/carbon",
- "version": "1.22.1",
+ "version": "1.27.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
- "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc"
+ "reference": "ef81c39b67200dcd7401c24363dcac05ac3a4fe9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
- "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/ef81c39b67200dcd7401c24363dcac05ac3a4fe9",
+ "reference": "ef81c39b67200dcd7401c24363dcac05ac3a4fe9",
"shasum": ""
},
"require": {
- "php": ">=5.3.0",
- "symfony/translation": "~2.6 || ~3.0"
+ "php": ">=5.3.9",
+ "symfony/translation": "~2.6 || ~3.0 || ~4.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~2",
- "phpunit/phpunit": "~4.0 || ~5.0"
+ "phpunit/phpunit": "^4.8.35 || ^5.7"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.23-dev"
- }
- },
"autoload": {
"psr-4": {
- "Carbon\\": "src/Carbon/"
+ "": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1580,20 +1576,20 @@
"datetime",
"time"
],
- "time": "2017-01-16T07:55:07+00:00"
+ "time": "2018-04-23T09:02:57+00:00"
},
{
"name": "paragonie/random_compat",
- "version": "v2.0.11",
+ "version": "v2.0.12",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
- "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8"
+ "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8",
- "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb",
+ "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb",
"shasum": ""
},
"require": {
@@ -1628,7 +1624,7 @@
"pseudorandom",
"random"
],
- "time": "2017-09-27T21:40:39+00:00"
+ "time": "2018-04-04T21:24:14+00:00"
},
{
"name": "phenx/php-font-lib",
@@ -1905,16 +1901,16 @@
},
{
"name": "psr/simple-cache",
- "version": "1.0.0",
+ "version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/simple-cache.git",
- "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24"
+ "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/753fa598e8f3b9966c886fe13f370baa45ef0e24",
- "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+ "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"shasum": ""
},
"require": {
@@ -1949,7 +1945,7 @@
"psr-16",
"simple-cache"
],
- "time": "2017-01-02T13:31:39+00:00"
+ "time": "2017-10-23T01:57:42+00:00"
},
{
"name": "ramsey/uuid",
@@ -2077,21 +2073,21 @@
},
{
"name": "socialiteproviders/gitlab",
- "version": "v3.0.1",
+ "version": "v3.0.2",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/GitLab.git",
- "reference": "c96dc004563a3caf157608fe9aa9e45c79065d00"
+ "reference": "bab80e8e16853e062c58013b1c1f474bd5a5c49a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/GitLab/zipball/c96dc004563a3caf157608fe9aa9e45c79065d00",
- "reference": "c96dc004563a3caf157608fe9aa9e45c79065d00",
+ "url": "https://api.github.com/repos/SocialiteProviders/GitLab/zipball/bab80e8e16853e062c58013b1c1f474bd5a5c49a",
+ "reference": "bab80e8e16853e062c58013b1c1f474bd5a5c49a",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0",
- "socialiteproviders/manager": "~3.0"
+ "socialiteproviders/manager": "~2.0 || ~3.0"
},
"type": "library",
"autoload": {
@@ -2110,7 +2106,7 @@
}
],
"description": "GitLab OAuth2 Provider for Laravel Socialite",
- "time": "2017-01-31T05:06:13+00:00"
+ "time": "2018-05-11T03:10:27+00:00"
},
{
"name": "socialiteproviders/manager",
@@ -2795,16 +2791,16 @@
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.7.0",
+ "version": "v1.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b"
+ "reference": "3296adf6a6454a050679cde90f95350ad604b171"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b",
- "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171",
+ "reference": "3296adf6a6454a050679cde90f95350ad604b171",
"shasum": ""
},
"require": {
@@ -2816,7 +2812,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.7-dev"
+ "dev-master": "1.8-dev"
}
},
"autoload": {
@@ -2850,7 +2846,7 @@
"portable",
"shim"
],
- "time": "2018-01-30T19:27:44+00:00"
+ "time": "2018-04-26T10:06:28+00:00"
},
{
"name": "symfony/process",
@@ -3213,16 +3209,16 @@
"packages-dev": [
{
"name": "barryvdh/laravel-debugbar",
- "version": "v3.1.1",
+ "version": "v3.1.4",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-debugbar.git",
- "reference": "f0018d359a2ad6968ad11b283283a925e017f3c9"
+ "reference": "7a91480cc6e597caed5117a3c5d685f06d35c5a1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/f0018d359a2ad6968ad11b283283a925e017f3c9",
- "reference": "f0018d359a2ad6968ad11b283283a925e017f3c9",
+ "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/7a91480cc6e597caed5117a3c5d685f06d35c5a1",
+ "reference": "7a91480cc6e597caed5117a3c5d685f06d35c5a1",
"shasum": ""
},
"require": {
@@ -3277,7 +3273,7 @@
"profiler",
"webprofiler"
],
- "time": "2018-02-07T08:29:09+00:00"
+ "time": "2018-03-06T08:35:31+00:00"
},
{
"name": "barryvdh/laravel-ide-helper",
@@ -3725,16 +3721,16 @@
},
{
"name": "mockery/mockery",
- "version": "1.0",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
- "reference": "1bac8c362b12f522fdd1f1fa3556284c91affa38"
+ "reference": "99e29d3596b16dabe4982548527d5ddf90232e99"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/mockery/mockery/zipball/1bac8c362b12f522fdd1f1fa3556284c91affa38",
- "reference": "1bac8c362b12f522fdd1f1fa3556284c91affa38",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/99e29d3596b16dabe4982548527d5ddf90232e99",
+ "reference": "99e29d3596b16dabe4982548527d5ddf90232e99",
"shasum": ""
},
"require": {
@@ -3743,7 +3739,8 @@
"php": ">=5.6.0"
},
"require-dev": {
- "phpunit/phpunit": "~5.7|~6.1"
+ "phpdocumentor/phpdocumentor": "^2.9",
+ "phpunit/phpunit": "~5.7.10|~6.5"
},
"type": "library",
"extra": {
@@ -3772,8 +3769,8 @@
"homepage": "http://davedevelopment.co.uk"
}
],
- "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.",
- "homepage": "http://github.com/mockery/mockery",
+ "description": "Mockery is a simple yet flexible PHP mock object framework",
+ "homepage": "https://github.com/mockery/mockery",
"keywords": [
"BDD",
"TDD",
@@ -3786,7 +3783,7 @@
"test double",
"testing"
],
- "time": "2017-10-06T16:20:43+00:00"
+ "time": "2018-05-08T08:54:48+00:00"
},
{
"name": "myclabs/deep-copy",
@@ -4089,28 +4086,28 @@
},
{
"name": "phpspec/prophecy",
- "version": "1.7.3",
+ "version": "1.7.6",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
- "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf"
+ "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf",
- "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712",
+ "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
- "sebastian/comparator": "^1.1|^2.0",
+ "sebastian/comparator": "^1.1|^2.0|^3.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
},
"require-dev": {
"phpspec/phpspec": "^2.5|^3.2",
- "phpunit/phpunit": "^4.8.35 || ^5.7"
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
},
"type": "library",
"extra": {
@@ -4148,20 +4145,20 @@
"spy",
"stub"
],
- "time": "2017-11-24T13:59:53+00:00"
+ "time": "2018-04-18T13:57:24+00:00"
},
{
"name": "phpunit/php-code-coverage",
- "version": "5.3.0",
+ "version": "5.3.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1"
+ "reference": "c89677919c5dd6d3b3852f230a663118762218ac"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/661f34d0bd3f1a7225ef491a70a020ad23a057a1",
- "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac",
+ "reference": "c89677919c5dd6d3b3852f230a663118762218ac",
"shasum": ""
},
"require": {
@@ -4211,7 +4208,7 @@
"testing",
"xunit"
],
- "time": "2017-12-06T09:29:45+00:00"
+ "time": "2018-04-06T15:36:58+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -4401,16 +4398,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "6.5.6",
+ "version": "6.5.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "3330ef26ade05359d006041316ed0fa9e8e3cefe"
+ "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3330ef26ade05359d006041316ed0fa9e8e3cefe",
- "reference": "3330ef26ade05359d006041316ed0fa9e8e3cefe",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4f21a3c6b97c42952fd5c2837bb354ec0199b97b",
+ "reference": "4f21a3c6b97c42952fd5c2837bb354ec0199b97b",
"shasum": ""
},
"require": {
@@ -4481,7 +4478,7 @@
"testing",
"xunit"
],
- "time": "2018-02-01T05:57:37+00:00"
+ "time": "2018-04-10T11:38:34+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@@ -5103,16 +5100,16 @@
},
{
"name": "squizlabs/php_codesniffer",
- "version": "3.2.2",
+ "version": "3.2.3",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
- "reference": "d7c00c3000ac0ce79c96fcbfef86b49a71158cd1"
+ "reference": "4842476c434e375f9d3182ff7b89059583aa8b27"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7c00c3000ac0ce79c96fcbfef86b49a71158cd1",
- "reference": "d7c00c3000ac0ce79c96fcbfef86b49a71158cd1",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/4842476c434e375f9d3182ff7b89059583aa8b27",
+ "reference": "4842476c434e375f9d3182ff7b89059583aa8b27",
"shasum": ""
},
"require": {
@@ -5122,7 +5119,7 @@
"php": ">=5.4.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0"
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"bin": [
"bin/phpcs",
@@ -5150,7 +5147,7 @@
"phpcs",
"standards"
],
- "time": "2017-12-19T21:44:46+00:00"
+ "time": "2018-02-20T21:35:23+00:00"
},
{
"name": "symfony/class-loader",
diff --git a/config/session.php b/config/session.php
index 8d8c14fe9..b334ffb3c 100644
--- a/config/session.php
+++ b/config/session.php
@@ -135,7 +135,7 @@ return [
|
*/
- 'domain' => null,
+ 'domain' => env('SESSION_DOMAIN', null),
/*
|--------------------------------------------------------------------------
@@ -148,6 +148,34 @@ return [
|
*/
- 'secure' => false,
+ 'secure' => env('SESSION_SECURE_COOKIE', false),
+
+ /*
+ |--------------------------------------------------------------------------
+ | HTTP Access Only
+ |--------------------------------------------------------------------------
+ |
+ | Setting this value to true will prevent JavaScript from accessing the
+ | value of the cookie and the cookie will only be accessible through
+ | the HTTP protocol. You are free to modify this option if needed.
+ |
+ */
+
+ 'http_only' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Same-Site Cookies
+ |--------------------------------------------------------------------------
+ |
+ | This option determines how your cookies behave when cross-site requests
+ | take place, and can be used to mitigate CSRF attacks. By default, we
+ | do not enable this as other CSRF protection services are in place.
+ |
+ | Supported: "lax", "strict"
+ |
+ */
+
+ 'same_site' => null,
];
diff --git a/package-lock.json b/package-lock.json
index 30f58c000..917a8b75e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -693,6 +693,30 @@
}
}
},
+ "@babel/polyfill": {
+ "version": "7.0.0-beta.46",
+ "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.0.0-beta.46.tgz",
+ "integrity": "sha512-eFFWNiI3Os7bBkIA10ZGBUMywK+1/OTVg+qsrlaXRBTpAN0n1g1pXCkNN4rcGpgLPNyfZHQEj+aVAyWPGerSIQ==",
+ "dev": true,
+ "requires": {
+ "core-js": "2.5.5",
+ "regenerator-runtime": "0.11.1"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz",
+ "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=",
+ "dev": true
+ },
+ "regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
+ "dev": true
+ }
+ }
+ },
"@babel/preset-env": {
"version": "7.0.0-beta.40",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.0.0-beta.40.tgz",
@@ -1888,17 +1912,6 @@
"babel-types": "6.26.0"
}
},
- "babel-polyfill": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
- "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
- "dev": true,
- "requires": {
- "babel-runtime": "6.26.0",
- "core-js": "2.5.1",
- "regenerator-runtime": "0.10.5"
- }
- },
"babel-preset-es2015": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz",
@@ -8765,12 +8778,6 @@
"regenerate": "1.3.3"
}
},
- "regenerator-runtime": {
- "version": "0.10.5",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
- "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
- "dev": true
- },
"regenerator-transform": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz",
@@ -9079,9 +9086,9 @@
}
},
"sass-loader": {
- "version": "6.0.7",
- "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-6.0.7.tgz",
- "integrity": "sha512-JoiyD00Yo1o61OJsoP2s2kb19L1/Y2p3QFcCdWdF6oomBGKVYuZyqHWemRBfQ2uGYsk+CH3eCguXNfpjzlcpaA==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.0.1.tgz",
+ "integrity": "sha512-MeVVJFejJELlAbA7jrRchi88PGP6U9yIfqyiG+bBC4a9s2PX+ulJB9h8bbEohtPBfZmlLhNZ0opQM9hovRXvlw==",
"dev": true,
"requires": {
"clone-deep": "2.0.2",
@@ -9851,9 +9858,9 @@
}
},
"style-loader": {
- "version": "0.20.3",
- "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz",
- "integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==",
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz",
+ "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==",
"dev": true,
"requires": {
"loader-utils": "1.1.0",
diff --git a/package.json b/package.json
index 2c2d8e370..e6b7180b9 100644
--- a/package.json
+++ b/package.json
@@ -14,15 +14,15 @@
"@babel/preset-env": "^7.0.0-beta.40",
"autoprefixer": "^8.1.0",
"babel-loader": "^8.0.0-beta.0",
- "babel-polyfill": "^6.26.0",
+ "@babel/polyfill": "^7.0.0-beta.40",
"css-loader": "^0.28.10",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"livereload": "^0.7.0",
"node-sass": "^4.7.2",
"npm-run-all": "^4.1.2",
"postcss-loader": "^2.1.1",
- "sass-loader": "^6.0.7",
- "style-loader": "^0.20.3",
+ "sass-loader": "^7.0.1",
+ "style-loader": "^0.21.0",
"uglifyjs-webpack-plugin": "^1.2.3",
"webpack": "^4.1.1",
"webpack-cli": "^2.0.11"
diff --git a/resources/assets/icons/spanner.svg b/resources/assets/icons/spanner.svg
new file mode 100644
index 000000000..8ab25a247
--- /dev/null
+++ b/resources/assets/icons/spanner.svg
@@ -0,0 +1,4 @@
+
diff --git a/resources/assets/icons/star.svg b/resources/assets/icons/star.svg
new file mode 100644
index 000000000..c7686389d
--- /dev/null
+++ b/resources/assets/icons/star.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/resources/assets/icons/warning.svg b/resources/assets/icons/warning.svg
index dc1aefc25..b1d1ad02c 100644
--- a/resources/assets/icons/warning.svg
+++ b/resources/assets/icons/warning.svg
@@ -1,4 +1,4 @@
-