FacetListBuilder.php 12.4 KB
Newer Older
Nick_vh's avatar
Nick_vh committed
1 2
<?php

3
namespace Drupal\facets;
Nick_vh's avatar
Nick_vh committed
4

5
use Drupal\Core\Config\Entity\DraggableListBuilder;
Nick_vh's avatar
Nick_vh committed
6
use Drupal\Core\Entity\EntityInterface;
7
use Drupal\Core\Form\FormStateInterface;
8
use Drupal\Core\Link;
9
use Drupal\Component\Utility\Html;
10
use Drupal\Core\Url;
11 12
use Drupal\facets_summary\Entity\FacetsSummary;
use Drupal\facets_summary\FacetsSummaryInterface;
Nick_vh's avatar
Nick_vh committed
13 14

/**
15
 * Builds a listing of facet entities.
Nick_vh's avatar
Nick_vh committed
16
 */
17 18
class FacetListBuilder extends DraggableListBuilder {

Nick_vh's avatar
Nick_vh committed
19 20 21
  /**
   * {@inheritdoc}
   */
22 23
  public function getFormId() {
    return 'facets_overview';
Nick_vh's avatar
Nick_vh committed
24 25 26 27 28 29
  }

  /**
   * {@inheritdoc}
   */
  public function getDefaultOperations(EntityInterface $entity) {
30
    $operations = parent::getDefaultOperations($entity);
Nick_vh's avatar
Nick_vh committed
31 32

    if ($entity instanceof FacetInterface) {
Nick_vh's avatar
Nick_vh committed
33

Nick_vh's avatar
Nick_vh committed
34
      if ($entity->access('update') && $entity->hasLinkTemplate('edit-form')) {
35
        $operations['edit'] = [
Nick_vh's avatar
Nick_vh committed
36 37
          'title' => $this->t('Edit'),
          'weight' => 10,
38
          'url' => $entity->toUrl('edit-form'),
39
        ];
Nick_vh's avatar
Nick_vh committed
40
      }
41
      if ($entity->access('update') && $entity->hasLinkTemplate('settings-form')) {
42
        $operations['settings'] = [
43
          'title' => $this->t('Facet settings'),
44
          'weight' => 20,
45
          'url' => $entity->toUrl('settings-form'),
46
        ];
47
      }
48
      if ($entity->access('update') && $entity->hasLinkTemplate('clone-form')) {
49
        $operations['clone'] = [
50 51 52
          'title' => $this->t('Clone facet'),
          'weight' => 90,
          'url' => $entity->toUrl('clone-form'),
53
        ];
54
      }
Nick_vh's avatar
Nick_vh committed
55
      if ($entity->access('delete') && $entity->hasLinkTemplate('delete-form')) {
56
        $operations['delete'] = [
Nick_vh's avatar
Nick_vh committed
57 58
          'title' => $this->t('Delete'),
          'weight' => 100,
59
          'url' => $entity->toUrl('delete-form'),
60
        ];
Nick_vh's avatar
Nick_vh committed
61
      }
62 63
    }
    elseif ($entity instanceof FacetsSummaryInterface) {
64
      $operations['edit'] = [
65 66 67
        'title' => $this->t('Edit'),
        'weight' => 10,
        'url' => $entity->toUrl('edit-form'),
68
      ];
69
      if ($entity->access('update') && $entity->hasLinkTemplate('settings-form')) {
70
        $operations['settings'] = [
71 72 73
          'title' => $this->t('Facet Summary settings'),
          'weight' => 20,
          'url' => $entity->toUrl('settings-form'),
74
        ];
75 76
      }
      if ($entity->access('delete') && $entity->hasLinkTemplate('delete-form')) {
77
        $operations['delete'] = [
78 79 80
          'title' => $this->t('Delete'),
          'weight' => 100,
          'url' => $entity->toUrl('delete-form'),
81
        ];
82
      }
Nick_vh's avatar
Nick_vh committed
83 84 85 86 87 88 89 90 91
    }

    return $operations;
  }

  /**
   * {@inheritdoc}
   */
  public function buildHeader() {
92
    $header = [
93 94 95
      'type' => $this->t('Type'),
      'title' => [
        'data' => $this->t('Title'),
96
      ],
97 98
    ];
    return $header + parent::buildHeader();
Nick_vh's avatar
Nick_vh committed
99 100 101 102 103 104
  }

  /**
   * {@inheritdoc}
   */
  public function buildRow(EntityInterface $entity) {
105
    /** @var \Drupal\facets\FacetInterface $entity */
106
    $facet = $entity;
107 108 109
    $facet_configs = \Drupal::entityTypeManager()
      ->getStorage('facets_facet')
      ->load($facet->getConfigTarget());
110 111 112 113 114 115 116
    $row = [
      'type' => [
        '#theme_wrappers' => [
          'container' => [
            '#attributes' => ['class' => 'facets-type'],
          ],
        ],
117 118
        '#type' => 'markup',
        '#markup' => 'Facet',
119 120
      ],
      'title' => [
121
        '#type' => 'link',
122
        '#title' => $facet_configs->get('name'),
123
        '#suffix' => '<div>' . $entity->getFieldAlias() . ' - ' . $facet->getWidget()['type'] . '</div>',
124 125 126 127 128 129 130
        '#attributes' => [
          'class' => ['search-api-title'],
        ],
      ] + $facet->toUrl('edit-form')->toRenderArray(),
      '#attributes' => [
        'title' => $this->t('ID: @name', ['@name' => $facet->id()]),
        'class' => [
131
          'facet',
132 133 134
        ],
      ],
    ];
135 136 137 138 139 140 141
    return array_merge_recursive($row, parent::buildRow($entity));
  }

  /**
   * Builds an array of facet summary for display in the overview.
   */
  public function buildFacetSummaryRow(FacetsSummaryInterface $entity) {
142
    /** @var \Drupal\facets\FacetInterface $entity */
143 144
    $facet = $entity;
    $row = parent::buildRow($entity);
145 146 147 148 149 150 151
    return [
      'type' => [
        '#theme_wrappers' => [
          'container' => [
            '#attributes' => ['class' => 'facets-summary-type'],
          ],
        ],
152 153
        '#type' => 'markup',
        '#markup' => 'Facets Summary',
154 155
      ],
      'title' => [
156 157 158
        '#theme_wrappers' => [
          'container' => [
            '#attributes' => ['class' => 'facets-title'],
159
          ],
160 161 162 163 164 165 166 167 168 169 170
        ],
        '#type' => 'link',
        '#title' => $facet->label(),
        '#attributes' => [
          'class' => ['search-api-title'],
        ],
        '#wrapper_attributes' => [
          'colspan' => 2,
        ],
      ] + $facet->toUrl('edit-form')->toRenderArray(),
      'operations' => $row['operations'],
171 172 173
      '#attributes' => [
        'title' => $this->t('ID: @name', ['@name' => $facet->id()]),
        'class' => [
174
          'facet',
175 176 177
        ],
      ],
    ];
178 179 180
  }

  /**
181
   * Builds an array of facet sources for display in the overview.
182
   */
183
  public function buildFacetSourceRow(array $facet_source = []) {
184 185 186 187 188 189 190
    return [
      'type' => [
        '#theme_wrappers' => [
          'container' => [
            '#attributes' => ['class' => 'facets-type'],
          ],
        ],
191 192
        '#type' => 'markup',
        '#markup' => 'Facet source',
193 194 195 196 197
      ],
      'title' => [
        '#theme_wrappers' => [
          'container' => [
            '#attributes' => ['class' => 'facets-title'],
198
          ],
199
        ],
200 201
        '#type' => 'markup',
        '#markup' => $facet_source['id'],
202
        '#wrapper_attributes' => [
203
          'colspan' => 2,
204 205 206
        ],
      ],
      'operations' => [
207 208 209 210 211
        'data' => Link::createFromRoute(
          $this->t('Configure'),
          'entity.facets_facet_source.edit_form',
          ['facets_facet_source' => $facet_source['id']]
        )->toRenderable(),
212 213 214
      ],
      '#attributes' => [
        'class' => ['facet-source', 'facet-source-' . $facet_source['id']],
215
        'no_striping' => TRUE,
216 217
      ],
    ];
Nick_vh's avatar
Nick_vh committed
218 219 220 221 222
  }

  /**
   * {@inheritdoc}
   */
223
  public function buildForm(array $form, FormStateInterface $form_state) {
224
    $groups = $this->loadGroups();
225

226
    $form['facets'] = [
227 228 229
      '#type' => 'table',
      '#header' => $this->buildHeader(),
      '#empty' => $groups['lone_facets'] ? '' : $this->t('There are no facet sources or facets defined.'),
230 231
      '#attributes' => [
        'class' => [
232
          'facets-groups-list',
233 234 235
        ],
      ],
    ];
236

237 238 239 240 241 242 243
    // When no facet sources are found, we should show a message that you can't
    // add facets yet.
    if (empty($groups['facet_source_groups'])) {
      return [
        '#markup' => $this->t(
          'You currently have no facet sources defined. You should start by adding a facet source before creating facets.<br />
           An example of a facet source is a view based on Search API or a Search API page.
244
           Other modules can also implement a facet source by providing a plugin that implements the FacetSourcePluginInterface.'
borisson_'s avatar
borisson_ committed
245
        ),
246 247 248
      ];
    }

249
    $form['#attached']['library'][] = 'facets/drupal.facets.admin_css';
250 251

    foreach ($groups['facet_source_groups'] as $facet_source_group) {
252 253 254
      $subgroup_class = Html::cleanCssIdentifier('facets-weight-' . $facet_source_group['facet_source']['id']);
      $delta = round(count($facet_source_group['facets']) / 2);

255
      $form['facets']['#tabledrag'][] = [
256 257 258 259
        'action' => 'order',
        'relationship' => 'sibling',
        'group' => 'weight',
        'subgroup' => $subgroup_class,
260
      ];
261
      $form['facets'][$facet_source_group['facet_source']['id']] = $this->buildFacetSourceRow($facet_source_group['facet_source']);
262
      foreach ($facet_source_group['facets'] as $facet) {
263 264 265 266 267
        if ($facet instanceof FacetInterface) {
          $form['facets'][$facet->id()] = $this->buildRow($facet);
          $form['facets'][$facet->id()]['weight']['#attributes']['class'][] = $subgroup_class;
          $form['facets'][$facet->id()]['weight']['#delta'] = $delta;
        }
268
        elseif ($facet instanceof FacetsSummaryInterface) {
269 270
          $form['facets'][$facet->id()] = $this->buildFacetSummaryRow($facet);
        }
271 272 273 274 275
      }
    }

    // Output the list of facets without a facet source separately.
    if (!empty($groups['lone_facets'])) {
276
      $subgroup_class = 'facets-weight-lone-facets';
277
      $form['facets']['#tabledrag'][] = [
278 279 280 281
        'action' => 'order',
        'relationship' => 'sibling',
        'group' => 'weight',
        'subgroup' => $subgroup_class,
282 283 284 285 286 287 288 289
      ];
      $form['facets']['lone_facets'] = [
        'type' => [
          '#theme_wrappers' => [
            'container' => [
              '#attributes' => ['class' => 'facets-type'],
            ],
          ],
290 291
          '#type' => 'markup',
          '#markup' => '<h3>' . $this->t('Facets not currently associated with any facet source') . '</h3>',
292 293
        ],
        '#wrapper_attributes' => [
294
          'colspan' => 4,
295 296
        ],
      ];
297 298
      /** @var \Drupal\facets\FacetInterface $facet */
      foreach ($groups['lone_facets'] as $facet) {
299 300
        // Facets core search moved into a separate project. Show a clean
        // message to notify users how to resolve their broken facets.
301 302
        if (substr($facet->getFacetSourceId(), 0, 16) == 'core_node_search') {
          $project_link = Link::fromTextAndUrl('https://www.drupal.org/project/facets_core_search', Url::fromUri('https://www.drupal.org/project/facets_core_search'))->toString();
303 304
          drupal_set_message(t('Core search facets has been moved to a separate project. You need to download and enable this module from @project_link to continue using your core search facets.', ['@project_link' => $project_link]), 'error');
        }
305 306
        $form['facets'][$facet->id()] = $this->buildRow($facet);
        $form['facets'][$facet->id()]['weight']['#attributes']['class'][] = $subgroup_class;
307 308
      }
    }
309

310
    $form['actions']['#type'] = 'actions';
311
    $form['actions']['submit'] = [
312 313 314
      '#type' => 'submit',
      '#value' => t('Save'),
      '#button_type' => 'primary',
315
    ];
316 317 318 319 320 321 322 323 324 325 326

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $entities = $this->storage->loadMultiple(array_keys($form_state->getValue('facets')));
    /** @var \Drupal\block\BlockInterface[] $entities */
    foreach ($entities as $entity_id => $entity) {
327
      $entity_values = $form_state->getValue(['facets', $entity_id]);
328 329 330 331
      $entity->setWeight($entity_values['weight']);
      $entity->save();
    }
    drupal_set_message(t('The facets have been updated.'));
Nick_vh's avatar
Nick_vh committed
332 333
  }

334 335 336 337 338 339 340 341 342 343
  /**
   * Loads facet sources and facets, grouped by facet sources.
   *
   * @return \Drupal\Core\Config\Entity\ConfigEntityInterface[][]
   *   An associative array with two keys:
   *   - facet sources: All available facet sources, each followed by all facets
   *     attached to it.
   *   - lone_facets: All facets that aren't attached to any facet source.
   */
  public function loadGroups() {
344
    $facet_source_plugin_manager = \Drupal::service('plugin.manager.facets.facet_source');
345
    $facets = $this->load();
346 347 348 349
    $facets_summaries = [];
    if (\Drupal::moduleHandler()->moduleExists('facets_summary')) {
      $facets_summaries = FacetsSummary::loadMultiple();
    }
350 351
    $facet_sources = $facet_source_plugin_manager->getDefinitions();

352
    $facet_source_groups = [];
353 354 355
    foreach ($facet_sources as $facet_source) {
      $facet_source_groups[$facet_source['id']] = [
        'facet_source' => $facet_source,
borisson_'s avatar
borisson_ committed
356
        'facets' => [],
357 358 359
      ];

      foreach ($facets as $facet) {
360
        /** @var \Drupal\facets\FacetInterface $facet */
361
        if ($facet->getFacetSourceId() == $facet_source['id']) {
362 363 364 365 366 367
          $facet_source_groups[$facet_source['id']]['facets'][$facet->id()] = $facet;
          // Remove this facet from $facet so it will finally only contain those
          // facets not belonging to any facet_source.
          unset($facets[$facet->id()]);
        }
      }
368 369 370 371 372 373 374 375 376 377

      foreach ($facets_summaries as $summary) {
        /** @var \Drupal\facets_summary\FacetsSummaryInterface $summary */
        if ($summary->getFacetSourceId() == $facet_source['id']) {
          $facet_source_groups[$facet_source['id']]['facets'][$summary->id()] = $summary;
          // Remove this facet from $facet so it will finally only contain those
          // facets not belonging to any facet_source.
          unset($facets_summaries[$summary->id()]);
        }
      }
378 379
    }

380
    return [
381 382
      'facet_source_groups' => $facet_source_groups,
      'lone_facets' => $facets,
383
    ];
384
  }
385

Nick_vh's avatar
Nick_vh committed
386
}