htools-8.x-1.x-dev/src/Plugin/views/display/BetterEmbed.php
src/Plugin/views/display/BetterEmbed.php
<?php
namespace Drupal\htools\Plugin\views\display;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\path_alias\AliasManagerInterface;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\Url;
use Drupal\views\Plugin\Block\ViewsBlock;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* The plugin that handles an embed display.
*
* @ingroup views_display_plugins
*
* @todo: Wait until annotations/plugins support access methods.
* no_ui => !\Drupal::config('views.settings')->get('ui.show.display_embed'),
*
* @ViewsDisplay(
* id = "better_embed",
* title = @Translation("Embed (Better)"),
* help = @Translation("Provide a display which can be embedded using the views api."),
* theme = "views_view",
* uses_hook_block = TRUE,
* uses_menu_links = FALSE
* )
*/
class BetterEmbed extends DisplayPluginBase {
/**
* Current path.
*
* @var \Drupal\Core\Path\CurrentPathStack
*/
protected $currentPath;
/**
* PrismaEmbed::aliasManager.
*
* @var \Drupal\Core\Path\AliasManagerInterface
*/
protected $aliasManager;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, CurrentPathStack $currentPath, AliasManagerInterface $aliasManager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->currentPath = $currentPath;
$this->aliasManager = $aliasManager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('path.current'),
$container->get('path_alias.manager')
);
}
/**
* {@inheritdoc}
*/
public function optionsSummary(&$categories, &$options) {
parent::optionsSummary($categories, $options);
$categories['page'] = [
'title' => $this->t('Page settings'),
'column' => 'second',
'build' => [
'#weight' => -10,
],
];
if ($this->getOption('path') === TRUE) {
$path = '';
}
else {
$path = strip_tags($this->getOption('path'));
}
if (empty($path)) {
$path = $this->t('No path is set (Current url will be use)');
}
else {
$path = '/' . $path;
}
$options['path'] = [
'category' => 'page',
'title' => $this->t('Path'),
'value' => views_ui_truncate($path, 50),
];
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
switch ($form_state->get('section')) {
case 'path':
$url = Url::fromRoute('<none>', [], ['absolute' => TRUE]);
$form['#title'] .= $this->t('The menu path or URL of this view');
$form['path'] = [
'#type' => 'textfield',
'#title' => $this->t('Path'),
'#description' => $this->t('This view will be displayed by visiting this path on your site. You may use "%" in your URL to represent values that will be used for contextual filters: For example, "node/%/feed". If needed you can even specify named route parameters like taxonomy/term/%taxonomy_term'),
'#default_value' => $this->getOption('path'),
'#field_prefix' => '<span dir="ltr">' . $url->toString(),
'#field_suffix' => '</span>‎',
'#attributes' => ['dir' => LanguageInterface::DIRECTION_LTR],
// Account for the leading backslash.
'#maxlength' => 254,
];
break;
}
}
/**
* {@inheritdoc}
*/
public function validateOptionsForm(&$form, FormStateInterface $form_state) {
parent::validateOptionsForm($form, $form_state);
if ($form_state->get('section') == 'path') {
$errors = $this->validatePath($form_state->getValue('path'));
foreach ($errors as $error) {
$form_state->setError($form['path'], $error);
}
// Automatically remove '/' and trailing whitespace from path.
$form_state->setValue('path', trim($form_state->getValue('path'), '/ '));
}
}
/**
* Validates the path of the display.
*
* @param string $path
* The path to validate.
*
* @return array
* A list of error strings.
*/
protected function validatePath($path) {
$errors = [];
if (strpos($path, '%') === 0) {
$errors[] = $this->t('"%" may not be used for the first segment of a path.');
}
$parsed_url = UrlHelper::parse($path);
if (!empty($parsed_url['query'])) {
$errors[] = $this->t('No query allowed.');
}
if (!parse_url('internal:/' . $path)) {
$errors[] = $this->t('Invalid path. Valid characters are alphanumerics as well as "-", ".", "_" and "~".');
}
$path_sections = explode('/', $path);
// Symfony routing does not allow to use numeric placeholders.
// @see \Symfony\Component\Routing\RouteCompiler
$numeric_placeholders = array_filter($path_sections, function ($section) {
return (preg_match('/^%(.*)/', $section, $matches)
&& is_numeric($matches[1]));
});
if (!empty($numeric_placeholders)) {
$errors[] = $this->t("Numeric placeholders may not be used. Please use plain placeholders (%).");
}
return $errors;
}
/**
* {@inheritdoc}
*/
public function submitOptionsForm(&$form, FormStateInterface $form_state) {
parent::submitOptionsForm($form, $form_state);
if ($form_state->get('section') == 'path') {
$this->setOption('path', $form_state->getValue('path'));
}
}
/**
* {@inheritdoc}
*/
public function buildRenderable(array $args = [], $cache = TRUE) {
$build = parent::buildRenderable($args, $cache);
$build['#embed'] = TRUE;
return $build;
}
/**
* Block views use exposed widgets only if AJAX is set.
*/
// public function usesExposed() {
// return TRUE;
// }
/**
* {@inheritdoc}
*/
public function getPath() {
$path = $this->getOption('path');
if (empty($path)) {
$useAjax = $this->usesAJAX();
if ($useAjax) {
$request = $this->view->getRequest();
$params = $request->request->all();
// @TODO Read POST parameters from request object.
if (!empty($params['ajax_page_state']) &&
!empty($_POST['view_base_path'])) {
$path = $_POST['view_base_path'];
}
}
}
if (empty($path)) {
$current_path = $this->currentPath->getPath();
$path = $this->aliasManager->getAliasByPath($current_path);
}
$bits = explode('/', $path);
if (empty($bits[0])) {
unset($bits[0]);
}
return implode('/', $bits);
}
/**
* {@inheritdoc}
*/
public function usesExposedFormInBlock() {
return TRUE;
}
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['path'] = ['default' => ''];
return $options;
}
/**
* {@inheritdoc}
*/
public function execute() {
// Prior to this being called, the $view should already be set to this
// display, and arguments should be set on the view.
$this->view->build();
if (!empty($this->view->build_info['fail'])) {
throw new NotFoundHttpException();
}
if (!empty($this->view->build_info['denied'])) {
throw new AccessDeniedHttpException();
}
// And now render the view.
$render = $this->view->render();
return $render;
}
/**
* Returns plugin-specific settings for the block.
*
* @param array $settings
* The settings of the block.
*
* @return array
* An array of block-specific settings to override the defaults provided in
* \Drupal\views\Plugin\Block\ViewsBlock::defaultConfiguration().
*
* @see \Drupal\views\Plugin\Block\ViewsBlock::defaultConfiguration()
*/
public function blockSettings(array $settings) {
$settings['items_per_page'] = 'none';
return $settings;
}
/**
* Adds the configuration form elements specific to this views block plugin.
*
* This method allows block instances to override the views items_per_page.
*
* @param \Drupal\views\Plugin\Block\ViewsBlock $block
* The ViewsBlock plugin.
* @param array $form
* The form definition array for the block configuration form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* The renderable form array representing the entire configuration form.
*
* @see \Drupal\views\Plugin\Block\ViewsBlock::blockForm()
*/
public function blockForm(ViewsBlock $block, array &$form, FormStateInterface $form_state) {
if (!empty ($this->getOption('allow'))) {
$allow_settings = array_filter($this->getOption('allow'));
$block_configuration = $block->getConfiguration();
foreach ($allow_settings as $type => $enabled) {
if (empty($enabled)) {
continue;
}
switch ($type) {
case 'items_per_page':
$form['override']['items_per_page'] = [
'#type' => 'select',
'#title' => $this->t('Items per block'),
'#options' => [
'none' => $this->t('@count (default setting)', ['@count' => $this->getPlugin('pager')->getItemsPerPage()]),
5 => 5,
10 => 10,
20 => 20,
40 => 40,
],
'#default_value' => $block_configuration['items_per_page'],
];
break;
}
}
}
return $form;
}
/**
* Handles form validation for the views block configuration form.
*
* @param \Drupal\views\Plugin\Block\ViewsBlock $block
* The ViewsBlock plugin.
* @param array $form
* The form definition array for the block configuration form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @see \Drupal\views\Plugin\Block\ViewsBlock::blockValidate()
*/
public function blockValidate(ViewsBlock $block, array $form, FormStateInterface $form_state) {
}
/**
* Handles form submission for the views block configuration form.
*
* @param \Drupal\views\Plugin\Block\ViewsBlock $block
* The ViewsBlock plugin.
* @param array $form
* The form definition array for the full block configuration form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @see \Drupal\views\Plugin\Block\ViewsBlock::blockSubmit()
*/
public function blockSubmit(ViewsBlock $block, $form, FormStateInterface $form_state) {
if ($items_per_page = $form_state->getValue(['override', 'items_per_page'])) {
$block->setConfigurationValue('items_per_page', $items_per_page);
}
$form_state->unsetValue(['override', 'items_per_page']);
}
/**
* Allows to change the display settings right before executing the block.
*
* @param \Drupal\views\Plugin\Block\ViewsBlock $block
* The block plugin for views displays.
*/
public function preBlockBuild(ViewsBlock $block) {
$config = $block->getConfiguration();
if ($config['items_per_page'] !== 'none') {
$this->view->setItemsPerPage($config['items_per_page']);
}
}
}
