htools-8.x-1.x-dev/modules/htools_entity_views_filter/src/Form/EntityViewsFilterForm.php
modules/htools_entity_views_filter/src/Form/EntityViewsFilterForm.php
<?php
namespace Drupal\htools_entity_views_filter\Form;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Form\FormBuilder;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Url;
use Drupal\htools_entity_views_filter\Entity\EntityViewsFilterType;
use Drupal\views\Ajax\ScrollTopCommand;
use Drupal\views\Ajax\ViewAjaxResponse;
use Drupal\views\Entity\View;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Form controller for Entity views filter edit forms.
*
* @ingroup entity_views_filter
*/
class EntityViewsFilterForm extends ContentEntityForm {
/**
* The current user account.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $account;
/**
* The factory to load a view executable with.
*
* @var \Drupal\views\ViewExecutableFactory
*/
protected $executableFactory;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* True when the build of the form is in the refresh method.
*
* @var bool
*/
protected $ajaxRefreshing;
/**
* The form builder.
*
* @var \Drupal\Core\Form\FormBuilderInterface
*/
protected $formBuilder;
/**
* The current path.
*
* @var \Drupal\Core\Path\CurrentPathStack
*/
protected $currentPath;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
// Instantiates this form class.
$instance = parent::create($container);
$instance->account = $container->get('current_user');
$instance->executableFactory = $container->get('views.executable');
$instance->renderer = $container->get('renderer');
$instance->ajaxRefreshing = FALSE;
$instance->formBuilder = $container->get('form_builder');
$instance->currentPath = $container->get('path.current');
$instance->requestStack = $container->get('request_stack');
return $instance;
}
/**
* {@inheritdoc}
*/
public function getFormId() {
$form_id = parent::getFormId();
if ($this->ajaxRefreshing === FALSE) {
return $form_id;
}
return $form_id . '_form_ajax_refreshing';
}
/**
*
*/
public function setAjaxRefreshing($state) {
$this->ajaxRefreshing = $state;
}
/**
*
*/
public function getAjaxRefreshing() {
return $this->ajaxRefreshing;
}
/**
* {@inheritdoc}
*/
public function processForm($element, FormStateInterface $form_state, $form) {
$element = parent::processForm($element, $form_state, $form);
if ($this->ajaxRefreshing === FALSE) {
return $element;
}
$form_id = parent::getFormId();
$element['#form_id'] = $form_id;
unset($element['#id']);
$this->formBuilder->prepareForm($form_id, $element, $form_state);
return $element;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Remove button and internal Form API values from submitted values.
$form_state->cleanValues();
$this->entity = $this->buildEntity($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
$form['#action'] = $this->buildFormAction();
return $form;
}
/**
* Builds the $form['#action'].
*
* @return string
* The URL to be used as the $form['#action'].
*/
protected function buildFormAction() {
// @todo Use <current> instead of the master request in
// https://www.drupal.org/node/2505339.
$request = $this->requestStack->getMainRequest();
$request_uri = $request->getRequestUri();
// Prevent cross site requests via the Form API by using an absolute URL
// when the request uri starts with multiple slashes..
if (strpos($request_uri, '//') === 0) {
$request_uri = $request->getUri();
}
// @todo Remove this parsing once these are removed from the request in
// https://www.drupal.org/node/2504709.
$parsed = UrlHelper::parse($request_uri);
unset($parsed['query'][FormBuilder::AJAX_FORM_REQUEST], $parsed['query'][MainContentViewSubscriber::WRAPPER_FORMAT]);
return $parsed['path'];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$bundle = EntityViewsFilterType::load($this->entity->bundle());
if ($bundle === NULL) {
return parent::buildForm($form, $form_state);
}
/* @var \Drupal\htools_entity_views_filter\Entity\EntityViewsFilter $entity */
$form = parent::buildForm($form, $form_state);
if (!empty($bundle->get('autosubmit_exclude_textfield'))) {
foreach (Element::children($form) as $element) {
if (isset($form[$element]['#type']) && $form[$element]['#type'] === 'textfield') {
$form[$element]['#attributes'] = ['data-evf-auto-submit-exclude' => ''];
}
}
}
// Apply auto-submit values.
if (!empty($bundle->get('autosubmit'))) {
$form['#attached']['library'][] = 'htools_entity_views_filter/autosubmit';
$form = array_merge_recursive($form, [
'#attributes' => [
'data-evf-auto-submit-full-form' => '',
'data-evf-auto-submit' => '',
],
]);
}
return $form;
}
/**
* {@inheritdoc}
*/
public function actions(array $form, FormStateInterface $form_state) {
$actions = parent::actions($form, $form_state);
$bundle = EntityViewsFilterType::load($this->entity->bundle());
if ($bundle === NULL) {
return $actions;
}
$submit_button = $bundle->get('submit_button') ? $bundle->get('submit_button') : 'Search';
$actions['submit']['#value'] = $this->t($submit_button);
// Apply auto-submit values.
if (!empty($bundle->get('autosubmit'))) {
$actions['submit']['#attributes']['data-evf-auto-submit-click'] = '';
if (!empty($bundle->get('autosubmit_hide'))) {
$actions['submit']['#attributes']['class'][] = 'js-hide';
}
}
// Apply ajax-submit values.
$view = $form_state->get('view');
if ($view instanceof ViewExecutable && !empty($view->display_handler) && $view->display_handler->ajaxEnabled() === TRUE) {
$actions['submit']['#ajax'] = [
'callback' => [$this, 'refresh'],
'submit' => ['view_dom_id' => $view->dom_id],
];
}
return $actions;
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$view_instance = $form_state->get('view');
$entity = $this->entity;
$status = parent::save($form, $form_state);
$bundle = EntityViewsFilterType::load($this->entity->bundle());
$view_name = $bundle->get('view_name');
$view_display_id = $bundle->get('view_display_id');
/** @var \Drupal\views\ViewExecutable $view */
$view = Views::getView($view_name);
$view->build($view_display_id);
$filters = $view->getHandlers('filter');
$view_url = $view->getPath();
$field_mapping = $bundle->get('field_mapping');
$querystring = [];
foreach ($filters as $filter_id => $filter) {
if ($filter['exposed'] === TRUE) {
$field_name = $field_mapping[$filter_id]['field_name'];
if ($entity->get($field_name)->isEmpty()) {
continue;
}
$field_property = $field_mapping[$filter_id]['field_property'];
$values = $entity->get($field_name)->getValue();
$identifier = $filter['expose']['identifier'];
if (!empty($values)) {
if ($values[0][$field_property] == '0') {
$querystring[$identifier] = 'All';
}
elseif ($filter['expose']['multiple'] == 1) {
if (count($values) > 1) {
foreach ($values as $rawvalue) {
$value = $rawvalue[$field_property];
$querystring[$identifier][$value] = $value;
}
}
else {
$value = $values[0][$field_property];
$querystring[$identifier][$value] = $value;
}
}
else {
$value = $values[0][$field_property];
$querystring[$identifier] = $value;
}
}
elseif (isset($querystring)) {
unset($querystring[$identifier]);
}
}
}
$sorts = ['sort_by', 'sort_order', 'sort_bef_combine'];
foreach ($sorts as $sort_id) {
if (!empty($field_mapping[$sort_id]['field_name'])) {
$field_name = $field_mapping[$sort_id]['field_name'];
$field_property = $field_mapping[$sort_id]['field_property'];
$values = $entity->get($field_name)->getValue();
$querystring[$field_name] = !empty($values[0][$field_property]) ? $values[0][$field_property] : '';
if ($view_instance instanceof ViewExecutable && !empty($view_instance->display_handler) && $view_instance->display_handler->ajaxEnabled() === FALSE && $entity->get($field_name)
->isEmpty()) {
unset($querystring[$field_name]);
}
}
}
if ($view_instance instanceof ViewExecutable && !empty($view_instance->display_handler) && $view_instance->display_handler->ajaxEnabled() === TRUE) {
$form_state->set('exposed_values', $querystring);
return $status;
}
$redirect_path = '/' . $view_url . '?' . http_build_query($querystring);
$url = Url::fromUserInput($redirect_path);
$form_state->setRedirectUrl($url);
return $status;
}
/**
* Ajax callback to refresh views.
*
* @param array $form
* The form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* The response.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function refresh(array $form, FormStateInterface $form_state) {
$bundle = EntityViewsFilterType::load($this->entity->bundle());
/** @var \Drupal\views\ViewExecutable $view_instance */
$view_instance = $form_state->get('view');
/** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache_plugin */
$cache_plugin = $view_instance->display_handler->getPlugin('cache');
$cache_plugin->getCacheBackend()
->invalidate($cache_plugin->generateResultsKey());
$exposed_values = $form_state->get('exposed_values');
$inputs = $form_state->getUserInput();
$dom_id = $inputs['view_dom_id'];
$request = $this->requestStack->getMasterRequest();
$keys = [
'view_name',
'view_display_id',
'view_args',
'view_path',
'view_dom_id',
'pager_element',
'view_base_path',
AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER,
FormBuilderInterface::AJAX_FORM_REQUEST,
MainContentViewSubscriber::WRAPPER_FORMAT,
];
foreach ($keys as $key) {
$request->query->remove($key);
$request->request->remove($key);
}
// Add all POST data, because AJAX is always a post and many things,
// such as tablesorts, exposed filters and paging assume GET.
$query_all = $request->query->all();
foreach ($exposed_values as $k => $v) {
if (!empty($v)) {
$query_all[$k] = $v;
}
elseif (isset($query_all[$k])) {
unset($query_all[$k]);
}
}
$request->query->replace($query_all);
$response = new ViewAjaxResponse();
$name = $bundle->get('view_name');
$display_id = $bundle->get('view_display_id');
// Load the view.
if (!$entity = View::load($name)) {
throw new NotFoundHttpException();
}
$view = $this->executableFactory->get($entity);
if ($view && $view->access($display_id) && $view->setDisplay($display_id) && $view->display_handler->ajaxEnabled()) {
$view->ajaxRefreshing = TRUE;
$args = $view_instance->args;
$view->setArguments($args);
$response->setView($view);
// $preview = $view->buildRenderable($display_id, $args);.
$selector = '.js-view-dom-id-' . $dom_id;
$context = new RenderContext();
$preview = $this->renderer->executeInRenderContext($context, function () use ($view, $display_id, $args) {
return $view->preview($display_id, $args);
});
if (!$context->isEmpty()) {
$bubbleable_metadata = $context->pop();
BubbleableMetadata::createFromRenderArray($preview)
->merge($bubbleable_metadata)
->applyTo($preview);
}
$response->addCommand(new ReplaceCommand($selector, $preview));
$response->addCommand(new ScrollTopCommand($selector));
return $response;
}
throw new AccessDeniedHttpException();
}
}
