mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-01-19 05:52:48 +08:00
Converted image-manager to be component/HTML based
Instead of vue based.
This commit is contained in:
parent
b6aa232205
commit
02dc3154e3
|
@ -30,7 +30,10 @@ class DrawioImageController extends Controller
|
|||
$parentTypeFilter = $request->get('filter_type', null);
|
||||
|
||||
$imgData = $this->imageRepo->getEntityFiltered('drawio', $parentTypeFilter, $page, 24, $uploadedToFilter, $searchTerm);
|
||||
return response()->json($imgData);
|
||||
return view('components.image-manager-list', [
|
||||
'images' => $imgData['images'],
|
||||
'hasMore' => $imgData['has_more'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,6 +75,7 @@ class DrawioImageController extends Controller
|
|||
if ($imageData === null) {
|
||||
return $this->jsonError("Image data could not be found");
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'content' => base64_encode($imageData)
|
||||
]);
|
||||
|
|
|
@ -6,6 +6,7 @@ use BookStack\Exceptions\ImageUploadException;
|
|||
use BookStack\Uploads\ImageRepo;
|
||||
use Illuminate\Http\Request;
|
||||
use BookStack\Http\Controllers\Controller;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class GalleryImageController extends Controller
|
||||
{
|
||||
|
@ -13,7 +14,6 @@ class GalleryImageController extends Controller
|
|||
|
||||
/**
|
||||
* GalleryImageController constructor.
|
||||
* @param ImageRepo $imageRepo
|
||||
*/
|
||||
public function __construct(ImageRepo $imageRepo)
|
||||
{
|
||||
|
@ -24,8 +24,6 @@ class GalleryImageController extends Controller
|
|||
/**
|
||||
* Get a list of gallery images, in a list.
|
||||
* Can be paged and filtered by entity.
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function list(Request $request)
|
||||
{
|
||||
|
@ -35,14 +33,15 @@ class GalleryImageController extends Controller
|
|||
$parentTypeFilter = $request->get('filter_type', null);
|
||||
|
||||
$imgData = $this->imageRepo->getEntityFiltered('gallery', $parentTypeFilter, $page, 24, $uploadedToFilter, $searchTerm);
|
||||
return response()->json($imgData);
|
||||
return view('components.image-manager-list', [
|
||||
'images' => $imgData['images'],
|
||||
'hasMore' => $imgData['has_more'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new gallery image in the system.
|
||||
* @param Request $request
|
||||
* @return Illuminate\Http\JsonResponse
|
||||
* @throws \Exception
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function create(Request $request)
|
||||
{
|
||||
|
|
|
@ -6,8 +6,11 @@ use BookStack\Http\Controllers\Controller;
|
|||
use BookStack\Repos\PageRepo;
|
||||
use BookStack\Uploads\Image;
|
||||
use BookStack\Uploads\ImageRepo;
|
||||
use Exception;
|
||||
use Illuminate\Filesystem\Filesystem as File;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class ImageController extends Controller
|
||||
{
|
||||
|
@ -17,9 +20,6 @@ class ImageController extends Controller
|
|||
|
||||
/**
|
||||
* ImageController constructor.
|
||||
* @param Image $image
|
||||
* @param File $file
|
||||
* @param ImageRepo $imageRepo
|
||||
*/
|
||||
public function __construct(Image $image, File $file, ImageRepo $imageRepo)
|
||||
{
|
||||
|
@ -31,8 +31,6 @@ class ImageController extends Controller
|
|||
|
||||
/**
|
||||
* Provide an image file from storage.
|
||||
* @param string $path
|
||||
* @return mixed
|
||||
*/
|
||||
public function showImage(string $path)
|
||||
{
|
||||
|
@ -47,13 +45,10 @@ class ImageController extends Controller
|
|||
|
||||
/**
|
||||
* Update image details
|
||||
* @param Request $request
|
||||
* @param integer $id
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws ImageUploadException
|
||||
* @throws \Exception
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'name' => 'required|min:2|string'
|
||||
|
@ -64,47 +59,50 @@ class ImageController extends Controller
|
|||
$this->checkOwnablePermission('image-update', $image);
|
||||
|
||||
$image = $this->imageRepo->updateImageDetails($image, $request->all());
|
||||
return response()->json($image);
|
||||
|
||||
$this->imageRepo->loadThumbs($image);
|
||||
return view('components.image-manager-form', [
|
||||
'image' => $image,
|
||||
'dependantPages' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the usage of an image on pages.
|
||||
* Get the form for editing the given image.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function usage(int $id)
|
||||
public function edit(Request $request, string $id)
|
||||
{
|
||||
$image = $this->imageRepo->getById($id);
|
||||
$this->checkImagePermission($image);
|
||||
|
||||
$pages = Page::visible()->where('html', 'like', '%' . $image->url . '%')->get(['id', 'name', 'slug', 'book_id']);
|
||||
foreach ($pages as $page) {
|
||||
$page->url = $page->getUrl();
|
||||
$page->html = '';
|
||||
$page->text = '';
|
||||
if ($request->has('delete')) {
|
||||
$dependantPages = $this->imageRepo->getPagesUsingImage($image);
|
||||
}
|
||||
$result = count($pages) > 0 ? $pages : false;
|
||||
|
||||
return response()->json($result);
|
||||
$this->imageRepo->loadThumbs($image);
|
||||
return view('components.image-manager-form', [
|
||||
'image' => $image,
|
||||
'dependantPages' => $dependantPages ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an image and all thumbnail/image files
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
public function destroy($id)
|
||||
public function destroy(string $id)
|
||||
{
|
||||
$image = $this->imageRepo->getById($id);
|
||||
$this->checkOwnablePermission('image-delete', $image);
|
||||
$this->checkImagePermission($image);
|
||||
|
||||
$this->imageRepo->destroyImage($image);
|
||||
return response()->json(trans('components.images_deleted'));
|
||||
return response('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check related page permission and ensure type is drawio or gallery.
|
||||
* @param Image $image
|
||||
*/
|
||||
protected function checkImagePermission(Image $image)
|
||||
{
|
||||
|
|
|
@ -185,7 +185,7 @@ class ImageRepo
|
|||
* Load thumbnails onto an image object.
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function loadThumbs(Image $image)
|
||||
public function loadThumbs(Image $image)
|
||||
{
|
||||
$image->thumbs = [
|
||||
'gallery' => $this->getThumbnail($image, 150, 150, false),
|
||||
|
@ -219,4 +219,20 @@ class ImageRepo
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user visible pages using the given image.
|
||||
*/
|
||||
public function getPagesUsingImage(Image $image): array
|
||||
{
|
||||
$pages = Page::visible()
|
||||
->where('html', 'like', '%' . $image->url . '%')
|
||||
->get(['id', 'name', 'slug', 'book_id']);
|
||||
|
||||
foreach ($pages as $page) {
|
||||
$page->url = $page->getUrl();
|
||||
}
|
||||
|
||||
return $pages->all();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,52 +5,76 @@ import {onEnterPress, onSelect} from "../services/dom";
|
|||
* Will handle button clicks or input enter press events and submit
|
||||
* the data over ajax. Will always expect a partial HTML view to be returned.
|
||||
* Fires an 'ajax-form-success' event when submitted successfully.
|
||||
*
|
||||
* Will handle a real form if that's what the component is added to
|
||||
* otherwise will act as a fake form element.
|
||||
*
|
||||
* @extends {Component}
|
||||
*/
|
||||
class AjaxForm {
|
||||
setup() {
|
||||
this.container = this.$el;
|
||||
this.responseContainer = this.container;
|
||||
this.url = this.$opts.url;
|
||||
this.method = this.$opts.method || 'post';
|
||||
this.successMessage = this.$opts.successMessage;
|
||||
this.submitButtons = this.$manyRefs.submit || [];
|
||||
|
||||
if (this.$opts.responseContainer) {
|
||||
this.responseContainer = this.container.closest(this.$opts.responseContainer);
|
||||
}
|
||||
|
||||
this.setupListeners();
|
||||
}
|
||||
|
||||
setupListeners() {
|
||||
|
||||
if (this.container.tagName === 'FORM') {
|
||||
this.container.addEventListener('submit', this.submitRealForm.bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
onEnterPress(this.container, event => {
|
||||
this.submit();
|
||||
this.submitFakeForm();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
this.submitButtons.forEach(button => onSelect(button, this.submit.bind(this)));
|
||||
this.submitButtons.forEach(button => onSelect(button, this.submitFakeForm.bind(this)));
|
||||
}
|
||||
|
||||
async submit() {
|
||||
submitFakeForm() {
|
||||
const fd = new FormData();
|
||||
const inputs = this.container.querySelectorAll(`[name]`);
|
||||
console.log(inputs);
|
||||
for (const input of inputs) {
|
||||
fd.append(input.getAttribute('name'), input.value);
|
||||
}
|
||||
this.submit(fd);
|
||||
}
|
||||
|
||||
submitRealForm(event) {
|
||||
event.preventDefault();
|
||||
const fd = new FormData(this.container);
|
||||
this.submit(fd);
|
||||
}
|
||||
|
||||
async submit(formData) {
|
||||
this.responseContainer.style.opacity = '0.7';
|
||||
this.responseContainer.style.pointerEvents = 'none';
|
||||
|
||||
this.container.style.opacity = '0.7';
|
||||
this.container.style.pointerEvents = 'none';
|
||||
try {
|
||||
const resp = await window.$http[this.method.toLowerCase()](this.url, fd);
|
||||
this.container.innerHTML = resp.data;
|
||||
this.$emit('success', {formData: fd});
|
||||
const resp = await window.$http[this.method.toLowerCase()](this.url, formData);
|
||||
this.$emit('success', {formData});
|
||||
this.responseContainer.innerHTML = resp.data;
|
||||
if (this.successMessage) {
|
||||
window.$events.emit('success', this.successMessage);
|
||||
}
|
||||
} catch (err) {
|
||||
this.container.innerHTML = err.data;
|
||||
this.responseContainer.innerHTML = err.data;
|
||||
}
|
||||
|
||||
window.components.init(this.container);
|
||||
this.container.style.opacity = null;
|
||||
this.container.style.pointerEvents = null;
|
||||
window.components.init(this.responseContainer);
|
||||
this.responseContainer.style.opacity = null;
|
||||
this.responseContainer.style.pointerEvents = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ class Dropzone {
|
|||
}
|
||||
|
||||
onSuccess(file, data) {
|
||||
this.container.dispatchEvent(new Event('dropzone'))
|
||||
this.$emit('success', {file, data});
|
||||
|
||||
if (this.successMessage) {
|
||||
|
|
201
resources/js/components/image-manager.js
Normal file
201
resources/js/components/image-manager.js
Normal file
|
@ -0,0 +1,201 @@
|
|||
import {onChildEvent, onSelect, removeLoading, showLoading} from "../services/dom";
|
||||
|
||||
/**
|
||||
* ImageManager
|
||||
* @extends {Component}
|
||||
*/
|
||||
class ImageManager {
|
||||
|
||||
setup() {
|
||||
|
||||
// Options
|
||||
this.uploadedTo = this.$opts.uploadedTo;
|
||||
|
||||
// Element References
|
||||
this.container = this.$el;
|
||||
this.popupEl = this.$refs.popup;
|
||||
this.searchForm = this.$refs.searchForm;
|
||||
this.searchInput = this.$refs.searchInput;
|
||||
this.cancelSearch = this.$refs.cancelSearch;
|
||||
this.listContainer = this.$refs.listContainer;
|
||||
this.filterTabs = this.$manyRefs.filterTabs;
|
||||
this.selectButton = this.$refs.selectButton;
|
||||
this.formContainer = this.$refs.formContainer;
|
||||
this.dropzoneContainer = this.$refs.dropzoneContainer;
|
||||
|
||||
// Instance data
|
||||
this.type = 'gallery';
|
||||
this.lastSelected = {};
|
||||
this.lastSelectedTime = 0;
|
||||
this.resetState = () => {
|
||||
this.callback = null;
|
||||
this.hasData = false;
|
||||
this.page = 1;
|
||||
this.filter = 'all';
|
||||
};
|
||||
this.resetState();
|
||||
|
||||
this.setupListeners();
|
||||
|
||||
window.ImageManager = this;
|
||||
}
|
||||
|
||||
setupListeners() {
|
||||
onSelect(this.filterTabs, e => {
|
||||
this.resetAll();
|
||||
this.filter = e.target.dataset.filter;
|
||||
this.setActiveFilterTab(this.filter);
|
||||
this.loadGallery();
|
||||
});
|
||||
|
||||
this.searchForm.addEventListener('submit', event => {
|
||||
this.resetListView();
|
||||
this.loadGallery();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
onSelect(this.cancelSearch, event => {
|
||||
this.resetListView();
|
||||
this.resetSearchView();
|
||||
this.loadGallery();
|
||||
this.cancelSearch.classList.remove('active');
|
||||
});
|
||||
|
||||
this.searchInput.addEventListener('input', event => {
|
||||
this.cancelSearch.classList.toggle('active', this.searchInput.value.trim());
|
||||
});
|
||||
|
||||
onChildEvent(this.listContainer, '.load-more', 'click', async event => {
|
||||
showLoading(event.target);
|
||||
this.page++;
|
||||
await this.loadGallery();
|
||||
event.target.remove();
|
||||
});
|
||||
|
||||
this.listContainer.addEventListener('event-emit-select-image', this.onImageSelectEvent.bind(this));
|
||||
|
||||
onSelect(this.selectButton, () => {
|
||||
if (this.callback) {
|
||||
this.callback(this.lastSelected);
|
||||
}
|
||||
this.hide();
|
||||
});
|
||||
|
||||
onChildEvent(this.formContainer, '#image-manager-delete', 'click', event => {
|
||||
if (this.lastSelected) {
|
||||
this.loadImageEditForm(this.lastSelected.id, true);
|
||||
}
|
||||
});
|
||||
|
||||
this.formContainer.addEventListener('ajax-form-success', this.refreshGallery.bind(this));
|
||||
this.container.addEventListener('dropzone-success', this.refreshGallery.bind(this));
|
||||
}
|
||||
|
||||
show(callback, type = 'gallery') {
|
||||
this.resetAll();
|
||||
|
||||
this.callback = callback;
|
||||
this.type = type;
|
||||
this.popupEl.components.popup.show();
|
||||
this.dropzoneContainer.classList.toggle('hidden', type !== 'gallery');
|
||||
|
||||
if (!this.hasData) {
|
||||
this.loadGallery();
|
||||
this.hasData = true;
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.popupEl.components.popup.hide();
|
||||
}
|
||||
|
||||
async loadGallery() {
|
||||
const params = {
|
||||
page: this.page,
|
||||
search: this.searchInput.value || null,
|
||||
uploaded_to: this.uploadedTo,
|
||||
filter_type: this.filter === 'all' ? null : this.filter,
|
||||
};
|
||||
|
||||
const {data: html} = await window.$http.get(`images/${this.type}`, params);
|
||||
this.addReturnedHtmlElementsToList(html);
|
||||
removeLoading(this.listContainer);
|
||||
}
|
||||
|
||||
addReturnedHtmlElementsToList(html) {
|
||||
const el = document.createElement('div');
|
||||
el.innerHTML = html;
|
||||
window.components.init(el);
|
||||
for (const child of [...el.children]) {
|
||||
this.listContainer.appendChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
setActiveFilterTab(filterName) {
|
||||
this.filterTabs.forEach(t => t.classList.remove('selected'));
|
||||
const activeTab = this.filterTabs.find(t => t.dataset.filter === filterName);
|
||||
if (activeTab) {
|
||||
activeTab.classList.add('selected');
|
||||
}
|
||||
}
|
||||
|
||||
resetAll() {
|
||||
this.resetState();
|
||||
this.resetListView();
|
||||
this.resetSearchView();
|
||||
this.formContainer.innerHTML = '';
|
||||
this.setActiveFilterTab('all');
|
||||
}
|
||||
|
||||
resetSearchView() {
|
||||
this.searchInput.value = '';
|
||||
}
|
||||
|
||||
resetListView() {
|
||||
showLoading(this.listContainer);
|
||||
this.page = 1;
|
||||
}
|
||||
|
||||
refreshGallery() {
|
||||
this.resetListView();
|
||||
this.loadGallery();
|
||||
}
|
||||
|
||||
onImageSelectEvent(event) {
|
||||
const image = JSON.parse(event.detail.data);
|
||||
const isDblClick = ((image && image.id === this.lastSelected.id)
|
||||
&& Date.now() - this.lastSelectedTime < 400);
|
||||
const alreadySelected = event.target.classList.contains('selected');
|
||||
[...this.listContainer.querySelectorAll('.selected')].forEach(el => {
|
||||
el.classList.remove('selected');
|
||||
});
|
||||
|
||||
if (!alreadySelected) {
|
||||
event.target.classList.add('selected');
|
||||
this.loadImageEditForm(image.id);
|
||||
}
|
||||
this.selectButton.classList.toggle('hidden', alreadySelected);
|
||||
|
||||
if (isDblClick && this.callback) {
|
||||
this.callback(image);
|
||||
this.hide();
|
||||
}
|
||||
|
||||
this.lastSelected = image;
|
||||
this.lastSelectedTime = Date.now();
|
||||
}
|
||||
|
||||
async loadImageEditForm(imageId, requestDelete = false) {
|
||||
if (!requestDelete) {
|
||||
this.formContainer.innerHTML = '';
|
||||
}
|
||||
|
||||
const params = requestDelete ? {delete: true} : {};
|
||||
const {data: formHtml} = await window.$http.get(`/images/edit/${imageId}`, params);
|
||||
this.formContainer.innerHTML = formHtml;
|
||||
window.components.init(this.formContainer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ImageManager;
|
|
@ -106,4 +106,15 @@ export function findText(selector, text) {
|
|||
*/
|
||||
export function showLoading(element) {
|
||||
element.innerHTML = `<div class="loading-container"><div></div><div></div><div></div></div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any loading indicators within the given element.
|
||||
* @param {Element} element
|
||||
*/
|
||||
export function removeLoading(element) {
|
||||
const loadingEls = element.querySelectorAll('.loading-container');
|
||||
for (const el of loadingEls) {
|
||||
el.remove();
|
||||
}
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
import * as Dates from "../services/dates";
|
||||
import dropzone from "./components/dropzone";
|
||||
|
||||
let page = 1;
|
||||
let previousClickTime = 0;
|
||||
let previousClickImage = 0;
|
||||
let dataLoaded = false;
|
||||
let callback = false;
|
||||
let baseUrl = '';
|
||||
|
||||
let preSearchImages = [];
|
||||
let preSearchHasMore = false;
|
||||
|
||||
const data = {
|
||||
images: [],
|
||||
|
||||
imageType: false,
|
||||
uploadedTo: false,
|
||||
|
||||
selectedImage: false,
|
||||
dependantPages: false,
|
||||
showing: false,
|
||||
filter: null,
|
||||
hasMore: false,
|
||||
searching: false,
|
||||
searchTerm: '',
|
||||
|
||||
imageUpdateSuccess: false,
|
||||
imageDeleteSuccess: false,
|
||||
deleteConfirm: false,
|
||||
};
|
||||
|
||||
const methods = {
|
||||
|
||||
show(providedCallback, imageType = null) {
|
||||
callback = providedCallback;
|
||||
this.showing = true;
|
||||
this.$el.children[0].components.popup.show();
|
||||
|
||||
// Get initial images if they have not yet been loaded in.
|
||||
if (dataLoaded && imageType === this.imageType) return;
|
||||
if (imageType) {
|
||||
this.imageType = imageType;
|
||||
this.resetState();
|
||||
}
|
||||
this.fetchData();
|
||||
dataLoaded = true;
|
||||
},
|
||||
|
||||
hide() {
|
||||
if (this.$refs.dropzone) {
|
||||
this.$refs.dropzone.onClose();
|
||||
}
|
||||
this.showing = false;
|
||||
this.selectedImage = false;
|
||||
this.$el.children[0].components.popup.hide();
|
||||
},
|
||||
|
||||
async fetchData() {
|
||||
const params = {
|
||||
page,
|
||||
search: this.searching ? this.searchTerm : null,
|
||||
uploaded_to: this.uploadedTo || null,
|
||||
filter_type: this.filter,
|
||||
};
|
||||
|
||||
const {data} = await this.$http.get(baseUrl, params);
|
||||
this.images = this.images.concat(data.images);
|
||||
this.hasMore = data.has_more;
|
||||
page++;
|
||||
},
|
||||
|
||||
setFilterType(filterType) {
|
||||
this.filter = filterType;
|
||||
this.resetState();
|
||||
this.fetchData();
|
||||
},
|
||||
|
||||
resetState() {
|
||||
this.cancelSearch();
|
||||
this.resetListView();
|
||||
this.deleteConfirm = false;
|
||||
baseUrl = window.baseUrl(`/images/${this.imageType}`);
|
||||
},
|
||||
|
||||
resetListView() {
|
||||
this.images = [];
|
||||
this.hasMore = false;
|
||||
page = 1;
|
||||
},
|
||||
|
||||
searchImages() {
|
||||
if (this.searchTerm === '') return this.cancelSearch();
|
||||
|
||||
// Cache current settings for later
|
||||
if (!this.searching) {
|
||||
preSearchImages = this.images;
|
||||
preSearchHasMore = this.hasMore;
|
||||
}
|
||||
|
||||
this.searching = true;
|
||||
this.resetListView();
|
||||
this.fetchData();
|
||||
},
|
||||
|
||||
cancelSearch() {
|
||||
if (!this.searching) return;
|
||||
this.searching = false;
|
||||
this.searchTerm = '';
|
||||
this.images = preSearchImages;
|
||||
this.hasMore = preSearchHasMore;
|
||||
},
|
||||
|
||||
imageSelect(image) {
|
||||
const dblClickTime = 300;
|
||||
const currentTime = Date.now();
|
||||
const timeDiff = currentTime - previousClickTime;
|
||||
const isDblClick = timeDiff < dblClickTime && image.id === previousClickImage;
|
||||
|
||||
if (isDblClick) {
|
||||
this.callbackAndHide(image);
|
||||
} else {
|
||||
this.selectedImage = image;
|
||||
this.deleteConfirm = false;
|
||||
this.dependantPages = false;
|
||||
}
|
||||
|
||||
previousClickTime = currentTime;
|
||||
previousClickImage = image.id;
|
||||
},
|
||||
|
||||
callbackAndHide(imageResult) {
|
||||
if (callback) callback(imageResult);
|
||||
this.hide();
|
||||
},
|
||||
|
||||
async saveImageDetails() {
|
||||
let url = window.baseUrl(`/images/${this.selectedImage.id}`);
|
||||
try {
|
||||
await this.$http.put(url, this.selectedImage)
|
||||
} catch (error) {
|
||||
if (error.response.status === 422) {
|
||||
let errors = error.response.data;
|
||||
let message = '';
|
||||
Object.keys(errors).forEach((key) => {
|
||||
message += errors[key].join('\n');
|
||||
});
|
||||
this.$events.emit('error', message);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async deleteImage() {
|
||||
|
||||
if (!this.deleteConfirm) {
|
||||
const url = window.baseUrl(`/images/usage/${this.selectedImage.id}`);
|
||||
try {
|
||||
const {data} = await this.$http.get(url);
|
||||
this.dependantPages = data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
this.deleteConfirm = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const url = window.baseUrl(`/images/${this.selectedImage.id}`);
|
||||
await this.$http.delete(url);
|
||||
this.images.splice(this.images.indexOf(this.selectedImage), 1);
|
||||
this.selectedImage = false;
|
||||
this.$events.emit('success', trans('components.image_delete_success'));
|
||||
this.deleteConfirm = false;
|
||||
},
|
||||
|
||||
getDate(stringDate) {
|
||||
return Dates.formatDateTime(new Date(stringDate));
|
||||
},
|
||||
|
||||
uploadSuccess(event) {
|
||||
this.images.unshift(event.data);
|
||||
this.$events.emit('success', trans('components.image_upload_success'));
|
||||
},
|
||||
};
|
||||
|
||||
const computed = {
|
||||
uploadUrl() {
|
||||
return window.baseUrl(`/images/${this.imageType}`);
|
||||
}
|
||||
};
|
||||
|
||||
function mounted() {
|
||||
window.ImageManager = this;
|
||||
this.imageType = this.$el.getAttribute('image-type');
|
||||
this.uploadedTo = this.$el.getAttribute('uploaded-to');
|
||||
baseUrl = window.baseUrl('/images/' + this.imageType)
|
||||
}
|
||||
|
||||
export default {
|
||||
mounted,
|
||||
methods,
|
||||
data,
|
||||
computed,
|
||||
components: {dropzone},
|
||||
};
|
|
@ -4,10 +4,7 @@ function exists(id) {
|
|||
return document.getElementById(id) !== null;
|
||||
}
|
||||
|
||||
import imageManager from "./image-manager";
|
||||
|
||||
let vueMapping = {
|
||||
'image-manager': imageManager,
|
||||
};
|
||||
|
||||
window.vues = {};
|
||||
|
|
|
@ -33,6 +33,7 @@ return [
|
|||
'copy' => 'Copy',
|
||||
'reply' => 'Reply',
|
||||
'delete' => 'Delete',
|
||||
'delete_confirm' => 'Confirm Deletion',
|
||||
'search' => 'Search',
|
||||
'search_clear' => 'Clear Search',
|
||||
'reset' => 'Reset',
|
||||
|
|
|
@ -15,7 +15,7 @@ return [
|
|||
'image_load_more' => 'Load More',
|
||||
'image_image_name' => 'Image Name',
|
||||
'image_delete_used' => 'This image is used in the pages below.',
|
||||
'image_delete_confirm' => 'Click delete again to confirm you want to delete this image.',
|
||||
'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
|
||||
'image_select_image' => 'Select Image',
|
||||
'image_dropzone' => 'Drop images or click here to upload',
|
||||
'images_deleted' => 'Images Deleted',
|
||||
|
|
|
@ -51,6 +51,11 @@
|
|||
fill: currentColor !important;
|
||||
}
|
||||
|
||||
.text-white {
|
||||
color: #fff;
|
||||
fill: currentColor !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Entity text colors
|
||||
*/
|
||||
|
|
|
@ -197,11 +197,8 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||
transition: all cubic-bezier(.4, 0, 1, 1) 160ms;
|
||||
overflow: hidden;
|
||||
&.selected {
|
||||
//transform: scale3d(0.92, 0.92, 0.92);
|
||||
border: 4px solid #FFF;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
|
||||
transform: scale3d(0.92, 0.92, 0.92);
|
||||
outline: currentColor 2px solid;
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
|
@ -231,7 +228,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||
}
|
||||
}
|
||||
|
||||
#image-manager .load-more {
|
||||
.image-manager .load-more {
|
||||
display: block;
|
||||
text-align: center;
|
||||
@include lightDark(background-color, #EEE, #444);
|
||||
|
@ -243,6 +240,10 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
.image-manager .loading-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.image-manager-sidebar {
|
||||
width: 300px;
|
||||
overflow-y: auto;
|
||||
|
@ -250,6 +251,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||
border-inline-start: 1px solid #DDD;
|
||||
@include lightDark(border-color, #ddd, #000);
|
||||
.inner {
|
||||
min-height: auto;
|
||||
padding: $-m;
|
||||
}
|
||||
img {
|
||||
|
@ -291,6 +293,12 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||
}
|
||||
}
|
||||
|
||||
.image-manager .corner-button {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
padding: $-m;
|
||||
}
|
||||
|
||||
// Dropzone
|
||||
/*
|
||||
* The MIT License
|
||||
|
@ -298,7 +306,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||
*/
|
||||
.dz-message {
|
||||
font-size: 1em;
|
||||
line-height: 2.35;
|
||||
line-height: 2.85;
|
||||
font-style: italic;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
|
@ -601,9 +609,14 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||
display: inline-block;
|
||||
@include lightDark(color, #666, #999);
|
||||
cursor: pointer;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-bottom: 2px solid transparent;
|
||||
&.selected {
|
||||
border-bottom: 2px solid var(--color-primary);
|
||||
}
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -121,6 +121,11 @@ body.flexbox {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.flex-container-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
|
|
|
@ -140,8 +140,10 @@ $btt-size: 40px;
|
|||
|
||||
.contained-search-box {
|
||||
display: flex;
|
||||
height: 38px;
|
||||
input, button {
|
||||
border-radius: 0;
|
||||
border: 1px solid #ddd;
|
||||
@include lightDark(border-color, #ddd, #000);
|
||||
margin-inline-start: -1px;
|
||||
}
|
||||
|
@ -162,6 +164,9 @@ $btt-size: 40px;
|
|||
background-color: $negative;
|
||||
color: #EEE;
|
||||
}
|
||||
svg {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.entity-selector {
|
||||
|
|
60
resources/views/components/image-manager-form.blade.php
Normal file
60
resources/views/components/image-manager-form.blade.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<div class="image-manager-details">
|
||||
|
||||
<form component="ajax-form"
|
||||
option:ajax-form:success-message="{{ trans('components.image_update_success') }}"
|
||||
option:ajax-form:method="put"
|
||||
option:ajax-form:response-container=".image-manager-details"
|
||||
option:ajax-form:url="{{ url('images/' . $image->id) }}">
|
||||
|
||||
<div class="image-manager-viewer">
|
||||
<a href="{{ $image->url }}" target="_blank" class="block">
|
||||
<img src="{{ $image->thumbs['display'] }}"
|
||||
alt="{{ $image->name }}"
|
||||
class="anim fadeIn"
|
||||
title="{{ $image->name }}">
|
||||
</a>
|
||||
</div>
|
||||
<div class="form-group stretch-inputs">
|
||||
<label for="name">{{ trans('components.image_image_name') }}</label>
|
||||
<input id="name" class="input-base" type="text" name="name" value="{{ $image->name }}">
|
||||
</div>
|
||||
<div class="grid half">
|
||||
<div>
|
||||
<button type="button"
|
||||
id="image-manager-delete"
|
||||
title="{{ trans('common.delete') }}"
|
||||
class="button icon outline">@icon('delete')</button>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<button type="submit"
|
||||
class="button icon outline">{{ trans('common.save') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if(!is_null($dependantPages))
|
||||
@if(count($dependantPages) > 0)
|
||||
<p class="text-neg mb-xs mt-m">{{ trans('components.image_delete_used') }}</p>
|
||||
<ul class="text-neg">
|
||||
@foreach($dependantPages as $page)
|
||||
<li>
|
||||
<a href="{{ $page->url }}"
|
||||
target="_blank"
|
||||
class="text-neg">{{ $page->name }}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
<p class="text-neg mb-xs">{{ trans('components.image_delete_confirm_text') }}</p>
|
||||
<form component="ajax-form"
|
||||
option:ajax-form:success-message="{{ trans('components.image_delete_success') }}"
|
||||
option:ajax-form:method="delete"
|
||||
option:ajax-form:response-container=".image-manager-details"
|
||||
option:ajax-form:url="{{ url('images/' . $image->id) }}">
|
||||
<button type="submit" class="button neg">
|
||||
{{ trans('common.delete_confirm') }}
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
</div>
|
23
resources/views/components/image-manager-list.blade.php
Normal file
23
resources/views/components/image-manager-list.blade.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
@foreach($images as $index => $image)
|
||||
<div>
|
||||
<div component="event-emit-select"
|
||||
option:event-emit-select:name="image"
|
||||
option:event-emit-select:data="{{ json_encode($image) }}"
|
||||
class="image anim fadeIn text-primary"
|
||||
style="animation-delay: {{ $index > 26 ? '160ms' : ($index * 25) . 'ms' }};">
|
||||
<img src="{{ $image->thumbs['gallery'] }}"
|
||||
alt="{{ $image->name }}"
|
||||
width="150"
|
||||
height="150"
|
||||
loading="lazy"
|
||||
title="{{ $image->name }}">
|
||||
<div class="image-meta">
|
||||
<span class="name">{{ $image->name }}</span>
|
||||
<span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => $image->created_at->format('Y-m-d H:i:s')]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@if($hasMore)
|
||||
<div class="load-more">{{ trans('components.image_load_more') }}</div>
|
||||
@endif
|
|
@ -1,101 +1,62 @@
|
|||
<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to ?? 0 }}">
|
||||
<div component="image-manager"
|
||||
option:image-manager:uploaded-to="{{ $uploaded_to ?? 0 }}"
|
||||
class="image-manager">
|
||||
|
||||
@exposeTranslations([
|
||||
'components.image_delete_success',
|
||||
'components.image_upload_success',
|
||||
'errors.server_upload_limit',
|
||||
'components.image_upload_remove',
|
||||
'components.file_upload_timeout',
|
||||
])
|
||||
|
||||
<div component="popup" class="popup-background" v-cloak @click="hide">
|
||||
<div class="popup-body" tabindex="-1" @click.stop>
|
||||
<div component="popup"
|
||||
refs="image-manager@popup"
|
||||
class="popup-background">
|
||||
<div class="popup-body" tabindex="-1">
|
||||
|
||||
<div class="popup-header primary-background">
|
||||
<div class="popup-title">{{ trans('components.image_select') }}</div>
|
||||
<button class="popup-header-close" @click="hide()">x</button>
|
||||
<button refs="popup@hide" type="button" class="popup-header-close">x</button>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill image-manager-body">
|
||||
|
||||
<div class="image-manager-content">
|
||||
<div v-if="imageType === 'gallery' || imageType === 'drawio'" class="image-manager-header primary-background-light nav-tabs grid third">
|
||||
<div class="tab-item" title="{{ trans('components.image_all_title') }}" :class="{selected: !filter}" @click="setFilterType(null)">@icon('images') {{ trans('components.image_all') }}</div>
|
||||
<div class="tab-item" title="{{ trans('components.image_book_title') }}" :class="{selected: (filter=='book')}" @click="setFilterType('book')">@icon('book', ['class' => 'text-book svg-icon']) {{ trans('entities.book') }}</div>
|
||||
<div class="tab-item" title="{{ trans('components.image_page_title') }}" :class="{selected: (filter=='page')}" @click="setFilterType('page')">@icon('page', ['class' => 'text-page svg-icon']) {{ trans('entities.page') }}</div>
|
||||
<div class="image-manager-header primary-background-light nav-tabs grid third no-gap">
|
||||
<button refs="image-manager@filterTabs"
|
||||
data-filter="all"
|
||||
type="button" class="tab-item selected" title="{{ trans('components.image_all_title') }}">@icon('images') {{ trans('components.image_all') }}</button>
|
||||
<button refs="image-manager@filterTabs"
|
||||
data-filter="book"
|
||||
type="button" class="tab-item" title="{{ trans('components.image_book_title') }}">@icon('book', ['class' => 'text-book svg-icon']) {{ trans('entities.book') }}</button>
|
||||
<button refs="image-manager@filterTabs"
|
||||
data-filter="page"
|
||||
type="button" class="tab-item" title="{{ trans('components.image_page_title') }}">@icon('page', ['class' => 'text-page svg-icon']) {{ trans('entities.page') }}</button>
|
||||
</div>
|
||||
<div>
|
||||
<form @submit.prevent="searchImages" class="contained-search-box">
|
||||
<input placeholder="{{ trans('components.image_search_hint') }}" v-model="searchTerm" type="text">
|
||||
<button :class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" @click="cancelSearch()" class="text-button cancel">@icon('close')</button>
|
||||
<button title="{{ trans('common.search') }}" class="text-button">@icon('search')</button>
|
||||
<form refs="image-manager@searchForm" class="contained-search-box">
|
||||
<input refs="image-manager@searchInput"
|
||||
placeholder="{{ trans('components.image_search_hint') }}"
|
||||
type="text">
|
||||
<button refs="image-manager@cancelSearch"
|
||||
title="{{ trans('common.search_clear') }}"
|
||||
type="button"
|
||||
class="cancel">@icon('close')</button>
|
||||
<button type="submit" class="primary-background text-white"
|
||||
title="{{ trans('common.search') }}">@icon('search')</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="image-manager-list">
|
||||
<div v-if="images.length > 0" v-for="(image, idx) in images">
|
||||
<div class="image anim fadeIn" :style="{animationDelay: (idx > 26) ? '160ms' : ((idx * 25) + 'ms')}"
|
||||
:class="{selected: (image==selectedImage)}" @click="imageSelect(image)">
|
||||
<img :src="image.thumbs.gallery" :alt="image.title" :title="image.name">
|
||||
<div class="image-meta">
|
||||
<span class="name" v-text="image.name"></span>
|
||||
<span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="load-more" v-show="hasMore" @click="fetchData">{{ trans('components.image_load_more') }}</div>
|
||||
</div>
|
||||
<div refs="image-manager@listContainer" class="image-manager-list"></div>
|
||||
</div>
|
||||
|
||||
<div class="image-manager-sidebar">
|
||||
|
||||
<dropzone v-if="imageType !== 'drawio'" ref="dropzone" placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
|
||||
|
||||
<div class="inner">
|
||||
|
||||
<div class="image-manager-details anim fadeIn" v-if="selectedImage">
|
||||
|
||||
<form @submit.prevent="saveImageDetails">
|
||||
<div class="image-manager-viewer">
|
||||
<a :href="selectedImage.url" target="_blank" style="display: block;">
|
||||
<img :src="selectedImage.thumbs.display" :alt="selectedImage.name"
|
||||
:title="selectedImage.name">
|
||||
</a>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">{{ trans('components.image_image_name') }}</label>
|
||||
<input id="name" class="input-base" name="name" v-model="selectedImage.name">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="clearfix">
|
||||
<div class="float left">
|
||||
<button type="button" class="button icon outline" @click="deleteImage">@icon('delete')</button>
|
||||
|
||||
</div>
|
||||
<button class="button anim fadeIn float right" v-show="selectedImage" @click="callbackAndHide(selectedImage)">
|
||||
{{ trans('components.image_select_image') }}
|
||||
</button>
|
||||
<div class="clearfix"></div>
|
||||
<div v-show="dependantPages">
|
||||
<p class="text-neg text-small">
|
||||
{{ trans('components.image_delete_used') }}
|
||||
</p>
|
||||
<ul class="text-neg">
|
||||
<li v-for="page in dependantPages">
|
||||
<a :href="page.url" target="_blank" class="text-neg" v-text="page.name"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-show="deleteConfirm" class="text-neg text-small">
|
||||
{{ trans('components.image_delete_confirm') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="image-manager-sidebar flex-container-column">
|
||||
|
||||
<div refs="image-manager@dropzoneContainer">
|
||||
@include('components.dropzone', [
|
||||
'placeholder' => trans('components.image_dropzone'),
|
||||
'successMessage' => trans('components.image_upload_success'),
|
||||
'url' => url('/images/gallery?' . http_build_query(['uploaded_to' => $uploaded_to ?? 0]))
|
||||
])
|
||||
</div>
|
||||
|
||||
<div refs="image-manager@formContainer" class="inner flex"></div>
|
||||
|
||||
<button refs="image-manager@selectButton" type="button" class="hidden button corner-button">
|
||||
{{ trans('components.image_select_image') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -20,8 +20,7 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
@include('components.image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
|
||||
@include('components.image-manager', ['uploaded_to' => $page->id])
|
||||
@include('components.code-editor')
|
||||
@include('components.entity-selector-popup')
|
||||
|
||||
@stop
|
|
@ -275,6 +275,5 @@
|
|||
|
||||
</div>
|
||||
|
||||
@include('components.image-manager', ['imageType' => 'system'])
|
||||
@include('components.entity-selector-popup', ['entityTypes' => 'page'])
|
||||
@stop
|
||||
|
|
|
@ -101,22 +101,14 @@ Route::group(['middleware' => 'auth'], function () {
|
|||
Route::get('/user/{userId}', 'UserController@showProfilePage');
|
||||
|
||||
// Image routes
|
||||
Route::group(['prefix' => 'images'], function () {
|
||||
|
||||
// Gallery
|
||||
Route::get('/gallery', 'Images\GalleryImageController@list');
|
||||
Route::post('/gallery', 'Images\GalleryImageController@create');
|
||||
|
||||
// Drawio
|
||||
Route::get('/drawio', 'Images\DrawioImageController@list');
|
||||
Route::get('/drawio/base64/{id}', 'Images\DrawioImageController@getAsBase64');
|
||||
Route::post('/drawio', 'Images\DrawioImageController@create');
|
||||
|
||||
// Shared gallery & draw.io endpoint
|
||||
Route::get('/usage/{id}', 'Images\ImageController@usage');
|
||||
Route::put('/{id}', 'Images\ImageController@update');
|
||||
Route::delete('/{id}', 'Images\ImageController@destroy');
|
||||
});
|
||||
Route::get('/images/gallery', 'Images\GalleryImageController@list');
|
||||
Route::post('/images/gallery', 'Images\GalleryImageController@create');
|
||||
Route::get('/images/drawio', 'Images\DrawioImageController@list');
|
||||
Route::get('/images/drawio/base64/{id}', 'Images\DrawioImageController@getAsBase64');
|
||||
Route::post('/images/drawio', 'Images\DrawioImageController@create');
|
||||
Route::get('/images/edit/{id}', 'Images\ImageController@edit');
|
||||
Route::put('/images/{id}', 'Images\ImageController@update');
|
||||
Route::delete('/images/{id}', 'Images\ImageController@destroy');
|
||||
|
||||
// Attachments routes
|
||||
Route::get('/attachments/{id}', 'AttachmentController@get');
|
||||
|
|
|
@ -71,11 +71,7 @@ class ImageTest extends TestCase
|
|||
$newName = Str::random();
|
||||
$update = $this->put('/images/' . $image->id, ['name' => $newName]);
|
||||
$update->assertSuccessful();
|
||||
$update->assertJson([
|
||||
'id' => $image->id,
|
||||
'name' => $newName,
|
||||
'type' => 'gallery',
|
||||
]);
|
||||
$update->assertSee($newName);
|
||||
|
||||
$this->deleteImage($imgDetails['path']);
|
||||
|
||||
|
@ -92,31 +88,22 @@ class ImageTest extends TestCase
|
|||
$imgDetails = $this->uploadGalleryImage();
|
||||
$image = Image::query()->first();
|
||||
|
||||
$emptyJson = ['images' => [], 'has_more' => false];
|
||||
$resultJson = [
|
||||
'images' => [
|
||||
[
|
||||
'id' => $image->id,
|
||||
'name' => $imgDetails['name'],
|
||||
]
|
||||
],
|
||||
'has_more' => false,
|
||||
];
|
||||
|
||||
$pageId = $imgDetails['page']->id;
|
||||
$firstPageRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}");
|
||||
$firstPageRequest->assertSuccessful()->assertJson($resultJson);
|
||||
$firstPageRequest->assertSuccessful()->assertElementExists('div');
|
||||
$firstPageRequest->assertSuccessful()->assertSeeText($image->name);
|
||||
|
||||
$secondPageRequest = $this->get("/images/gallery?page=2&uploaded_to={$pageId}");
|
||||
$secondPageRequest->assertSuccessful()->assertExactJson($emptyJson);
|
||||
$secondPageRequest->assertSuccessful()->assertElementNotExists('div');
|
||||
|
||||
$namePartial = substr($imgDetails['name'], 0, 3);
|
||||
$searchHitRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}&search={$namePartial}");
|
||||
$searchHitRequest->assertSuccessful()->assertJson($resultJson);
|
||||
$searchHitRequest->assertSuccessful()->assertSee($imgDetails['name']);
|
||||
|
||||
$namePartial = Str::random(16);
|
||||
$searchHitRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}&search={$namePartial}");
|
||||
$searchHitRequest->assertSuccessful()->assertExactJson($emptyJson);
|
||||
$searchFailRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}&search={$namePartial}");
|
||||
$searchFailRequest->assertSuccessful()->assertDontSee($imgDetails['name']);
|
||||
$searchFailRequest->assertSuccessful()->assertElementNotExists('div');
|
||||
}
|
||||
|
||||
public function test_image_usage()
|
||||
|
@ -131,14 +118,10 @@ class ImageTest extends TestCase
|
|||
$page->html = '<img src="'.$image->url.'">';
|
||||
$page->save();
|
||||
|
||||
$usage = $this->get('/images/usage/' . $image->id);
|
||||
$usage = $this->get('/images/edit/' . $image->id . '?delete=true');
|
||||
$usage->assertSuccessful();
|
||||
$usage->assertJson([
|
||||
[
|
||||
'id' => $page->id,
|
||||
'name' => $page->name
|
||||
]
|
||||
]);
|
||||
$usage->assertSeeText($page->name);
|
||||
$usage->assertSee($page->getUrl());
|
||||
|
||||
$this->deleteImage($imgDetails['path']);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user