mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Started the page attributes interface
This commit is contained in:
parent
fcfb9470c9
commit
1fa079b466
@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
class Attribute extends Model
|
class Attribute extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = ['name', 'value'];
|
protected $fillable = ['name', 'value', 'order'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the entity that this attribute belongs to
|
* Get the entity that this attribute belongs to
|
||||||
|
@ -38,18 +38,15 @@ class AttributeController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function updateForEntity($entityType, $entityId, Request $request)
|
public function updateForEntity($entityType, $entityId, Request $request)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->validate($request, [
|
|
||||||
'attributes.*.name' => 'required|min:3|max:250',
|
|
||||||
'attributes.*.value' => 'max:250'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$entity = $this->attributeRepo->getEntity($entityType, $entityId, 'update');
|
$entity = $this->attributeRepo->getEntity($entityType, $entityId, 'update');
|
||||||
if ($entity === null) return $this->jsonError("Entity not found", 404);
|
if ($entity === null) return $this->jsonError("Entity not found", 404);
|
||||||
|
|
||||||
$inputAttributes = $request->input('attributes');
|
$inputAttributes = $request->input('attributes');
|
||||||
$attributes = $this->attributeRepo->saveAttributesToEntity($entity, $inputAttributes);
|
$attributes = $this->attributeRepo->saveAttributesToEntity($entity, $inputAttributes);
|
||||||
return response()->json($attributes);
|
return response()->json([
|
||||||
|
'attributes' => $attributes,
|
||||||
|
'message' => 'Attributes successfully updated'
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,7 +72,7 @@ class PageController extends Controller
|
|||||||
$this->checkOwnablePermission('page-create', $book);
|
$this->checkOwnablePermission('page-create', $book);
|
||||||
$this->setPageTitle('Edit Page Draft');
|
$this->setPageTitle('Edit Page Draft');
|
||||||
|
|
||||||
return view('pages/create', ['draft' => $draft, 'book' => $book]);
|
return view('pages/edit', ['page' => $draft, 'book' => $book, 'isDraft' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,6 +80,7 @@ class AttributeRepo
|
|||||||
$entity->attributes()->delete();
|
$entity->attributes()->delete();
|
||||||
$newAttributes = [];
|
$newAttributes = [];
|
||||||
foreach ($attributes as $attribute) {
|
foreach ($attributes as $attribute) {
|
||||||
|
if (trim($attribute['name']) === '') continue;
|
||||||
$newAttributes[] = $this->newInstanceFromInput($attribute);
|
$newAttributes[] = $this->newInstanceFromInput($attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,10 +18,12 @@ class CreateAttributesTable extends Migration
|
|||||||
$table->string('entity_type', 100);
|
$table->string('entity_type', 100);
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('value');
|
$table->string('value');
|
||||||
|
$table->integer('order');
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
||||||
$table->index('name');
|
$table->index('name');
|
||||||
$table->index('value');
|
$table->index('value');
|
||||||
|
$table->index('order');
|
||||||
$table->index(['entity_id', 'entity_type']);
|
$table->index(['entity_id', 'entity_type']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -400,4 +400,96 @@ module.exports = function (ngApp, events) {
|
|||||||
|
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
};
|
ngApp.controller('PageAttributeController', ['$scope', '$http', '$attrs',
|
||||||
|
function ($scope, $http, $attrs) {
|
||||||
|
|
||||||
|
const pageId = Number($attrs.pageId);
|
||||||
|
$scope.attributes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push an empty attribute to the end of the scope attributes.
|
||||||
|
*/
|
||||||
|
function addEmptyAttribute() {
|
||||||
|
$scope.attributes.push({
|
||||||
|
name: '',
|
||||||
|
value: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all attributes for the current book and add into scope.
|
||||||
|
*/
|
||||||
|
function getAttributes() {
|
||||||
|
$http.get('/ajax/attributes/get/page/' + pageId).then((responseData) => {
|
||||||
|
$scope.attributes = responseData.data;
|
||||||
|
addEmptyAttribute();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getAttributes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the order property on all attributes.
|
||||||
|
*/
|
||||||
|
function setAttributeOrder() {
|
||||||
|
for (let i = 0; i < $scope.attributes.length; i++) {
|
||||||
|
$scope.attributes[i].order = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When an attribute changes check if another empty editable
|
||||||
|
* field needs to be added onto the end.
|
||||||
|
* @param attribute
|
||||||
|
*/
|
||||||
|
$scope.attributeChange = function(attribute) {
|
||||||
|
let cPos = $scope.attributes.indexOf(attribute);
|
||||||
|
if (cPos !== $scope.attributes.length-1) return;
|
||||||
|
|
||||||
|
if (attribute.name !== '' || attribute.value !== '') {
|
||||||
|
addEmptyAttribute();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When an attribute field loses focus check the attribute to see if its
|
||||||
|
* empty and therefore could be removed from the list.
|
||||||
|
* @param attribute
|
||||||
|
*/
|
||||||
|
$scope.attributeBlur = function(attribute) {
|
||||||
|
let isLast = $scope.attributes.length - 1 === $scope.attributes.indexOf(attribute);
|
||||||
|
if (attribute.name === '' && attribute.value === '' && !isLast) {
|
||||||
|
let cPos = $scope.attributes.indexOf(attribute);
|
||||||
|
$scope.attributes.splice(cPos, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.saveAttributes = function() {
|
||||||
|
setAttributeOrder();
|
||||||
|
let postData = {attributes: $scope.attributes};
|
||||||
|
$http.post('/ajax/attributes/update/page/' + pageId, postData).then((responseData) => {
|
||||||
|
$scope.attributes = responseData.data.attributes;
|
||||||
|
addEmptyAttribute();
|
||||||
|
events.emit('success', responseData.data.message);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
}]);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -201,4 +201,18 @@ $btt-size: 40px;
|
|||||||
background-color: $negative;
|
background-color: $negative;
|
||||||
color: #EEE;
|
color: #EEE;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute form
|
||||||
|
.floating-toolbox {
|
||||||
|
background-color: #FFF;
|
||||||
|
border: 1px solid #BBB;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: $-l;
|
||||||
|
position: fixed;
|
||||||
|
right: $-xl*2;
|
||||||
|
top: 100px;
|
||||||
|
z-index: 99;
|
||||||
|
height: 800px;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
@ -1,17 +0,0 @@
|
|||||||
@extends('base')
|
|
||||||
|
|
||||||
@section('head')
|
|
||||||
<script src="/libs/tinymce/tinymce.min.js?ver=4.3.7"></script>
|
|
||||||
@stop
|
|
||||||
|
|
||||||
@section('body-class', 'flexbox')
|
|
||||||
|
|
||||||
@section('content')
|
|
||||||
|
|
||||||
<div class="flex-fill flex">
|
|
||||||
<form action="{{$book->getUrl() . '/page/' . $draft->id}}" method="POST" class="flex flex-fill">
|
|
||||||
@include('pages/form', ['model' => $draft])
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $draft->id])
|
|
||||||
@stop
|
|
@ -10,9 +10,24 @@
|
|||||||
|
|
||||||
<div class="flex-fill flex">
|
<div class="flex-fill flex">
|
||||||
<form action="{{$page->getUrl()}}" data-page-id="{{ $page->id }}" method="POST" class="flex flex-fill">
|
<form action="{{$page->getUrl()}}" data-page-id="{{ $page->id }}" method="POST" class="flex flex-fill">
|
||||||
<input type="hidden" name="_method" value="PUT">
|
@if(!isset($isDraft))
|
||||||
|
<input type="hidden" name="_method" value="PUT">
|
||||||
|
@endif
|
||||||
@include('pages/form', ['model' => $page])
|
@include('pages/form', ['model' => $page])
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div class="floating-toolbox" ng-controller="PageAttributeController" page-id="{{ $page->id or 0 }}">
|
||||||
|
<form ng-submit="saveAttributes()">
|
||||||
|
<table>
|
||||||
|
<tr ng-repeat="attribute in attributes">
|
||||||
|
<td><input type="text" ng-model="attribute.name" ng-change="attributeChange(attribute)" ng-blur="attributeBlur(attribute)" placeholder="Attribute Name"></td>
|
||||||
|
<td><input type="text" ng-model="attribute.value" ng-change="attributeChange(attribute)" ng-blur="attributeBlur(attribute)" placeholder="Value"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<button class="button pos" type="submit">Save attributes</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
|
@include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
@include('form/text', ['name' => 'name', 'placeholder' => 'Page Title'])
|
@include('form/text', ['name' => 'name', 'placeholder' => 'Page Title'])
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="edit-area flex-fill flex">
|
<div class="edit-area flex-fill flex">
|
||||||
@if(setting('app-editor') === 'wysiwyg')
|
@if(setting('app-editor') === 'wysiwyg')
|
||||||
<textarea id="html-editor" tinymce="editorOptions" mce-change="editorChange" mce-model="editContent" name="html" rows="5"
|
<textarea id="html-editor" tinymce="editorOptions" mce-change="editorChange" mce-model="editContent" name="html" rows="5"
|
||||||
|
@ -97,19 +97,32 @@ class AttributeTests extends \TestCase
|
|||||||
['name' => 'country', 'value' => 'England'],
|
['name' => 'country', 'value' => 'England'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Do update request
|
||||||
$this->asAdmin()->json("POST", "/ajax/attributes/update/page/" . $page->id, ['attributes' => $testJsonData]);
|
$this->asAdmin()->json("POST", "/ajax/attributes/update/page/" . $page->id, ['attributes' => $testJsonData]);
|
||||||
$this->asAdmin()->get("/ajax/attributes/get/page/" . $page->id);
|
$updateData = json_decode($this->response->getContent());
|
||||||
$jsonData = json_decode($this->response->getContent());
|
|
||||||
// Check counts
|
|
||||||
$this->assertTrue(count($jsonData) === count($testJsonData), "The received attribute count is incorrect");
|
|
||||||
// Check data is correct
|
// Check data is correct
|
||||||
$testDataCorrect = true;
|
$testDataCorrect = true;
|
||||||
foreach ($jsonData as $data) {
|
foreach ($updateData->attributes as $data) {
|
||||||
$testItem = ['name' => $data->name, 'value' => $data->value];
|
$testItem = ['name' => $data->name, 'value' => $data->value];
|
||||||
if (!in_array($testItem, $testResponseJsonData)) $testDataCorrect = false;
|
if (!in_array($testItem, $testResponseJsonData)) $testDataCorrect = false;
|
||||||
}
|
}
|
||||||
$testMessage = "Expected data was not found in the response.\nExpected Data: %s\nRecieved Data: %s";
|
$testMessage = "Expected data was not found in the response.\nExpected Data: %s\nRecieved Data: %s";
|
||||||
$this->assertTrue($testDataCorrect, sprintf($testMessage, json_encode($testResponseJsonData), json_encode($jsonData)));
|
$this->assertTrue($testDataCorrect, sprintf($testMessage, json_encode($testResponseJsonData), json_encode($updateData)));
|
||||||
|
$this->assertTrue(isset($updateData->message), "No message returned in attribute update response");
|
||||||
|
|
||||||
|
// Do get request
|
||||||
|
$this->asAdmin()->get("/ajax/attributes/get/page/" . $page->id);
|
||||||
|
$getResponseData = json_decode($this->response->getContent());
|
||||||
|
// Check counts
|
||||||
|
$this->assertTrue(count($getResponseData) === count($testJsonData), "The received attribute count is incorrect");
|
||||||
|
// Check data is correct
|
||||||
|
$testDataCorrect = true;
|
||||||
|
foreach ($getResponseData as $data) {
|
||||||
|
$testItem = ['name' => $data->name, 'value' => $data->value];
|
||||||
|
if (!in_array($testItem, $testResponseJsonData)) $testDataCorrect = false;
|
||||||
|
}
|
||||||
|
$testMessage = "Expected data was not found in the response.\nExpected Data: %s\nRecieved Data: %s";
|
||||||
|
$this->assertTrue($testDataCorrect, sprintf($testMessage, json_encode($testResponseJsonData), json_encode($getResponseData)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user