Skip to content
Snippets Groups Projects

Feature/istwcms 6358 ebremner waterloo news block

Merged Eric Bremner requested to merge feature/ISTWCMS-6358-ebremner-waterloo-news-block into 1.1.x
Compare and
3 files
+ 642
0
Compare changes
  • Side-by-side
  • Inline
Files
3
+ 619
0
<?php
namespace Drupal\uw_custom_blocks\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\uw_cfg_common\Service\UwService;
use GuzzleHttp\ClientInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* UW Waterloo News block.
*
* @Block(
* id = "uw_cbl_waterloo_news",
* admin_label = @Translation("Waterloo News"),
* )
*/
class UwCblWaterlooNews extends BlockBase implements ContainerFactoryPluginInterface {
// The url to the news site.
const NEWSURL = 'https://uwaterloo.ca/news';
// The url to the news api.
const NEWSURLAPI = self::NEWSURL . '/api/v1.0/news';
// The url to the news api.
const NEWSURLALIAS = self::NEWSURL . '/api/v1.0/alias';
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* An http client.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $httpClient;
/**
* UW node content.
*
* @var \Drupal\uw_cfg_common\Service\UwService
*/
protected $uwService;
/**
* {@inheritdoc}
*/
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition
) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('http_client'),
$container->get('uw_cfg_common.uw_service')
);
}
/**
* Constructs a BlockComponentRenderArray object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
* @param \GuzzleHttp\ClientInterface $httpClient
* An HTTP client.
* @param \Drupal\uw_cfg_common\Service\UwService $uwService
* UW Service.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
EntityTypeManagerInterface $entityTypeManager,
ClientInterface $httpClient,
UwService $uwService
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entityTypeManager;
$this->httpClient = $httpClient;
$this->uwService = $uwService;
}
/**
* {@inheritdoc}
*/
public function build() {
// Get the uw news from the block config.
$values = $this->configuration['uwnews'];
// Get the url for the featured news item.
$url = self::NEWSURLAPI . '?filter[id]=' . $values['featured']['id'];
// Set the featured news item.
$uwnews['featured'] = $this->getNewsInfoFromApi($url, TRUE);
// Get id for multiple featured news items/images on a page.
$uwnews['featured']['id'] = $values['featured']['id'];
// If this is a custom image, then get the info from the
// media library.
if ($values['featured']['use_custom_image']) {
// Load the media entity from the image selected.
$media = $this->entityTypeManager
->getStorage('media')
->load($values['featured']['custom_image']);
// Set the values for the image.
$uwnews['featured']['image'] = [
'alt' => $media->field_media_image->alt,
'type' => 'custom',
'sources' => $this->uwService->prepareResponsiveImage($media, 'uw_ris_media'),
];
// Set img_fallback for backup from the xl in the responsive array.
$uwnews['featured']['image']['img_fallback'] = $uwnews['featured']['image']['sources']['responsive_sources'][0]['srcset'];
}
// Step through each of the items and get the info from the api.
foreach ($values['items'] as $item) {
// Only get the info from the api if there is a url.
if ($item['url']) {
// Get the url for the news item.
$url = self::NEWSURLAPI . '?filter[id]=' . $item['id'];
// Set the news item.
$uwnews['items'][] = $this->getNewsInfoFromApi($url, FALSE);
}
}
// Get the show all and position value.
$uwnews['show_all'] = $values['show_all'];
$uwnews['position'] = $values['position'];
// Use news url and text for button.
$uwnews['show_all_url'] = self::NEWSURL;
$uwnews['show_all_text'] = $this->t('View all Waterloo News');
// Return the render array.
return [
'#theme' => 'uw_block_waterloo_news',
'#uwnews' => $uwnews,
];
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
// Get the block config.
$uwnews = $this->configuration['uwnews'] ?? [];
// Adding the ability to do ajax forms.
$form['#tree'] = TRUE;
// The featured news item details element.
$form['featured'] = [
'#type' => 'details',
'#title' => $this->t('Feature item'),
'#open' => TRUE,
'#summary_attributes' => [
'class' => ['form-required'],
],
];
// The URL to the featured news item element.
$form['featured']['url'] = [
'#type' => 'textfield',
'#title' => $this->t('Waterloo News item URL'),
'#description' => $this->t('Enter a URL starting with of https://uwaterloo.ca/news/.'),
'#default_value' => $uwnews['featured']['url'] ?? NULL,
'#label_attributes' => [
'class' => ['form-required'],
],
];
// The featured item position form element.
$form['featured']['position'] = [
'#type' => 'select',
'#title' => $this->t('Feature item position'),
'#options' => [
'left' => $this->t('Left'),
'right' => $this->t('Right'),
],
'#description' => $this->t('Select where the feature Waterloo News item will be displayed when there are additional items.'),
'#default_value' => $uwnews['position'] ?? 'left',
];
// The use custom image checkbox element.
$form['featured']['use_custom_image'] = [
'#type' => 'checkbox',
'#title' => $this->t('Custom background'),
'#description' => $this->t('Check this box if you want to use a custom background image for the feature Waterloo News item. If left unchecked, this item will use the listing page image from the source as the background.'),
'#default_value' => $uwnews['featured']['use_custom_image'] ?? NULL,
];
// The custom image element.
$form['featured']['custom_image'] = [
'#type' => 'media_library',
'#allowed_bundles' => ['uw_mt_image'],
'#title' => $this->t('Upload your image'),
'#default_value' => $uwnews['featured']['custom_image'] ?? '',
'#states' => [
'visible' => [
'input[name="settings[featured][use_custom_image]"]' => ['checked' => TRUE],
],
],
];
// The details to hold the news items.
$form['items_fieldset'] = [
'#type' => 'details',
'#title' => $this->t('Additional items'),
'#description' => $this->t('You may enter up to three additional Waterloo News items.'),
'#open' => TRUE,
];
// The class to be used for groups.
$group_class = 'group-order-weight';
// Build the table.
$form['items_fieldset']['items'] = [
'#type' => 'table',
'#header' => [
[
'data' => $this->t('<h4 class="label">Add Waterloo News items</h4>'),
'colspan' => 2,
],
// @todo Make this work properly with the remove button.
// phpcs:disable
// '',
// phpcs:enable
$this->t('Weight'),
],
'#tableselect' => FALSE,
'#tabledrag' => [
[
'action' => 'order',
'relationship' => 'sibling',
'group' => $group_class,
],
],
'#prefix' => '<div class="uw-links__table">',
'#suffix' => '</div>',
];
// Step through and add three news item.
for ($i = 0; $i < 3; $i++) {
// Reset the settings array.
$settings = [];
// Set the table to be draggable.
$settings['#attributes']['class'][] = 'draggable';
// Set the weight.
$settings['#weight'] = $i;
// The first column of the table, that will house the arrows
// for rearranging e/c groups.
$settings['arrow'] = [
'#type' => 'markup',
'#markup' => '',
'#title_display' => 'invisible',
];
// The link element.
$settings['url'] = [
'#type' => 'textfield',
'#title' => $this->t('Waterloo News item URL'),
'#description' => $this->t('Enter a URL starting with https://uwaterloo.ca/news/.'),
'#default_value' => $uwnews['items'][$i]['url'] ?? NULL,
];
// The weight element.
$settings['weight'] = [
'#type' => 'weight',
'#title_display' => 'invisible',
'#default_value' => $i,
'#attributes' => ['class' => [$group_class]],
];
// Add the settings to the items array, which is full row
// in the table.
$form['items_fieldset']['items'][] = $settings;
}
// The show all details element.
$form['options'] = [
'#type' => 'details',
'#title' => $this->t('"View all" button'),
'#open' => TRUE,
];
// Show all form element.
$form['options']['show_all'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show the "View all news" button'),
'#default_value' => $uwnews['show_all'] ?? FALSE,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function blockValidate($form, FormStateInterface $form_state) {
// Get the values from the form state.
$values = $form_state->getValues();
// If there is a use custom image selected
// and there is no media selected, throw
// an error about requiring media.
if (
$values['featured']['use_custom_image'] &&
$values['featured']['custom_image'] == NULL
) {
// Set the form error. Note that for some
// reason the setError or setErrorByName is
// not working correctly with inline form errors,
// so the error is only shown at the top.
// Displaying with the checkbox instead.
$form_state->setError(
$form['featured']['use_custom_image'],
$this->t('You must provide an image when using a custom background.')
);
}
// If there is a nid, meaning the url exists,
// then set the id, if not throw an error.
if ($nid = $this->getNewsIdFromLink($values['featured']['url'])) {
$values['featured']['id'] = $nid;
$form_state->setValue('featured', $values['featured']);
}
else {
$form_state->setError(
$form['featured']['url'],
$this->t('The URL entered is invalid.')
);
}
// Array for urls used so that we ensure that
// no two urls are the same.
$urls_used[] = $values['featured']['url'];
// Get the news items from the form state.
$items = $values['items_fieldset']['items'];
// Step through the items and validate the url
// and get the nid.
foreach ($items as $index => $item) {
// If there is a url, then continue to validate.
// If there is no url add a blank so that we
// can validate unique urls.
if ($item['url']) {
// Add url to urls used array to check later.
$urls_used[] = $item['url'];
// If there is a nid from the url, then set it in the
// form state. If not, then throw an error.
if ($nid = $this->getNewsIdFromLink($item['url'])) {
$values['items_fieldset']['items'][$index]['id'] = $nid;
}
else {
// Throw error about invalid url.
$form_state->setError(
$form['items_fieldset']['items'][$index],
$this->t('Invalid url.'),
);
// Break from the loop to save computational time.
break;
}
}
else {
// Add a blank URL so we can validate unique URLs.
$urls_used[] = '';
}
}
// Flag for unique urls.
$unique_url_flag = FALSE;
// Validate unique URLs.
// Step through each of the urls.
foreach ($urls_used as $url_used) {
if ($url_used !== '') {
// The count of urls.
$url_count = 0;
// Step through each of the urls again, and check
// if there are any duplicates.
foreach ($urls_used as $index => $url_used2) {
// If there is the same url, increment the count of urls.
if ($url_used == $url_used2) {
$url_count++;
}
// If there is more than one url, break out of the loop,
// to save computational tim, and we will handle the form
// error and break out of the next loop, since we have
// found a duplicate.
if ($url_count > 1) {
break;
}
}
// If there is more than one url, set the form error and
// break out of the loop.
if ($url_count > 1) {
$form_state->setError(
$form['items_fieldset']['items'][$index - 1]['url'],
$this->t('You must enter unique Waterloo News URLs.')
);
// Set the flag that we have unique urls.
$unique_url_flag = TRUE;
// Break out of the loop so that we do not check
// any other urls.
break;
}
}
}
// If there is no unique urls, set the items that contain
// the nid to the waterloo news.
if (!$unique_url_flag) {
$form_state->setValue(
'items_fieldset',
$values['items_fieldset']
);
}
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
// Load in the values from the form_sate.
$values = $form_state->getValues();
// Set the url for the featured news item.
$uwnews['featured']['url'] = $values['featured']['url'];
$uwnews['featured']['id'] = $values['featured']['id'];
// Set the use custom featured image.
$uwnews['featured']['use_custom_image'] = $values['featured']['use_custom_image'];
if ($uwnews['featured']['use_custom_image']) {
$uwnews['featured']['custom_image'] = $values['featured']['custom_image'];
}
else {
$uwnews['featured']['custom_image'] = NULL;
}
// Step through the items and get the info.
foreach ($values['items_fieldset']['items'] as $index => $item) {
$uwnews['items'][] = [
'url' => $values['items_fieldset']['items'][$index]['url'],
'id' => $values['items_fieldset']['items'][$index]['id'] ?? NULL,
];
}
// Get the show all and featured item position values.
$uwnews['show_all'] = $values['options']['show_all'];
$uwnews['position'] = $values['featured']['position'];
// Use news url and text for button.
$uwnews['show_all_url'] = self::NEWSURL;
$uwnews['show_all_text'] = $this->t('View all Waterloo News');
// Set the uwnews config for the block.
$this->configuration['uwnews'] = $uwnews;
}
/**
* Function to get the nid from provided url.
*
* @param string $url
* The link of the news item.
*
* @return int
* The nid.
*/
private function getNewsIdFromLink(string $url): int {
// If nid is 0, there is no nid or url does not exist.
$nid = 0;
// Get the alias from the url provided.
$alias = str_replace(self::NEWSURL . '/', '', $url);
// The url to alias api.
$news_url = self::NEWSURLALIAS . '?alias=' . $alias;
// Make the call to the API.
$news = $this->httpClient->get($news_url, ['verify' => FALSE]);
// Decode the API call.
$news = json_decode($news->getBody(), TRUE);
// If there is only one node, get the nid.
if (count($news['nodes']) == 1) {
$nid = $news['nodes'][0]['node']['Nid'];
}
return $nid;
}
/**
* Get the info about the news from the api.
*
* @param string $url
* The url to the api.
* @param bool $image_flag
* Flag to get the image from api.
*
* @return array
* Array of news info.
*/
private function getNewsInfoFromApi(string $url, bool $image_flag = FALSE): array {
// Make the call to the API.
$news = $this->httpClient->get($url, ['verify' => FALSE]);
// Decode the API call.
$news = json_decode($news->getBody(), TRUE);
// Get the actual news data.
if (isset($news['data'][0])) {
$news = $news['data'][0];
}
else {
// Content has likely been unpublished (since we validate URLs).
// Return an empty array.
return [];
}
// Set the tags to be blank in case there are none.
$tags = NULL;
// If there are faculty tags, set the tag array.
if ($news['faculties']) {
$tags = [
[
'url' => NULL,
'title' => $news['faculties'][0]['name'],
'faculty' => $news['faculties'][0]['name'] ? uw_get_org_class_from_name($news['faculties'][0]['name']) : NULL,
'type' => 'full',
],
];
}
// Set the news info.
$news_info = [
'headline' => $news['headline'],
'subhead' => [
'#type' => 'processed_text',
'#text' => $news['subhead'],
'#format' => 'uw_tf_standard',
],
'url' => $news['link'],
'tags' => $tags,
];
// If the image flag is set, and there is an image available,
// grab it from the api.
if ($image_flag && isset($news['image']['image_styles'])) {
$news_info['image'] = [
'type' => 'source',
'sources' => $news['image']['image_styles'],
'alt' => $news['image']['alt'],
];
}
return $news_info;
}
}
Loading