mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-03-28 10:15:18 +08:00
Reverted work on revisions
Improved linkage of drawings and image manager. Updated image updates to create new versions.
This commit is contained in:
parent
6cdb943916
commit
0c9c1e4c6b
@ -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 updateDrawing(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->updateDrawing($image, $imageBase64Data);
|
||||
} catch (ImageUploadException $e) {
|
||||
return response($e->getMessage(), 500);
|
||||
}
|
||||
|
||||
return response()->json($image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content of an image based64 encoded.
|
||||
* @param $id
|
||||
@ -257,22 +231,11 @@ class ImageController extends Controller
|
||||
return response()->json($pageSearch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the revisions for an image.
|
||||
* @param $id
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getRevisions($id)
|
||||
{
|
||||
$image = $this->imageRepo->getById($id);
|
||||
$revisions = $image->revisions()->orderBy('id', 'desc')->get();
|
||||
return response()->json($revisions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an image and all thumbnail/image files
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
|
@ -20,23 +20,4 @@ class Image extends Ownable
|
||||
return Images::getThumbnail($this, $width, $height, $keepRatio);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the revisions for this image.
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function revisions()
|
||||
{
|
||||
return $this->hasMany(ImageRevision::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of revisions made to this image.
|
||||
* Based off numbers on revisions rather than raw count of attached revisions
|
||||
* as they may be cleared up or revisions deleted at some point.
|
||||
* @return int
|
||||
*/
|
||||
public function revisionCount()
|
||||
{
|
||||
return intval($this->revisions()->max('revision'));
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ImageRevision extends Model
|
||||
{
|
||||
/**
|
||||
* Relation for the user that created this entity.
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function createdBy()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image that this is a revision of.
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function image()
|
||||
{
|
||||
return $this->belongsTo(Image::class);
|
||||
}
|
||||
}
|
@ -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 updateDrawing(Image $image, string $base64Uri)
|
||||
{
|
||||
return $this->imageService->updateImageFromBase64Uri($image, $base64Uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the details of an image via an array of properties.
|
||||
@ -251,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);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
use BookStack\Exceptions\ImageUploadException;
|
||||
use BookStack\Image;
|
||||
use BookStack\ImageRevision;
|
||||
use BookStack\User;
|
||||
use Exception;
|
||||
use Intervention\Image\Exception\NotSupportedException;
|
||||
@ -82,22 +81,6 @@ class ImageService extends UploadService
|
||||
return $this->saveNew($name, $data, $type, $uploadedTo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Image $image
|
||||
* @param string $base64Uri
|
||||
* @return Image
|
||||
* @throws ImageUploadException
|
||||
*/
|
||||
public function updateImageFromBase64Uri(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]);
|
||||
return $this->update($image, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an image from url and saves it to the database.
|
||||
* @param $url
|
||||
@ -168,59 +151,6 @@ class ImageService extends UploadService
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the content of an existing image.
|
||||
* Uploaded the new image content and creates a revision for the old image content.
|
||||
* @param Image $image
|
||||
* @param $imageData
|
||||
* @return Image
|
||||
* @throws ImageUploadException
|
||||
*/
|
||||
private function update(Image $image, $imageData)
|
||||
{
|
||||
// Save image revision if not previously exists to ensure we always have
|
||||
// a reference to the image files being uploaded.
|
||||
if ($image->revisions()->count() === 0) {
|
||||
$this->saveImageRevision($image);
|
||||
}
|
||||
|
||||
$pathInfo = pathinfo($image->path);
|
||||
$revisionCount = $image->revisionCount() + 1;
|
||||
$newFileName = preg_replace('/^(.+?)(-v\d+)?$/', '$1-v' . $revisionCount, $pathInfo['filename']);
|
||||
|
||||
$image->path = str_replace_last($pathInfo['filename'], $newFileName, $image->path);
|
||||
$image->url = $this->getPublicUrl($image->path);
|
||||
$image->updated_by = user()->id;
|
||||
|
||||
$storage = $this->getStorage();
|
||||
|
||||
try {
|
||||
$storage->put($image->path, $imageData);
|
||||
$storage->setVisibility($image->path, 'public');
|
||||
$image->save();
|
||||
$this->saveImageRevision($image);
|
||||
} catch (Exception $e) {
|
||||
throw new ImageUploadException(trans('errors.path_not_writable', ['filePath' => $image->path]));
|
||||
}
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new revision for an image.
|
||||
* @param Image $image
|
||||
* @return ImageRevision
|
||||
*/
|
||||
protected function saveImageRevision(Image $image)
|
||||
{
|
||||
$revision = new ImageRevision();
|
||||
$revision->image_id = $image->id;
|
||||
$revision->path = $image->path;
|
||||
$revision->url = $image->url;
|
||||
$revision->created_by = user()->id;
|
||||
$revision->revision = $image->revisionCount() + 1;
|
||||
$revision->save();
|
||||
return $revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the image is a gif. Returns true if it is, else false.
|
||||
@ -309,13 +239,6 @@ class ImageService extends UploadService
|
||||
*/
|
||||
public function destroy(Image $image)
|
||||
{
|
||||
// Destroy image revisions
|
||||
foreach ($image->revisions as $revision) {
|
||||
$this->destroyImagesFromPath($revision->path);
|
||||
$revision->delete();
|
||||
}
|
||||
|
||||
// Destroy main image
|
||||
$this->destroyImagesFromPath($image->path);
|
||||
$image->delete();
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateImageRevisionsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('image_revisions', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('image_id');
|
||||
$table->integer('revision');
|
||||
$table->string('path');
|
||||
$table->string('url');
|
||||
$table->integer('created_by');
|
||||
|
||||
$table->index('image_id');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('image_revisions');
|
||||
}
|
||||
}
|
@ -229,16 +229,18 @@ function drawIoPlugin() {
|
||||
}
|
||||
|
||||
function showDrawingManager(mceEditor, selectedNode = null) {
|
||||
// TODO - Handle how image manager links in.
|
||||
pageEditor = mceEditor;
|
||||
currentNode = selectedNode;
|
||||
// Show image manager
|
||||
window.ImageManager.show(function (image) {
|
||||
|
||||
|
||||
// // Replace the actively selected content with the linked image
|
||||
// let html = `<a href="${image.url}" target="_blank">`;
|
||||
// html += `<img src="${image.thumbs.display}" alt="${image.name}">`;
|
||||
// html += '</a>';
|
||||
// win.tinyMCE.activeEditor.execCommand('mceInsertContent', false, html);
|
||||
if (selectedNode) {
|
||||
let imgElem = selectedNode.querySelector('img');
|
||||
pageEditor.dom.setAttrib(imgElem, 'src', image.url);
|
||||
pageEditor.dom.setAttrib(selectedNode, 'drawio-diagram', image.id);
|
||||
} else {
|
||||
let imgHTML = `<div drawio-diagram="${image.id}" contenteditable="false"><img src="${image.url}"></div>`;
|
||||
pageEditor.insertContent(imgHTML);
|
||||
}
|
||||
}, 'drawio');
|
||||
}
|
||||
|
||||
@ -260,9 +262,9 @@ function drawIoPlugin() {
|
||||
if (currentNode) {
|
||||
DrawIO.close();
|
||||
let imgElem = currentNode.querySelector('img');
|
||||
let drawingId = currentNode.getAttribute('drawio-diagram');
|
||||
window.$http.put(window.baseUrl(`/images/drawing/upload/${drawingId}`), data).then(resp => {
|
||||
pageEditor.dom.setAttrib(imgElem, 'src', `${resp.data.url}?updated=${Date.now()}`);
|
||||
window.$http.post(window.baseUrl(`/images/drawing/upload`), data).then(resp => {
|
||||
pageEditor.dom.setAttrib(imgElem, 'src', resp.data.url);
|
||||
pageEditor.dom.setAttrib(currentNode, 'drawio-diagram', resp.data.id);
|
||||
}).catch(err => {
|
||||
window.$events.emit('error', trans('errors.image_upload_error'));
|
||||
console.log(err);
|
||||
@ -300,17 +302,23 @@ function drawIoPlugin() {
|
||||
|
||||
editor.addCommand('drawio', () => {
|
||||
let selectedNode = editor.selection.getNode();
|
||||
if (isDrawing(selectedNode)) {
|
||||
showDrawingManager(editor, selectedNode);
|
||||
} else {
|
||||
showDrawingEditor(editor);
|
||||
}
|
||||
showDrawingEditor(editor, isDrawing(selectedNode) ? selectedNode : null);
|
||||
});
|
||||
|
||||
editor.addButton('drawio', {
|
||||
type: 'splitbutton',
|
||||
tooltip: 'Drawing',
|
||||
image: window.baseUrl('/icon/drawing.svg?color=000000'),
|
||||
cmd: 'drawio'
|
||||
cmd: 'drawio',
|
||||
menu: [
|
||||
{
|
||||
text: 'Drawing Manager',
|
||||
onclick() {
|
||||
let selectedNode = editor.selection.getNode();
|
||||
showDrawingManager(editor, isDrawing(selectedNode) ? selectedNode : null);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
editor.on('dblclick', event => {
|
||||
|
@ -24,9 +24,6 @@ const data = {
|
||||
searching: false,
|
||||
searchTerm: '',
|
||||
|
||||
revisions: [],
|
||||
selectedRevision: null,
|
||||
|
||||
imageUpdateSuccess: false,
|
||||
imageDeleteSuccess: false,
|
||||
deleteConfirm: false,
|
||||
@ -50,9 +47,11 @@ const methods = {
|
||||
},
|
||||
|
||||
hide() {
|
||||
if (this.$refs.dropzone) {
|
||||
this.$refs.dropzone.onClose();
|
||||
}
|
||||
this.showing = false;
|
||||
this.selectedImage = false;
|
||||
this.$refs.dropzone.onClose();
|
||||
this.$el.children[0].components.overlay.hide();
|
||||
},
|
||||
|
||||
@ -113,8 +112,6 @@ const methods = {
|
||||
let currentTime = Date.now();
|
||||
let timeDiff = currentTime - previousClickTime;
|
||||
let isDblClick = timeDiff < dblClickTime && image.id === previousClickImage;
|
||||
this.revisions = [];
|
||||
this.selectedRevision = null
|
||||
|
||||
if (isDblClick) {
|
||||
this.callbackAndHide(image);
|
||||
@ -122,11 +119,6 @@ const methods = {
|
||||
this.selectedImage = image;
|
||||
this.deleteConfirm = false;
|
||||
this.dependantPages = false;
|
||||
if (this.imageType === 'drawio') {
|
||||
this.$http.get(window.baseUrl(`/images/revisions/${image.id}`)).then(resp => {
|
||||
this.revisions = resp.data;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
previousClickTime = currentTime;
|
||||
@ -182,11 +174,6 @@ const methods = {
|
||||
this.images.unshift(event.data);
|
||||
this.$events.emit('success', trans('components.image_upload_success'));
|
||||
},
|
||||
|
||||
selectRevision(revision) {
|
||||
let rev = (this.selectedRevision === revision) ? null : revision;
|
||||
this.selectedRevision = rev;
|
||||
}
|
||||
};
|
||||
|
||||
const computed = {
|
||||
|
@ -41,20 +41,13 @@
|
||||
|
||||
<div class="image-manager-sidebar">
|
||||
|
||||
<dropzone ref="dropzone" placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
|
||||
<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">
|
||||
|
||||
<div v-if="selectedRevision" class="image-manager-viewer">
|
||||
<a :href="selectedRevision.url" target="_blank" style="display: block;">
|
||||
<img :src="selectedRevision.url" :alt="selectedImage.name"
|
||||
:title="selectedImage.name">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="saveImageDetails" v-if="!selectedRevision">
|
||||
<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"
|
||||
@ -67,7 +60,7 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div v-if="!selectedRevision" class="clearfix">
|
||||
<div class="clearfix">
|
||||
<div class="float left">
|
||||
<button type="button" class="button icon outline" @click="deleteImage">@icon('delete')</button>
|
||||
|
||||
@ -91,28 +84,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{--Revisions View--}}
|
||||
<div v-show="imageType === 'drawio'">
|
||||
<hr>
|
||||
<h5>Revisions</h5>
|
||||
<table v-if="revisions.length > 0" class="table">
|
||||
<tr v-for="(revision, index) in revisions" :key="revision.id" :class="{'primary-background-light': selectedRevision === revision}" @click="selectRevision(revision)">
|
||||
<td>
|
||||
<span v-text="revision.revision"></span>
|
||||
<span class="italic text-muted" v-if="index === 0 && revision.path === selectedImage.path">(Current)</span>
|
||||
</td>
|
||||
<td class="text-small">
|
||||
<span v-text="(new Date(revision.created_at + 'Z')).toLocaleTimeString()"></span>
|
||||
<br>
|
||||
<span v-text="(new Date(revision.created_at + 'Z')).toLocaleDateString()"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p v-if="revisions.length === 0" class="text-muted italic">
|
||||
No revisions found
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -96,9 +96,7 @@ Route::group(['middleware' => 'auth'], function () {
|
||||
Route::get('/base64/{id}', 'ImageController@getBase64Image');
|
||||
Route::put('/update/{imageId}', 'ImageController@update');
|
||||
Route::post('/drawing/upload', 'ImageController@uploadDrawing');
|
||||
Route::put('/drawing/upload/{id}', 'ImageController@updateDrawing');
|
||||
Route::get('/usage/{id}', 'ImageController@usage');
|
||||
Route::get('/revisions/{id}', 'ImageController@getRevisions');
|
||||
Route::post('/{type}/upload', 'ImageController@uploadByType');
|
||||
Route::get('/{type}/all', 'ImageController@getAllByType');
|
||||
Route::get('/{type}/all/{page}', 'ImageController@getAllByType');
|
||||
|
Loading…
x
Reference in New Issue
Block a user