/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, IndexInterface $index = NULL) { if (!isset($index)) { return array(); } $form['#index'] = $index; $form['#attached']['library'][] = 'search_api/drupal.search_api.admin_css'; if ($index->hasValidTracker()) { if (!\Drupal::getContainer()->get('search_api.index_task_manager')->isTrackingComplete($index)) { $form['tracking'] = array('#type' => 'details', '#title' => $this->t('Track items for index'), '#description' => $this->t('Not all items have been tracked for this index. This means the displayed index status is incomplete and not all items will currently be indexed.'), '#open' => TRUE, '#attributes' => array('class' => array('container-inline'))); $form['tracking']['index_now'] = array('#type' => 'submit', '#value' => $this->t('Track now'), '#name' => 'track_now'); } // Add the "Index now" form. $form['index'] = array('#type' => 'details', '#title' => $this->t('Index now'), '#open' => TRUE, '#attributes' => array('class' => array('container-inline'))); $has_remaining_items = $index->getTrackerInstance()->getRemainingItemsCount() > 0; $all_value = $this->t('all', array(), array('context' => 'items to index')); $limit = array('#type' => 'textfield', '#default_value' => $all_value, '#size' => 4, '#attributes' => array('class' => array('search-api-limit')), '#disabled' => !$has_remaining_items); $batch_size = array('#type' => 'textfield', '#default_value' => $index->getOption('cron_limit', $this->config('search_api.settings')->get('default_cron_limit')), '#size' => 4, '#attributes' => array('class' => array('search-api-batch-size')), '#disabled' => !$has_remaining_items); // Here it gets complicated. We want to build a sentence from the form // input elements, but to translate that we have to make the two form // elements (for limit and batch size) pseudo-variables in the $this->t() // call. // Since we can't pass them directly, we split the translated sentence // (which still has the two tokens), figure out their order and then put // the pieces together again using the form elements' #prefix and #suffix // properties. $sentence = preg_split('/@(limit|batch_size)/', $this->t('Index @limit items in batches of @batch_size items'), -1, PREG_SPLIT_DELIM_CAPTURE); // Check if the sentence contains the expected amount of parts. if (count($sentence) === 5) { $first = $sentence[1]; $form['index'][$first] = ${$first}; $form['index'][$first]['#prefix'] = $sentence[0]; $form['index'][$first]['#suffix'] = $sentence[2]; $second = $sentence[3]; $form['index'][$second] = ${$second}; $form['index'][$second]['#suffix'] = "{$sentence[4]} "; } else { // Sentence is broken. Use fallback method instead. $limit['#title'] = $this->t('Number of items to index'); $form['index']['limit'] = $limit; $batch_size['#title'] = $this->t('Number of items per batch run'); $form['index']['batch_size'] = $batch_size; } // Add the value "all" so it can be used by the validation. $form['index']['all'] = array('#type' => 'value', '#value' => $all_value); $form['index']['index_now'] = array('#type' => 'submit', '#value' => $this->t('Index now'), '#disabled' => !$has_remaining_items, '#name' => 'index_now'); // Add actions for reindexing and for clearing the index. $form['actions']['#type'] = 'actions'; $form['actions']['reindex'] = array('#type' => 'submit', '#value' => $this->t('Queue all items for reindexing'), '#name' => 'reindex', '#button_type' => 'danger'); $form['actions']['clear'] = array('#type' => 'submit', '#value' => $this->t('Clear all indexed data'), '#name' => 'clear', '#button_type' => 'danger'); } return $form; }
/** * Builds the tracker configuration form. * * @param \Drupal\search_api\IndexInterface $index * The index being created or edited. */ public function buildTrackerConfigForm(array &$form, FormStateInterface $form_state, IndexInterface $index) { if ($index->hasValidTracker()) { $tracker = $index->getTracker(); // @todo Create, use and save SubFormState already here, not only in // validate(). Also, use proper subset of $form for first parameter? if ($config_form = $tracker->buildConfigurationForm(array(), $form_state)) { $form['tracker_config']['#type'] = 'details'; $form['tracker_config']['#title'] = $this->t('Configure %plugin', array('%plugin' => $tracker->label())); $form['tracker_config']['#description'] = SafeMarkup::checkPlain($tracker->getDescription()); $form['tracker_config']['#open'] = $index->isNew(); $form['tracker_config'] += $config_form; } } elseif (!$index->isNew()) { drupal_set_message($this->t('The tracker plugin is missing or invalid.'), 'error'); } }
/** * Checks whether the index switched tracker plugin and reacts accordingly. * * Used as a helper method in postSave(). Should only be called when the index * was enabled before the change and remained so. * * @param \Drupal\search_api\IndexInterface $original * The previous version of the index. */ protected function reactToTrackerSwitch(IndexInterface $original) { // Asserts that the index was enabled before saving and will still be // enabled afterwards. Otherwise, this method should not be called. assert('$this->status() && $original->status()', '::reactToTrackerSwitch should only be called when the index is enabled'); if ($this->getTrackerId() != $original->getTrackerId()) { $index_task_manager = \Drupal::getContainer()->get('search_api.index_task_manager'); if ($original->hasValidTracker()) { $index_task_manager->stopTracking($this); } if ($this->hasValidTracker()) { $index_task_manager->startTracking($this); } } }
/** * Checks whether the index switched tracker plugin and reacts accordingly. * * Used as a helper method in postSave(). Should only be called when the index * was enabled before the change and remained so. * * @param \Drupal\search_api\IndexInterface $original * The previous version of the index. */ protected function reactToTrackerSwitch(IndexInterface $original) { if ($this->tracker != $original->getTrackerId()) { $index_task_manager = Utility::getIndexTaskManager(); if ($original->hasValidTracker()) { $index_task_manager->stopTracking($this); } if ($this->hasValidTracker()) { $index_task_manager->startTracking($this); } } }
/** * Checks whether the index switched tracker plugin and reacts accordingly. * * Used as a helper method in postSave(). Should only be called when the index * was enabled before the change and remained so. * * @param \Drupal\search_api\IndexInterface $original * The previous version of the index. */ protected function reactToTrackerSwitch(IndexInterface $original) { if ($this->tracker != $original->getTrackerId()) { if ($original->hasValidTracker()) { $original->stopTracking(); } if ($this->hasValidTracker()) { $this->startTracking(); } } }
/** * Processes an index batch operation. * * @param \Drupal\search_api\IndexInterface $index * The index on which items should be indexed. * @param int $batch_size * The maximum number of items to index per batch pass. * @param int $limit * The maximum number of items to index in total, or -1 to index all items. * @param array $context * The current batch context, as defined in the * @link batch Batch operations @endlink documentation. */ public static function process(IndexInterface $index, $batch_size, $limit, array &$context) { // Check if the sandbox should be initialized. if (!isset($context['sandbox']['limit'])) { // Initialize the sandbox with data which is shared among the batch runs. $context['sandbox']['limit'] = $limit; $context['sandbox']['batch_size'] = $batch_size; $context['sandbox']['progress'] = 0; } // Check if the results should be initialized. if (!isset($context['results']['indexed'])) { // Initialize the results with data which is shared among the batch runs. $context['results']['indexed'] = 0; $context['results']['not indexed'] = 0; } // Get the remaining item count. When no valid tracker is available then // the value will be set to zero which will cause the batch process to // stop. $remaining_item_count = ($index->hasValidTracker() ? $index->getTracker()->getRemainingItemsCount() : 0); // Check if an explicit limit needs to be used. if ($context['sandbox']['limit'] > -1) { // Calculate the remaining amount of items that can be indexed. Note that // a minimum is taking between the allowed number of items and the // remaining item count to prevent incorrect reporting of not indexed // items. $actual_limit = min($context['sandbox']['limit'] - $context['sandbox']['progress'], $remaining_item_count); } else { // Use the remaining item count as actual limit. $actual_limit = $remaining_item_count; } // Store original count of items to be indexed to show progress properly. if (empty($context['sandbox']['original_item_count'])) { $context['sandbox']['original_item_count'] = min($remaining_item_count, $actual_limit); } // Determine the number of items to index for this run. $to_index = min($actual_limit, $context['sandbox']['batch_size']); // Catch any exception that may occur during indexing. try { // Index items limited by the given count. $indexed = $index->index($to_index); // Increment the indexed result and progress. $context['results']['indexed'] += $indexed; $context['results']['not indexed'] += ($to_index - $indexed); $context['sandbox']['progress'] += $to_index; // Display progress message. if ($indexed > 0) { $context['message'] = static::formatPlural($context['results']['indexed'], 'Successfully indexed 1 item.', 'Successfully indexed @count items.'); } // Everything has been indexed? if ($indexed === 0 || $context['sandbox']['progress'] >= $context['sandbox']['original_item_count']) { $context['finished'] = 1; } else { $context['finished'] = ($context['sandbox']['progress'] / $context['sandbox']['original_item_count']); } } catch (\Exception $ex) { // Log exception to watchdog and abort the batch job. watchdog_exception('search_api', $ex); $context['message'] = static::t('An error occurred during indexing: @message', array('@message' => $ex->getMessage())); $context['finished'] = 1; $context['results']['not indexed'] += ($context['sandbox']['limit'] - $context['sandbox']['progress']); } }
/** * {@inheritdoc} */ public function stopTracking(IndexInterface $index, array $datasource_ids = NULL) { $valid_tracker = $index->hasValidTracker(); if (!isset($datasource_ids)) { $this->state->delete($this->getIndexStateKey($index)); if ($valid_tracker) { $index->getTrackerInstance()->trackAllItemsDeleted(); } return; } // Catch the case of being called with an empty array of datasources. if (!$datasource_ids) { return; } // If no state is saved, this will return NULL, making the following unset() // statements no-ops. $index_state = $this->getIndexState($index, FALSE); foreach ($datasource_ids as $datasource_id) { unset($index_state['pages'][$datasource_id]); if ($valid_tracker) { $index->getTrackerInstance()->trackAllItemsDeleted($datasource_id); } } // If we had an index state saved, update it now. if (isset($index_state)) { if (empty($index_state['pages'])) { $this->state->delete($this->getIndexStateKey($index)); } else { $this->state->set($this->getIndexStateKey($index), $index_state); } } }
/** * {@inheritdoc} */ public function hasValidTracker() { return $this->entity->hasValidTracker(); }