murmurations-1.0.0-alpha1/murmurations.module
murmurations.module
<?php use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Access\AccessResult; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\murmurations\Plugin\Murmurations\PluginMultipleBase; use Drupal\murmurations\MurmurationsPluginInterface; use Drupal\murmurations\MurmurationsPluginMultipleInterface; /** * @todo remind user after installation to set the index url */ /** * Implements hook_ENTITY_TYPE_insert(). */ function murmurations_entity_insert(EntityInterface $entity) { $plugin_manager = \Drupal::service('plugin.manager.murmurations'); if ($plugin = $plugin_manager->getByEntityType($entity->getEntityTypeId())) { /** @var $plugin Drupal\murmurations\MurmurationsPluginMultipleInterface */ if ($plugin->setEntity($entity)->publishable()) { $publish_path = $plugin->getPublishPath(); if (!\Drupal::service('murmurations.index')->reindex($publish_path)) { \Drupal::messenger()->AddStatus('Item will be indexed later.'); \Drupal::queue('murmurations_update_index')->createItem($publish_path); } } } } /** * Implements hook_ENTITY_TYPE_update(). */ function murmurations_entity_update(EntityInterface $entity) { $plugin_manager = \Drupal::service('plugin.manager.murmurations'); if ($plugin = $plugin_manager->getByEntityType($entity->getEntityTypeId())) { /** @var $plugin Drupal\murmurations\MurmurationsPluginMultipleInterface */ if ($plugin->setEntity($entity)->publishable()) { $publish_path = $plugin->getPublishPath(); if (!\Drupal::service('murmurations.index')->reindex($publish_path)) { \Drupal::queue('murmurations_update_index')->createItem($publish_path); } } } } /** * Implements hook_ENTITY_TYPE_delete(). * Murmurations index will forget the item anyway when the profile_url returns a 404. */ function murmurations_entity_predelete(EntityInterface $entity) { $plugin_manager = \Drupal::service('plugin.manager.murmurations'); if ($plugin = $plugin_manager->getByEntityType($entity->getEntityTypeId())) { // @todo check if the entity is indexed. $plugin->setEntity($entity); if ($plugin instanceOf PluginMultipleBase and !$plugin->publishable()) { return; } try { \Drupal::service('murmurations.index')->deindex($plugin->getPublishPath()); } catch (\Exception $e) { } } } /** * Add reindex jobs to the drupal queue. * * @param array $rel_paths * * @todo How to process some of these queue items immediately? */ function murmurations_queue_items(array $rel_paths) { $queue = \Drupal::queue('murmurations_update_index'); foreach ($rel_paths as $rel_path) { $queue->createItem($rel_path); } } /** * Helper to move a point randomly * * @param array $point * An array with keys 'lat' and 'lon' * @param integer $scale * 1/1000s of a degree range of variation. */ function displace_point(array $point, $scale) : array { if ($point['lat']) { foreach ([$point['lat'], $point['lon']] as &$val) { $offset = rand(2, 7)* 0.001*$scale; if (rand(0, 1)) { $offset = -$offset; } $val += $offset; } } return $point; } /** * not sure about this. */ function murmurations_theme() { return [ 'murm_search_result_text' => [ 'pattern' => 'murm_search_result_text__', 'variables' => [ 'title' => '', 'body' => '', 'url' => '', 'tags' => '', // Might be better as an array 'distance' => 0 ] ], 'murm_search_result_map' => [ 'pattern' => 'murm_search_result_map__', 'variables' => [ 'title' => '', 'body' => '', 'url' => '', 'tags' => '', ] ] ]; } /** * Implements hook_entity_access(). * Ensure all entities exposed by murmurations are visible to anonymous users. */ function murmurations_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation) { $result = AccessResult::neutral(); if ($operation == 'view' and $entity instanceOf Drupal\Core\Entity\ContentEntityInterface) { if ($plugin = \Drupal::service('plugin.manager.murmurations')->getByEntityType($entity->getEntityTypeId())) { if (in_array($entity->id(), $plugin->getEntityIds())) { $result = AccessResult::allowed()->addCacheableDependency($entity); } } } return $result; } /** * Identify a geofield on the entity and return it as lat and lon. * * @param ContentEntityInterface $entity * @return array * with keys lat, lon */ function murmurations_geolocate_entity_with_fallback(ContentEntityInterface $entity) : array { if ($coords = murmurations_entity_field_type_value($entity, 'geofield')) { return array_intersect_key($coords, array_flip(['lat', 'lon'])); } return \Drupal::config('murmurations.settings')->get('fallback_point'); } /** * Get the first value on a field of the given type on the given entity. * * @param \Drupal\user\EntityOwnerInterface $entity * @param string $field_type * @return array */ function murmurations_entity_field_type_value(ContentEntityInterface $entity, $field_type) : array { if ($field_name = murmurations_get_fieldname_of_entitytype($entity, $field_type)) { $field_items = $entity->{$field_name}; if (!$field_items->isEmpty()) { return $field_items[0]->getValue(); // using arrayAccess on FieldItemList } } if ($entity instanceof \Drupal\user\EntityOwnerInterface) { return murmurations_entity_field_type_value($entity->getOwner(), $field_type); } \Drupal::logger('murmurations')->error( "No @type field on @entity_type @bundle", ['@type' => $field_type, '@entity_type' => $entity->getEntityTypeId(), '@bundle' => $entity->bundle()] ); return []; } /** * Get a field name of the given type on the given entity * @param ContentEntityInterface $entity * @param string $type_name * @return string */ function murmurations_get_fieldname_of_entitytype(ContentEntityInterface $entity, $type_name) : string { $fields = \Drupal::service('entity_field.manager') ->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()); foreach ($fields as $field_name => $field_info) { if ($field_info->getType() == $type_name) { return $field_name; } } return ''; } /** * Immediately remove all items from the index. * @throws \Exception */ function murmurations_deindex_all() { // Can't we just load all plugins? $murm_plugins = \Drupal::service('plugin.manager.murmurations'); foreach ($murm_plugins->getDefinitions() as $plugin_id => $def) { $plugin = $murm_plugins->createInstance($plugin_id); $batch_def = [ 'title' => 'deindexing everything on the currency murms index', 'operations' => \Drupal::Service('murmurations.index')->deindexPlugin($plugin) ]; batch_set($batch_def); // catch (\Exception $e){ // Drupal::messenger()->warning( // t('There was a problem immediately removing @plugin items from the index: @message'), // ['@plugin' => $plugin_id, '@message' => $e->getMessage()] // ); // throw $e; // } } } /** * @param MurmurationsPluginInterface $plugin * * @return array[] * Batch operations */ function murmurations_deindex_plugin_operations(MurmurationsPluginInterface $plugin) : array { $operations = []; if ($plugin instanceof MurmurationsPluginMultipleInterface) { foreach (array_chunk($plugin->getEntityIds(), 5) as $ids) { foreach ($ids as $id) { $profile_path = preg_replace('/\{[a-z]+\}/', $id, $plugin->getPluginDefinition()['profile_path']); $profile_paths[] = $profile_path; } $operations[] = ['murmurations_deindex_chunk', [$profile_paths]]; } } else { $profile_path = $plugin->getPluginDefinition()['profile_path']; $operations[] = ['murmurations_deindex_chunk', [[$profile_path]]]; } return $operations; } /** * @param MurmurationsPluginInterface $plugin * * @return array[] * Batch operations */ function murmurations_reindex_plugin_operations(MurmurationsPluginInterface $plugin) { $operations = []; if ($plugin instanceof MurmurationsPluginMultipleInterface) { foreach (array_chunk($plugin->getEntityIds(), 5) as $ids) { // 5x5sec timeout < 30 secs php timeout. foreach ($ids as $id) { $profile_path = preg_replace('/\{[a-z]+\}/', $id, $plugin->getPluginDefinition()['profile_path']); $profile_paths[] = $profile_path; } $operations[] = ['murmurations_reindex_chunk', [$profile_paths, TRUE]]; } } else { $profile_paths = [$plugin->getPluginDefinition()['profile_path']]; $operations[] = ['murmurations_reindex_chunk', [$profile_paths, TRUE]]; } return $operations; } /** * Batch callback. * @param array $profile_rel_urls */ function murmurations_deindex_chunk(array $profile_rel_urls) { $service = \Drupal::service('murmurations.index'); foreach ($profile_rel_urls as $url) { $service->deindex($url); } } /** * Batch callback. * @param array $profile_rel_urls */ function murmurations_reindex_chunk(array $profile_rel_urls, bool $drop_on_fail = FALSE) { $service = \Drupal::service('murmurations.index'); foreach ($profile_rel_urls as $url) { try { $service->reindex($url); } catch(\Exception $e) { if (!$drop_on_fail) { \Drupal::messenger('There was a problem reindexing. The system will retry later.'); murmurations_queue_items($profile_rel_urls); return; } } } }