Skip to content

Commit

Permalink
Merge branch '8.x-1.x' into feature/d11-compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
kepol committed Jan 19, 2024
2 parents 10755ff + c039587 commit 6152bd0
Show file tree
Hide file tree
Showing 20 changed files with 385 additions and 165 deletions.
44 changes: 38 additions & 6 deletions modules/quant_api/src/Client/QuantClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
*/
class QuantClient implements QuantClientInterface {

/**
* The configuration object for Quant API.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;

/**
* The logger service.
*
Expand Down Expand Up @@ -63,15 +70,40 @@ class QuantClient implements QuantClientInterface {
* {@inheritdoc}
*/
public function __construct(Client $client, ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory) {
$config = $config_factory->get('quant_api.settings');
$this->config = $config_factory->get('quant_api.settings');
$this->client = $client;
$this->logger = $logger_factory->get('quant_api');

$this->username = $config->get('api_account');
$this->token = $config->get('api_token');
$this->project = $config->get('api_project');
$this->endpoint = $config->get('api_endpoint') . '/v1';
$this->tlsDisabled = $config->get('api_tls_disabled');
$this->username = $this->config->get('api_account');
$this->token = $this->config->get('api_token');
$this->project = $this->config->get('api_project');
$this->endpoint = $this->config->get('api_endpoint') . '/v1';
$this->tlsDisabled = $this->config->get('api_tls_disabled');
}

/**
* Get API overrides.
*/
public function getOverrides() {
// Note this has to be processed in this class instead of in the
// SettingsForm because the overrides aren't available in the form.
$overrides = [];
$keys = [
'api_endpoint',
'api_account',
'api_project',
'api_token',
'api_tls_disabled',
];
foreach ($keys as $key) {
$original = $this->config->getOriginal($key, FALSE);
$active = $this->config->get($key);
if ($original != $active) {
$overrides[$key] = $active;
}
}

return $overrides;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion modules/quant_api/src/EventSubscriber/QuantApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ public function onUnpublish(QuantEvent $event) {
}
catch (\Exception $error) {
// Don't log it if it's a 404, since the content is unpublished.
if (strpos($error->getMessage(), '404 Not Found') === FALSE) {
if (!str_contains($error->getMessage(), '404 Not Found') && !str_contains($error->getMessage(), 'Resource is already unpublished')) {
$this->logger->error($error->getMessage());
}
return;
Expand Down
27 changes: 26 additions & 1 deletion modules/quant_api/src/Form/SettingsForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,35 @@ public function buildForm(array $form, FormStateInterface $form_state) {
$form['api_tls_disabled'] = [
'#type' => 'checkbox',
'#title' => $this->t('Disable TLS verification'),
'#description' => $this->t('You can optionally disable SSL verification for all Quant API requests. This is <strong>not recommended</strong>, but may be necessary in some configurations. For example, old web servers may have issues validating modern SSL certificates.'),
'#description' => $this->t('You can optionally disable TLS verification for all Quant API requests. This is <strong>not recommended</strong>, but may be necessary in some configurations. For example, old web servers may have issues validating modern TSL/SSL certificates.'),
'#default_value' => $config->get('api_tls_disabled', FALSE),
];

// API values might be overridden in the settings file.
$overrides = $this->client->getOverrides();
foreach ($overrides as $key => $value) {
if ($key === 'api_token') {
// Don't show the token in the UI.
$message = $this->t('QuantAPI override: <code>api_token</code> has been overridden in the settings file.');
}
else {
$message = $this->t('QuantAPI override: <em>@key</em> has been overridden in the settings file with <em>@value</em>.',
[
'@key' => $key,
'@value' => $value,
]);
}

// Show warning and add to description.
\Drupal::messenger()->addWarning($message);
$form[$key]['#description'] = $form[$key]['#description'] . ' <strong>' . $message . '</strong>';
}

// Show error if not using TSL verification.
if ((isset($overrides['api_tls_disabled']) && !$overrides['api_tls_disabled']) || !$config->get('api_tls_disabled')) {
\Drupal::messenger()->addError($this->t('<strong>DANGER ZONE:</strong> TLS verification is disabled for Quant API connections. It is <strong>highly recommended</strong> that you update your server configuration to handle TLS rather than disabling TLS verification.'));
}

return parent::buildForm($form, $form_state);
}

Expand Down
62 changes: 27 additions & 35 deletions modules/quant_cron/quant_cron.module
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use Drupal\Core\Form\FormState;
use Drupal\quant\Plugin\QueueItem\RouteItem;
use Drupal\quant\Plugin\QueueItem\FileItem;
use Drupal\node\Entity\Node;
use Drupal\taxonomy\Entity\Term;
use Drupal\user\Entity\User;
use Drupal\views\Views;
use Drupal\Core\Url;

/**
* Implements hook_cron().
Expand Down Expand Up @@ -82,6 +82,28 @@ function quant_cron_cron() {
}
}

// Add taxonomy terms.
if ($form_state->getValue('entity_taxonomy_term')) {
$query = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->getQuery();

$terms = $query->accessCheck(TRUE)->execute();

foreach ($terms as $tid) {
$term = Term::load($tid);

foreach ($term->getTranslationLanguages() as $langcode => $language) {
Seed::seedTaxonomyTerm($term, $langcode);

\Drupal::logger('quant_cron')->notice("quant_cron sending term: tid: @tid, langcode: @lang",
[
'@tid' => $tid,
'@lang' => $langcode,
]
);
}
}
}

// Add custom routes.
if ($form_state->getValue('routes')) {
foreach (explode(PHP_EOL, $form_state->getValue('routes_textarea')) as $route) {
Expand Down Expand Up @@ -113,11 +135,6 @@ function quant_cron_cron() {
$routes = array_merge($routes, quant_cron_get_views_routes());
}

// Add taxonomy term routes.
if ($form_state->getValue('entity_taxonomy_term')) {
$routes = array_merge($routes, quant_cron_get_taxonomy_routes());
}

// Send files.
foreach ($files as $file) {
\Drupal::logger('quant_cron')->notice("quant_cron sending file: @route", ['@route' => $file]);
Expand All @@ -134,35 +151,6 @@ function quant_cron_cron() {

}

/**
* Helper: Get taxonomy routes.
*/
function quant_cron_get_taxonomy_routes() {

$paths = [];
$taxonomy_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');

foreach ($taxonomy_storage->loadMultiple() as $term) {
foreach ($term->getTranslationLanguages() as $langcode => $language) {
// Retrieve the translated version.
$term = $term->getTranslation($langcode);
$tid = $term->id();

$options = ['absolute' => FALSE];

if (!empty($langcode)) {
$language = \Drupal::languageManager()->getLanguage($langcode);
$options['language'] = $language;
}

$url = Url::fromRoute('entity.taxonomy_term.canonical', ['taxonomy_term' => $tid], $options)->toString();
$paths[] = $url;
}
}

return $paths;
}

/**
* Helper: Get theme assets.
*/
Expand Down Expand Up @@ -253,6 +241,10 @@ function quant_cron_get_views_routes() {
$paths = [];

foreach ($views_storage->loadMultiple() as $view) {
if ($view->get('id') == 'taxonomy_term') {
// Taxonomy terms are handled as entities.
continue;
}

$view = Views::getView($view->get('id'));

Expand Down
25 changes: 24 additions & 1 deletion modules/quant_purger/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
# Quant cache tag purger

Adds a cache tag plugin which listen to invalidation events to queue Quant repopulation. This allows node_saves to update the main page but also trigger updates for pages that might be affected by the content update (eg. views pages). These will be added to the queue for the next core cron run to publish the updates.
Adds a cache tag plugin which listens to Drupal invalidation events in order to
queue Quant updates for related content.

For example, this allows node edits to trigger the main (`/node`) page to update
along with any other pages associated with the node through cache tags (e.g.
views pages, taxonomy term pages, etc).

This also works with other entities. For example, if a term is associated with
several nodes, those nodes will be queued for updates when the term is edited.

To ensure that queued content is processed in a timely manner, you can set up a
Quant cron process that is separate from the core cron which just processes the
Quant queue. This Quant cron can be run more regularly than the core cron.

See [Quant Purger documentation](https://docs.quantcdn.io/docs/integrations/drupal/purger)
for additional information.

## Requirements

- quant
- purge

## Recommendations

For the best performance, it is highly recommended that your settings include:

```
$settings['queue_service_quant_seed_worker'] = 'quant.queue_factory';
```
4 changes: 4 additions & 0 deletions modules/quant_purger/config/install/quant_purger.settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ tag_blocklist:
- 'local_task'
path_blocklist:
- '/admin/*'
tag_allowlist:
- ''
path_allowlist:
- ''
10 changes: 10 additions & 0 deletions modules/quant_purger/quant_purger.install
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,13 @@ function quant_purger_update_9101(&$sandbox) {
$config->set('path_blocklist', ['/admin/*']);
$config->save();
}

/**
* Update configuration for module settings.
*/
function quant_purger_update_9102(&$sandbox) {
$config = \Drupal::configFactory()->getEditable('quant_purger.settings');
$config->set('tag_allowlist', ['']);
$config->set('path_allowlist', ['']);
$config->save();
}
27 changes: 21 additions & 6 deletions modules/quant_purger/src/Form/ConfigurationForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace Drupal\quant_purger\Form;

use Drupal\Core\Form\FormStateInterface;
use Drupal\purge_ui\Form\QueuerConfigFormBase;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\PrependCommand;
use Drupal\Core\Form\FormStateInterface;
use Drupal\purge_ui\Form\QueuerConfigFormBase;

/**
* Configuration form for the Quant queuer.
Expand All @@ -31,7 +31,17 @@ public function getFormId() {
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('quant_purger.settings');
$settings = ['path_blocklist', 'tag_blocklist'];
$settings = [
'path_blocklist',
'path_allowlist',
'tag_blocklist',
'tag_allowlist',
];

$form['info'] = [
'#markup' => $this->t('<p class="messages messages--warning">After making changes to the Quant Purger configuration, all content must be re-seeded so the database reflects the changes.<p>'),
'#weight' => -10,
];

foreach ($settings as $key) {
$form["{$key}_fieldset"] = [
Expand Down Expand Up @@ -81,8 +91,9 @@ public function buildForm(array $form, FormStateInterface $form_state) {
}

$form['path_blocklist_fieldset']['#description'] = $this->t('The Quant purge queuer collects HTTP requests that the Quant module makes to generate static representations of content. It requires that the request has a valid token to limit the performance impact of gathering traffic information in such a manner. This is a user-managed list of paths and query strings that will be excluded from traffic gathering.');

$form['path_allowlist_fieldset']['#description'] = $this->t('This list overrides paths in the blocklist for more granularly. For example, <code>/admin/*</code> could be in the blocklist and <code>/admin/special/page</code> could be in the allowlist so all admin pages except that one would be excluded.');
$form['tag_blocklist_fieldset']['#description'] = $this->t('If this list is empty, all cache tag invalidations will trigger a queue entry. Some of these invalidations can have widespread effects on the site and require a full content seed. This setting allows you to exclude certain tags from triggering a content re-seed.');
$form['tag_allowlist_fieldset']['#description'] = $this->t('This list overrides tags in the blocklist for more granularly. For example, <code>config:*</code> could be in the blocklist and <code>config:system.menu.main</code> could be in the allowlist so all config except that one would be excluded.');

$form['actions']['clear'] = [
'#type' => 'submit',
Expand Down Expand Up @@ -137,8 +148,12 @@ public function addMoreCallback(array &$form, FormStateInterface $form_state) {
public function submitFormSuccess(array &$form, FormStateInterface $form_state) {
$this->config('quant_purger.settings')
->set('tag_blocklist', $form_state->getValue('tag_blocklist'))
->set('tag_allowlist', $form_state->getValue('tag_allowlist'))
->set('path_blocklist', $form_state->getValue('path_blocklist'))
->set('path_allowlist', $form_state->getValue('path_allowlist'))
->save();

\Drupal::messenger()->addMessage($this->t('Succesfully saved the Quant Purger configuration. All content must be re-seeded so the database reflects the changes.'));
}

/**
Expand All @@ -155,7 +170,7 @@ public function submitFormClear(array &$form, FormStateInterface $form_state) {
if (!$form_state->getErrors()) {
\Drupal::service('quant_purger.registry')->clear();
$status = 'status';
$message = $this->t('Succesfully cleared the traffic registry.');
$message = $this->t('Succesfully cleared the traffic registry. All content must be re-seeded so the database reflects the configuration.');
}
else {
$status = 'error';
Expand All @@ -171,7 +186,7 @@ public function submitFormClear(array &$form, FormStateInterface $form_state) {
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// @todo Add validation for path_blocklist and tag_blocklist.
// @todo Add validation.
}

}
36 changes: 25 additions & 11 deletions modules/quant_purger/src/StackMiddleware/TraitUrlRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

namespace Drupal\quant_purger\StackMiddleware;

use Drupal\Core\Cache\CacheableResponseInterface;
use Drupal\quant\Utility;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Cache\CacheableResponseInterface;

/**
* Methods for the URL registrar classes.
Expand All @@ -27,16 +28,14 @@ public function determine(Request $request, Response $response) {
}

// Allow paths to be excluded from the traffic repository.
$path = $this->generateUrl($request);
$blocklist = $this->config->get('path_blocklist');
if (is_array($blocklist)) {
$path = $this->generateUrl($request);
foreach (array_filter($blocklist) as $needle) {
$pattern = preg_quote($needle, '/');
$pattern = str_replace('\*', '.*', $pattern);
preg_match('/^(' . $pattern . ')/', $path, $is_match);
if (!empty($is_match)) {
return FALSE;
}
$blocked = Utility::inList($path, $blocklist);
if ($blocked) {
$allowlist = $this->config->get('path_allowlist');
$allowed = Utility::inList($path, $allowlist);
if (!$allowed) {
return FALSE;
}
}

Expand Down Expand Up @@ -77,9 +76,24 @@ protected function generateUrl(Request $request) {
* A list of cache tags for the URL.
*/
protected function getAcceptedCacheTags(array $tag_list) {
// Remove tags from blocklist.
$tags1 = [];
$blocklist = $this->config->get('tag_blocklist');
$blocklist = is_array($blocklist) ? array_filter($blocklist) : [];
$tags = preg_grep('/^(' . implode('|', $blocklist) . ')/', $tag_list, PREG_GREP_INVERT);
if (!empty($blocklist)) {
$tags1 = preg_grep('/^(' . implode('|', $blocklist) . ')/', $tag_list, PREG_GREP_INVERT);
}

// Add tags from allowlist. This must be done after the blocklist.
$tags2 = [];
$allowlist = $this->config->get('tag_allowlist');
$allowlist = is_array($allowlist) ? array_filter($allowlist) : [];
if (!empty($allowlist)) {
$tags2 = preg_grep('/^(' . implode('|', $allowlist) . ')/', $tag_list);
}

$tags = array_unique(array_merge($tags1, $tags2));

return array_filter($tags);
}

Expand Down
Loading

0 comments on commit 6152bd0

Please sign in to comment.