From 3359bc39353ab2ad68e2988a88ff10508a49dbf1 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Tue, 14 Mar 2023 14:28:16 +0000 Subject: [PATCH 01/16] Drupal 10 deprecation fixes - Updated *.info.yml to be compatible with Drupal 10. - Replaced deprecated drupal_get_path() with extension.list.module service for module path retrieval. - Replaced deprecated jQuery $.once to pure javascript .once library. - Replaced deprecated entityManager with entityTypeManager. - Applied code style improvements using Rector for enhanced readability and consistency. - Added is_countable() checks before count(); lack of it results in a warning starting from PHP 7.2. - Standardized constant declarations to follow modern PHP visibility and style conventions. - Rewrote forms using ContentEntityForm to match the updated constructor in the latest Drupal core. - Replaced deprecated drupal_set_message() with \Drupal::messenger() ->addMessage() and \Drupal::messenger()->addWarning(). - Replaced deprecated urlInfo() with toUrl() for generating URLs. - Replaced deprecated entity url() with toUrl()->toString() for generating URL string. - Updated entity queries to explicitly manage access checks. - Replaced deprecated format_date() function with date.formatter service. - Added explicit array initialization to prevent warnings and ensure proper variable usage. - Replaced fully qualified class names with imported class names for improved readability. - Replaced deprecated pager_default_initialize() with pager.manager service for pager initialization. - Moved Rss::$base_field property initialization to the constructor to avoid potential conflicts and adhere to best practices. - Replaced deprecated entity_view() with entityTypeManager() ->getViewBuilder()->view() for rendering entities. - Replaced deprecated getVocabularyId() with bundle() for retrieving vocabulary IDs. - Replaced deprecated prepareQuery with prepareStatement. - Replaced deprecated getUsername() with getDisplayName() for retrieving user display names. --- asklib.info.yml | 1 + asklib.install | 4 +- asklib.libraries.yml | 3 + asklib.module | 46 ++++++++---- asklib.routing.yml | 2 +- public/js/kifiform-tags-custom.js | 3 +- src/AnswerListBuilder.php | 10 +-- src/Breadcrumb/QuestionArchiveCrumb.php | 2 +- .../QuestionFromCollectionCrumb.php | 4 +- .../QuestionFromKeywordIndexCrumb.php | 4 +- src/Breadcrumb/TaxonomyCrumb.php | 6 +- src/Controller/AdminController.php | 3 +- src/Controller/MailGroupController.php | 4 +- src/Controller/MailUserController.php | 8 +- .../MeteorCompatibilityController.php | 2 +- src/Entity/Answer.php | 4 +- src/Entity/Lock.php | 1 + src/Entity/Question.php | 5 +- .../AllowRemoteQuestionFrames.php | 5 +- src/EventSubscriber/UserPageOverride.php | 4 +- src/Form/AdminForm.php | 4 +- src/Form/MailGroupUserForm.php | 21 ++++-- src/Form/MailingGroupsForm.php | 3 +- src/Form/ProvideEntityFormActionGetter.php | 2 +- src/Form/QuestionAdminForm.php | 73 ++++++++++++------- src/Form/QuestionChannelForm.php | 6 +- src/Form/QuestionDeleteForm.php | 2 +- src/Form/QuestionEmailPreviewForm.php | 21 ++++-- src/Form/QuestionForm.php | 5 +- src/Form/QuestionRedirectForm.php | 4 +- src/Form/RemoteQuestionForm.php | 4 +- src/Form/RespondentsForm.php | 3 +- src/Form/UserMailingGroupsForm.php | 3 +- src/MailGroupListBuilder.php | 7 +- src/MailUserListBuilder.php | 11 +-- src/Plugin/Block/NewForumMessages.php | 12 ++- src/Plugin/Block/SimilarQuestions.php | 2 +- src/Plugin/Field/FieldFormatter/SlugLink.php | 5 +- src/Plugin/Field/FieldWidget/AnswerWidget.php | 5 +- .../Field/FieldWidget/ParentQuestion.php | 2 +- src/Plugin/Search/QuestionSearch.php | 6 +- src/Plugin/views/row/Rss.php | 23 ++++-- src/QuestionInterface.php | 12 +-- src/QuestionStorage.php | 2 +- src/QuestionViewBuilder.php | 6 +- src/Routing/KeywordIndexRoutes.php | 4 +- src/Slugger/KeywordSlugger.php | 2 +- src/Statistics/LibraryOverview.php | 10 ++- src/Statistics/MunicipalityOverview.php | 8 +- src/Statistics/Overview.php | 16 ++-- src/Statistics/PersonOverview.php | 8 +- src/UserMailGroupHelper.php | 10 +-- 52 files changed, 246 insertions(+), 177 deletions(-) diff --git a/asklib.info.yml b/asklib.info.yml index d67e68a..eeb5a67 100644 --- a/asklib.info.yml +++ b/asklib.info.yml @@ -4,6 +4,7 @@ core: 8.x package: Kirjastot.fi description: "Kysy kirjastonhoitajalta" configure: asklib.admin +core_version_requirement: ">=8" dependencies: - autoslug - finto_taxonomy diff --git a/asklib.install b/asklib.install index e12fbbc..6210518 100644 --- a/asklib.install +++ b/asklib.install @@ -7,7 +7,7 @@ use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\StringTranslation\TranslatableMarkup; function asklib_install() { - $em = Drupal::entityManager(); + $em = \Drupal::service('entity_type.manager'); $em->getStorage('field_storage_config')->create([ 'entity_type' => 'asklib_question', @@ -58,7 +58,7 @@ function asklib_update_8002() { $entity = \Drupal::entityTypeManager()->getStorage('entity_form_mode')->load('asklib_question.edit'); if(!$entity) { // If not load and write the configuration to Drupal config. - $config_path = drupal_get_path('module', 'asklib') . '/config/install'; + $config_path = \Drupal::service('extension.list.module')->getPath('asklib') . '/config/install'; $source = new FileStorage($config_path); $config_storage = \Drupal::service('config.storage'); $config_storage->write('core.entity_form_mode.asklib_question.edit', $source->read('core.entity_form_mode.asklib_question.edit')); diff --git a/asklib.libraries.yml b/asklib.libraries.yml index e33b428..55df9e1 100644 --- a/asklib.libraries.yml +++ b/asklib.libraries.yml @@ -17,6 +17,9 @@ question-edit-form: js: public/js/toggle-answered.js: {} public/js/kifiform-tags-custom.js: {} + dependencies: + - core/jquery + - core/once field-target-library: css: component: diff --git a/asklib.module b/asklib.module index 4cd9727..8ce5991 100644 --- a/asklib.module +++ b/asklib.module @@ -1,5 +1,11 @@ setFormClass('asklib_admin', \Drupal\asklib\Form\MailGroupTermForm::class); + $term->setFormClass('asklib_admin', MailGroupTermForm::class); $term->setLinkTemplate('asklib-mail-group-form', '/admin/config/asklib/emails/{taxonomy_term}'); } if (isset($entity_types['user'])) { $user = $entity_types['user']; - $user->setFormClass('asklib_admin', \Drupal\asklib\Form\MailGroupUserForm::class); + $user->setFormClass('asklib_admin', MailGroupUserForm::class); $user->setLinkTemplate('asklib-mail-group-form', '/admin/config/asklib/emails/users/{user}'); } } @@ -112,8 +118,9 @@ function asklib_mail($key, &$message, $params) { function asklib_cron() { $day_limit = Drupal::config('asklib.settings')->get('reserved_window'); $date_limit = date('Y-m-d', strtotime(sprintf('-%d days', $day_limit))); - $storage = Drupal::entityManager()->getStorage('asklib_question'); + $storage = \Drupal::service('entity_type.manager')->getStorage('asklib_question'); $qids = $storage->getQuery() + ->accessCheck(FALSE) ->condition('state', QuestionInterface::STATE_RESERVED) ->execute(); $questions = $storage->loadMultiple($qids); @@ -146,7 +153,7 @@ function _asklib_clear_old_question_contact_info() { $two_years_ago = mktime(1, 0, 0, date("m"), date("d"), date("Y") - 2); $storage = \Drupal::entityTypeManager()->getStorage('asklib_question'); - $query = $storage->getQuery(); + $query = $storage->getQuery()->accessCheck(FALSE); $info_not_cleared = $query->orConditionGroup() ->exists('email') ->condition('name', 'Kysyjätieto poistettu', '<>'); @@ -267,8 +274,9 @@ function asklib_views_query_alter(ViewExecutable $view, QueryPluginBase $query) function asklib_bug_fix_rss_query(array &$conditions) { foreach ($conditions as &$rule) { if (is_array($rule)) { - if ($rule['field'] instanceof \Drupal\Core\Database\Query\ConditionInterface) { - asklib_bug_fix_rss_query($rule['field']->conditions()); + if ($rule['field'] instanceof ConditionInterface) { + $conditions = $rule['field']->conditions(); + asklib_bug_fix_rss_query($conditions); } elseif (is_string($rule['field'])) { if (trim($rule['field']) == 'asklib_question__feeds.feeds = :asklib_question__feeds_feeds') { $rule['field'] = 'asklib_question__feeds.feeds_target_id = :asklib_question__feeds_feeds'; @@ -280,7 +288,8 @@ function asklib_bug_fix_rss_query(array &$conditions) { function asklib_views_pre_execute(ViewExecutable $view) { if ($view->storage->id() == 'asklib_rss') { - asklib_bug_fix_rss_query($view->build_info['query']->conditions()); + $conditions = $view->build_info['query']->conditions(); + asklib_bug_fix_rss_query($conditions); } } @@ -463,7 +472,7 @@ function asklib_user_update(UserInterface $user) { } } -function asklib_system_info_alter(array &$info, \Drupal\Core\Extension\Extension $file, $type) { +function asklib_system_info_alter(array &$info, Extension $file, $type) { if ($type != 'theme' || !in_array($file->getName(), ['seven', 'kifi_admin'])) { return; } @@ -560,7 +569,7 @@ function asklib_tokens($type, array $tokens, array $data, array $options, Bubble } if ($type == 'asklib_answer') { - $answer = isset($data['asklib_answer']) ? $data['asklib_answer'] : $data['asklib_question']->getAnswer(); + $answer = $data['asklib_answer'] ?? $data['asklib_question']->getAnswer(); if ($answer) { foreach ($tokens as $name => $original) { @@ -615,12 +624,13 @@ function asklib_preprocess_html(array &$variables) { if (!$user->get('field_asklib_library')->target_id) { $url = Url::fromRoute('entity.user.asklib_mail_group_form', ['user' => $user->id()])->toString(); - drupal_set_message(t('Changes to Ask a Librarian: Set your home library in settings.', [':url' => $url]), 'warning'); + \Drupal::messenger()->addWarning(t('Changes to Ask a Librarian: Set your home library in settings.', [':url' => $url])); } } } function asklib_toolbar() { + $items = []; $items['asklib'] = [ '#type' => 'toolbar_item', '#weight' => 200, @@ -750,10 +760,16 @@ function asklib_asklib_question_delete(QuestionInterface $question) { /* * NOTE: Makes deleting questions in hordes veeeery slow! */ - $url = Url::fromRoute('entity.asklib_question.canonical', ['asklib_question' => $question->id()]); - Drupal::service('path.alias_storage')->delete(['source' => $url]); + $path = '/asklib_question/' . $question->id(); - search_index_clear('asklib_search_elastic', $question->id()); + // Drupal::service('path.alias_storage')->delete(['source' => $url]); + $aliases = Drupal::service('entity_type.manager')->getStorage('path_alias')->loadByProperties(['path' => $path]); + foreach ($aliases as $alias) { + $alias->delete(); + } + + // Get searchindex service and clear the index. + Drupal::service('search.index')->clear('asklib_search_elastic', $question->id()); } function asklib_delete_question_index(QuestionInterface $question) { @@ -768,9 +784,9 @@ function asklib_delete_question_index(QuestionInterface $question) { 'type' => 'content', 'id' => sprintf('asklib_question:%d:%s', $question->id(), $language->getId()) ]); - } catch (\Elasticsearch\Common\Exceptions\Missing404Exception $e) { + } catch (Missing404Exception $e) { // Question was not indexed before, pass. - } catch (\Elasticsearch\Common\Exceptions\NoNodesAvailableException $e) { + } catch (NoNodesAvailableException $e) { // Elasticsearch is down (or not installed), pass. } } diff --git a/asklib.routing.yml b/asklib.routing.yml index 56bdfcd..bc9fc11 100644 --- a/asklib.routing.yml +++ b/asklib.routing.yml @@ -81,7 +81,7 @@ asklib.admin_email_groups: _controller: 'Drupal\asklib\Controller\MailGroupController::groups' requirements: _permission: 'administer asklib' -asklib.admin_email_groups: +asklib.admin_email_single_group: path: '/admin/config/asklib/emails/groups/{taxonomy_term}' defaults: _title: 'Email groups' diff --git a/public/js/kifiform-tags-custom.js b/public/js/kifiform-tags-custom.js index 73ac3cf..bc2c9c9 100644 --- a/public/js/kifiform-tags-custom.js +++ b/public/js/kifiform-tags-custom.js @@ -1,8 +1,7 @@ (function($) { "use strict"; - $("form.asklib-question-edit-form input.form-autocomplete") - .once("asklib-tag-insert") + $(once("asklib-tag-insert", "form.asklib-question-edit-form input.form-autocomplete")) .on("kififormtaginsert", function(event, ui) { console.log(event); diff --git a/src/AnswerListBuilder.php b/src/AnswerListBuilder.php index d635864..0093c89 100644 --- a/src/AnswerListBuilder.php +++ b/src/AnswerListBuilder.php @@ -17,7 +17,7 @@ class AnswerListBuilder extends EntityListBuilder public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { return new static( $entity_type, - $container->get('entity.manager')->getStorage($entity_type->id()), + $container->get('entity_type.manager')->getStorage($entity_type->id()), $container->get('current_user') ); } @@ -45,10 +45,10 @@ public function buildRow(EntityInterface $answer) $row['answer']['data'] = [ '#type' => 'link', '#title' => $this->trimTitle($answer->getBody(), 80), - '#url' => $answer->urlInfo(), + '#url' => $answer->toUrl(), ]; $row['rating'] = $answer->getRating(); - $row['created'] = format_date($answer->getCreatedTime(), 'short'); + $row['created'] = \Drupal::service('date.formatter')->format($answer->getCreatedTime(), 'short'); $row += parent::buildRow($answer); @@ -82,12 +82,12 @@ protected function getDefaultOperations(EntityInterface $answer) // } $ops['edit'] = [ 'title' => $this->t('Edit'), - 'url' => $answer->urlInfo('edit-form'), + 'url' => $answer->toUrl('edit-form'), 'weight' => 5, ]; $ops['delete'] = [ 'title' => $this->t('Delete'), - 'url' => $answer->urlInfo('delete-form'), + 'url' => $answer->toUrl('delete-form'), 'weight' => 10, ]; return $ops; diff --git a/src/Breadcrumb/QuestionArchiveCrumb.php b/src/Breadcrumb/QuestionArchiveCrumb.php index 2e82ef4..08969e6 100644 --- a/src/Breadcrumb/QuestionArchiveCrumb.php +++ b/src/Breadcrumb/QuestionArchiveCrumb.php @@ -81,7 +81,7 @@ protected function from() { if (isset(self::$sourcePages[$from])) { $route = self::$sourcePages[$from]; - list(, $view_id, $display_id) = explode('.', $route); + [, $view_id, $display_id] = explode('.', $route); $view = \Drupal::entityTypeManager()->getStorage('view')->load($view_id); $display = $view->getDisplay($display_id); diff --git a/src/Breadcrumb/QuestionFromCollectionCrumb.php b/src/Breadcrumb/QuestionFromCollectionCrumb.php index 24d4311..841b00d 100644 --- a/src/Breadcrumb/QuestionFromCollectionCrumb.php +++ b/src/Breadcrumb/QuestionFromCollectionCrumb.php @@ -22,7 +22,7 @@ class QuestionFromCollectionCrumb extends PathBasedBreadcrumbBuilder { public static function collectionIdFromQuery($from) { // Variable value should be 'collection/{nid}' - list($foo, $nid) = explode('/', $from . '//'); + [$foo, $nid] = explode('/', $from . '//'); if ($foo == 'collection' && ctype_digit($nid)) { return $nid; } @@ -48,7 +48,7 @@ public function build(RouteMatchInterface $route_match) { $node = reset($nodes); - $request = $this->getRequestForPath($node->url(), []); + $request = $this->getRequestForPath($node->toUrl()->toString(), []); $this->context->fromRequest($request); $crumb = parent::build($route_match); diff --git a/src/Breadcrumb/QuestionFromKeywordIndexCrumb.php b/src/Breadcrumb/QuestionFromKeywordIndexCrumb.php index c45995e..bc1bb21 100644 --- a/src/Breadcrumb/QuestionFromKeywordIndexCrumb.php +++ b/src/Breadcrumb/QuestionFromKeywordIndexCrumb.php @@ -22,7 +22,7 @@ class QuestionFromKeywordIndexCrumb extends PathBasedBreadcrumbBuilder { public static function termIdFromQuery($from) { // Variable value should be 'term/{tid}' - list($foo, $tid) = explode('/', $from . '//'); + [$foo, $tid] = explode('/', $from . '//'); if ($foo == 'term' && ctype_digit($tid)) { return $tid; } @@ -48,7 +48,7 @@ public function build(RouteMatchInterface $route_match) { if (!empty($terms)) { $term = reset($terms); - if ($request = $this->getRequestForPath($term->url(), [])) { + if ($request = $this->getRequestForPath($term->toUrl()->toString(), [])) { $this->context->fromRequest($request); } $crumb = parent::build($route_match); diff --git a/src/Breadcrumb/TaxonomyCrumb.php b/src/Breadcrumb/TaxonomyCrumb.php index 2a92583..35f45db 100644 --- a/src/Breadcrumb/TaxonomyCrumb.php +++ b/src/Breadcrumb/TaxonomyCrumb.php @@ -49,9 +49,9 @@ public static function fixLinkTitles(RouteMatchInterface $route_match, Breadcrum if (isset($links[3])) { // By default This link has the same title as the previous one. Rewrite it to make more sense. - $parts = explode('/', $route_match->getParameter('taxonomy_term')->url()); + $parts = explode('/', $route_match->getParameter('taxonomy_term')->toUrl()->toString()); $letter = strtoupper($parts[count($parts) - 2]); - $links[3]->setText(t('Letter @letter', ['@letter' => $letter]), $links[3]->getText()); + $links[3]->setText(t('Letter @letter', ['@letter' => $letter])); } return $crumb; @@ -66,7 +66,7 @@ public function __construct(RequestContext $context, AccessManagerInterface $acc public function applies(RouteMatchInterface $route_match) { if (in_array($route_match->getRouteName(), $this->allowedRoutes)) { if ($route_match->getRouteName() == 'entity.taxonomy_term.canonical') { - $vid = $route_match->getParameter('taxonomy_term')->getVocabularyId(); + $vid = $route_match->getParameter('taxonomy_term')->bundle(); return in_array($vid, $this->allowedVocabularies); } return TRUE; diff --git a/src/Controller/AdminController.php b/src/Controller/AdminController.php index cf4eba3..0c16191 100755 --- a/src/Controller/AdminController.php +++ b/src/Controller/AdminController.php @@ -2,6 +2,7 @@ namespace Drupal\asklib\Controller; +use Drupal\Core\Database\Database; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Config\ConfigFactoryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -23,7 +24,7 @@ public function index() { $help = check_markup($this->config->get('help'), $this->config->get('help_format')); if (isset($_GET['create_index'])) { - $query = \Drupal\Core\Database\Database::getConnection()->select('asklib_questions', 'a') + $query = Database::getConnection()->select('asklib_questions', 'a') ->distinct() ->fields('a', ['id']) ->condition('published', 1) diff --git a/src/Controller/MailGroupController.php b/src/Controller/MailGroupController.php index 2a80115..c09b4ef 100755 --- a/src/Controller/MailGroupController.php +++ b/src/Controller/MailGroupController.php @@ -28,14 +28,14 @@ public function showGroup($taxonomy_term) private function getGroupListBuilder() { $container = \Drupal::getContainer(); - $type = $this->entityManager()->getDefinition('taxonomy_term'); + $type = \Drupal::service('entity_type.manager')->getDefinition('taxonomy_term'); return MailGroupListBuilder::createInstance($container, $type); } private function getUserListBuilder() { $container = \Drupal::getContainer(); - $type = $this->entityManager()->getDefinition('user'); + $type = \Drupal::service('entity_type.manager')->getDefinition('user'); return MailUserListBuilder::createInstance($container, $type); } } diff --git a/src/Controller/MailUserController.php b/src/Controller/MailUserController.php index 16acf81..3ab783f 100644 --- a/src/Controller/MailUserController.php +++ b/src/Controller/MailUserController.php @@ -15,15 +15,15 @@ public function showUser($user) private function getGroupListBuilder() { - $type = $this->entityManager()->getDefinition('taxonomy_term'); - $storage = $this->entityManager()->getStorage('taxonomy_term'); + $type = \Drupal::service('entity_type.manager')->getDefinition('taxonomy_term'); + $storage = \Drupal::service('entity_type.manager')->getStorage('taxonomy_term'); return new MailGroupListBuilder($type, $storage); } private function getUserListBuilder() { - $type = $this->entityManager()->getDefinition('user'); - $storage = $this->entityManager()->getStorage('user'); + $type = \Drupal::service('entity_type.manager')->getDefinition('user'); + $storage = \Drupal::service('entity_type.manager')->getStorage('user'); return new MailUserListBuilder($type, $storage); } } diff --git a/src/Controller/MeteorCompatibilityController.php b/src/Controller/MeteorCompatibilityController.php index 346e8f6..053983e 100644 --- a/src/Controller/MeteorCompatibilityController.php +++ b/src/Controller/MeteorCompatibilityController.php @@ -28,7 +28,7 @@ public function redirectToQuestion($uuid) { $question = reset($result); if ($question && $question->access('view')) { - header('Location: ' . $question->url()); + header('Location: ' . $question->toUrl()->toString()); exit; } else { throw new NotFoundHttpException; diff --git a/src/Entity/Answer.php b/src/Entity/Answer.php index c8d96f8..18c73fb 100644 --- a/src/Entity/Answer.php +++ b/src/Entity/Answer.php @@ -279,9 +279,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['created'] = BaseFieldDefinition::create('created') ->setLabel(t('Created')) ->setDescription(t('The time that the node was created.')) - ->setDisplayOptions('view', array( - 'type' => 'hidden', - )); + ->setDisplayOptions('view', ['type' => 'hidden']); $fields['changed'] = BaseFieldDefinition::create('changed') ->setLabel(t('Updated')) diff --git a/src/Entity/Lock.php b/src/Entity/Lock.php index 8b52b1e..204128f 100644 --- a/src/Entity/Lock.php +++ b/src/Entity/Lock.php @@ -55,6 +55,7 @@ public function getCreatedTime() { } public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = []; $fields['id'] = BaseFieldDefinition::create('integer') ->setLabel(t('ID')) ->setDescription(t('Lock ID')) diff --git a/src/Entity/Question.php b/src/Entity/Question.php index 9c77e23..82d69ab 100755 --- a/src/Entity/Question.php +++ b/src/Entity/Question.php @@ -2,6 +2,7 @@ namespace Drupal\asklib\Entity; +use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; use Drupal; use LogicException; use RuntimeException; @@ -105,7 +106,7 @@ public function setNotificationFlags($flags) { public function reserve($user_or_id) { $uid = is_object($user_or_id) ? $user_or_id->id() : $user_or_id; - $lock = Drupal::entityManager()->getStorage('asklib_lock')->create(); + $lock = \Drupal::service('entity_type.manager')->getStorage('asklib_lock')->create(); $lock->setUser($uid); $lock->setQuestion($this); @@ -326,7 +327,7 @@ public function setAnsweredTime($time) { throw new LogicException('Cannot set answer time without Answer object'); } if ($time instanceof DateTime) { - $time = $time->format(DATETIME_DATETIME_STORAGE_FORMAT); + $time = $time->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT); } $this->getAnswer()->setAnsweredTime($time); } diff --git a/src/EventSubscriber/AllowRemoteQuestionFrames.php b/src/EventSubscriber/AllowRemoteQuestionFrames.php index 894a3c1..bb27452 100644 --- a/src/EventSubscriber/AllowRemoteQuestionFrames.php +++ b/src/EventSubscriber/AllowRemoteQuestionFrames.php @@ -2,17 +2,18 @@ namespace Drupal\asklib\EventSubscriber; +use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\HttpKernel\Event\FilterResponseEvent; class AllowRemoteQuestionFrames implements EventSubscriberInterface { public static function getSubscribedEvents() { + $events = []; $events[KernelEvents::RESPONSE] = [['onResponse']]; return $events; } - public function onResponse(FilterResponseEvent $event) { + public function onResponse(ResponseEvent $event) { $path = $event->getRequest()->getPathInfo(); if (strpos($path, '/asklib/embed/') === 0) { diff --git a/src/EventSubscriber/UserPageOverride.php b/src/EventSubscriber/UserPageOverride.php index acb477c..06bd594 100644 --- a/src/EventSubscriber/UserPageOverride.php +++ b/src/EventSubscriber/UserPageOverride.php @@ -2,12 +2,12 @@ namespace Drupal\asklib\EventSubscriber; +use Symfony\Component\HttpKernel\Event\RequestEvent; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; class UserPageOverride implements EventSubscriberInterface { public static function getSubscribedEvents() { @@ -21,7 +21,7 @@ public function __construct(RouteMatchInterface $route_match, AccountInterface $ $this->currentUser = $current_user; } - public function onRequest(GetResponseEvent $event) { + public function onRequest(RequestEvent $event) { $allowed = $this->currentUser->hasPermission('answer questions'); if ($allowed && $this->currentRoute->getRouteName() == 'user.page') { diff --git a/src/Form/AdminForm.php b/src/Form/AdminForm.php index 60bb5bf..91a5c08 100644 --- a/src/Form/AdminForm.php +++ b/src/Form/AdminForm.php @@ -14,7 +14,7 @@ class AdminForm extends ConfigFormBase { public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), - $container->get('entity.manager')->getStorage("taxonomy_term") + $container->get('entity_type.manager')->getStorage("taxonomy_term") ); } @@ -85,7 +85,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ]; $terms = $this->terms->loadByProperties(['vid' => 'forums']); - $options = array_map(function($term) { return $term->label(); }, $terms); + $options = array_map(fn($term) => $term->label(), $terms); $form['field_forum'] = [ '#type' => 'details', diff --git a/src/Form/MailGroupUserForm.php b/src/Form/MailGroupUserForm.php index 71cb548..ca0dc75 100644 --- a/src/Form/MailGroupUserForm.php +++ b/src/Form/MailGroupUserForm.php @@ -8,6 +8,9 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Form\FormStateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Core\Entity\EntityRepositoryInterface; +use Drupal\Core\Entity\EntityTypeBundleInfoInterface; +use Drupal\Component\Datetime\TimeInterface; class MailGroupUserForm extends ContentEntityForm { private $groupHelper; @@ -21,14 +24,19 @@ class MailGroupUserForm extends ContentEntityForm { public static function create(ContainerInterface $container) { return new static( - $container->get('entity.manager'), + $container->get('entity.repository'), + $container->get('entity_type.bundle.info'), + $container->get('datetime.time'), $container->get('user.data'), $container->get('asklib.user_mail_group_helper') ); } - public function __construct(EntityManagerInterface $entity_manager, UserDataInterface $config, UserMailGroupHelper $group_helper) { - parent::__construct($entity_manager); + public function __construct( + EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info, TimeInterface $time, + UserDataInterface $config, UserMailGroupHelper $group_helper) { + + parent::__construct($entity_repository, $entity_type_bundle_info, $time); $this->userData = $config; $this->groupHelper = $group_helper; } @@ -39,7 +47,7 @@ public function form(array $form, FormStateInterface $form_state) { $user_groups = $this->groupHelper->getGroupsForUser($this->entity->id()); $groups = $this->sortGroups($this->groups(), $user_groups); - $labels = array_map(function($term) { return $term->label(); }, $groups); + $labels = array_map(fn($term) => $term->label(), $groups); if (empty($form['field_asklib_mail']['widget'][0]['value']['#default_value'])) { $form['field_asklib_mail']['widget'][0]['value']['#default_value'] = $this->entity->getEmail(); @@ -50,7 +58,7 @@ public function form(array $form, FormStateInterface $form_state) { $form['field_asklib_signature']['widget'][0]['value']['#default_value'] = $this->getSetting('email.signature'); } - $label_suffix = array_map(function($g) { return $g->getName(); }, $user_groups); + $label_suffix = array_map(fn($g) => $g->getName(), $user_groups); $label_suffix = implode(', ', $label_suffix); if ($label_suffix) { @@ -82,8 +90,9 @@ public function submitForm(array &$form, FormStateInterface $form_state) { } private function groups() { - $storage = $this->entityManager->getStorage('taxonomy_term'); + $storage = $this->entityTypeManager->getStorage('taxonomy_term'); $tids = $storage->getQuery() + ->accessCheck(false) ->sort('tid') ->condition('vid', ['asklib_libraries', 'asklib_municipalities'], 'in') ->execute(); diff --git a/src/Form/MailingGroupsForm.php b/src/Form/MailingGroupsForm.php index 1f0626b..8796969 100644 --- a/src/Form/MailingGroupsForm.php +++ b/src/Form/MailingGroupsForm.php @@ -14,7 +14,7 @@ public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), - $container->get('entity.manager')->getStorage('taxonomy_term') + $container->get('entity_type.manager')->getStorage('taxonomy_term') ); } @@ -30,6 +30,7 @@ public function getFormId() public function buildForm(array $form, FormStateInterface $form_state) { + $mopts = []; $config = $this->config('asklib.settings'); $form = parent::buildForm($form, $form_state); diff --git a/src/Form/ProvideEntityFormActionGetter.php b/src/Form/ProvideEntityFormActionGetter.php index 9887779..370f62f 100644 --- a/src/Form/ProvideEntityFormActionGetter.php +++ b/src/Form/ProvideEntityFormActionGetter.php @@ -9,7 +9,7 @@ */ trait ProvideEntityFormActionGetter { public function action($id) { - return $this->entityManager->getStorage('action')->load($id); + return $this->entityTypeManager->getStorage('action')->load($id); } public function executeAction($action_id, QuestionInterface $question) { diff --git a/src/Form/QuestionAdminForm.php b/src/Form/QuestionAdminForm.php index 0f52fe1..399db18 100644 --- a/src/Form/QuestionAdminForm.php +++ b/src/Form/QuestionAdminForm.php @@ -8,7 +8,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Path\AliasStorageInterface; +use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Render\Element; use Drupal\Core\Url; use Drupal\asklib\ProvideQuestionFormHeader; @@ -18,8 +18,11 @@ use Drupal\autoslug\SluggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Drupal\Core\Entity\EntityTypeBundleInfoInterface; +use Drupal\Component\Datetime\TimeInterface; use Drupal\Core\Config\Config; +use Drupal\Core\Entity\EntityRepositoryInterface; class QuestionAdminForm extends ContentEntityForm { use ProvideEntityFormActionGetter; @@ -32,17 +35,23 @@ class QuestionAdminForm extends ContentEntityForm { public static function create(ContainerInterface $container) { return new static( - $container->get('entity.manager'), + $container->get('entity.repository'), + $container->get('entity_type.bundle.info'), + $container->get('datetime.time'), $container->get('date.formatter'), - $container->get('path.alias_storage'), + $container->get('entity_type.manager')->getStorage('path_alias'), $container->get('autoslug.slugger.default'), $container->get('config.factory')->get('asklib.settings'), $container->get('asklib.user_mail_group_helper') ); } - public function __construct(EntityManagerInterface $em, DateFormatterInterface $dates, AliasStorageInterface $aliases, SluggerInterface $alias_generator, Config $config, UserMailGroupHelper $mail_groups) { - parent::__construct($em); + public function __construct( + EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info, TimeInterface $time, + DateFormatterInterface $dates, EntityStorageInterface $aliases, SluggerInterface $alias_generator, Config $config, UserMailGroupHelper $mail_groups + ) { + + parent::__construct($entity_repository, $entity_type_bundle_info, $time); $this->dates = $dates; $this->aliases = $aliases; $this->aliasGenerator = $alias_generator; @@ -55,7 +64,7 @@ public function form(array $form, FormStateInterface $form_state) { $answer = $question->getAnswer(); if (!$answer && $question->isReservedTo($this->currentUser())) { - $answer = $this->entityManager->getStorage('asklib_answer')->create(); + $answer = $this->entityTypeManager->getStorage('asklib_answer')->create(); $question->setAnswer($answer); } @@ -151,7 +160,7 @@ public function form(array $form, FormStateInterface $form_state) { '#type' => 'link', '#weight' => 100, '#title' => $this->t('Redirect to another group'), - '#url' => $question->urlInfo('redirect-form'), + '#url' => $question->toUrl('redirect-form'), '#access' => $question->isAvailableTo($this->currentUser()) && !$question->isAnswered(), ] ]; @@ -472,7 +481,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { } else { $answer_data['email_sent'] = NULL; - $answer = $this->entityManager->getStorage('asklib_answer')->create($answer_data); + $answer = $this->entityTypeManager->getStorage('asklib_answer')->create($answer_data); $answer->setUser($this->currentUser()->id()); $this->entity->setAnswer($answer); } @@ -486,9 +495,9 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $old_langcode = $term->language()->getId(); $new_langcode = $this->entity->language()->getId(); - $new_term = $this->entityManager->getStorage('taxonomy_term')->create([ + $new_term = $this->entityTypeManager->getStorage('taxonomy_term')->create([ 'name' => $term->getName(), - 'vid' => $term->getVocabularyId(), + 'vid' => $term->bundle(), 'langcode' => $new_langcode, ]); @@ -504,7 +513,7 @@ public function save(array $form, FormStateInterface $form_state) { $answer->save(); } - drupal_set_message(t('Changes have been saved.')); + $this->messenger()->addStatus(t('Changes have been saved.')); return $status; } @@ -515,20 +524,20 @@ public function processMarkAnswered(array $form, FormStateInterface $form_state) if ($skip_email && !$question->isAnswered()) { $this->executeAction('asklib_mark_question_answered', $question); - drupal_set_message(t('Question was marked answered.')); + $this->messenger()->addStatus(t('Question was marked answered.')); $form_state->setRedirect('view.asklib_index.page_1'); } else if (!$skip_email && !$question->getEmailSentTime() && $question->isAnswered()) { $question->getAnswer()->setAnsweredTime(NULL); - drupal_set_message(t('Question was marked unanswered.')); + $this->messenger()->addStatus(t('Question was marked unanswered.')); } } public function processSlug(array $form, FormStateInterface $form_state) { $langcode = $this->entity->language()->getId(); - $source = '/' . $this->entity->urlInfo()->getInternalPath(); - $match = $this->aliases->load([ - 'source' => $source, + $source = '/' . $this->entity->toUrl()->getInternalPath(); + $path_alias = $this->aliases->loadByProperties([ + 'path' => $source, 'langcode' => $langcode, ]); @@ -538,8 +547,18 @@ public function processSlug(array $form, FormStateInterface $form_state) { $alias = substr_replace($alias, $slug, strrpos($alias, '/') + 1); } - $pid = empty($match) ? NULL : $match['pid']; - $this->aliases->save($source, $alias, $langcode, $pid); + if (empty($path_alias)) { + $path_alias = \Drupal::entityTypeManager()->getStorage('path_alias')->create([ + 'path' => $source, + 'alias' => $alias, + 'langcode' => $langcode + ]); + } else { + // First element is the only one we need. + $path_alias = reset($path_alias); + $path_alias->setAlias($alias); + $path_alias->save(); + } } public function validateReserve(array $form, FormStateInterface $form_state) { @@ -581,7 +600,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) { } public function redirectToPreview(array $form, FormStateInterface $form_state) { - $form_state->setRedirectUrl($this->entity->urlInfo('email-form')); + $form_state->setRedirectUrl($this->entity->toUrl('email-form')); } public function reserve(array $form, FormStateInterface $form_state) { @@ -599,7 +618,7 @@ public function release(array $form, FormStateInterface $form_state) { $question->release()->save(); $form_state->setRedirect('view.asklib_index.page_1'); - drupal_set_message(t('Question released successfully.')); + $this->messenger()->addStatus(t('Question released successfully.')); } protected function rowCountForQuestion($body, $fallback = 4) { @@ -610,11 +629,11 @@ protected function rowCountForQuestion($body, $fallback = 4) { } protected function slugForQuestion() { - $slug = substr(strrchr($this->entity->url(), '/'), 1); + $slug = substr(strrchr($this->entity->toUrl()->toString(), '/'), 1); if (!ctype_digit($slug)) { // Strip query variables potentially injected by other modules etc. - list($slug, $_) = explode('?', $slug . '?'); + [$slug, $_] = explode('?', $slug . '?'); return $slug; } @@ -646,7 +665,7 @@ protected function getQuestionFormHeader(QuestionInterface $question) { 'class' => ['messages', 'messages--error'], ], '#value' => $this->t('This question is reserved to user @user until @date. You can only view this question.', [ - '@user' => $lock->getUser()->getUsername(), + '@user' => $lock->getUser()->getDisplayName(), '@date' => $this->dates->format($expires, 'month_and_day'), ]), ]; @@ -686,10 +705,11 @@ protected function getQuestionFormHeader(QuestionInterface $question) { } protected function buildQuestionLockHistory() { - $storage = $this->entityManager->getStorage('asklib_lock'); + $storage = $this->entityTypeManager->getStorage('asklib_lock'); $lids = $storage->getQuery() ->condition('question', $this->entity->id()) ->sort('created', 'DESC') + ->accessCheck(false) ->execute(); $locks = $storage->loadMultiple($lids); @@ -702,12 +722,13 @@ protected function buildQuestionLockHistory() { ]; foreach ($locks as $lock) { + $table['#rows'][] = [ [ 'data' => [ '#type' => 'link', - '#title' => $lock->getUser()->getUsername(), - '#url' => $lock->getUser()->urlInfo(), + '#title' => $lock->getUser()->getAccountName(), + '#url' => $lock->getUser()->toUrl(), ] ], [ diff --git a/src/Form/QuestionChannelForm.php b/src/Form/QuestionChannelForm.php index 1766c39..04007b6 100644 --- a/src/Form/QuestionChannelForm.php +++ b/src/Form/QuestionChannelForm.php @@ -25,12 +25,12 @@ public function form(array $form, FormStateInterface $form_state) { public function save(array $form, FormStateInterface $form_state) { $status = parent::save($form, $form_state); - $form_state->setRedirectUrl($this->entity->urlInfo('collection')); + $form_state->setRedirectUrl($this->entity->toUrl('collection')); if ($status == SAVED_NEW) { - drupal_set_message($this->t('Channel @id has been created.', ['@id' => $this->entity->id()])); + $this->messenger()->addStatus($this->t('Channel @id has been created.', ['@id' => $this->entity->id()])); } else { - drupal_set_message($this->t('Changes have been saved.')); + $this->messenger()->addStatus($this->t('Changes have been saved.')); } return $status; diff --git a/src/Form/QuestionDeleteForm.php b/src/Form/QuestionDeleteForm.php index 6812190..e925691 100644 --- a/src/Form/QuestionDeleteForm.php +++ b/src/Form/QuestionDeleteForm.php @@ -31,7 +31,7 @@ public function getDescription() public function submitForm(array &$form, FormStateInterface $form_state) { $this->entity->delete(); - drupal_set_message($this->t('The question has been deleted.')); + $this->messenger()->addStatus($this->t('The question has been deleted.')); $this->logger('content')->notice('Deleted question @id', ['@id' => $this->entity->id()]); $form_state->setRedirectUrl($this->getCancelUrl()); } diff --git a/src/Form/QuestionEmailPreviewForm.php b/src/Form/QuestionEmailPreviewForm.php index e40c67d..759dfce 100644 --- a/src/Form/QuestionEmailPreviewForm.php +++ b/src/Form/QuestionEmailPreviewForm.php @@ -6,7 +6,6 @@ use Drupal\Component\Utility\Html; use Drupal\Core\Url; use Drupal\Core\Entity\ContentEntityForm; -use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\File\FileUsage\FileUsageInterface; @@ -14,6 +13,9 @@ use Drupal\asklib\UserMailGroupHelper; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Drupal\Core\Entity\EntityRepositoryInterface; +use Drupal\Core\Entity\EntityTypeBundleInfoInterface; +use Drupal\Component\Datetime\TimeInterface; use Drupal\Core\Session\AccountInterface; @@ -24,13 +26,18 @@ class QuestionEmailPreviewForm extends ContentEntityForm { public static function create(ContainerInterface $container) { return new static( - $container->get('entity.manager'), + $container->get('entity.repository'), + $container->get('entity_type.bundle.info'), + $container->get('datetime.time'), $container->get('asklib.user_mail_group_helper') ); } - public function __construct(EntityManagerInterface $em, UserMailGroupHelper $mail_groups) { - parent::__construct($em); + public function __construct( + EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info, TimeInterface $time, + UserMailGroupHelper $mail_groups + ) { + parent::__construct($entity_repository, $entity_type_bundle_info, $time); $this->mailGroups = $mail_groups; } @@ -40,7 +47,7 @@ public function form(array $form, FormStateInterface $form_state) { $question = $this->entity; $answer = $question->getAnswer(); - $sender = $this->entityManager->getStorage('user')->load($this->currentUser()->id()); + $sender = $this->entityTypeManager->getStorage('user')->load($this->currentUser()->id()); foreach (Element::children($form) as $name) { $form[$name]['#access'] = FALSE; @@ -126,7 +133,7 @@ public function form(array $form, FormStateInterface $form_state) { '#tag' => 'iframe', '#attributes' => [ 'class' => ['email-preview-frame'], - 'src' => $question->urlInfo('email-preview')->toString(), + 'src' => $question->toUrl('email-preview')->toString(), 'style' => 'width: 100%; min-height: 400px;', ], ]; @@ -205,7 +212,7 @@ public function sendEmail(array $form, FormStateInterface $form_state) { $this->executeAction('asklib_mark_question_answered', $this->entity); $form_state->setRedirect('view.asklib_index.page_1'); - drupal_set_message(t('Email was sent successfully.')); + $this->messenger()->addStatus(t('Email was sent successfully.')); } public function save(array $form, FormStateInterface $form_state) { diff --git a/src/Form/QuestionForm.php b/src/Form/QuestionForm.php index 89ca830..873107b 100755 --- a/src/Form/QuestionForm.php +++ b/src/Form/QuestionForm.php @@ -92,8 +92,9 @@ public function actions(array $form, FormStateInterface $form_state) { public function processTheme(array $form, FormStateInterface $form_state) { if ($tid = $form_state->getValue('theme')) { - $term_storage = $this->entityManager->getStorage('taxonomy_term'); + $term_storage = $this->entityTypeManager->getStorage('taxonomy_term'); $query = $term_storage->getQuery() + ->accessCheck(FALSE) ->condition('vid', 'asklib_libraries') ->condition('field_asklib_theme', $tid); @@ -116,7 +117,7 @@ public function submitForm(array & $form, FormStateInterface $form_state) { public function save(array $form, FormStateInterface $form_state) { $this->entity->setNotificationFlags(-1); - drupal_set_message(t('Thank you for your question! We will answer you within three days. If you do not hear from us, please contact us at @email.', [ + $this->messenger()->addStatus(t('Thank you for your question! We will answer you within three days. If you do not hear from us, please contact us at @email.', [ '@email' => 'toimitus@kirjastot.fi' ])); diff --git a/src/Form/QuestionRedirectForm.php b/src/Form/QuestionRedirectForm.php index f7b54e7..8d2c558 100644 --- a/src/Form/QuestionRedirectForm.php +++ b/src/Form/QuestionRedirectForm.php @@ -33,7 +33,7 @@ public function form(array $form, FormStateInterface $form_state) { } protected function filterDisabledAnswerers(array $options) { - $storage = $this->entityManager->getStorage('taxonomy_term'); + $storage = $this->entityTypeManager->getStorage('taxonomy_term'); foreach ($options as $id => $item) { if (is_array($item)) { @@ -60,7 +60,7 @@ public function actions(array $form, FormStateInterface $form_state) { public function save(array $form, FormStateInterface $form_state) { $this->entity->setNotificationFlags(QuestionInterface::NOTIFY_SUBSCRIBERS); $form_state->setRedirect('view.asklib_index.page_1'); - drupal_set_message(t('Target library updated.')); + $this->messenger()->addStatus(t('Target library updated.')); return parent::save($form, $form_state); } diff --git a/src/Form/RemoteQuestionForm.php b/src/Form/RemoteQuestionForm.php index a6e0346..9360dad 100644 --- a/src/Form/RemoteQuestionForm.php +++ b/src/Form/RemoteQuestionForm.php @@ -63,7 +63,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { public function save(array $form, FormStateInterface $form_state) { $this->entity->setNotificationFlags(-1); $message = \Drupal::config('asklib.settings')->get('confirmation'); - drupal_set_message($message); + $this->messenger()->addStatus($message); return parent::save($form, $form_state); } @@ -71,7 +71,7 @@ public function save(array $form, FormStateInterface $form_state) { private function channelFromRoute() { $term = \Drupal::routeMatch()->getParameter('channel'); - if ($term->getVocabularyId() != 'asklib_channels') { + if ($term->bundle() != 'asklib_channels') { throw new NotFoundHttpException; } diff --git a/src/Form/RespondentsForm.php b/src/Form/RespondentsForm.php index 038cab4..1fbcdb5 100644 --- a/src/Form/RespondentsForm.php +++ b/src/Form/RespondentsForm.php @@ -12,7 +12,7 @@ class RespondentsForm extends ConfigFormBase { public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), - $container->get('entity.manager')->getStorage('taxonomy_term') + $container->get('entity_type.manager')->getStorage('taxonomy_term') ); } @@ -26,6 +26,7 @@ public function getFormId() { } public function buildForm(array $form, FormStateInterface $form_state) { + $mopts = []; $config = $this->config('asklib.settings'); $form = parent::buildForm($form, $form_state); diff --git a/src/Form/UserMailingGroupsForm.php b/src/Form/UserMailingGroupsForm.php index ed1c07d..751d850 100644 --- a/src/Form/UserMailingGroupsForm.php +++ b/src/Form/UserMailingGroupsForm.php @@ -15,7 +15,7 @@ class UserMailingGroupsForm extends ConfigFormBase { public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), - $container->get('entity.manager')->getStorage('user') + $container->get('entity_type.manager')->getStorage('user') ); } @@ -29,6 +29,7 @@ public function getFormId() { } public function buildForm(array $form, FormStateInterface $form_state) { + $mopts = []; $config = $this->config('asklib.settings'); $form = parent::buildForm($form, $form_state); diff --git a/src/MailGroupListBuilder.php b/src/MailGroupListBuilder.php index ed5ab55..8d90cb7 100644 --- a/src/MailGroupListBuilder.php +++ b/src/MailGroupListBuilder.php @@ -30,6 +30,7 @@ public function buildHeader() { } public function buildRow(EntityInterface $term) { + $row = []; $status = [ $this->t('Disabled'), $this->t('Enabled'), @@ -37,7 +38,7 @@ public function buildRow(EntityInterface $term) { $row['name']['data'] = [ '#type' => 'link', '#title' => $term->label(), - '#url' => $term->urlInfo(), + '#url' => $term->toUrl(), ]; $row['type'] = $term->vid->entity->label(); $row['enabled'] = $status[$term->get('field_asklib_active')->value]; @@ -49,7 +50,7 @@ protected function getDefaultOperations(EntityInterface $term) { $ops['edit'] = [ 'title' => $this->t('Edit'), - 'url' => $term->urlInfo('asklib-mail-group-form'), + 'url' => $term->toUrl('asklib-mail-group-form'), 'weight' => 0, ]; @@ -71,6 +72,6 @@ protected function getEntityIds() { $query->pager($this->limit); } - return $query->execute(); + return $query->accessCheck(FALSE)->execute(); } } diff --git a/src/MailUserListBuilder.php b/src/MailUserListBuilder.php index 88b6593..a56ef21 100644 --- a/src/MailUserListBuilder.php +++ b/src/MailUserListBuilder.php @@ -32,6 +32,7 @@ public function buildHeader() public function buildRow(EntityInterface $user) { + $row = []; $groups = []; foreach ($this->groups($user->id()) as $tid) { $groups[] = $this->term_cache[$tid]->label(); @@ -67,7 +68,7 @@ protected function getEntityIds() { $query->pager($this->limit); } - return $query->execute(); + return $query->accessCheck(FALSE)->execute(); } protected function getDefaultOperations(EntityInterface $term) { @@ -75,7 +76,7 @@ protected function getDefaultOperations(EntityInterface $term) { $ops['edit'] = [ 'title' => $this->t('Edit'), - 'url' => $term->urlInfo('asklib-mail-group-form'), + 'url' => $term->toUrl('asklib-mail-group-form'), 'weight' => 0, ]; @@ -90,13 +91,13 @@ private function cacheGroups($uids) { if (empty($uids)) { return; } - $phs = implode(',', array_fill(0, count($uids), '?')); + $phs = implode(',', array_fill(0, is_countable($uids) ? count($uids) : 0, '?')); $query = sprintf(' SELECT entity_id tid, field_asklib_subscribers_target_id uid FROM {taxonomy_term__field_asklib_subscribers} WHERE field_asklib_subscribers_target_id IN (%s) ', $phs); - $smt = \Drupal::service('database')->prepareQuery($query); + $smt = \Drupal::service('database')->prepareStatement($query, ['allow_square_brackets' => FALSE]); $smt->execute(array_values($uids)); $tids = []; @@ -106,6 +107,6 @@ private function cacheGroups($uids) { $tids[] = $row->tid; } - $this->term_cache = \Drupal::entityManager()->getStorage('taxonomy_term')->loadMultiple($tids); + $this->term_cache = \Drupal::service('entity_type.manager')->getStorage('taxonomy_term')->loadMultiple($tids); } } diff --git a/src/Plugin/Block/NewForumMessages.php b/src/Plugin/Block/NewForumMessages.php index bc519f1..73585ec 100644 --- a/src/Plugin/Block/NewForumMessages.php +++ b/src/Plugin/Block/NewForumMessages.php @@ -61,8 +61,8 @@ public function build() { $basedir = \Drupal::moduleHandler()->getModule('asklib')->getPath(); foreach ($this->content() as $delta => $item) { - $user_url = $this->userStorage->create(['uid' => $item->user->id])->urlInfo(); - $post_url = $this->nodeStorage->create(['nid' => $item->nid, 'type' => 'forum'])->urlInfo(); + $user_url = $this->userStorage->create(['uid' => $item->user->id])->toUrl(); + $post_url = $this->nodeStorage->create(['nid' => $item->nid, 'type' => 'forum'])->toUrl(); if ($item->type == 'comment') { $icon = 'icon-comment.svg'; @@ -160,7 +160,7 @@ protected function content() { } protected function newTopics() { - $query = db_select('forum_index', 'f') + $query = \Drupal::database()->select('forum_index', 'f') ->fields('f') ->fields('b', ['body_value', 'body_format']) ->fields('n', ['langcode']) @@ -201,7 +201,7 @@ protected function newTopics() { } protected function newComments() { - $query = db_select('comment_field_data', 'c') + $query = \Drupal::database()->select('comment_field_data', 'c') ->fields('c') ->fields('b', ['comment_body_value', 'comment_body_format']) ->fields('f', ['title']) @@ -248,9 +248,7 @@ protected function forum() { protected function mergeItems(array $topics, array $comments) { $items = array_merge($topics, $comments); - usort($items, function($a, $b) { - return $b->created - $a->created; - }); + usort($items, fn($a, $b) => $b->created - $a->created); return array_slice($items, 0, $this->configuration['block_count']); } diff --git a/src/Plugin/Block/SimilarQuestions.php b/src/Plugin/Block/SimilarQuestions.php index 79561b9..c34b493 100644 --- a/src/Plugin/Block/SimilarQuestions.php +++ b/src/Plugin/Block/SimilarQuestions.php @@ -54,7 +54,7 @@ public function build() { 'link' => [ '#type' => 'link', '#title' => $question->label(), - '#url' => $question->urlInfo(), + '#url' => $question->toUrl(), ] ]; } diff --git a/src/Plugin/Field/FieldFormatter/SlugLink.php b/src/Plugin/Field/FieldFormatter/SlugLink.php index 1928ba3..2c4ed9f 100644 --- a/src/Plugin/Field/FieldFormatter/SlugLink.php +++ b/src/Plugin/Field/FieldFormatter/SlugLink.php @@ -29,13 +29,10 @@ public function viewElements(FieldItemListInterface $items, $langcode) { $entity = $item->getEntity(); if ($entity) { - $url = $entity->url(); - // $alias = $aliases->lookupPathAlias($url, $langcode); - $elements[$delta] = [ '#type' => 'link', '#title' => $entity->label(), - '#url' => $entity->urlInfo(), + '#url' => $entity->toUrl(), ]; } } diff --git a/src/Plugin/Field/FieldWidget/AnswerWidget.php b/src/Plugin/Field/FieldWidget/AnswerWidget.php index 4c811a6..47347eb 100644 --- a/src/Plugin/Field/FieldWidget/AnswerWidget.php +++ b/src/Plugin/Field/FieldWidget/AnswerWidget.php @@ -2,6 +2,7 @@ namespace Drupal\asklib\Plugin\Field\FieldWidget; +use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal; use Drupal\Component\Utility\Html; use Drupal\Core\Entity\Display\EntityFormDisplayInterface; @@ -37,7 +38,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen ]; } - $display = \Drupal\Core\Entity\Entity\EntityFormDisplay::collectRenderDisplay($answer, 'default'); + $display = EntityFormDisplay::collectRenderDisplay($answer, 'default'); $element['body'] = $display->getRenderer('body')->form($answer->get('body'), $form, $form_state); @@ -51,7 +52,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $element['rating'] = $display->getRenderer('rating')->form($answer->get('rating'), $form, $form_state); - $fids = array_map(function($o) { return $o->id(); }, $answer->getAttachments()); + $fids = array_map(fn($o) => $o->id(), $answer->getAttachments()); $element['attachments'] = [ '#type' => 'managed_file', diff --git a/src/Plugin/Field/FieldWidget/ParentQuestion.php b/src/Plugin/Field/FieldWidget/ParentQuestion.php index 230b742..62efd33 100644 --- a/src/Plugin/Field/FieldWidget/ParentQuestion.php +++ b/src/Plugin/Field/FieldWidget/ParentQuestion.php @@ -36,7 +36,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen 'link' => [ '#type' => 'link', '#title' => $parent->label(), - '#url' => $parent->urlInfo('edit-form'), + '#url' => $parent->toUrl('edit-form'), ], 'value' => [ '#type' => 'value', diff --git a/src/Plugin/Search/QuestionSearch.php b/src/Plugin/Search/QuestionSearch.php index 3e67a49..d61d2dc 100644 --- a/src/Plugin/Search/QuestionSearch.php +++ b/src/Plugin/Search/QuestionSearch.php @@ -38,7 +38,7 @@ * ) */ class QuestionSearch extends ContentSearch { - const SEARCH_ID = 'asklib_search'; + public const SEARCH_ID = 'asklib_search'; /** * @param $result Elasticsearch response. @@ -52,7 +52,7 @@ protected function prepareResults(array $result) { $cache = $this->loadMatchedEntities($result); - pager_default_initialize($total, 10); + \Drupal::service('pager.manager')->createPager($total, 10); foreach ($result['hits']['hits'] as $hit) { $entity_type = $hit['_source']['entity_type']; @@ -300,7 +300,7 @@ public function buildSearchUrlQuery(FormStateInterface $form_state) { } if ($tags = $form_state->getValue('tags')) { - $query['tags'] = Tags::implode(array_map(function($t) { return $t['target_id']; }, $tags)); + $query['tags'] = Tags::implode(array_map(fn($t) => $t['target_id'], $tags)); } if ($feeds = array_filter($form_state->getValue('feeds', []))) { diff --git a/src/Plugin/views/row/Rss.php b/src/Plugin/views/row/Rss.php index 36a29f6..2cc8474 100644 --- a/src/Plugin/views/row/Rss.php +++ b/src/Plugin/views/row/Rss.php @@ -3,7 +3,8 @@ namespace Drupal\asklib\Plugin\views\row; use stdClass; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityDisplayRepositoryInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\views\Plugin\views\row\RssPluginBase; /** @@ -21,7 +22,6 @@ */ class Rss extends RssPluginBase { public $base_table = 'asklib_questions'; - public $base_field = 'id'; // Stores the questions loaded in preRender. public $questions = []; @@ -29,6 +29,14 @@ class Rss extends RssPluginBase { protected $entityTypeId = 'asklib_question'; + /** + * {@inheritdoc} + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $entity_display_repository); + $this->base_field = 'id'; + } + public function buildOptionsForm_summary_options() { $options = parent::buildOptionsForm_summary_options(); $options['title'] = $this->t('Title only'); @@ -47,14 +55,15 @@ public function preRender($values) { $qids[] = $row->{$this->field_alias}; } if (!empty($qids)) { - $this->questions = $this->entityManager->getStorage('asklib_question')->loadMultiple($qids); + $this->questions = $this->entityTypeManager->getStorage('asklib_question')->loadMultiple($qids); - $aids = $this->entityManager->getStorage('asklib_answer') + $aids = $this->entityTypeManager->getStorage('asklib_answer') ->getQuery() + ->accessCheck(TRUE) ->condition('question', $qids, 'IN') ->execute(); - $this->answers = $this->entityManager->getStorage('asklib_answer')->loadMultiple($aids); + $this->answers = $this->entityTypeManager->getStorage('asklib_answer')->loadMultiple($aids); } } @@ -75,7 +84,7 @@ public function render($row) { $answer = $question->getAnswer(); $library = $answer->getLibrary(); - $question->link = $question->url('canonical', ['absolute' => FALSE]); + $question->link = $question->toUrl('canonical', ['absolute' => FALSE]); $question->rss_namespaces = []; $question->rss_elements = [ [ @@ -93,7 +102,7 @@ public function render($row) { ] ]; - $build = entity_view($question, $display_mode, $question->language()->getId()); + $build = \Drupal::entityTypeManager()->getViewBuilder($question->getEntityTypeId())->view($question, $display_mode, $question->language()->getId()); unset($build['#theme']); if (!empty($question->rss_namespaces)) { diff --git a/src/QuestionInterface.php b/src/QuestionInterface.php index 3d24525..623dce6 100644 --- a/src/QuestionInterface.php +++ b/src/QuestionInterface.php @@ -6,13 +6,13 @@ use Drupal\Core\Entity\EntityPublishedInterface; interface QuestionInterface extends ContentEntityInterface, EntityPublishedInterface { - const STATE_OPEN = 0; - const STATE_RESERVED = 1; - const STATE_ANSWERED = 2; + public const STATE_OPEN = 0; + public const STATE_RESERVED = 1; + public const STATE_ANSWERED = 2; - const NO_NOTIFICATIONS = 0; - const NOTIFY_AUTHOR = 1; - const NOTIFY_SUBSCRIBERS = 2; + public const NO_NOTIFICATIONS = 0; + public const NOTIFY_AUTHOR = 1; + public const NOTIFY_SUBSCRIBERS = 2; public function getAdminNotes(); public function setAdminNotes($details); diff --git a/src/QuestionStorage.php b/src/QuestionStorage.php index 39675d0..17a6db0 100755 --- a/src/QuestionStorage.php +++ b/src/QuestionStorage.php @@ -11,7 +11,7 @@ public function findSimilarQuestions(QuestionInterface $question, $limit = 10) { return []; } - $tags = array_map(function($t) { return $t->id(); }, $tags); + $tags = array_map(fn($t) => $t->id(), $tags); $query = $this->database->select('asklib_questions', 'q') ->fields('q', ['id']) diff --git a/src/QuestionViewBuilder.php b/src/QuestionViewBuilder.php index 3eaf314..37114b8 100644 --- a/src/QuestionViewBuilder.php +++ b/src/QuestionViewBuilder.php @@ -18,7 +18,7 @@ public function buildComponents(array &$build, array $entities, array $displays, '#theme' => 'asklib_ref_question', '#weight' => -100, - '#url' => $question->urlInfo('add-form', ['query' => [ + '#url' => $question->toUrl('add-form', ['query' => [ 'ref' => $question->id(), ]]), ]; @@ -40,9 +40,7 @@ protected function sortTags(array &$tags) { $keys = Element::children($tags, TRUE); $items = []; - usort($keys, function($a, $b) use ($tags) { - return strcasecmp($tags[$a]['#title'], $tags[$b]['#title']); - }); + usort($keys, fn($a, $b) => strcasecmp($tags[$a]['#title'], $tags[$b]['#title'])); foreach ($keys as $i => $key) { $items[$i] = $tags[$key]; diff --git a/src/Routing/KeywordIndexRoutes.php b/src/Routing/KeywordIndexRoutes.php index 3485a9d..38a3747 100644 --- a/src/Routing/KeywordIndexRoutes.php +++ b/src/Routing/KeywordIndexRoutes.php @@ -8,7 +8,7 @@ use Symfony\Component\Routing\Route; class KeywordIndexRoutes { - private static $aliases = [ + private static array $aliases = [ 'fi' => '/kysy/asiasanat', 'en' => '/ask/keywords', 'sv' => '/fraga/amnesord' @@ -39,7 +39,7 @@ public static function access(AccountInterface $account) { $path = Drupal::request()->getPathInfo(); foreach (self::$aliases as $langcode => $alias) { - if ($active_langcode == $langcode && strpos($path, $alias) === 0) { + if ($active_langcode == $langcode && strpos($path, (string) $alias) === 0) { return AccessResult::allowed()->addCacheContexts(['languages:language_content']); } } diff --git a/src/Slugger/KeywordSlugger.php b/src/Slugger/KeywordSlugger.php index 4b8a61a..3f56b19 100644 --- a/src/Slugger/KeywordSlugger.php +++ b/src/Slugger/KeywordSlugger.php @@ -9,7 +9,7 @@ class KeywordSlugger extends DefaultSlugger { public function applies(EntityInterface $entity) { $vids = ['asklib_tags', 'finto']; - return $entity instanceof TermInterface && in_array($entity->getVocabularyId(), $vids, TRUE); + return $entity instanceof TermInterface && in_array($entity->bundle(), $vids, TRUE); } protected function extractTokens(EntityInterface $entity, $pattern, $max_words = 0) { diff --git a/src/Statistics/LibraryOverview.php b/src/Statistics/LibraryOverview.php index b3cdd7f..18900ec 100644 --- a/src/Statistics/LibraryOverview.php +++ b/src/Statistics/LibraryOverview.php @@ -2,6 +2,8 @@ namespace Drupal\asklib\Statistics; +use Drupal\Core\Database\Query\PagerSelectExtender; +use Drupal\Core\Database\Query\TableSortExtender; use PDO; use Drupal\Core\Form\FormStateInterface; use Drupal\asklib\QuestionInterface; @@ -26,7 +28,7 @@ public function alterForm(array &$form, FormStateInterface $form_state) { 'city' => $this->t('Municipality'), 'special' => $this->t('Special library'), ], - '#default_value' => isset($this->parameters['t']) ? $this->parameters['t'] : '', + '#default_value' => $this->parameters['t'] ?? '', ] ]; @@ -36,7 +38,7 @@ public function alterForm(array &$form, FormStateInterface $form_state) { 'name' => [ '#type' => 'textfield', '#title' => $this->t('Name'), - '#default_value' => isset($this->parameters['n']) ? $this->parameters['n'] : '', + '#default_value' => $this->parameters['n'] ?? '', '#size' => 20, ], ]; @@ -87,8 +89,8 @@ protected function countByLibrary() { ]; $query = $this->getQuery() - ->extend('Drupal\Core\Database\Query\PagerSelectExtender') - ->extend('Drupal\Core\Database\Query\TableSortExtender'); + ->extend(PagerSelectExtender::class) + ->extend(TableSortExtender::class); $query->innerJoin('taxonomy_term_field_data', 't', 'a.library = t.tid'); $query->addExpression('COUNT(*)', 'total'); $query->addExpression('MAX(a.answered)', 'last_answer'); diff --git a/src/Statistics/MunicipalityOverview.php b/src/Statistics/MunicipalityOverview.php index d671801..ccf103a 100644 --- a/src/Statistics/MunicipalityOverview.php +++ b/src/Statistics/MunicipalityOverview.php @@ -2,6 +2,8 @@ namespace Drupal\asklib\Statistics; +use Drupal\Core\Database\Query\PagerSelectExtender; +use Drupal\Core\Database\Query\TableSortExtender; use PDO; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; @@ -21,7 +23,7 @@ public function alterForm(array &$form, FormStateInterface $form_state) { 'name' => [ '#type' => 'textfield', '#title' => $this->t('Name'), - '#default_value' => isset($this->parameters['n']) ? $this->parameters['n'] : '', + '#default_value' => $this->parameters['n'] ?? '', '#size' => 30, ], ]; @@ -69,8 +71,8 @@ protected function countByMunicipality() { ]; $query = $this->getQuery() - ->extend('Drupal\Core\Database\Query\PagerSelectExtender') - ->extend('Drupal\Core\Database\Query\TableSortExtender'); + ->extend(PagerSelectExtender::class) + ->extend(TableSortExtender::class); $query->innerJoin('taxonomy_term_field_data', 't', 'q.municipality = t.tid'); $query->addExpression('COUNT(*)', 'total'); $query->addExpression('MAX(a.answered)', 'last_answer'); diff --git a/src/Statistics/Overview.php b/src/Statistics/Overview.php index 57dadeb..a14273e 100644 --- a/src/Statistics/Overview.php +++ b/src/Statistics/Overview.php @@ -128,7 +128,7 @@ protected function countByDelay() { $result = $query->execute()->fetchAll(PDO::FETCH_ASSOC); $rows = []; $rest = ['delay' => $this->t('@days days', ['@days' => '4+']), 'total' => 0]; - $total = array_reduce($result, function($total, $row) { return $total + $row['total']; }, 0); + $total = array_reduce($result, fn($total, $row) => $total + $row['total'], 0); foreach ($result as $row) { if ($row['delay'] <= 3) { @@ -213,16 +213,12 @@ protected function countByChannel() { $result = $query->execute()->fetchAll(PDO::FETCH_UNIQUE); - $data = array_map(function($c) use ($result) { - return [ - 'name' => $c->label(), - 'total' => isset($result[$c->id()]) ? $result[$c->id()]->total : 0, - ]; - }, $channels); + $data = array_map(fn($c) => [ + 'name' => $c->label(), + 'total' => isset($result[$c->id()]) ? $result[$c->id()]->total : 0, + ], $channels); - usort($data, function($a, $b) { - return strcasecmp($a['name'], $b['name']); - }); + usort($data, fn($a, $b) => strcasecmp($a['name'], $b['name'])); $table = [ '#type' => 'table', diff --git a/src/Statistics/PersonOverview.php b/src/Statistics/PersonOverview.php index 3bdd8e6..2403596 100644 --- a/src/Statistics/PersonOverview.php +++ b/src/Statistics/PersonOverview.php @@ -2,6 +2,8 @@ namespace Drupal\asklib\Statistics; +use Drupal\Core\Database\Query\PagerSelectExtender; +use Drupal\Core\Database\Query\TableSortExtender; use PDO; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; @@ -21,7 +23,7 @@ public function alterForm(array &$form, FormStateInterface $form_state) { 'name' => [ '#type' => 'textfield', '#title' => $this->t('User name'), - '#default_value' => isset($this->parameters['n']) ? $this->parameters['n'] : '', + '#default_value' => $this->parameters['n'] ?? '', '#size' => 30, ], ]; @@ -67,8 +69,8 @@ protected function countByUser() { ]; $query = $this->getQuery() - ->extend('Drupal\Core\Database\Query\PagerSelectExtender') - ->extend('Drupal\Core\Database\Query\TableSortExtender'); + ->extend(PagerSelectExtender::class) + ->extend(TableSortExtender::class); $query->innerJoin('users_field_data', 'u', 'a.user = u.uid'); $query->addExpression('COUNT(*)', 'total'); $query->addExpression('MAX(a.answered)', 'last_answer'); diff --git a/src/UserMailGroupHelper.php b/src/UserMailGroupHelper.php index 282a9d1..e0336c1 100644 --- a/src/UserMailGroupHelper.php +++ b/src/UserMailGroupHelper.php @@ -27,6 +27,7 @@ public function getGroupsForUsers(array $uids, $active_only = FALSE) { $storage = $this->entityManager->getStorage('taxonomy_term'); $query = $storage->getQuery() + ->accessCheck(false) ->condition('field_asklib_subscribers', $uids) ->sort('vid', 'DESC'); @@ -42,7 +43,7 @@ public function getGroupsForUsers(array $uids, $active_only = FALSE) { FROM {taxonomy_term__field_asklib_subscribers} WHERE field_asklib_subscribers_target_id IN (%s) ', $phs); - $smt = $this->database->prepareQuery($query); + $smt = $this->database->prepareStatement($query, []); $smt->execute(array_values($uids)); $tids = []; @@ -60,13 +61,12 @@ public function setGroupsForUser($uid, array $gids) { $storage = $this->entityManager->getStorage('taxonomy_term'); $tids = $storage->getQuery() ->condition('field_asklib_subscribers', $uid) + ->accessCheck(FALSE) ->execute(); // Filter user from terms that the user is not subscribed to anymore. foreach ($storage->loadMultiple($tids) as $term) { - $term->get('field_asklib_subscribers')->filter(function($field) use ($term, $uid, $gids) { - return $field->target_id != $uid || in_array($term->id(), $gids); - }); + $term->get('field_asklib_subscribers')->filter(fn($field) => $field->target_id != $uid || in_array($term->id(), $gids)); $term->save(); } @@ -88,7 +88,7 @@ public function getUserMainGroup($user_id) { $groups = $this->getGroupsForUser($user_id, TRUE); foreach ($groups as $i => $term) { - if ($term->getVocabularyId() == 'asklib_municipalities') { + if ($term->bundle() == 'asklib_municipalities') { return $term; } } From 52c6a9b28406de9e17219ef254e3b6f63979bdd6 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Wed, 24 Jan 2024 12:54:03 +0000 Subject: [PATCH 02/16] Redirect user using form submit - Replaced hook_user_login with form submit handler for redirecting users, as the former caused login failures. - Removed leftover console.log from kifiform-tags-custom.js file. --- asklib.module | 22 +++++++++++++++++----- public/js/kifiform-tags-custom.js | 1 - 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/asklib.module b/asklib.module index 8ce5991..6d20325 100644 --- a/asklib.module +++ b/asklib.module @@ -456,13 +456,25 @@ function asklib_form_comment_comment_asklib_form_alter(&$form, FormStateInterfac $form['field_email']['widget'][0]['value']['#attributes']['autocomplete'] = 'email'; } -function asklib_user_login($account) { - $route_name = Drupal::routeMatch()->getRouteName(); +/** + * Implements hook_form_FORM_ID_alter(). + */ +function asklib_form_user_login_form_alter(&$form, FormStateInterface $form_state) { + $form['#submit'][] = 'asklib_user_login_submit'; +} + +/** + * Form submission handler for user_login_form(). + * + * Redirects the user to the asklib dashboard user can answer questions. + */ +function asklib_user_login_submit(&$form, FormStateInterface $form_state) { + $account = Drupal::currentUser(); // Redirect only when request comes from the login form. - if ($route_name == 'user.login' && $account->hasPermission('answer questions')) { - $url = Url::fromRoute('asklib.admin_index')->toString(); - RedirectResponse::create($url)->send(); + if ($account->hasPermission('answer questions')) { + $url = Url::fromRoute('asklib.admin_index'); + $form_state->setRedirectUrl($url); } } diff --git a/public/js/kifiform-tags-custom.js b/public/js/kifiform-tags-custom.js index bc2c9c9..cdc296e 100644 --- a/public/js/kifiform-tags-custom.js +++ b/public/js/kifiform-tags-custom.js @@ -3,7 +3,6 @@ $(once("asklib-tag-insert", "form.asklib-question-edit-form input.form-autocomplete")) .on("kififormtaginsert", function(event, ui) { - console.log(event); if (ui.item.autocompleted == true) { // This is valid as long autocomplete is configured to match only Finto terms. From 11fc4e6bd55923ab8584bfbe3217fb70c3a6d573 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Mon, 14 Aug 2023 13:13:38 +0000 Subject: [PATCH 03/16] Switch search system from Elasticsearch to RediSearch --- asklib.module | 10 +- composer.json | 3 +- .../search.page.asklib_question_elastic.yml | 12 -- src/Plugin/Search/QuestionSearch.php | 127 +++++------------- src/QuestionIndexer.php | 39 +++++- 5 files changed, 74 insertions(+), 117 deletions(-) delete mode 100644 config/optional/search.page.asklib_question_elastic.yml diff --git a/asklib.module b/asklib.module index 6d20325..e3c5ea3 100644 --- a/asklib.module +++ b/asklib.module @@ -4,8 +4,6 @@ use Drupal\asklib\Form\MailGroupTermForm; use Drupal\asklib\Form\MailGroupUserForm; use Drupal\Core\Database\Query\ConditionInterface; use Drupal\Core\Extension\Extension; -use Elasticsearch\Common\Exceptions\Missing404Exception; -use Elasticsearch\Common\Exceptions\NoNodesAvailableException; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Unicode; use Drupal\Core\Access\AccessResult; @@ -780,8 +778,6 @@ function asklib_asklib_question_delete(QuestionInterface $question) { $alias->delete(); } - // Get searchindex service and clear the index. - Drupal::service('search.index')->clear('asklib_search_elastic', $question->id()); } function asklib_delete_question_index(QuestionInterface $question) { @@ -796,10 +792,8 @@ function asklib_delete_question_index(QuestionInterface $question) { 'type' => 'content', 'id' => sprintf('asklib_question:%d:%s', $question->id(), $language->getId()) ]); - } catch (Missing404Exception $e) { - // Question was not indexed before, pass. - } catch (NoNodesAvailableException $e) { - // Elasticsearch is down (or not installed), pass. + } catch (\Exception $e) { + \Drupal::logger('asklib')->error($e->getMessage()); } } } diff --git a/composer.json b/composer.json index e571b79..6c93734 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,6 @@ "type": "drupal-module", "license": "GPL-2.0+", "require": { - "html2text/html2text": "^4.0", - "elasticsearch/elasticsearch": "^5.3" + "html2text/html2text": "^4.0" } } diff --git a/config/optional/search.page.asklib_question_elastic.yml b/config/optional/search.page.asklib_question_elastic.yml deleted file mode 100644 index 19f1d2f..0000000 --- a/config/optional/search.page.asklib_question_elastic.yml +++ /dev/null @@ -1,12 +0,0 @@ -uuid: a9ae4234-3efd-48fd-80b9-863a8f7455a0 -langcode: en -status: true -dependencies: - module: - - asklib -id: asklib_question_elastic -label: 'Ask a Librarian' -path: asklib -weight: -10 -plugin: asklib_search_elastic -configuration: { } diff --git a/src/Plugin/Search/QuestionSearch.php b/src/Plugin/Search/QuestionSearch.php index d61d2dc..cd43ffa 100644 --- a/src/Plugin/Search/QuestionSearch.php +++ b/src/Plugin/Search/QuestionSearch.php @@ -21,13 +21,12 @@ use Drupal\asklib\QuestionInterface; use Drupal\search\Plugin\SearchIndexingInterface; use Drupal\search\Plugin\SearchPluginBase; -use Elasticsearch\Common\Exceptions\BadRequest400Exception; -use Elasticsearch\Common\Exceptions\NoNodesAvailableException; use Html2Text\Html2Text; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\kifisearch\Plugin\Search\ContentSearch; use Drupal\asklib\QuestionIndexer; +use Ehann\RediSearch\Query\BuilderInterface; /** * Search and indexing for asklib_question and asklib_answer entities. @@ -41,12 +40,12 @@ class QuestionSearch extends ContentSearch { public const SEARCH_ID = 'asklib_search'; /** - * @param $result Elasticsearch response. + * @param $result Redisearch response. */ protected function prepareResults(array $result) { - $total = $result['hits']['total']; - $time = $result['took']; - $rows = $result['hits']['hits']; + + $total = $result['total']; + $rows = $result['hits']; $prepared = []; @@ -54,9 +53,9 @@ protected function prepareResults(array $result) { \Drupal::service('pager.manager')->createPager($total, 10); - foreach ($result['hits']['hits'] as $hit) { - $entity_type = $hit['_source']['entity_type']; - $entity_id = $hit['_source']['id']; + foreach ($result['hits'] as $hit) { + $entity_type = $hit['entity_type']; + $entity_id = $hit['entity_id']; if (!isset($cache[$entity_type][$entity_id])) { user_error(sprintf('Stale search entry: %s #%d does not exist', $entity_type, $entity_id)); @@ -66,11 +65,11 @@ protected function prepareResults(array $result) { $question = $cache[$entity_type][$entity_id]; $build = [ - 'link' => $question->url('canonical', ['absolute' => TRUE, 'language' => $question->language()]), + 'link' => $question->toUrl('canonical', ['absolute' => TRUE, 'language' => $question->language()])->toString(), 'asklib_question' => $question, 'title' => $question->label(), - 'score' => $hit['_score'], - 'date' => strtotime($hit['_source']['created']), + 'score' => $hit['asklib_score'], + 'date' => $hit['created'], 'langcode' => $question->language()->getId(), 'snippet' => $this->processSnippet($hit), ]; @@ -91,100 +90,35 @@ protected function prepareResults(array $result) { public function updateIndex() { $storage = $this->entityManager->getStorage('asklib_question'); $batch_size = $this->searchSettings->get('index.cron_limit'); - $indexer = new QuestionIndexer($this->database, $storage, $this->client, [], $batch_size); + $indexer = new QuestionIndexer($this->database, $storage, $this->kifi_index, [], $batch_size); $indexer->updateIndex(); } public function indexStatus() { $storage = $this->entityManager->getStorage('asklib_question'); $batch_size = $this->searchSettings->get('index.cron_limit'); - $indexer = new QuestionIndexer($this->database, $storage, $this->client, [], $batch_size); + $indexer = new QuestionIndexer($this->database, $storage, $this->kifi_index, [], $batch_size); return $indexer->indexStatus(); } - protected function compileSearchQuery($query_string) { - /* - * Elasticsearch will throw an exception when the syntax is invalid, so we - * do a simple sanity check here. - */ - // $query_string = preg_replace('/^(AND|OR|NOT)/', '', trim($query_string)); - // $query_string = preg_replace('/(AND|OR|NOT)$/', '', trim($query_string)); - - if (empty($this->searchParameters['all_languages'])) { - $langcode = $this->languageManager->getCurrentLanguage()->getId(); - } else { - $langcode = NULL; - } - - $query = [ - 'bool' => [ - // 'must' => [], - // 'should' => [], - ] - ]; + protected function compileSearchQuery(BuilderInterface &$search_query, $keywords) { - $query['bool']['must'][] = [ - 'term' => [ - 'entity_type' => 'asklib_question' - ] - ]; + parent::compileSearchQuery($search_query, $keywords); - $query['bool']['must'][] = [ - 'multi_match' => [ - 'query' => $query_string, - 'fields' => ['body', 'title', 'tags'], - ] - ]; + // Only search only from asklib questions. + $search_query->tagFilter('entity_type', ['asklib_question']); - if ($langcode) { - $query['bool']['must'][] = [ - 'term' => ['langcode' => [ - 'value' => $langcode, - ]], - ]; + // Apply ordering + if ($this->getParameter('order') == 'newest') + { + $search_query->sortBy('created', 'DESC'); } + // Apply channel/feeds filtering if (!empty($this->searchParameters['feeds'])) { - foreach (Tags::explode($this->searchParameters['feeds']) as $fid) { - $query['bool']['must'][] = [ - // Use the singular 'term' query to require every single term in the result. - 'term' => [ - 'terms' => (int)$fid - ] - ]; - } + $search_query->tagFilter('terms', Tags::explode($this->searchParameters['feeds'])); } - if (!empty($this->searchParameters['tags'])) { - foreach (Tags::explode($this->searchParameters['tags']) as $tid) { - $query['bool']['must'][] = [ - // Use the singular 'term' query to require every single term in the result. - 'term' => [ - 'terms' => (int)$tid - ] - ]; - } - } - - if (!empty($this->searchParameters['order'])) { - switch ($this->searchParameters['order']) { - case 'newest': - $sort = ['created' => 'desc']; - break; - } - } else { - $sort = ['_score' => 'desc']; - } - - return [ - 'query' => $query, - 'sort' => $sort, - 'highlight' => [ - 'fields' => ['body' => (object)[]], - 'pre_tags' => [''], - 'post_tags' => [''], - ] - ]; } public function searchFormAlter(array &$form, FormStateInterface $form_state) { @@ -214,10 +148,10 @@ public function searchFormAlter(array &$form, FormStateInterface $form_state) { '#type' => 'details', '#title' => $this->t('Advanced search'), '#open' => count(array_diff(array_keys($parameters), ['page', 'keys'])) > 1, - 'all_languages' => [ + 'anylang' => [ '#type' => 'checkbox', '#title' => $this->t('Search all languages'), - '#default_value' => !empty($parameters['all_languages']) + '#default_value' => !empty($parameters['anylang']) ], 'tags_container' => [ /* @@ -267,7 +201,7 @@ public function searchFormAlter(array &$form, FormStateInterface $form_state) { ]; if ($langcode != 'fi') { - $form['all_languages'] = $form['advanced']['all_languages']; + $form['anylang'] = $form['advanced']['anylang']; unset($form['advanced']); } } @@ -285,6 +219,11 @@ protected function getFeedOptions() { continue; } + // Skip unblished channels + if (!$term->isPublished()) { + continue; + } + $options[$term->id()] = (string)$term->label(); } @@ -295,8 +234,8 @@ protected function getFeedOptions() { public function buildSearchUrlQuery(FormStateInterface $form_state) { $query = parent::buildSearchUrlQuery($form_state); - if ($form_state->getValue('all_languages')) { - $query['all_languages'] = '1'; + if ($form_state->getValue('anylang')) { + $query['anylang'] = '1'; } if ($tags = $form_state->getValue('tags')) { diff --git a/src/QuestionIndexer.php b/src/QuestionIndexer.php index 04afc04..ade537a 100644 --- a/src/QuestionIndexer.php +++ b/src/QuestionIndexer.php @@ -13,6 +13,8 @@ public function getTotal() { ->condition('entity.state', QuestionInterface::STATE_ANSWERED) ->condition('entity.published', 1); + $this->excludeOldEChannelQuestions($query); + $total = $query->countQuery()->execute()->fetchField(); return $total; } @@ -24,6 +26,8 @@ public function getRemaining() { ->condition('entity.state', QuestionInterface::STATE_ANSWERED) ->condition('entity.published', 1); + $this->excludeOldEChannelQuestions($query); + // NOTE: Reindex is NULL when left join is to zero rows. $query->condition($query->orConditionGroup() ->condition('search.reindex', NULL, 'IS') @@ -95,7 +99,7 @@ public function updateIndex() { $document['tags'] = array_values(array_unique($document['tags'])); } - $document['fields']['asklib_question']['score'] = (int)$answer->getRating(); + $document['asklib_score'] = (int)$answer->getRating(); $this->index($document); } } @@ -110,6 +114,8 @@ protected function fetchItemsForIndexing() { ->condition('entity.state', QuestionInterface::STATE_ANSWERED) ->condition('entity.published', 1); + $this->excludeOldEChannelQuestions($query); + // NOTE: Reindex is NULL when left join is to zero rows. $query->condition($query->orConditionGroup() ->condition('search.reindex', NULL, 'IS') @@ -130,4 +136,35 @@ protected function fetchItemsForIndexing() { return []; } } + + protected function excludeOldEChannelQuestions(&$query) { + $echannel_id = 188298; + $echannel_cutoff_date = '23-04-2024'; + + // We need to filter out all questions, that 1. belong to the e-channel and 2. are older than 23.4. + // Unfortunately, we need to verify this in two places, 1. from 'channel' in asklib_questions, + // and 2. From table 'asklib_question__feeds'. In future, it probably would be better sync to + // either to question's channel or to the *_feeds table and then remove one of the query groups. + + + // First query based on question 'channel' parameter. + $query->condition($query->orConditionGroup() + ->condition('entity.channel', NULL, 'IS') + ->condition('entity.channel', $echannel_id, '<>') + ->condition($query->andConditionGroup() + ->condition('entity.channel', $echannel_id) + ->condition('entity.created', strtotime($echannel_cutoff_date), '>='))); + + + // Second query based on the addtional 'asklib_question__feeds' table. + $subquery = $this->database->select('asklib_questions', 'entity') + ->fields('entity', ['id']) + ->condition('entity.state', QuestionInterface::STATE_ANSWERED) + ->condition('entity.published', 1) + ->condition('entity.created', strtotime($echannel_cutoff_date), '<'); + $subquery->join('asklib_question__feeds', 'af', 'af.entity_id = entity.id'); + $subquery->condition('af.feeds_target_id', $echannel_id); + + $query->condition('entity.id', $subquery, 'NOT IN'); + } } From d6ec1fdd3d9561933deee3f4f7ac7d89931ea866 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Wed, 8 Nov 2023 13:35:28 +0000 Subject: [PATCH 04/16] Switch to newer symfony_mailer email system --- asklib.info.yml | 2 +- asklib.module | 9 + ...ony_mailer.mailer_policy.asklib.answer.yml | 13 ++ ...iler.mailer_policy.asklib.new_question.yml | 13 ++ ...ailer_policy.asklib.new_question_admin.yml | 13 ++ src/Controller/QuestionViewController.php | 11 +- src/Plugin/Action/EmailActionBase.php | 39 +++-- src/Plugin/Action/SendAnswerEmail.php | 4 +- src/Plugin/Action/SendClientReceiptEmail.php | 2 +- src/Plugin/Action/SendQuestionNotifyEmail.php | 11 +- .../EmailBuilder/AskLibEmailBuilder.php | 154 ++++++++++++++++++ 11 files changed, 245 insertions(+), 26 deletions(-) create mode 100644 config/mailer_override/symfony_mailer.mailer_policy.asklib.answer.yml create mode 100644 config/mailer_override/symfony_mailer.mailer_policy.asklib.new_question.yml create mode 100644 config/mailer_override/symfony_mailer.mailer_policy.asklib.new_question_admin.yml create mode 100644 src/Plugin/EmailBuilder/AskLibEmailBuilder.php diff --git a/asklib.info.yml b/asklib.info.yml index eeb5a67..68c6f4a 100644 --- a/asklib.info.yml +++ b/asklib.info.yml @@ -10,7 +10,7 @@ dependencies: - finto_taxonomy - forum - kifiform - - kifimail + - symfony_mailer - kifisearch - kifistats - language diff --git a/asklib.module b/asklib.module index e3c5ea3..de77f5c 100644 --- a/asklib.module +++ b/asklib.module @@ -28,6 +28,8 @@ use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\cache\CachePluginBase; use Drupal\views\Plugin\views\query\QueryPluginBase; use Symfony\Component\HttpFoundation\RedirectResponse; +use Drupal\symfony_mailer\EmailInterface; +use Drupal\symfony_mailer\Exception\SkipMailException; function asklib_theme() { return [ @@ -816,3 +818,10 @@ function asklib_build_question_index(QuestionInterface $question) { ->execute(); } } +function asklib_mailer_post_render(EmailInterface $email) { + $do_not_send = $email->getParam('do_not_send'); + if ($do_not_send) + { + throw new SkipMailException(); + } +} diff --git a/config/mailer_override/symfony_mailer.mailer_policy.asklib.answer.yml b/config/mailer_override/symfony_mailer.mailer_policy.asklib.answer.yml new file mode 100644 index 0000000..3c1b4a1 --- /dev/null +++ b/config/mailer_override/symfony_mailer.mailer_policy.asklib.answer.yml @@ -0,0 +1,13 @@ +langcode: fi +status: true +dependencies: + module: + - asklib +id: asklib.answer +configuration: + email_subject: + value: 'Vastaus Kysy kirjastonhoitajalta -palvelusta' + email_body: + content: + value: "

Hei!

\r\n

Vastaus Kysy kirjastonhoitajalta -palveluun lähettämääsi kysymykseen.

\r\n\r\n
\r\n

[asklib_answer:body]

\r\n {% if has_answer_details %}\r\n

[asklib_answer:details]

\r\n {% endif %}\r\n {% if is_admin %}\r\n [asklib_answer:author], Kirjastot.fi-toimitus\r\n {% else %}\r\n [asklib_answer:author], [asklib_answer:library]\r\n {% endif %}\r\n
\r\n

[asklib_question:body]

\r\n {% if has_question_details %}\r\n

[asklib_question:details]

\r\n {% endif %}\r\n
\r\n\r\n \r\n\r\n

\r\n

[asklib_answer:signature]

\r\n
" + format: email_html diff --git a/config/mailer_override/symfony_mailer.mailer_policy.asklib.new_question.yml b/config/mailer_override/symfony_mailer.mailer_policy.asklib.new_question.yml new file mode 100644 index 0000000..ede3b42 --- /dev/null +++ b/config/mailer_override/symfony_mailer.mailer_policy.asklib.new_question.yml @@ -0,0 +1,13 @@ +langcode: fi +status: true +dependencies: + module: + - asklib +id: asklib.new_question +configuration: + email_subject: + value: '[Kysy kirjastonhoitajalta] Kuittaus vastaanotetusta kysymyksestä (asiakas)' + email_body: + content: + value: "

Hei!

\r\n

Kiitos kysymyksestäsi. Vastaamme siihen kolmen työpäivän kuluessa. Vastaamme vain tietopalvelukysymyksiin. Käytämme kirjaston käytössä olevia lähteitä ja vastaamme kirjastoammattilaisen osaamisella. Kirjastot.fi-toimitus, toimitus@kirjastot.fi.

\r\n
\r\n [asklib_question:email]\r\n

[asklib_question:body]

\r\n
" + format: email_html diff --git a/config/mailer_override/symfony_mailer.mailer_policy.asklib.new_question_admin.yml b/config/mailer_override/symfony_mailer.mailer_policy.asklib.new_question_admin.yml new file mode 100644 index 0000000..b516d80 --- /dev/null +++ b/config/mailer_override/symfony_mailer.mailer_policy.asklib.new_question_admin.yml @@ -0,0 +1,13 @@ +langcode: fi +status: true +dependencies: + module: + - asklib +id: asklib.new_question_admin +configuration: + email_subject: + value: '[Kysy kirjastonhoitajalta] Ilmoitus uudesta kysymyksestä (vastaajat)' + email_body: + content: + value: "

Kirjastonne alueelta on saapunut uusi kysymys vastattavaksi Kysy kirjastonhoitajalta -palveluun. Kirjaudu osoitteessa https://www.kirjastot.fi/user/login

\r\n

Odottavat kysymykset

\r\n
\r\n [asklib_question:email]\r\n

[asklib_question:body]

\r\n
\r\n" + format: email_html diff --git a/src/Controller/QuestionViewController.php b/src/Controller/QuestionViewController.php index 655e77f..bcb076b 100755 --- a/src/Controller/QuestionViewController.php +++ b/src/Controller/QuestionViewController.php @@ -2,6 +2,7 @@ namespace Drupal\asklib\Controller; +use Drupal\asklib\Entity\Question; use Exception; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\Controller\EntityViewController; @@ -27,13 +28,19 @@ public function renderEmail(QuestionInterface $asklib_question) { $langcode = $asklib_question->language()->getId(); $recipient = $asklib_question->getEmail(); - $data = [ + // Set the question "answered" for preview. + $asklib_question->setState(Question::STATE_ANSWERED); + + $mail = [ 'from' => \Drupal::currentUser(), 'asklib_question' => $asklib_question, 'files' => $asklib_question->getAnswer()->getAttachments(), + 'do_not_send' => true, // This will cancel the mailing in 'asklib_mailer_post_render'. ]; - $mail = \Drupal::service('plugin.manager.mail')->mail('asklib', 'answer', $recipient, $langcode, $data, null, false); + $reply_to = $asklib_question->getAnsweredBy(); + + $mail = \Drupal::service('plugin.manager.mail')->mail('asklib', 'answer', $recipient, $langcode, $mail, $reply_to); return new Response($mail['body']); } diff --git a/src/Plugin/Action/EmailActionBase.php b/src/Plugin/Action/EmailActionBase.php index f5b75f8..25afe9f 100644 --- a/src/Plugin/Action/EmailActionBase.php +++ b/src/Plugin/Action/EmailActionBase.php @@ -42,9 +42,6 @@ public function access($object, AccountInterface $account = NULL, $return_as_obj } protected function mail($mail_id, $recipients, $langcode, $mail, $reply_to = null) { - if (is_array($recipients)) { - $recipients = implode(', ', $recipients); - } if (is_null($reply_to)) { $reply_to = $this->getGenericSenderAddress(); @@ -53,16 +50,30 @@ protected function mail($mail_id, $recipients, $langcode, $mail, $reply_to = nul $mail = $this->mailer->mail('asklib', $mail_id, $recipients, $langcode, $mail, $reply_to); if ($mail['result']) { - $storage = \Drupal::entityTypeManager()->getStorage('kifimail_log'); - $storage->create([ - 'module' => $mail['module'], - 'message_id' => $mail['key'], - 'email' => $mail['to'], - 'langcode' => $mail['langcode'], - 'subject' => $mail['subject'], - 'body' => $mail['body'], - 'user' => \Drupal::currentUser()->id(), - ])->save(); + + $log_message = <<notice($log_message, [ + '@module' => $mail['module'], + '@message_id' => $mail['key'], + '@email' => $mail['to'], + '@langcode' => $mail['langcode'], + '@subject' => $mail['subject'], + '@body' => $mail['body'], + '@user' => \Drupal::currentUser()->id(), + ]); + + } } @@ -71,6 +82,6 @@ protected function getGenericSenderAddress() { $site_config = $this->config->get('system.site'); $name = $config->get('reply.name'); $email = $config->get('reply.address') ?: $site_config->get('mail'); - return $name ? sprintf('%s <%s>', $name, $email) : $email; + return ['name' => $name, 'email' => $email]; } } diff --git a/src/Plugin/Action/SendAnswerEmail.php b/src/Plugin/Action/SendAnswerEmail.php index 3010d9b..f8487d8 100644 --- a/src/Plugin/Action/SendAnswerEmail.php +++ b/src/Plugin/Action/SendAnswerEmail.php @@ -23,9 +23,7 @@ public function execute($question = NULL) { 'files' => $question->getAnswer()->getAttachments() ]; - $sender = $question->getAnsweredBy(); - $sender_email = $sender->get('field_asklib_mail')->value ?: $sender->getEmail(); - $reply_to = sprintf('%s <%s>', $sender->field_real_name->value, $sender_email); + $reply_to = $question->getAnsweredBy(); $this->mail('answer', $question->getEmail(), $question->language()->getId(), $mail, $reply_to); diff --git a/src/Plugin/Action/SendClientReceiptEmail.php b/src/Plugin/Action/SendClientReceiptEmail.php index d6750a2..3e31f23 100644 --- a/src/Plugin/Action/SendClientReceiptEmail.php +++ b/src/Plugin/Action/SendClientReceiptEmail.php @@ -17,7 +17,7 @@ class SendClientReceiptEmail extends EmailActionBase { */ public function execute($question = NULL) { if ($question->getEmail()) { - $mail = ['asklib_question' => $question]; + $mail = ['asklib_question' => $question, 'files' => $question->getAttachments()]; $this->mail('new_question', $question->getEmail(), $question->language()->getId(), $mail, $this->getGenericSenderAddress()); } } diff --git a/src/Plugin/Action/SendQuestionNotifyEmail.php b/src/Plugin/Action/SendQuestionNotifyEmail.php index 7a1a370..1d90f47 100644 --- a/src/Plugin/Action/SendQuestionNotifyEmail.php +++ b/src/Plugin/Action/SendQuestionNotifyEmail.php @@ -37,8 +37,8 @@ public function execute($question = NULL) { $recipients = array_merge($recipients, $this->getGroupRecipients($city)); } - if ($recipients = array_unique($recipients)) { - $mail = ['asklib_question' => $question]; + if (is_array($recipients) && empty($recipients) === false) { + $mail = ['asklib_question' => $question, 'files' => $question->getAttachments()]; $langcode = $question->language()->getId(); $this->mail('new_question_admin', $recipients, $langcode, $mail); } @@ -61,15 +61,16 @@ protected function getGroupRecipients(TermInterface $group) { $recipients = []; if ($group->hasField('field_asklib_email') && $group->field_asklib_email->value) { - $recipients[] = sprintf('%s <%s>', $group->label(), $group->field_asklib_email->value); + $email = $group->field_asklib_email->value; + $recipients[$email] = ['name' => $group->label(), 'email' => $email]; } foreach ($users as $user) { $email = $user->get('field_asklib_mail')->value ?: $user->getEmail(); if ($user->hasField('field_real_name') && $name = $user->get('field_real_name')->value) { - $recipients[] = sprintf('%s <%s>', $name, $email); + $recipients[$email] = ['name' => $name, 'email' => $email]; } else { - $recipients[] = $email; + $recipients[$email] = ['name' => NULL, 'email' => $email]; } } return $recipients; diff --git a/src/Plugin/EmailBuilder/AskLibEmailBuilder.php b/src/Plugin/EmailBuilder/AskLibEmailBuilder.php new file mode 100644 index 0000000..bc0103a --- /dev/null +++ b/src/Plugin/EmailBuilder/AskLibEmailBuilder.php @@ -0,0 +1,154 @@ +setParam('langcode', $langcode); + + $email->setParam('asklib_question', $question); + $email->setVariable('asklib_question', $question); + + if($question->isAnswered()) + { + $email->setParam('asklib_answer', $question->getAnswer()); + $email->setVariable('asklib_answer', $question->getAnswer()); + + $email->setVariable('has_answer_details', !empty($question->getAnswer()->getDetails())); + } + + $email->setParam('recipients', $recipients); + $email->setParam('reply-to', $reply_to); + $email->setParam('from', $from); + $email->setParam('attachments', $attachments); + $email->setParam('do_not_send', $do_not_send); + + + $email->setVariable('is_admin', \Drupal::currentUser()->hasPermission('administer asklib')); + $email->setVariable('has_question_details', !empty($question->getDetails())); + } + + public function fromArray(EmailFactoryInterface $factory, array $message) { + + $recipients = $message['to']; + $reply_to = $message['reply-to']; + $from = NULL; + if(!empty($message['params']['from'])) + { + $from = $message['params']['from']->getEmail(); + } + + $attachments = []; + + if (isset($message['params']['files']) && is_array($message['params']['files']) && !empty($message['params']['files'])) + { + $attachments = $message['params']['files']; + } + + $do_not_send = (isset($message['params']['do_not_send'])) ? $message['params']['do_not_send'] : false; + + return $factory->newTypedEmail($message['module'], $message['key'], + $message['langcode'], $message['params']['asklib_question'], $recipients, $reply_to, $from, $attachments, $do_not_send); + } + + + /** + * {@inheritdoc} + */ + public function preRender(EmailInterface $email) { + $this->tokenOptions(['clear' => TRUE]); + } + + public function build(EmailInterface $email) { + + $recipients = $email->getParam('recipients'); + $question = $email->getParam('asklib_question'); + + if(is_string($recipients)) + { + $email->setTo(new Address($recipients)); + } else if(is_array($recipients)) + { + $addresses = []; + foreach($recipients as $recipient) + { + $addresses[] = new Address($recipient['email'], $recipient['name'] ?? NULL); + } + $email->setTo($addresses); + } + + $reply_to = $email->getParam('reply-to'); + if(!empty($reply_to)) + { + if(is_string($reply_to)) + { + $email->setReplyTo(new Address($reply_to)); + } + else if(is_array($reply_to) && isset($reply_to['email'])) + { + $email->setReplyTo(new Address($reply_to['email'], $reply_to['name'] ?? NULL)); + } else { + // We are dealing with User object. Get the wanted fields from there. + $sender_email = $reply_to->get('field_asklib_mail')->value ?: $reply_to->getEmail(); + $email->setReplyTo(new Address($sender_email, $reply_to->field_real_name->value)); + } + } + + $from = $email->getParam('from'); + if(!empty($from)) + { + $email->setFrom(new Address($from)); + } + + // Check for attachments + $attachments = $email->getParam('attachments'); + if (!empty($attachments)) + { + foreach($attachments as $attachment) + { + $email->attachFromPath($attachment->getFileUri()); + } + } + + // If this is an answer, then set the answeree as reply-to mail. + if($email->getSubType() == 'answer') + { + $answer = $email->getParam('asklib_answer'); + $anwerer = $answer->getUser(); + $email->setReplyTo(new Address($anwerer->get('field_asklib_mail')->value ?: $anwerer->getEmail(), $anwerer->field_real_name->value)); + } + + } + +} \ No newline at end of file From 6d3311d93b62fd778001727ccd4ccb71c88f258d Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Tue, 5 Dec 2023 15:15:59 +0000 Subject: [PATCH 05/16] Update Question Display - Hide asker's municipality and name in Question render. - Remove 'created' field from asklib_answer display. - Hide "title" label in Question view. Note: The hiding is currently implemented through code. Managing the Question display through the admin panel would be preferable. --- asklib.module | 11 +++++++++++ src/QuestionViewBuilder.php | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/asklib.module b/asklib.module index de77f5c..74ec541 100644 --- a/asklib.module +++ b/asklib.module @@ -399,6 +399,14 @@ function asklib_preprocess_asklib_question(&$variables) { 'resource' => 'https://www.kirjastot.fi/kysy/' . $variables['elements']['#asklib_question']->id(), ]; + /** + * NOTE: These fields should be hidden from Drupal's built-in display management form. + * Currently, asklib does not provide one. Asklib question has only one view mode called "full". + * For future development, we could implement view support for asklib_question and asklib_answer. + */ + unset($variables['question']['municipality']); + unset($variables['question']['user']); + $variables['question']['title_meta'] = [ '#weight' => -1000, '#theme' => 'rdf_metadata', @@ -427,6 +435,9 @@ function asklib_preprocess_asklib_answer(&$variables) { $variables['content'][$key] = $variables['elements'][$key]; } + // See the NOTE in asklib_preprocess_asklib_question about display managment. + unset($variables['content']['created']); + $view = Drupal::entityTypeManager()->getViewBuilder('asklib_question'); $displays = $variables['elements']['#asklib_answer']->getQuestion()->get('displays'); $rendered = $view->viewField($displays, $displays->getFieldDefinition()->getDisplayOptions('view')); diff --git a/src/QuestionViewBuilder.php b/src/QuestionViewBuilder.php index 37114b8..06330ef 100644 --- a/src/QuestionViewBuilder.php +++ b/src/QuestionViewBuilder.php @@ -31,6 +31,12 @@ public function buildComponents(array &$build, array $entities, array $displays, $build[$delta]['comments']['#access'] = FALSE; } } + + // See the NOTE in asklib_preprocess_asklib_question about display managment. + foreach ($build as $delta => $current_build) { + $build[$delta]['title']['#label_display'] = 'hidden'; + } + } /** From 43aca996b274acc381bf8f0f3e4a68742fc1bbac Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Thu, 31 Oct 2024 19:34:11 +0000 Subject: [PATCH 06/16] Refactor QuestionSearch to extend CustomSearchBase Kirjastot.fi search and Ask Libraries search need to be kept separate. For example, Asklib has its own sorting logic. --- src/Plugin/Search/QuestionSearch.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Plugin/Search/QuestionSearch.php b/src/Plugin/Search/QuestionSearch.php index cd43ffa..e610658 100644 --- a/src/Plugin/Search/QuestionSearch.php +++ b/src/Plugin/Search/QuestionSearch.php @@ -26,6 +26,8 @@ use Drupal\kifisearch\Plugin\Search\ContentSearch; use Drupal\asklib\QuestionIndexer; +use Drupal\kifisearch\Plugin\Search\CustomSearchBase; +use Drupal\kifisearch\Query\KifiBuilderInterface; use Ehann\RediSearch\Query\BuilderInterface; /** @@ -36,7 +38,7 @@ * title = @Translation("Ask a Librarian") * ) */ -class QuestionSearch extends ContentSearch { +class QuestionSearch extends CustomSearchBase { public const SEARCH_ID = 'asklib_search'; /** @@ -101,7 +103,7 @@ public function indexStatus() { return $indexer->indexStatus(); } - protected function compileSearchQuery(BuilderInterface &$search_query, $keywords) { + protected function compileSearchQuery(KifiBuilderInterface &$search_query, $keywords) { parent::compileSearchQuery($search_query, $keywords); From c8ac92ee4a660b787b88623f4807804ef4289764 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Mon, 18 Nov 2024 10:37:13 +0000 Subject: [PATCH 07/16] Restrict new term creation in 'asklib_tags' for Swedish language --- src/Form/QuestionAdminForm.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Form/QuestionAdminForm.php b/src/Form/QuestionAdminForm.php index 399db18..7b78b68 100644 --- a/src/Form/QuestionAdminForm.php +++ b/src/Form/QuestionAdminForm.php @@ -247,6 +247,15 @@ public function form(array $form, FormStateInterface $form_state) { $form['tags']['#group'] = 'tags_group'; $form['tags']['#attached']['library'][] = 'finto_taxonomy/kifiform-tags-plugin'; + // Add this block to restrict new term creation in 'asklib_tags' when language is Swedish + if ($this->entity->language()->getId() == 'sv') { + // Allow auto creation only in 'finto' vocabulary. + $form['tags']['widget']['#selection_settings']['auto_create_bundles'] = ['finto']; + $form['#attached']['drupalSettings']['kifiform'] = [ + 'disableEnter' => TRUE, + ]; + } + $form['tags']['widget']['target_id']['#description'] = $this->t('Select keywords from the drop-down list or press Enter to add a new one.'); $form['tags']['tags_legend'] = [ @@ -403,6 +412,7 @@ public function form(array $form, FormStateInterface $form_state) { // Add header after possibly disabling inputs. $form['header'] = $this->getQuestionFormHeader($question); + // dump($form['tags']); return $form; } From c45baf332615e32c82ba7c5d3dabbcacc1e8dedd Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Mon, 18 Nov 2024 11:34:28 +0000 Subject: [PATCH 08/16] Restrict new term creation in 'asklib_tags' for English also --- src/Form/QuestionAdminForm.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Form/QuestionAdminForm.php b/src/Form/QuestionAdminForm.php index 7b78b68..ec89637 100644 --- a/src/Form/QuestionAdminForm.php +++ b/src/Form/QuestionAdminForm.php @@ -247,8 +247,9 @@ public function form(array $form, FormStateInterface $form_state) { $form['tags']['#group'] = 'tags_group'; $form['tags']['#attached']['library'][] = 'finto_taxonomy/kifiform-tags-plugin'; - // Add this block to restrict new term creation in 'asklib_tags' when language is Swedish - if ($this->entity->language()->getId() == 'sv') { + // Add this block to restrict new term creation in 'asklib_tags' when language is Swedish or English + $content_langcode = $this->entity->language()->getId(); + if (in_array($content_langcode, ['sv', 'en'])) { // Allow auto creation only in 'finto' vocabulary. $form['tags']['widget']['#selection_settings']['auto_create_bundles'] = ['finto']; $form['#attached']['drupalSettings']['kifiform'] = [ From 08ccd88fbb1ffa6b6c08a7c6498c9bfac1eb3c44 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Mon, 18 Nov 2024 13:59:10 +0000 Subject: [PATCH 09/16] Update tag selection description in QuestionAdminForm --- src/Form/QuestionAdminForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Form/QuestionAdminForm.php b/src/Form/QuestionAdminForm.php index ec89637..e4837a0 100644 --- a/src/Form/QuestionAdminForm.php +++ b/src/Form/QuestionAdminForm.php @@ -257,7 +257,7 @@ public function form(array $form, FormStateInterface $form_state) { ]; } - $form['tags']['widget']['target_id']['#description'] = $this->t('Select keywords from the drop-down list or press Enter to add a new one.'); + $form['tags']['widget']['target_id']['#description'] = $this->t('Select keywords from the drop-down list.'); $form['tags']['tags_legend'] = [ 'finto_legend' => [ From 8031937ae0151aaedf29b2c915a4f4a18cb9e835 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Tue, 26 Nov 2024 14:31:32 +0000 Subject: [PATCH 10/16] Complete QuestionSearch separation from ContentSearch The indexing was broken for Questions. Additional setup for the QuestionSearch was added, ensuring proper indexing. --- src/Plugin/Search/QuestionSearch.php | 38 +++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/Plugin/Search/QuestionSearch.php b/src/Plugin/Search/QuestionSearch.php index e610658..4039012 100644 --- a/src/Plugin/Search/QuestionSearch.php +++ b/src/Plugin/Search/QuestionSearch.php @@ -24,11 +24,10 @@ use Html2Text\Html2Text; use Symfony\Component\DependencyInjection\ContainerInterface; -use Drupal\kifisearch\Plugin\Search\ContentSearch; +use Ehann\RediSearch\Index; use Drupal\asklib\QuestionIndexer; use Drupal\kifisearch\Plugin\Search\CustomSearchBase; use Drupal\kifisearch\Query\KifiBuilderInterface; -use Ehann\RediSearch\Query\BuilderInterface; /** * Search and indexing for asklib_question and asklib_answer entities. @@ -38,9 +37,34 @@ * title = @Translation("Ask a Librarian") * ) */ -class QuestionSearch extends CustomSearchBase { +class QuestionSearch extends CustomSearchBase implements SearchIndexingInterface { + + public const SEARCH_ID = 'asklib_search'; + protected $database; + protected $searchSettings; + + static public 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('language_manager'), + $container->get('kifisearch.client'), + $container->get('database'), + $container->get('config.factory')->get('search.settings') + ); + } + + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_manager, LanguageManagerInterface $languages, Index $kifi_index, Connection $database, Config $search_settings) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_manager, $languages, $kifi_index); + + $this->database = $database; + $this->searchSettings = $search_settings; + } + /** * @param $result Redisearch response. */ @@ -255,4 +279,12 @@ public function buildSearchUrlQuery(FormStateInterface $form_state) { return $query; } + + public function markForReindex() { + $this->database->query('UPDATE {kifisearch_index} SET reindex = 1'); + } + + public function indexClear() { + $this->database->query('DELETE FROM {kifisearch_index}'); + } } From 8d037d28ba734a6961056ef3c3895a8b985f8291 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Fri, 27 Dec 2024 11:35:32 +0000 Subject: [PATCH 11/16] Fix: Refactor breadcrumb services to include path matcher and cacheable metadata This will resolve the issue with running the 'drush updatedb' when Drupal has been updated to 10.4 from 10.1. --- asklib.services.yml | 8 ++++---- src/Breadcrumb/QuestionFromCollectionCrumb.php | 14 ++++++++------ src/Breadcrumb/QuestionFromKeywordIndexCrumb.php | 15 ++++++++------- src/Breadcrumb/TaxonomyCrumb.php | 13 +++++++------ 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/asklib.services.yml b/asklib.services.yml index 887af17..625022c 100644 --- a/asklib.services.yml +++ b/asklib.services.yml @@ -24,7 +24,7 @@ services: - { name: cache.context } asklib.breadcrumb.default: class: Drupal\system\PathBasedBreadcrumbBuilder - arguments: ['@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory', '@title_resolver', '@current_user', '@path.current'] + arguments: ['@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory', '@title_resolver', '@current_user', '@path.current', '@path.matcher'] tags: - { name: asklib_breadcrumb, priority: 0 } asklib.breadcrumb.archive: @@ -34,17 +34,17 @@ services: - { name: asklib_breadcrumb, priority: 100 } asklib.breadcrumb.question_from_collection: class: 'Drupal\asklib\Breadcrumb\QuestionFromCollectionCrumb' - arguments: ['@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory', '@title_resolver', '@current_user', '@path.current', '@request_stack', '@entity_type.manager'] + arguments: ['@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory', '@title_resolver', '@current_user', '@path.current', '@path.matcher', '@request_stack', '@entity_type.manager'] tags: - { name: asklib_breadcrumb, priority: 200 } asklib.breadcrumb.collection_from_keyword_index: class: 'Drupal\asklib\Breadcrumb\QuestionFromKeywordIndexCrumb' - arguments: ['@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory', '@title_resolver', '@current_user', '@path.current', '@request_stack', '@entity_type.manager'] + arguments: ['@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory', '@title_resolver', '@current_user', '@path.current', '@path.matcher', '@request_stack', '@entity_type.manager'] tags: - { name: asklib_breadcrumb, priority: 200 } asklib.breadcrumb.taxonomy: class: 'Drupal\asklib\Breadcrumb\TaxonomyCrumb' - arguments: ['@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory', '@title_resolver', '@current_user', '@path.current', '@request_stack'] + arguments: ['@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory', '@title_resolver', '@current_user', '@path.current', '@path.matcher', '@request_stack'] tags: - { name: asklib_breadcrumb, priority: 200 } asklib.breadcrumb_proxy: diff --git a/src/Breadcrumb/QuestionFromCollectionCrumb.php b/src/Breadcrumb/QuestionFromCollectionCrumb.php index 841b00d..8ca62d3 100644 --- a/src/Breadcrumb/QuestionFromCollectionCrumb.php +++ b/src/Breadcrumb/QuestionFromCollectionCrumb.php @@ -15,6 +15,8 @@ use Drupal\Core\Path\CurrentPathStack; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Drupal\Core\Path\PathMatcherInterface; +use Drupal\Core\Cache\CacheableMetadata; class QuestionFromCollectionCrumb extends PathBasedBreadcrumbBuilder { protected $requestStack; @@ -28,14 +30,14 @@ public static function collectionIdFromQuery($from) { } } - public function __construct(RequestContext $context, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, ConfigFactoryInterface $config_factory, TitleResolverInterface $title_resolver, AccountInterface $current_user, CurrentPathStack $current_path, RequestStack $request_stack, EntityTypeManagerInterface $entity_manager) { - parent::__construct($context, $access_manager, $router, $path_processor, $config_factory, $title_resolver, $current_user, $current_path); - $this->requestStack = $request_stack; - $this->nodeStorage = $entity_manager->getStorage('node'); - } + public function __construct(RequestContext $context, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, ConfigFactoryInterface $config_factory, TitleResolverInterface $title_resolver, AccountInterface $current_user, CurrentPathStack $current_path, ?PathMatcherInterface $path_matcher = NULL, RequestStack $request_stack, EntityTypeManagerInterface $entity_manager) { + parent::__construct($context, $access_manager, $router, $path_processor, $config_factory, $title_resolver, $current_user, $current_path, $path_matcher); + $this->requestStack = $request_stack; + $this->nodeStorage = $entity_manager->getStorage('node'); + } - public function applies(RouteMatchInterface $route_match) { + public function applies(RouteMatchInterface $route_match, ?CacheableMetadata $cacheable_metadata = NULL) { return ctype_digit(self::collectionIdFromQuery($this->from())); } diff --git a/src/Breadcrumb/QuestionFromKeywordIndexCrumb.php b/src/Breadcrumb/QuestionFromKeywordIndexCrumb.php index bc1bb21..601caa1 100644 --- a/src/Breadcrumb/QuestionFromKeywordIndexCrumb.php +++ b/src/Breadcrumb/QuestionFromKeywordIndexCrumb.php @@ -15,6 +15,8 @@ use Drupal\Core\Path\CurrentPathStack; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Drupal\Core\Path\PathMatcherInterface; +use Drupal\Core\Cache\CacheableMetadata; class QuestionFromKeywordIndexCrumb extends PathBasedBreadcrumbBuilder { protected $requestStack; @@ -28,14 +30,13 @@ public static function termIdFromQuery($from) { } } - public function __construct(RequestContext $context, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, ConfigFactoryInterface $config_factory, TitleResolverInterface $title_resolver, AccountInterface $current_user, CurrentPathStack $current_path, RequestStack $request_stack, EntityTypeManagerInterface $entity_manager) { - parent::__construct($context, $access_manager, $router, $path_processor, $config_factory, $title_resolver, $current_user, $current_path); - - $this->requestStack = $request_stack; - $this->termStorage = $entity_manager->getStorage('taxonomy_term'); - } + public function __construct(RequestContext $context, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, ConfigFactoryInterface $config_factory, TitleResolverInterface $title_resolver, AccountInterface $current_user, CurrentPathStack $current_path, ?PathMatcherInterface $path_matcher = NULL, RequestStack $request_stack, EntityTypeManagerInterface $entity_manager) { + parent::__construct($context, $access_manager, $router, $path_processor, $config_factory, $title_resolver, $current_user, $current_path, $path_matcher); + $this->requestStack = $request_stack; + $this->termStorage = $entity_manager->getStorage('taxonomy_term'); + } - public function applies(RouteMatchInterface $route_match) { + public function applies(RouteMatchInterface $route_match, ?CacheableMetadata $cacheable_metadata = NULL) { return ctype_digit(self::termIdFromQuery($this->from())); } diff --git a/src/Breadcrumb/TaxonomyCrumb.php b/src/Breadcrumb/TaxonomyCrumb.php index 35f45db..cedf76f 100644 --- a/src/Breadcrumb/TaxonomyCrumb.php +++ b/src/Breadcrumb/TaxonomyCrumb.php @@ -16,6 +16,8 @@ use Drupal\system\PathBasedBreadcrumbBuilder; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Drupal\Core\Path\PathMatcherInterface; +use Drupal\Core\Cache\CacheableMetadata; class TaxonomyCrumb extends PathBasedBreadcrumbBuilder { protected $requestStack; @@ -57,13 +59,12 @@ public static function fixLinkTitles(RouteMatchInterface $route_match, Breadcrum return $crumb; } - public function __construct(RequestContext $context, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, ConfigFactoryInterface $config_factory, TitleResolverInterface $title_resolver, AccountInterface $current_user, CurrentPathStack $current_path, RequestStack $request_stack) { - parent::__construct($context, $access_manager, $router, $path_processor, $config_factory, $title_resolver, $current_user, $current_path); - - $this->requestStack = $request_stack; - } + public function __construct(RequestContext $context, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, ConfigFactoryInterface $config_factory, TitleResolverInterface $title_resolver, AccountInterface $current_user, CurrentPathStack $current_path, ?PathMatcherInterface $path_matcher = NULL, RequestStack $request_stack) { + parent::__construct($context, $access_manager, $router, $path_processor, $config_factory, $title_resolver, $current_user, $current_path, $path_matcher); + $this->requestStack = $request_stack; + } - public function applies(RouteMatchInterface $route_match) { + public function applies(RouteMatchInterface $route_match, ?CacheableMetadata $cacheable_metadata = NULL) { if (in_array($route_match->getRouteName(), $this->allowedRoutes)) { if ($route_match->getRouteName() == 'entity.taxonomy_term.canonical') { $vid = $route_match->getParameter('taxonomy_term')->bundle(); From ad939cd30816309ee73e2b39527a2c73f863cddd Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Wed, 12 Feb 2025 15:03:21 +0000 Subject: [PATCH 12/16] Fix missing status message in asklib embedded form SameSite cookie restrictions prevent session-based messages in iframes. Use a `submitted=1` query parameter to ensure the confirmation message displays correctly via Drupal Messenger after form submission. --- src/Form/RemoteQuestionForm.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Form/RemoteQuestionForm.php b/src/Form/RemoteQuestionForm.php index 9360dad..eb4a947 100644 --- a/src/Form/RemoteQuestionForm.php +++ b/src/Form/RemoteQuestionForm.php @@ -28,6 +28,14 @@ public function form(array $form, FormStateInterface $form_state) { } } + // Check if form was submitted by looking for the URL query parameter. + $submitted = \Drupal::request()->query->get('submitted'); + + if ($submitted) { + $message = \Drupal::config('asklib.settings')->get('confirmation'); + $this->messenger()->addStatus($message); + } + // Need to set language manually because Question is not marked as 'translatable'. $form['langcode'] = [ '#type' => 'value', @@ -62,8 +70,8 @@ public function submitForm(array &$form, FormStateInterface $form_state) { public function save(array $form, FormStateInterface $form_state) { $this->entity->setNotificationFlags(-1); - $message = \Drupal::config('asklib.settings')->get('confirmation'); - $this->messenger()->addStatus($message); + $current_path = \Drupal::service('path.current')->getPath(); + $form_state->setRedirectUrl(\Drupal\Core\Url::fromUserInput($current_path, ['query' => ['submitted' => 1]])); return parent::save($form, $form_state); } From ea559a2db58aa3fb03e64dd3eed0b4ab564af6b6 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Tue, 8 Apr 2025 10:34:29 +0000 Subject: [PATCH 13/16] Filter unpublished municipalities from Question form --- config/schema/asklib.schema.yml | 3 ++ src/Entity/Question.php | 2 +- .../MunicipalitySelection.php | 34 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 config/schema/asklib.schema.yml create mode 100644 src/Plugin/EntityReferenceSelection/MunicipalitySelection.php diff --git a/config/schema/asklib.schema.yml b/config/schema/asklib.schema.yml new file mode 100644 index 0000000..4c94db0 --- /dev/null +++ b/config/schema/asklib.schema.yml @@ -0,0 +1,3 @@ +entity_reference_selection.asklib_question_municipality_selection: + type: entity_reference_selection.default + label: 'Question municipality selection handler settings' diff --git a/src/Entity/Question.php b/src/Entity/Question.php index 82d69ab..991baf8 100755 --- a/src/Entity/Question.php +++ b/src/Entity/Question.php @@ -563,7 +563,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDescription(t('Municipality of the user.')) ->setSettings([ 'target_type' => 'taxonomy_term', - 'handler' => 'default', + 'handler' => 'asklib_question_municipality_selection', 'handler_settings' => [ 'target_bundles' => ['asklib_municipalities' => 'asklib_municipalities'], 'sort' => [ diff --git a/src/Plugin/EntityReferenceSelection/MunicipalitySelection.php b/src/Plugin/EntityReferenceSelection/MunicipalitySelection.php new file mode 100644 index 0000000..09e7dca --- /dev/null +++ b/src/Plugin/EntityReferenceSelection/MunicipalitySelection.php @@ -0,0 +1,34 @@ +condition('status', 1); + + return $query; + } + +} From d985dc8f454817d4e4b6d0d01f9910dbf8a2aa04 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Fri, 11 Apr 2025 12:19:46 +0000 Subject: [PATCH 14/16] Hide unpublished themes from Question form --- src/Form/QuestionForm.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Form/QuestionForm.php b/src/Form/QuestionForm.php index 873107b..86bebf2 100755 --- a/src/Form/QuestionForm.php +++ b/src/Form/QuestionForm.php @@ -131,6 +131,7 @@ protected function getCategoryOptions() { ->select('taxonomy_term_field_data', 't') ->fields('t', ['tid', 'name']) ->condition('t.vid', 'asklib_themes') + ->condition('t.status', 1) ->condition('t.langcode', $langcode) ->orderBy('t.name') ; From f37ef68b30ed1ad3e083272a9708fad5b5327763 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Tue, 27 May 2025 18:50:01 +0000 Subject: [PATCH 15/16] Fix municipality dropdown to sort options by current language Ensures users see municipality options in the correct alphabetical order for their language in the asklib question form. --- asklib.module | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/asklib.module b/asklib.module index 74ec541..b3badde 100644 --- a/asklib.module +++ b/asklib.module @@ -315,6 +315,19 @@ function asklib_form_alter(&$form, FormStateInterface $form_state, $form_id) { } } +function asklib_form_asklib_question_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) { + if ( + isset($form['municipality']['widget']['#options']) && + is_array($form['municipality']['widget']['#options']) + ) { + $options = $form['municipality']['widget']['#options']; + uasort($options, function ($a, $b) { + return strcoll($a, $b); // Locale-aware string comparison + }); + $form['municipality']['widget']['#options'] = $options; + } +} + function asklib_preprocess_views_view_table(&$variables) { if ($variables['view']->id() != 'asklib_index') { return; From 700f5fcee7e358c56c6799b501b20ec2bbafb824 Mon Sep 17 00:00:00 2001 From: Elias Tertsunen Date: Fri, 12 Dec 2025 13:30:46 +0000 Subject: [PATCH 16/16] =?UTF-8?q?Fix=20broken=20=E2=80=9Crelease=E2=80=9D?= =?UTF-8?q?=20button=20in=20QuestionAdminForm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the actual question release submit button to the header and remove the duplicate button version entirely. This fix is needed due to the security hardening introduced in Drupal 10.5.2, which allows only one triggering element per form. Security fix: https://www.drupal.org/project/drupal/issues/3465041 Regression issue: https://www.drupal.org/project/drupal/issues/3541847 --- src/Form/QuestionAdminForm.php | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/Form/QuestionAdminForm.php b/src/Form/QuestionAdminForm.php index e4837a0..0f588e0 100644 --- a/src/Form/QuestionAdminForm.php +++ b/src/Form/QuestionAdminForm.php @@ -436,20 +436,6 @@ public function actions(array $form, FormStateInterface $form_state) { '#limit_validation_errors' => [], ]; } else if ($question->access('release')) { - $actions['release'] = [ - '#type' => 'submit', - // '#value' => $this->t('Release question'), - '#value' => $question->isAnswered() ? $this->t('Release question') : $this->t('Release to waiting queue'), - '#submit' => ['::release'], - '#validate' => ['::validateRelease'], - '#limit_validation_errors' => [], - - // This button is placed in the reserved status notification and we want to hide it - // from the bottom of the form. - '#attributes' => [ - 'style' => 'display: none' - ] - ]; $actions['submit']['#submit'] = [ '::submitForm', @@ -694,13 +680,19 @@ protected function getQuestionFormHeader(QuestionInterface $question) { } if ($question->access('release')) { - $header['release'] = [ - '#type' => 'button', - '#value' => $question->isAnswered() ? $this->t('Release question') : $this->t('Release to waiting queue'), - '#attributes' => [ - 'formnovalidate' => true, - ], - ]; + $header['release'] = [ + '#type' => 'submit', + '#value' => $question->isAnswered() + ? $this->t('Release question') + : $this->t('Release to waiting queue'), + '#name' => 'question_release_header', + '#submit' => ['::release'], + '#validate' => ['::validateRelease'], + '#limit_validation_errors' => [], + '#attributes' => [ + 'formnovalidate' => 'formnovalidate', + ], + ]; } } else { $header['reserved_status'] = [