chg: Added support of displayOnSuccess, non-dismissable modals and some
house cleaningpull/37/head
parent
41ca17ef36
commit
691032551b
|
@ -17,7 +17,7 @@ class AuthKeysController extends AppController
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$this->CRUD->index([
|
$this->CRUD->index([
|
||||||
'filters' => ['users.username', 'authkey', 'comment', 'users.id'],
|
'filters' => ['Users.username', 'authkey', 'comment', 'Users.id'],
|
||||||
'quickFilters' => ['authkey', 'comment'],
|
'quickFilters' => ['authkey', 'comment'],
|
||||||
'contain' => ['Users'],
|
'contain' => ['Users'],
|
||||||
'exclude_fields' => ['authkey']
|
'exclude_fields' => ['authkey']
|
||||||
|
@ -45,7 +45,9 @@ class AuthKeysController extends AppController
|
||||||
$this->CRUD->add([
|
$this->CRUD->add([
|
||||||
'displayOnSuccess' => 'authkey_display'
|
'displayOnSuccess' => 'authkey_display'
|
||||||
]);
|
]);
|
||||||
$responsePayload = $this->CRUD->getResponsePayload();
|
$responsePayload = $this->CRUD->getResponsePayload([
|
||||||
|
'displayOnSuccess' => 'authkey_display'
|
||||||
|
]);
|
||||||
if (!empty($responsePayload)) {
|
if (!empty($responsePayload)) {
|
||||||
return $responsePayload;
|
return $responsePayload;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ use Cake\Controller\Component;
|
||||||
use Cake\Error\Debugger;
|
use Cake\Error\Debugger;
|
||||||
use Cake\Utility\Hash;
|
use Cake\Utility\Hash;
|
||||||
use Cake\Utility\Inflector;
|
use Cake\Utility\Inflector;
|
||||||
|
use Cake\View\ViewBuilder;
|
||||||
|
|
||||||
class CRUDComponent extends Component
|
class CRUDComponent extends Component
|
||||||
{
|
{
|
||||||
|
@ -108,17 +109,16 @@ class CRUDComponent extends Component
|
||||||
$this->saveMetaFields($data->id, $input);
|
$this->saveMetaFields($data->id, $input);
|
||||||
}
|
}
|
||||||
if ($this->Controller->ParamHandler->isRest()) {
|
if ($this->Controller->ParamHandler->isRest()) {
|
||||||
$this->Controller->restResponsePayload = $this->RestResponse->viewData($data, 'json');
|
$this->Controller->restResponsePayload = $this->RestResponse->viewData($savedData, 'json');
|
||||||
} else if ($this->Controller->ParamHandler->isAjax()) {
|
} else if ($this->Controller->ParamHandler->isAjax()) {
|
||||||
|
if (!empty($params['displayOnSuccess'])) {
|
||||||
|
$displayOnSuccess = $this->renderViewInVariable($params['displayOnSuccess'], ['entity' => $data]);
|
||||||
|
$this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message, ['displayOnSuccess' => $displayOnSuccess]);
|
||||||
|
} else {
|
||||||
$this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message);
|
$this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'add', $savedData, $message);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->Controller->Flash->success($message);
|
$this->Controller->Flash->success($message);
|
||||||
if (!empty($params['displayOnSuccess'])) {
|
|
||||||
$this->Controller->set('entity', $data);
|
|
||||||
$this->Controller->set('referer', $this->Controller->referer());
|
|
||||||
$this->Controller->render($params['displayOnSuccess']);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (empty($params['redirect'])) {
|
if (empty($params['redirect'])) {
|
||||||
$this->Controller->redirect(['action' => 'view', $data->id]);
|
$this->Controller->redirect(['action' => 'view', $data->id]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -503,4 +503,12 @@ class CRUDComponent extends Component
|
||||||
return $this->Table->find()->distinct([$field])->all()->extract($field)->toList();
|
return $this->Table->find()->distinct([$field])->all()->extract($field)->toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function renderViewInVariable($templateRelativeName, $data)
|
||||||
|
{
|
||||||
|
$builder = new ViewBuilder();
|
||||||
|
$builder->disableAutoLayout()->setTemplate("{$this->TableAlias}/{$templateRelativeName}");
|
||||||
|
$view = $builder->build($data);
|
||||||
|
return $view->render();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -420,7 +420,7 @@ class RestResponseComponent extends Component
|
||||||
return $this->__sendResponse($response, 200, $format);
|
return $this->__sendResponse($response, 200, $format);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ajaxSuccessResponse($ObjectAlias, $action, $entity, $message)
|
public function ajaxSuccessResponse($ObjectAlias, $action, $entity, $message, $additionalData=[])
|
||||||
{
|
{
|
||||||
$action = $this->__dissectAdminRouting($action);
|
$action = $this->__dissectAdminRouting($action);
|
||||||
$response = [
|
$response = [
|
||||||
|
@ -429,6 +429,9 @@ class RestResponseComponent extends Component
|
||||||
'data' => $entity->toArray(),
|
'data' => $entity->toArray(),
|
||||||
'url' => $this->__generateURL($action, $ObjectAlias, $entity->id)
|
'url' => $this->__generateURL($action, $ObjectAlias, $entity->id)
|
||||||
];
|
];
|
||||||
|
if (!empty($additionalData)) {
|
||||||
|
$response['additionalData'] = $additionalData;
|
||||||
|
}
|
||||||
return $this->viewData($response);
|
return $this->viewData($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
<h4><?= __('Authkey created'); ?></h4>
|
<?php
|
||||||
<p><?= __('Please make sure that you note down the authkey below, this is the only time the authkey is shown in plain text, so make sure you save it. If you lose the key, simply remove the entry and generate a new one.'); ?></p>
|
echo $this->element('genericElements/genericModal', [
|
||||||
<p><?=__('Cerebrate will use the first and the last 4 digit for identification purposes.')?></p>
|
'title' => __('Authkey created'),
|
||||||
<p><?= sprintf('%s: <span class="text-weight-bold">%s</span>', __('Authkey'), h($entity->authkey_raw)) ?></p>
|
'body' => sprintf(
|
||||||
<a href="<?= $referer ?>" class="btn btn-primary"><?= __('I have noted down my key, take me back now') ?></a>
|
'<p>%s</p><p>%s</p><p>%s</p>',
|
||||||
|
__('Please make sure that you note down the authkey below, this is the only time the authkey is shown in plain text, so make sure you save it. If you lose the key, simply remove the entry and generate a new one.'),
|
||||||
|
__('Cerebrate will use the first and the last 4 digit for identification purposes.'),
|
||||||
|
sprintf('%s: <span class="font-weight-bold">%s</span>', __('Authkey'), h($entity->authkey_raw))
|
||||||
|
),
|
||||||
|
'actionButton' => sprintf('<button" class="btn btn-primary" data-dismiss="modal">%s</button>', __('I have noted down my key, take me back now')),
|
||||||
|
'noCancel' => true,
|
||||||
|
'staticBackdrop' => true,
|
||||||
|
]);
|
||||||
|
|
|
@ -10,7 +10,6 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
||||||
'data' => [
|
'data' => [
|
||||||
'type' => 'simple',
|
'type' => 'simple',
|
||||||
'text' => __('Add authentication key'),
|
'text' => __('Add authentication key'),
|
||||||
'class' => 'btn btn-primary',
|
|
||||||
'popover_url' => '/authKeys/add'
|
'popover_url' => '/authKeys/add'
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="modal-dialog <?= empty($class) ? '' : h($class) ?>" role="document">
|
<div class="modal-dialog <?= empty($class) ? '' : h($class) ?>" <?= !empty($staticBackdrop) ? 'data-backdrop="static"' : ''?> role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title"><?= h($title) ?></h5>
|
<h5 class="modal-title"><?= h($title) ?></h5>
|
||||||
|
@ -10,7 +10,9 @@
|
||||||
<?= $body ?>
|
<?= $body ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
<?php if (empty($noCancel)): ?>
|
||||||
<button type="button" class="btn btn-secondary cancel-button" data-dismiss="modal"><?= __('Cancel') ?></button>
|
<button type="button" class="btn btn-secondary cancel-button" data-dismiss="modal"><?= __('Cancel') ?></button>
|
||||||
|
<?php endif; ?>
|
||||||
<?= $actionButton ?>
|
<?= $actionButton ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -34,7 +34,7 @@ class UIFactory {
|
||||||
modalFromURL(url, POSTSuccessCallback, POSTFailCallback) {
|
modalFromURL(url, POSTSuccessCallback, POSTFailCallback) {
|
||||||
return AJAXApi.quickFetchURL(url).then((modalHTML) => {
|
return AJAXApi.quickFetchURL(url).then((modalHTML) => {
|
||||||
const theModal = new ModalFactory({
|
const theModal = new ModalFactory({
|
||||||
rawHTML: modalHTML,
|
rawHtml: modalHTML,
|
||||||
POSTSuccessCallback: POSTSuccessCallback !== undefined ? POSTSuccessCallback : () => {},
|
POSTSuccessCallback: POSTSuccessCallback !== undefined ? POSTSuccessCallback : () => {},
|
||||||
POSTFailCallback: POSTFailCallback !== undefined ? POSTFailCallback : (errorMessage) => {},
|
POSTFailCallback: POSTFailCallback !== undefined ? POSTFailCallback : (errorMessage) => {},
|
||||||
});
|
});
|
||||||
|
@ -46,31 +46,43 @@ class UIFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and display a modal where the modal's content is fetched from the provided URL. Reload the table after a successful operation
|
* Creates and displays a modal where the modal's content is fetched from the provided URL. Reloads the table after a successful operation and handles displayOnSuccess option
|
||||||
* @param {string} url - The URL from which the modal's content should be fetched
|
* @param {string} url - The URL from which the modal's content should be fetched
|
||||||
* @param {string} tableId - The table ID which should be reloaded on success
|
* @param {string} tableId - The table ID which should be reloaded on success
|
||||||
* @return {Promise<Object>} Promise object resolving to the ModalFactory object
|
* @return {Promise<Object>} Promise object resolving to the ModalFactory object
|
||||||
*/
|
*/
|
||||||
openModalFromURL(url, reloadUrl=false, tableId=false) {
|
openModalFromURL(url, reloadUrl=false, tableId=false) {
|
||||||
return UI.modalFromURL(url, () => {
|
return UI.modalFromURL(url, (data) => {
|
||||||
|
let reloaded = false
|
||||||
if (reloadUrl === false || tableId === false) { // Try to get information from the DOM
|
if (reloadUrl === false || tableId === false) { // Try to get information from the DOM
|
||||||
let $elligibleTable = $('table.table')
|
let $elligibleTable = $('table.table')
|
||||||
let currentModel = location.pathname.split('/')[1]
|
let currentModel = location.pathname.split('/')[1]
|
||||||
if ($elligibleTable.length == 1 && currentModel.length > 0) {
|
if ($elligibleTable.length == 1 && currentModel.length > 0) {
|
||||||
let $container = $elligibleTable.closest('div[id^="table-container-"]')
|
let $container = $elligibleTable.closest('div[id^="table-container-"]')
|
||||||
if ($container.length == 1) {
|
if ($container.length == 1) {
|
||||||
return UI.reload(`/${currentModel}/index`, $container, $elligibleTable)
|
UI.reload(`/${currentModel}/index`, $container, $elligibleTable)
|
||||||
|
reloaded = true
|
||||||
} else {
|
} else {
|
||||||
$container = $elligibleTable.closest('div[id^="single-view-table-container-"]')
|
$container = $elligibleTable.closest('div[id^="single-view-table-container-"]')
|
||||||
if ($container.length == 1) {
|
if ($container.length == 1) {
|
||||||
return UI.reload(location.pathname, $container, $elligibleTable)
|
UI.reload(location.pathname, $container, $elligibleTable)
|
||||||
|
reloaded = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return UI.reload(reloadUrl, $(`#table-container-${tableId}`), $(`#table-container-${tableId} table.table`))
|
UI.reload(reloadUrl, $(`#table-container-${tableId}`), $(`#table-container-${tableId} table.table`))
|
||||||
|
reloaded = true
|
||||||
}
|
}
|
||||||
|
if (data.additionalData !== undefined && data.additionalData.displayOnSuccess !== undefined) {
|
||||||
|
UI.modal({
|
||||||
|
rawHtml: data.additionalData.displayOnSuccess
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (!reloaded) {
|
||||||
location.reload()
|
location.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +259,7 @@ class ModalFactory {
|
||||||
*/
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.options = Object.assign({}, ModalFactory.defaultOptions, options)
|
this.options = Object.assign({}, ModalFactory.defaultOptions, options)
|
||||||
if (this.options.rawHTML) {
|
if (this.options.rawHtml && options.POSTSuccessCallback !== undefined) {
|
||||||
this.attachSubmitButtonListener = true
|
this.attachSubmitButtonListener = true
|
||||||
}
|
}
|
||||||
if (options.type === undefined && options.cancel !== undefined) {
|
if (options.type === undefined && options.cancel !== undefined) {
|
||||||
|
@ -256,6 +268,9 @@ class ModalFactory {
|
||||||
this.bsModalOptions = {
|
this.bsModalOptions = {
|
||||||
show: true
|
show: true
|
||||||
}
|
}
|
||||||
|
if (this.options.backdropStatic) {
|
||||||
|
this.bsModalOptions['backdrop'] = 'static'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -305,11 +320,12 @@ class ModalFactory {
|
||||||
* @property {string=('sm'|'lg'|'xl'|'')} size - The size of the modal
|
* @property {string=('sm'|'lg'|'xl'|'')} size - The size of the modal
|
||||||
* @property {boolean} centered - Should the modal be vertically centered
|
* @property {boolean} centered - Should the modal be vertically centered
|
||||||
* @property {boolean} scrollable - Should the modal be scrollable
|
* @property {boolean} scrollable - Should the modal be scrollable
|
||||||
|
* @property {boolean} backdropStatic - When set, the modal will not close when clicking outside it.
|
||||||
* @property {string} title - The title's content of the modal
|
* @property {string} title - The title's content of the modal
|
||||||
* @property {string} titleHtml - The raw HTML title's content of the modal
|
* @property {string} titleHtml - The raw HTML title's content of the modal
|
||||||
* @property {string} body - The body's content of the modal
|
* @property {string} body - The body's content of the modal
|
||||||
* @property {string} bodyHtml - The raw HTML body's content of the modal
|
* @property {string} bodyHtml - The raw HTML body's content of the modal
|
||||||
* @property {string} rawHTML - The raw HTML of the whole modal. If provided, will override any other content
|
* @property {string} rawHtml - The raw HTML of the whole modal. If provided, will override any other content
|
||||||
* @property {string=('primary'|'secondary'|'success'|'danger'|'warning'|'info'|'light'|'dark'|'white'|'transparent')} variant - The variant of the modal
|
* @property {string=('primary'|'secondary'|'success'|'danger'|'warning'|'info'|'light'|'dark'|'white'|'transparent')} variant - The variant of the modal
|
||||||
* @property {string} modalClass - Classes to be added to the modal's container
|
* @property {string} modalClass - Classes to be added to the modal's container
|
||||||
* @property {string} headerClass - Classes to be added to the modal's header
|
* @property {string} headerClass - Classes to be added to the modal's header
|
||||||
|
@ -334,11 +350,12 @@ class ModalFactory {
|
||||||
size: 'md',
|
size: 'md',
|
||||||
centered: false,
|
centered: false,
|
||||||
scrollable: false,
|
scrollable: false,
|
||||||
|
backdropStatic: false,
|
||||||
title: '',
|
title: '',
|
||||||
titleHtml: false,
|
titleHtml: false,
|
||||||
body: false,
|
body: false,
|
||||||
bodyHtml: false,
|
bodyHtml: false,
|
||||||
rawHTML: false,
|
rawHtml: false,
|
||||||
variant: '',
|
variant: '',
|
||||||
modalClass: '',
|
modalClass: '',
|
||||||
headerClass: '',
|
headerClass: '',
|
||||||
|
@ -375,6 +392,8 @@ class ModalFactory {
|
||||||
if (this.isValid()) {
|
if (this.isValid()) {
|
||||||
this.$modal = this.buildModal()
|
this.$modal = this.buildModal()
|
||||||
$('#mainModalContainer').append(this.$modal)
|
$('#mainModalContainer').append(this.$modal)
|
||||||
|
} else {
|
||||||
|
console.log('Modal not valid')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,6 +412,8 @@ class ModalFactory {
|
||||||
that.findSubmitButtonAndAddListener()
|
that.findSubmitButtonAndAddListener()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
console.log('Modal not valid')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,7 +434,7 @@ class ModalFactory {
|
||||||
isValid() {
|
isValid() {
|
||||||
return this.options.title !== false || this.options.titleHtml !== false ||
|
return this.options.title !== false || this.options.titleHtml !== false ||
|
||||||
this.options.body !== false || this.options.bodyHtml !== false ||
|
this.options.body !== false || this.options.bodyHtml !== false ||
|
||||||
this.options.rawHTML !== false
|
this.options.rawHtml !== false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -430,8 +451,11 @@ class ModalFactory {
|
||||||
$modal.addClass(this.options.modalClass)
|
$modal.addClass(this.options.modalClass)
|
||||||
}
|
}
|
||||||
let $modalDialog
|
let $modalDialog
|
||||||
if (this.options.rawHTML) {
|
if (this.options.rawHtml) {
|
||||||
$modalDialog = $(this.options.rawHTML)
|
$modalDialog = $(this.options.rawHtml)
|
||||||
|
if ($modalDialog.data('backdrop') == 'static') {
|
||||||
|
this.bsModalOptions['backdrop'] = 'static'
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$modalDialog = $('<div class="modal-dialog"/>')
|
$modalDialog = $('<div class="modal-dialog"/>')
|
||||||
const $modalContent = $('<div class="modal-content"/>')
|
const $modalContent = $('<div class="modal-content"/>')
|
||||||
|
@ -564,14 +588,12 @@ class ModalFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.options.APIConfirm = (tmpApi) => {
|
this.options.APIConfirm = (tmpApi) => {
|
||||||
tmpApi.mergeOptions({forceHTMLOnValidationFailure: true})
|
|
||||||
return tmpApi.postForm($form[0])
|
return tmpApi.postForm($form[0])
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
this.options.POSTSuccessCallback(data)
|
this.options.POSTSuccessCallback(data)
|
||||||
} else { // Validation error, replace modal content with new html
|
} else { // Validation error
|
||||||
this.$modal.html(data.html)
|
this.injectFormValidationFeedback(form, data.errors)
|
||||||
this.findSubmitButtonAndAddListener()
|
|
||||||
return Promise.reject('Validation error');
|
return Promise.reject('Validation error');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue