Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Callback/sticky args refactor #1377

Merged
merged 40 commits into from Aug 1, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c641fe7
WIP - Callback refactor
ibelar Jul 24, 2020
13fde21
clean up
ibelar Jul 25, 2020
658d316
fix rendering for Upload Control on a reload
ibelar Jul 25, 2020
0956d1f
fix build query
ibelar Jul 26, 2020
efb0f3b
Merge branch 'develop' into feature/refactor-callbacks
ibelar Jul 27, 2020
fcbeae7
wip
ibelar Jul 28, 2020
ca8d431
fix unit test
ibelar Jul 28, 2020
f7e8586
more test fix
ibelar Jul 28, 2020
f3d481e
catch exception
ibelar Jul 28, 2020
2c733fb
fix demo
ibelar Jul 28, 2020
82803b4
demos tutorial fix
ibelar Jul 28, 2020
33f295a
version fix
ibelar Jul 28, 2020
eab38e0
wip
ibelar Jul 28, 2020
42ee5fb
add behat test for Model action in panel
ibelar Jul 28, 2020
87e8c32
fix
ibelar Jul 28, 2020
32a1b3c
wip
ibelar Jul 29, 2020
f2412a7
fix unit test
ibelar Jul 29, 2020
4b6da45
fix wizard test
ibelar Jul 29, 2020
8af24ae
update comment
ibelar Jul 29, 2020
bdb1650
fix Upload Control
ibelar Jul 29, 2020
b459488
global sticky only when executing
mvorisek Jul 30, 2020
31a4eaf
Merge branch 'develop' into feature/refactor-callbacks
mvorisek Jul 30, 2020
681cb6e
small fix, improve cast
mvorisek Jul 30, 2020
eea1a75
improve Callback::canTrigger() method
mvorisek Jul 30, 2020
8c503ff
move canXxx method to a better place
mvorisek Jul 30, 2020
3bb7063
fix upload unpacking
mvorisek Jul 30, 2020
5b64bed
revert callback for Wizard fix
ibelar Jul 30, 2020
baaaa18
fix revert to revert "global sticky only when executing" only
mvorisek Jul 30, 2020
6084f0e
add test for trigger on reload
ibelar Jul 30, 2020
846e500
add callback in callback test
ibelar Jul 30, 2020
43715f7
cs fixer
ibelar Jul 30, 2020
db13a46
add comment
ibelar Jul 30, 2020
98ab381
Fix button signature for Behat
ibelar Jul 30, 2020
cc63104
dam button
ibelar Jul 30, 2020
a3d4cb4
test
ibelar Jul 30, 2020
9f31eb7
fix test
ibelar Jul 30, 2020
d31025e
test
ibelar Jul 30, 2020
cc6b970
damit
ibelar Jul 30, 2020
9dd6191
swap onUpload/onDelete before merge
mvorisek Jul 31, 2020
f9f2698
Merge branch 'develop' into feature/refactor-callbacks
mvorisek Jul 31, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 32 additions & 20 deletions src/Callback.php
Expand Up @@ -74,13 +74,23 @@ public function init(): void
$this->urlTrigger = $this->name;
}

$this->app->stickyGet($this->urlTrigger);
$this->setAppSticky();
}

public function setUrlTrigger(string $trigger)
{
$this->urlTrigger = $trigger;
$this->app->stickyGet($this->urlTrigger);
$this->setAppSticky();
}

/**
* Set app sticky argument only when using GET method.
*/
public function setAppSticky()
{
if (!$this->isPostTriggered) {
$this->app->stickyGet($this->urlTrigger);
}
}

public function getUrlTrigger(): string
Expand Down Expand Up @@ -115,12 +125,8 @@ public function set($callback, $args = [])
*/
public function terminateJson(View $view = null)
{
if (!$view) {
$view = $this->owner;
}

if ($this->canTerminate()) {
$this->app->terminateJson($view);
$this->app->terminateJson($view ?: $this->owner);
}
}

Expand All @@ -144,9 +150,9 @@ public function isTriggered(): bool
}

/**
* Return this callback mode.
* Return callback triggered value.
*/
public function getMode(): string
public function getTriggeredValue(): string
{
if ($this->isPostTriggered) {
return $_POST[$this->urlTrigger] ?? '';
Expand All @@ -159,26 +165,32 @@ public function getMode(): string
* Return URL that will trigger action on this call-back. If you intend to request
* the URL direcly in your browser (as iframe, new tab, or document location), you
* should use getUrl instead.
*
* @param string $mode
*
* @return string
*/
public function getJsUrl($mode = 'ajax')
public function getJsUrl(string $value = 'ajax'): string
{
return $this->owner->jsUrl([$this->urlTrigger => $mode, '__atk_callback' => 1]);
return $this->owner->jsUrl($this->getUrlArguments($value));
}

/**
* Return URL that will trigger action on this call-back. If you intend to request
* the URL loading from inside JavaScript, it's always advised to use getJsUrl instead.
*
* @param string $mode
*
* @return string
*/
public function getUrl($mode = 'callback')
public function getUrl(string $value = 'callback'): string
{
return $this->owner->url([$this->urlTrigger => $mode, '__atk_callback' => 1]);
return $this->owner->url($this->getUrlArguments($value));
}

/**
* Return proper url argument for this callback.
*/
private function getUrlArguments(string $value): array
{
$args = ['__atk_callback' => 1];
if (!$this->isPostTriggered) {
$args[$this->urlTrigger] = $value;
}

return $args;
}
}
42 changes: 15 additions & 27 deletions src/Component/InlineEdit.php
Expand Up @@ -108,23 +108,21 @@ public function setModel(\atk4\data\Model $model)
parent::setModel($model);
$this->field = $this->field ? $this->field : $this->model->title_field;
if ($this->autoSave && $this->model->loaded()) {
if ($this->cb->triggered()) {
$value = $_POST['value'] ? $_POST['value'] : null;
$this->cb->set(function () use ($value) {
try {
$this->model->set($this->field, $this->app->ui_persistence->typecastLoadField($this->model->getField($this->field), $value));
$this->model->save();

return $this->jsSuccess('Update successfully');
} catch (ValidationException $e) {
$this->app->terminateJson([
'success' => true,
'hasValidationError' => true,
'atkjs' => $this->jsError(call_user_func($this->formatErrorMsg, $e, $value))->jsRender(),
]);
}
});
}
$value = $_POST['value'] ?? null;
$this->cb->set(function () use ($value) {
try {
$this->model->set($this->field, $this->app->ui_persistence->typecastLoadField($this->model->getField($this->field), $value));
$this->model->save();

return $this->jsSuccess('Update successfully');
} catch (ValidationException $e) {
$this->app->terminateJson([
'success' => true,
'hasValidationError' => true,
'atkjs' => $this->jsError(call_user_func($this->formatErrorMsg, $e, $value))->jsRender(),
]);
}
});
}

return $this->model;
Expand Down Expand Up @@ -213,15 +211,5 @@ protected function renderView(): void
'url' => $this->cb->getJsUrl(),
'saveOnBlur' => $this->saveOnBlur,
]);

// $this->js(true, (new JsVueService())->createAtkVue(
// '#'.$this->name,
// 'atk-inline-edit',
// [
// 'initValue' => $initValue,
// 'url' => $this->cb->getJsUrl(),
// 'saveOnBlur' => $this->saveOnBlur,
// ]
// ));
}
}
20 changes: 9 additions & 11 deletions src/Form/Control/Multiline.php
Expand Up @@ -679,17 +679,15 @@ protected function renderView(): void
throw new Exception('Multiline field needs to have it\'s model setup.');
}

if ($this->cb->triggered()) {
$this->cb->set(function () {
try {
return $this->renderCallback();
} catch (\atk4\Core\Exception $e) {
$this->app->terminateJson(['success' => false, 'error' => $e->getMessage()]);
} catch (\Error $e) {
$this->app->terminateJson(['success' => false, 'error' => $e->getMessage()]);
}
});
}
$this->cb->set(function () {
try {
return $this->renderCallback();
} catch (\atk4\Core\Exception $e) {
mvorisek marked this conversation as resolved.
Show resolved Hide resolved
$this->app->terminateJson(['success' => false, 'error' => $e->getMessage()]);
} catch (\Error $e) {
$this->app->terminateJson(['success' => false, 'error' => $e->getMessage()]);
}
});

$this->multiLine->template->trySetHtml('Input', $this->getInput());
parent::renderView();
Expand Down
55 changes: 27 additions & 28 deletions src/Form/Control/Upload.php
Expand Up @@ -171,10 +171,10 @@ public function onDelete($fx = null)
if (is_callable($fx)) {
$this->hasDeleteCb = true;
$action = $_POST['action'] ?? null;
if ($this->cb->triggered() && $action === 'delete') {
$this->_isCbRunning = true;
if ($action === 'delete') {
$fileName = $_POST['f_name'] ?? null;
$this->cb->set(function () use ($fx, $fileName) {
$this->_isCbRunning = true;
$this->addJsAction(call_user_func_array($fx, [$fileName]));

return $this->jsActions;
Expand All @@ -193,31 +193,29 @@ public function onUpload($fx = null)
{
if (is_callable($fx)) {
$this->hasUploadCb = true;
if ($this->cb->triggered()) {
$this->_isCbRunning = true;
$action = $_POST['action'] ?? null;
$files = $_FILES ?? null;
if ($files) {
//set fileId to file name as default.
$this->fileId = $files['file']['name'];
// display file name to user as default.
$this->setInput($this->fileId);
}
if ($action === 'upload' && !$files['file']['error']) {
$this->cb->set(function () use ($fx, $files) {
$this->addJsAction(call_user_func_array($fx, $files));
//$value = $this->field ? $this->field->get() : $this->content;
$this->addJsAction([
$this->js()->atkFileUpload('updateField', [$this->fileId, $this->getInputValue()]),
]);

return $this->jsActions;
});
} elseif ($action === null || isset($files['file']['error'])) {
$this->cb->set(function () use ($fx, $files) {
return call_user_func($fx, 'error');
});
}
$action = $_POST['action'] ?? null;
$files = $_FILES ?? null;
if ($files) {
//set fileId to file name as default.
$this->fileId = $files['file']['name'];
// display file name to user as default.
$this->setInput($this->fileId);
}
if ($action === 'upload' && !$files['file']['error']) {
$this->cb->set(function () use ($fx, $files) {
$this->_isCbRunning = true;
$this->addJsAction(call_user_func_array($fx, $files));
//$value = $this->field ? $this->field->get() : $this->content;
$this->addJsAction([
$this->js()->atkFileUpload('updateField', [$this->fileId, $this->getInputValue()]),
]);

return $this->jsActions;
});
} elseif ($action === null || isset($files['file']['error'])) {
$this->cb->set(function () use ($fx, $files) {
return call_user_func($fx, 'error');
});
}
}
}
Expand All @@ -230,9 +228,10 @@ protected function renderView(): void
}
parent::renderView();

if (!$this->_isCbRunning && (!$this->hasUploadCb || !$this->hasDeleteCb)) {
if (!$this->_isCbRunning && !isset($_GET['__atk_reload']) && (!$this->hasUploadCb || !$this->hasDeleteCb)) {
Copy link
Member

@mvorisek mvorisek Jul 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check is still triggering/throwing in the nested CRUD

throw new Exception('onUpload and onDelete callback must be called to use file upload. Missing one or both of them.');
}

if (!empty($this->accept)) {
$this->template->trySet('accept', implode(',', $this->accept));
}
Expand Down
2 changes: 1 addition & 1 deletion src/JsCallback.php
Expand Up @@ -168,7 +168,7 @@ public function getAjaxec($response, $chain = null): string
return $ajaxec;
}

public function getUrl($mode = 'callback')
public function getUrl(string $mode = 'callback'): string
{
throw new Exception('Do not use getUrl on JsCallback, use getJsUrl()');
}
Expand Down
16 changes: 7 additions & 9 deletions src/JsSortable.php
Expand Up @@ -81,15 +81,13 @@ public function init(): void
public function onReorder($fx = null)
{
if (is_callable($fx)) {
if ($this->triggered()) {
$sortOrders = explode(',', $_POST['order'] ?? '');
$source = $_POST['source'] ?? null;
$newIdx = $_POST['new_idx'] ?? null;
$orgIdx = $_POST['org_idx'] ?? null;
$this->set(function () use ($fx, $sortOrders, $source, $newIdx, $orgIdx) {
return call_user_func_array($fx, [$sortOrders, $source, $newIdx, $orgIdx]);
});
}
$sortOrders = explode(',', $_POST['order'] ?? '');
$source = $_POST['source'] ?? null;
$newIdx = $_POST['new_idx'] ?? null;
$orgIdx = $_POST['org_idx'] ?? null;
$this->set(function () use ($fx, $sortOrders, $source, $newIdx, $orgIdx) {
return call_user_func_array($fx, [$sortOrders, $source, $newIdx, $orgIdx]);
});
mvorisek marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
10 changes: 4 additions & 6 deletions src/Table/Column/JsHeader.php
Expand Up @@ -19,12 +19,10 @@ class JsHeader extends JsCallback
public function onSelectItem($fx)
{
if (is_callable($fx)) {
if ($this->triggered()) {
$param = [$_GET['id'], $_GET['item'] ?? null];
$this->set(function () use ($fx, $param) {
return call_user_func_array($fx, $param);
});
}
$param = [$_GET['id'] ?? null, $_GET['item'] ?? null];
$this->set(function () use ($fx, $param) {
return call_user_func_array($fx, $param);
});
}
}
}
2 changes: 1 addition & 1 deletion src/VirtualPage.php
Expand Up @@ -106,7 +106,7 @@ public function getHtml()
{
$this->cb->set(function () {
// if virtual page callback is triggered
if ($mode = $this->cb->getMode()) {
if ($mode = $this->cb->getTriggeredValue()) {
// process callback
if ($this->fx) {
call_user_func($this->fx, $this);
Expand Down
6 changes: 3 additions & 3 deletions src/Wizard.php
Expand Up @@ -67,21 +67,21 @@ public function init(): void
$this->stepCallback = Callback::addTo($this, ['urlTrigger' => $this->name]);
}

$this->currentStep = (int) $this->stepCallback->getMode() ?: 0;
$this->currentStep = (int) $this->stepCallback->getTriggeredValue() ?: 0;
mvorisek marked this conversation as resolved.
Show resolved Hide resolved

$this->stepTemplate = $this->template->cloneRegion('Step');
$this->template->del('Step');

// add buttons
if ($this->currentStep) {
$this->buttonPrev = Button::addTo($this, ['Back', 'basic'], ['Left']);
$this->buttonPrev->link($this->stepCallback->getUrl($this->currentStep - 1));
$this->buttonPrev->link($this->stepCallback->getUrl((string) ($this->currentStep - 1)));
}

$this->buttonNext = Button::addTo($this, ['Next', 'primary'], ['Right']);
$this->buttonFinish = Button::addTo($this, ['Finish', 'primary'], ['Right']);

$this->buttonNext->link($this->stepCallback->getUrl($this->currentStep + 1));
$this->buttonNext->link($this->stepCallback->getUrl((string) ($this->currentStep + 1)));
}

/**
Expand Down