feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
823
modules/backend/classes/Controller.php
Normal file
823
modules/backend/classes/Controller.php
Normal file
@@ -0,0 +1,823 @@
|
||||
<?php
|
||||
|
||||
namespace Backend\Classes;
|
||||
|
||||
use Backend\Facades\Backend;
|
||||
use Backend\Facades\BackendAuth;
|
||||
use Backend\Facades\BackendMenu;
|
||||
use Backend\Models\Preference as BackendPreference;
|
||||
use Backend\Models\UserPreference;
|
||||
use Backend\Widgets\MediaManager;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\MassAssignmentException;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Routing\Controller as ControllerBase;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Winter\Storm\Exception\AjaxException;
|
||||
use Winter\Storm\Exception\ApplicationException;
|
||||
use Winter\Storm\Exception\SystemException;
|
||||
use Winter\Storm\Exception\ValidationException;
|
||||
use Winter\Storm\Support\Facades\Config;
|
||||
use Winter\Storm\Support\Facades\Flash;
|
||||
|
||||
/**
|
||||
* The Backend base controller class, used by Backend controllers.
|
||||
* The base controller services back end pages.
|
||||
*
|
||||
* @package winter\wn-backend-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Controller extends ControllerBase
|
||||
{
|
||||
use \System\Traits\ViewMaker;
|
||||
use \System\Traits\AssetMaker;
|
||||
use \System\Traits\ConfigMaker;
|
||||
use \System\Traits\EventEmitter;
|
||||
use \System\Traits\ResponseMaker;
|
||||
use \System\Traits\SecurityController;
|
||||
use \Backend\Traits\ErrorMaker;
|
||||
use \Backend\Traits\WidgetMaker;
|
||||
use \Winter\Storm\Extension\ExtendableTrait;
|
||||
|
||||
/**
|
||||
* @var array Behaviors implemented by this controller.
|
||||
*/
|
||||
public $implement;
|
||||
|
||||
/**
|
||||
* @var object Reference the logged in admin user.
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @var object Collection of WidgetBase objects used on this page.
|
||||
*/
|
||||
public $widget;
|
||||
|
||||
/**
|
||||
* @var bool Prevents the automatic view display.
|
||||
*/
|
||||
public $suppressView = false;
|
||||
|
||||
/**
|
||||
* @var array Routed parameters.
|
||||
*/
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* @var string Page action being called.
|
||||
*/
|
||||
protected $action;
|
||||
|
||||
/**
|
||||
* @var array Defines a collection of actions available without authentication.
|
||||
*/
|
||||
protected $publicActions = [];
|
||||
|
||||
/**
|
||||
* @var array Permissions required to view this page.
|
||||
*/
|
||||
protected $requiredPermissions = [];
|
||||
|
||||
/**
|
||||
* @var string Page title
|
||||
*/
|
||||
public $pageTitle;
|
||||
|
||||
/**
|
||||
* @var string Page title template
|
||||
*/
|
||||
public $pageTitleTemplate;
|
||||
|
||||
/**
|
||||
* @var string Body class property used for customising the layout on a controller basis.
|
||||
*/
|
||||
public $bodyClass = '';
|
||||
|
||||
/**
|
||||
* @var array Default methods which cannot be called as actions.
|
||||
*/
|
||||
public $hiddenActions = [
|
||||
'run',
|
||||
'actionExists',
|
||||
'pageAction',
|
||||
'getId',
|
||||
'setStatusCode',
|
||||
'handleError',
|
||||
'makeHintPartial'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Controller specified methods which cannot be called as actions.
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
/*
|
||||
* Allow early access to route data.
|
||||
*/
|
||||
$this->action = BackendController::$action;
|
||||
$this->params = BackendController::$params;
|
||||
|
||||
/*
|
||||
* Apply $guarded methods to hidden actions
|
||||
*/
|
||||
$this->hiddenActions = array_merge($this->hiddenActions, $this->guarded);
|
||||
|
||||
/*
|
||||
* Define layout and view paths
|
||||
*/
|
||||
$this->layout = $this->layout ?: 'default';
|
||||
$this->layoutPath = Skin::getActive()->getLayoutPaths();
|
||||
$this->viewPath = $this->configPath = $this->guessViewPath();
|
||||
|
||||
/*
|
||||
* Add layout paths from the plugin / module context
|
||||
*/
|
||||
$relativePath = dirname(dirname(strtolower(str_replace('\\', '/', get_called_class()))));
|
||||
$this->layoutPath[] = '~/modules/' . $relativePath . '/layouts';
|
||||
$this->layoutPath[] = '~/plugins/' . $relativePath . '/layouts';
|
||||
|
||||
/*
|
||||
* Create a new instance of the admin user
|
||||
*/
|
||||
$this->user = BackendAuth::getUser();
|
||||
|
||||
/*
|
||||
* Media Manager widget is available on all back-end pages
|
||||
*/
|
||||
if ($this->user && $this->user->hasAccess('media.*')) {
|
||||
$manager = new MediaManager($this, 'ocmediamanager');
|
||||
$manager->bindToController();
|
||||
}
|
||||
|
||||
$this->extendableConstruct();
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->extendableGet($name);
|
||||
}
|
||||
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->extendableSet($name, $value);
|
||||
}
|
||||
|
||||
public function __call($name, $params)
|
||||
{
|
||||
if ($name === 'extend') {
|
||||
if (empty($params[0]) || !is_callable($params[0])) {
|
||||
throw new \InvalidArgumentException('The extend() method requires a callback parameter or closure.');
|
||||
}
|
||||
if ($params[0] instanceof \Closure) {
|
||||
return $params[0]->call($this, $params[1] ?? $this);
|
||||
}
|
||||
return \Closure::fromCallable($params[0])->call($this, $params[1] ?? $this);
|
||||
}
|
||||
|
||||
return $this->extendableCall($name, $params);
|
||||
}
|
||||
|
||||
public static function __callStatic($name, $params)
|
||||
{
|
||||
if ($name === 'extend') {
|
||||
if (empty($params[0])) {
|
||||
throw new \InvalidArgumentException('The extend() method requires a callback parameter or closure.');
|
||||
}
|
||||
self::extendableExtendCallback($params[0], $params[1] ?? false, $params[2] ?? null);
|
||||
return;
|
||||
}
|
||||
|
||||
return self::extendableCallStatic($name, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the navigation context based on the current action & parameters
|
||||
*/
|
||||
protected function setNavigationContext(?string $action = null, array $params = []): void
|
||||
{
|
||||
$context = BackendMenu::getContext();
|
||||
|
||||
// @TODO: Support detecting module controllers as well
|
||||
$currentClass = explode('\\', get_class($this));
|
||||
$author = $currentClass[0];
|
||||
$plugin = $currentClass[1];
|
||||
$controller = $currentClass[count($currentClass) - 1];
|
||||
|
||||
$owner = $context->owner ?? "$author.$plugin";
|
||||
$mainMenuCode = $context->mainMenuCode ?? strtolower($plugin);
|
||||
$sideMenuCode = $context->sideMenuCode ?? strtolower($controller);
|
||||
|
||||
BackendMenu::setContext($owner, $mainMenuCode, $sideMenuCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the controller action.
|
||||
* @param string $action The action name.
|
||||
* @param array $params Routing parameters to pass to the action.
|
||||
* @return mixed The action result.
|
||||
*/
|
||||
public function run($action = null, $params = [])
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->params = $params;
|
||||
|
||||
/*
|
||||
* Short circuit requests without a valid CSRF token
|
||||
* @see \System\Traits\SecurityController
|
||||
*/
|
||||
if (!in_array(Request::method(), ['HEAD', 'GET', 'OPTIONS']) && !$this->verifyCsrfToken()) {
|
||||
return Response::make(Lang::get('system::lang.page.invalid_token.label'), 403);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check forced HTTPS protocol.
|
||||
* @see \System\Traits\SecurityController
|
||||
*/
|
||||
if (!$this->verifyForceSecure()) {
|
||||
return Redirect::secure(Request::path());
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if this request is a public action.
|
||||
*/
|
||||
$isPublicAction = in_array($action, $this->publicActions);
|
||||
|
||||
/*
|
||||
* Check that user is logged in and has permission to view this page
|
||||
*/
|
||||
if (!$isPublicAction) {
|
||||
/*
|
||||
* Not logged in, redirect to login screen or show ajax error.
|
||||
*/
|
||||
if (!BackendAuth::check()) {
|
||||
return Request::ajax()
|
||||
? Response::make(Lang::get('backend::lang.page.access_denied.label'), 403)
|
||||
: Backend::redirectGuest('backend/auth');
|
||||
}
|
||||
|
||||
/*
|
||||
* Check access groups against the page definition
|
||||
*/
|
||||
if ($this->requiredPermissions && !$this->user->hasAnyAccess($this->requiredPermissions)) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @event backend.page.beforeDisplay
|
||||
* Provides an opportunity to override backend page content
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* Event::listen('backend.page.beforeDisplay', function ((\Backend\Classes\Controller) $backendController, (string) $action, (array) $params) {
|
||||
* trace_log('redirect all backend pages to google');
|
||||
* return \Redirect::to('https://google.com');
|
||||
* });
|
||||
*
|
||||
* Or
|
||||
*
|
||||
* $backendController->bindEvent('page.beforeDisplay', function ((string) $action, (array) $params) {
|
||||
* trace_log('redirect all backend pages to google');
|
||||
* return \Redirect::to('https://google.com');
|
||||
* });
|
||||
*
|
||||
*/
|
||||
if ($event = $this->fireSystemEvent('backend.page.beforeDisplay', [$action, $params])) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the admin preference locale
|
||||
*/
|
||||
BackendPreference::setAppLocale();
|
||||
BackendPreference::setAppFallbackLocale();
|
||||
|
||||
/*
|
||||
* Set the navigation context
|
||||
*/
|
||||
$this->setNavigationContext($action, $params);
|
||||
|
||||
/*
|
||||
* Execute AJAX event
|
||||
*/
|
||||
if ($ajaxResponse = $this->execAjaxHandlers()) {
|
||||
$result = $ajaxResponse;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute postback handler
|
||||
*/
|
||||
elseif (
|
||||
($handler = post('_handler')) &&
|
||||
$this->verifyCsrfToken()
|
||||
) {
|
||||
$this->validateHandlerName($handler);
|
||||
|
||||
if (
|
||||
($handlerResponse = $this->runAjaxHandler($handler)) &&
|
||||
$handlerResponse !== true
|
||||
) {
|
||||
$result = $handlerResponse;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute page action
|
||||
*/
|
||||
else {
|
||||
$result = $this->execPageAction($action, $params);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare and return response
|
||||
* @see \System\Traits\ResponseMaker
|
||||
*/
|
||||
return $this->makeResponse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used internally.
|
||||
* Determines whether an action with the specified name exists.
|
||||
* Action must be a class public method. Action name can not be prefixed with the underscore character.
|
||||
* @param string $name Specifies the action name.
|
||||
* @param bool $internal Allow protected actions.
|
||||
* @return boolean
|
||||
*/
|
||||
public function actionExists($name, $internal = false)
|
||||
{
|
||||
if (!strlen($name) || substr($name, 0, 1) == '_' || !$this->methodExists($name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->hiddenActions as $method) {
|
||||
if (strtolower($name) == strtolower($method)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$ownMethod = method_exists($this, $name);
|
||||
|
||||
if ($ownMethod) {
|
||||
$methodInfo = new \ReflectionMethod($this, $name);
|
||||
|
||||
/*
|
||||
* Only allow lowercase actions. Compare the resolved method name rather than the
|
||||
* requested one - PHP method names are case-insensitive, so a lowercased URL
|
||||
* segment would otherwise pass this check and still resolve to the mixed-case
|
||||
* method (eg. "index_onemptylog" reaching index_onEmptyLog()).
|
||||
*/
|
||||
if (strtolower($methodInfo->getName()) !== $methodInfo->getName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$public = $methodInfo->isPublic();
|
||||
if ($public) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Extension methods are resolved through a case-sensitive lookup, so the requested
|
||||
* name is already the canonical one.
|
||||
*/
|
||||
elseif (strtolower($name) !== $name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($internal && (($ownMethod && $methodInfo->isProtected()) || !$ownMethod)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$ownMethod) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a URL for this controller and supplied action.
|
||||
*/
|
||||
public function actionUrl($action = null, $path = null)
|
||||
{
|
||||
if ($action === null) {
|
||||
$action = $this->action;
|
||||
}
|
||||
|
||||
$class = get_called_class();
|
||||
$uriPath = dirname(dirname(strtolower(str_replace('\\', '/', $class))));
|
||||
$controllerName = strtolower(class_basename($class));
|
||||
|
||||
$url = $uriPath.'/'.$controllerName.'/'.$action;
|
||||
if ($path) {
|
||||
$url .= '/'.$path;
|
||||
}
|
||||
|
||||
return Backend::url($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the current controller action without rendering a view,
|
||||
* used by AJAX handler that may rely on the logic inside the action.
|
||||
*/
|
||||
public function pageAction()
|
||||
{
|
||||
if (!$this->action) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->suppressView = true;
|
||||
$this->execPageAction($this->action, $this->params);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used internally.
|
||||
* Invokes the controller action and loads the corresponding view.
|
||||
* @param string $actionName Specifies a action name to execute.
|
||||
* @param array $parameters A list of the action parameters.
|
||||
*/
|
||||
protected function execPageAction($actionName, $parameters)
|
||||
{
|
||||
$result = null;
|
||||
|
||||
if (!$this->actionExists($actionName)) {
|
||||
if (Config::get('app.debug', false)) {
|
||||
throw new SystemException(sprintf(
|
||||
"Action %s is not found in the controller %s",
|
||||
$actionName,
|
||||
get_class($this)
|
||||
));
|
||||
} else {
|
||||
Response::make(View::make('backend::404'), 404);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the action
|
||||
$result = call_user_func_array([$this, $actionName], $parameters);
|
||||
|
||||
// Expecting \Response and \RedirectResponse
|
||||
if ($result instanceof \Symfony\Component\HttpFoundation\Response) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// No page title
|
||||
if (!$this->pageTitle) {
|
||||
$this->pageTitle = 'backend::lang.page.untitled';
|
||||
}
|
||||
|
||||
// Load the view
|
||||
if (!$this->suppressView && $result === null) {
|
||||
return $this->makeView($actionName);
|
||||
}
|
||||
|
||||
return $this->makeViewContent((string) $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AJAX handler for the current request, if available.
|
||||
* @return string
|
||||
*/
|
||||
public function getAjaxHandler()
|
||||
{
|
||||
if (!Request::ajax() || Request::method() != 'POST') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($handler = Request::header('X_WINTER_REQUEST_HANDLER')) {
|
||||
return trim($handler);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the AJAX handler name follows the expected format.
|
||||
*
|
||||
* @throws \Winter\Storm\Exception\SystemException if the handler name is invalid
|
||||
*/
|
||||
protected function validateHandlerName(string $handler): void
|
||||
{
|
||||
if (!preg_match('/^(?:\w+\:{2})?on[A-Z]{1}[\w+]*$/', $handler)) {
|
||||
throw new SystemException(Lang::get('backend::lang.ajax_handler.invalid_name', ['name' => $handler]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used internally.
|
||||
* Invokes a controller event handler and loads the supplied partials.
|
||||
*/
|
||||
protected function execAjaxHandlers()
|
||||
{
|
||||
if ($handler = $this->getAjaxHandler()) {
|
||||
try {
|
||||
/*
|
||||
* Validate the handler name
|
||||
*/
|
||||
$this->validateHandlerName($handler);
|
||||
|
||||
/*
|
||||
* Validate the handler partial list
|
||||
*/
|
||||
if ($partialList = trim(Request::header('X_WINTER_REQUEST_PARTIALS'))) {
|
||||
$partialList = explode('&', $partialList);
|
||||
|
||||
foreach ($partialList as $partial) {
|
||||
if (!preg_match('/^(?!.*\/\/)[a-z0-9\_][a-z0-9\_\-\/]*$/i', $partial)) {
|
||||
throw new SystemException(Lang::get('backend::lang.partial.invalid_name', ['name'=>$partial]));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$partialList = [];
|
||||
}
|
||||
|
||||
$responseContents = [];
|
||||
|
||||
/*
|
||||
* Execute the handler
|
||||
*/
|
||||
if (!$result = $this->runAjaxHandler($handler)) {
|
||||
throw new SystemException(Lang::get('backend::lang.ajax_handler.not_found', ['name'=>$handler]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Render partials and return the response as array that will be converted to JSON automatically.
|
||||
*/
|
||||
foreach ($partialList as $partial) {
|
||||
$responseContents[$partial] = $this->makePartial($partial);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the handler returned a redirect, process the URL and dispose of it so
|
||||
* framework.js knows to redirect the browser and not the request!
|
||||
*/
|
||||
if ($result instanceof RedirectResponse) {
|
||||
$responseContents['X_WINTER_REDIRECT'] = $result->getTargetUrl();
|
||||
$result = null;
|
||||
}
|
||||
/*
|
||||
* No redirect is used, look for any flash messages
|
||||
*/
|
||||
elseif (Flash::check()) {
|
||||
$responseContents['#layout-flash-messages'] = $this->makeLayoutPartial('flash_messages');
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect assets
|
||||
*/
|
||||
if ($this->hasAssetsDefined()) {
|
||||
$responseContents['X_WINTER_ASSETS'] = $this->getAssetPaths();
|
||||
}
|
||||
|
||||
/*
|
||||
* If the handler returned an array, we should add it to output for rendering.
|
||||
* If it is a string, add it to the array with the key "result".
|
||||
* If an object, pass it to Laravel as a response object.
|
||||
*/
|
||||
if (is_array($result)) {
|
||||
$responseContents = array_merge($responseContents, $result);
|
||||
}
|
||||
elseif (is_string($result)) {
|
||||
$responseContents['result'] = $result;
|
||||
}
|
||||
elseif (is_object($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return Response::make()->setContent($responseContents);
|
||||
}
|
||||
catch (ValidationException $ex) {
|
||||
/*
|
||||
* Handle validation error gracefully
|
||||
*/
|
||||
Flash::error($ex->getMessage());
|
||||
$responseContents = [];
|
||||
$responseContents['#layout-flash-messages'] = $this->makeLayoutPartial('flash_messages');
|
||||
$responseContents['X_WINTER_ERROR_FIELDS'] = $ex->getFields();
|
||||
throw new AjaxException($responseContents);
|
||||
}
|
||||
catch (MassAssignmentException $ex) {
|
||||
throw new ApplicationException(Lang::get('backend::lang.model.mass_assignment_failed', ['attribute' => $ex->getMessage()]));
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find and run an AJAX handler in the page action.
|
||||
* The method stops as soon as the handler is found.
|
||||
* @return boolean Returns true if the handler was found. Returns false otherwise.
|
||||
*/
|
||||
protected function runAjaxHandler($handler)
|
||||
{
|
||||
/**
|
||||
* @event backend.ajax.beforeRunHandler
|
||||
* Provides an opportunity to modify an AJAX request
|
||||
*
|
||||
* The parameter provided is `$handler` (the requested AJAX handler to be run)
|
||||
*
|
||||
* Example usage (forwards AJAX handlers to a backend widget):
|
||||
*
|
||||
* Event::listen('backend.ajax.beforeRunHandler', function ((\Backend\Classes\Controller) $controller, (string) $handler) {
|
||||
* if (strpos($handler, '::')) {
|
||||
* list($componentAlias, $handlerName) = explode('::', $handler);
|
||||
* if ($componentAlias === $this->getBackendWidgetAlias()) {
|
||||
* return $this->backendControllerProxy->runAjaxHandler($handler);
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* Or
|
||||
*
|
||||
* $this->controller->bindEvent('ajax.beforeRunHandler', function ((string) $handler) {
|
||||
* if (strpos($handler, '::')) {
|
||||
* list($componentAlias, $handlerName) = explode('::', $handler);
|
||||
* if ($componentAlias === $this->getBackendWidgetAlias()) {
|
||||
* return $this->backendControllerProxy->runAjaxHandler($handler);
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
*/
|
||||
if ($event = $this->fireSystemEvent('backend.ajax.beforeRunHandler', [$handler])) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process Widget handler
|
||||
*/
|
||||
if (strpos($handler, '::')) {
|
||||
list($widgetName, $handlerName) = explode('::', $handler);
|
||||
|
||||
/*
|
||||
* Execute the page action so widgets are initialized
|
||||
*/
|
||||
$this->pageAction();
|
||||
|
||||
if ($this->fatalError) {
|
||||
throw new SystemException($this->fatalError);
|
||||
}
|
||||
|
||||
if (!isset($this->widget->{$widgetName})) {
|
||||
throw new SystemException(Lang::get('backend::lang.widget.not_bound', ['name'=>$widgetName]));
|
||||
}
|
||||
|
||||
if (($widget = $this->widget->{$widgetName}) && $widget->methodExists($handlerName)) {
|
||||
$result = $this->runAjaxHandlerForWidget($widget, $handlerName);
|
||||
return $result ?: true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Process page specific handler (index_onSomething)
|
||||
*/
|
||||
$pageHandler = $this->action . '_' . $handler;
|
||||
|
||||
if ($this->methodExists($pageHandler)) {
|
||||
$result = call_user_func_array([$this, $pageHandler], array_values($this->params));
|
||||
return $result ?: true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process page global handler (onSomething)
|
||||
*/
|
||||
if ($this->methodExists($handler)) {
|
||||
$result = call_user_func_array([$this, $handler], array_values($this->params));
|
||||
return $result ?: true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cycle each widget to locate a usable handler (widget::onSomething)
|
||||
*/
|
||||
$this->suppressView = true;
|
||||
$this->execPageAction($this->action, $this->params);
|
||||
|
||||
foreach ((array) $this->widget as $widget) {
|
||||
if ($widget->methodExists($handler)) {
|
||||
$result = $this->runAjaxHandlerForWidget($widget, $handler);
|
||||
return $result ?: true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic handler that does nothing
|
||||
*/
|
||||
if ($handler == 'onAjax') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific code for executing an AJAX handler for a widget.
|
||||
* This will append the widget view paths to the controller and merge the vars.
|
||||
* @return mixed
|
||||
*/
|
||||
protected function runAjaxHandlerForWidget($widget, $handler)
|
||||
{
|
||||
$this->prependViewPath($widget->getViewPaths());
|
||||
|
||||
$result = call_user_func_array([$widget, $handler], array_values($this->params));
|
||||
|
||||
$this->vars = $widget->vars + $this->vars;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the controllers public actions.
|
||||
*/
|
||||
public function getPublicActions()
|
||||
{
|
||||
return $this->publicActions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique ID for the controller and route. Useful in creating HTML markup.
|
||||
*/
|
||||
public function getId($suffix = null)
|
||||
{
|
||||
$id = class_basename(get_called_class()) . '-' . $this->action;
|
||||
if ($suffix !== null) {
|
||||
$id .= '-' . $suffix;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
//
|
||||
// Hints
|
||||
//
|
||||
|
||||
/**
|
||||
* Renders a hint partial, used for displaying informative information that
|
||||
* can be hidden by the user. If you don't want to render a partial, you can
|
||||
* supply content via the 'content' key of $params.
|
||||
* @param string $name Unique key name
|
||||
* @param string $partial Reference to content (partial name)
|
||||
* @param array $params Extra parameters
|
||||
* @return string
|
||||
*/
|
||||
public function makeHintPartial($name, $partial = null, $params = [])
|
||||
{
|
||||
if (is_array($partial)) {
|
||||
$params = $partial;
|
||||
$partial = null;
|
||||
}
|
||||
|
||||
if (!$partial) {
|
||||
$partial = array_get($params, 'partial', $name);
|
||||
}
|
||||
|
||||
return $this->makeLayoutPartial('hint', [
|
||||
'hintName' => $name,
|
||||
'hintPartial' => $partial,
|
||||
'hintContent' => array_get($params, 'content'),
|
||||
'hintParams' => $params
|
||||
] + $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax handler to hide a backend hint, once hidden the partial
|
||||
* will no longer display for the user.
|
||||
* @return void
|
||||
*/
|
||||
public function onHideBackendHint()
|
||||
{
|
||||
if (!$name = post('name')) {
|
||||
throw new ApplicationException('Missing a hint name.');
|
||||
}
|
||||
|
||||
$preferences = UserPreference::forUser();
|
||||
$hiddenHints = $preferences->get('backend::hints.hidden', []);
|
||||
$hiddenHints[$name] = 1;
|
||||
|
||||
$preferences->set('backend::hints.hidden', $hiddenHints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a hint has been hidden by the user.
|
||||
* @param string $name Unique key name
|
||||
* @return boolean
|
||||
*/
|
||||
public function isBackendHintHidden($name)
|
||||
{
|
||||
$hiddenHints = UserPreference::forUser()->get('backend::hints.hidden', []);
|
||||
return array_key_exists($name, $hiddenHints);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user