/** * Loops through and displays all form errors. * * @param array $form * An associative array containing the structure of the form. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. */ protected function displayErrorMessages(array $form, FormStateInterface $form_state) { $error_links = []; $errors = $form_state->getErrors(); // Loop through all form errors and check if we need to display a link. foreach ($errors as $name => $error) { $form_element = FormElementHelper::getElementByName($name, $form); $title = FormElementHelper::getElementTitle($form_element); // Only show links to erroneous elements that are visible. $is_visible_element = Element::isVisibleElement($form_element); // Only show links for elements that have a title themselves or have // children with a title. $has_title = !empty($title); // Only show links for elements with an ID. $has_id = !empty($form_element['#id']); // Do not show links to elements with suppressed messages. Most often // their parent element is used for inline errors. if (!empty($form_element['#error_no_message'])) { unset($errors[$name]); } elseif ($is_visible_element && $has_title && $has_id) { // We need to pass this through SafeMarkup::escape() so // drupal_set_message() does not encode the links. $error_links[] = SafeMarkup::escape($this->l($title, Url::fromRoute('<none>', [], ['fragment' => $form_element['#id'], 'external' => TRUE]))); unset($errors[$name]); } } // Set normal error messages for all remaining errors. foreach ($errors as $error) { $this->drupalSetMessage($error, 'error'); } if (!empty($error_links)) { $message = $this->formatPlural(count($error_links), '1 error has been found: !errors', '@count errors have been found: !errors', ['!errors' => SafeMarkup::set(implode(', ', $error_links))]); $this->drupalSetMessage($message, 'error'); } }
/** * {@inheritdoc} */ public function buildRow(EntityInterface $view) { $row = parent::buildRow($view); $display_paths = ''; $separator = ''; foreach ($this->getDisplayPaths($view) as $display_path) { $display_paths .= $separator . SafeMarkup::escape($display_path); $separator = ', '; } return array('data' => array('view_name' => array('data' => array('#theme' => 'views_ui_view_info', '#view' => $view, '#displays' => $this->getDisplaysList($view))), 'description' => array('data' => array('#markup' => String::checkPlain($view->get('description'))), 'class' => array('views-table-filter-text-source')), 'tag' => $view->get('tag'), 'path' => SafeMarkup::set($display_paths), 'operations' => $row['operations']), 'title' => $this->t('Machine name: @name', array('@name' => $view->id())), 'class' => array($view->status() ? 'views-ui-list-enabled' : 'views-ui-list-disabled')); }
/** * Prepares search results for rendering. * * @param \Drupal\Core\Database\StatementInterface $found * Results found from a successful search query execute() method. * * @return array * Array of search result item render arrays (empty array if no results). */ protected function prepareResults(StatementInterface $found) { $results = array(); $node_storage = $this->entityManager->getStorage('node'); $node_render = $this->entityManager->getViewBuilder('node'); $keys = $this->keywords; foreach ($found as $item) { // Render the node. /** @var \Drupal\node\NodeInterface $node */ $node = $node_storage->load($item->sid)->getTranslation($item->langcode); $build = $node_render->view($node, 'search_result', $item->langcode); /** @var \Drupal\node\NodeTypeInterface $type*/ $type = $this->entityManager->getStorage('node_type')->load($node->bundle()); unset($build['#theme']); $build['#pre_render'][] = array($this, 'removeSubmittedInfo'); // Fetch comment count for snippet. $rendered = SafeMarkup::set($this->renderer->renderPlain($build) . ' ' . SafeMarkup::escape($this->moduleHandler->invoke('comment', 'node_update_index', array($node, $item->langcode)))); $extra = $this->moduleHandler->invokeAll('node_search_result', array($node, $item->langcode)); $language = $this->languageManager->getLanguage($item->langcode); $username = array('#theme' => 'username', '#account' => $node->getOwner()); $result = array('link' => $node->url('canonical', array('absolute' => TRUE, 'language' => $language)), 'type' => SafeMarkup::checkPlain($type->label()), 'title' => $node->label(), 'node' => $node, 'extra' => $extra, 'score' => $item->calculated_score, 'snippet' => search_excerpt($keys, $rendered, $item->langcode), 'langcode' => $node->language()->getId()); if ($type->displaySubmitted()) { $result += array('user' => $this->renderer->renderPlain($username), 'date' => $node->getChangedTime()); } $results[] = $result; } return $results; }
/** * Handles any exception as a generic error page for HTML. * * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event * The event to process. */ protected function onHtml(GetResponseForExceptionEvent $event) { $exception = $event->getException(); $error = Error::decodeException($exception); // Display the message if the current error reporting level allows this type // of message to be displayed, and unconditionally in update.php. if (error_displayable($error)) { $class = 'error'; // If error type is 'User notice' then treat it as debug information // instead of an error message. // @see debug() if ($error['%type'] == 'User notice') { $error['%type'] = 'Debug'; $class = 'status'; } // Attempt to reduce verbosity by removing DRUPAL_ROOT from the file path // in the message. This does not happen for (false) security. $root_length = strlen(DRUPAL_ROOT); if (substr($error['%file'], 0, $root_length) == DRUPAL_ROOT) { $error['%file'] = substr($error['%file'], $root_length + 1); } // Do not translate the string to avoid errors producing more errors. unset($error['backtrace']); $message = SafeMarkup::format('%type: !message in %function (line %line of %file).', $error); // Check if verbose error reporting is on. if ($this->getErrorLevel() == ERROR_REPORTING_DISPLAY_VERBOSE) { $backtrace_exception = $exception; while ($backtrace_exception->getPrevious()) { $backtrace_exception = $backtrace_exception->getPrevious(); } $backtrace = $backtrace_exception->getTrace(); // First trace is the error itself, already contained in the message. // While the second trace is the error source and also contained in the // message, the message doesn't contain argument values, so we output it // once more in the backtrace. array_shift($backtrace); // Generate a backtrace containing only scalar argument values. Make // sure the backtrace is escaped as it can contain user submitted data. $message .= '<pre class="backtrace">' . SafeMarkup::escape(Error::formatBacktrace($backtrace)) . '</pre>'; } drupal_set_message(SafeMarkup::set($message), $class, TRUE); } $content = $this->t('The website encountered an unexpected error. Please try again later.'); $response = $this->bareHtmlPageRenderer->renderBarePage(['#markup' => $content], $this->t('Error'), 'maintenance_page'); if ($exception instanceof HttpExceptionInterface) { $response->setStatusCode($exception->getStatusCode()); $response->headers->add($exception->getHeaders()); } else { $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR, '500 Service unavailable (with message)'); } $event->setResponse($response); }
/** * {@inheritdoc} */ public function buildRow(EntityInterface $field) { if ($field->locked) { $row['class'] = array('menu-disabled'); $row['data']['id'] = t('@field_name (Locked)', array('@field_name' => $field->name)); } else { $row['data']['id'] = $field->name; } $field_type = $this->fieldTypes[$field->type]; $row['data']['type'] = t('@type (module: @module)', array('@type' => $field_type['label'], '@module' => $field_type['provider'])); $usage = array(); foreach ($field->getBundles() as $bundle) { if ($route_info = FieldUI::getOverviewRouteInfo($field->entity_type, $bundle)) { $usage[] = \Drupal::linkGenerator()->generateFromUrl($this->bundles[$field->entity_type][$bundle]['label'], $route_info); } else { $usage[] = $this->bundles[$field->entity_type][$bundle]['label']; } } $usage_escaped = ''; $separator = ''; foreach ($usage as $usage_item) { $usage_escaped .= $separator . SafeMarkup::escape($usage_item); $separator = ', '; } $row['data']['usage'] = SafeMarkup::set($usage_escaped); return $row; }
/** * {@inheritdoc} * * For anonymous users, the "active" class will be calculated on the server, * because most sites serve each anonymous user the same cached page anyway. * For authenticated users, the "active" class will be calculated on the * client (through JavaScript), only data- attributes are added to links to * prevent breaking the render cache. The JavaScript is added in * system_page_attachments(). * * @see system_page_attachments() */ public function generate($text, Url $url) { // Performance: avoid Url::toString() needing to retrieve the URL generator // service from the container. $url->setUrlGenerator($this->urlGenerator); // Start building a structured representation of our link to be altered later. $variables = array('text' => is_array($text) ? drupal_render($text) : $text, 'url' => $url, 'options' => $url->getOptions()); // Merge in default options. $variables['options'] += array('attributes' => array(), 'query' => array(), 'language' => NULL, 'set_active_class' => FALSE, 'absolute' => FALSE); // Add a hreflang attribute if we know the language of this link's url and // hreflang has not already been set. if (!empty($variables['options']['language']) && !isset($variables['options']['attributes']['hreflang'])) { $variables['options']['attributes']['hreflang'] = $variables['options']['language']->getId(); } // Set the "active" class if the 'set_active_class' option is not empty. if (!empty($variables['options']['set_active_class']) && !$url->isExternal()) { // Add a "data-drupal-link-query" attribute to let the // drupal.active-link library know the query in a standardized manner. if (!empty($variables['options']['query'])) { $query = $variables['options']['query']; ksort($query); $variables['options']['attributes']['data-drupal-link-query'] = Json::encode($query); } // Add a "data-drupal-link-system-path" attribute to let the // drupal.active-link library know the path in a standardized manner. if ($url->isRouted() && !isset($variables['options']['attributes']['data-drupal-link-system-path'])) { // @todo System path is deprecated - use the route name and parameters. $system_path = $url->getInternalPath(); // Special case for the front page. $variables['options']['attributes']['data-drupal-link-system-path'] = $system_path == '' ? '<front>' : $system_path; } } // Remove all HTML and PHP tags from a tooltip, calling expensive strip_tags() // only when a quick strpos() gives suspicion tags are present. if (isset($variables['options']['attributes']['title']) && strpos($variables['options']['attributes']['title'], '<') !== FALSE) { $variables['options']['attributes']['title'] = strip_tags($variables['options']['attributes']['title']); } // Allow other modules to modify the structure of the link. $this->moduleHandler->alter('link', $variables); // Move attributes out of options. generateFromRoute(() doesn't need them. $attributes = new Attribute($variables['options']['attributes']); unset($variables['options']['attributes']); $url->setOptions($variables['options']); // The result of the url generator is a plain-text URL. Because we are using // it here in an HTML argument context, we need to encode it properly. $url = String::checkPlain($url->toString()); // Make sure the link text is sanitized. $safe_text = SafeMarkup::escape($variables['text']); return SafeMarkup::set('<a href="' . $url . '"' . $attributes . '>' . $safe_text . '</a>'); }
/** * Testing hello world style request. */ public function processRequest(Request $request, RouteMatchInterface $route_match) { return SafeMarkup::escape($request->getMethod() . ' - ' . $request->getUri()); }
/** * Render all items in this field together. * * When using advanced render, each possible item in the list is rendered * individually. Then the items are all pasted together. */ protected function renderItems($items) { if (!empty($items)) { $output = ''; if (!$this->options['group_rows']) { foreach ($items as $item) { $output .= SafeMarkup::escape($item); } return SafeMarkup::set($output); } if ($this->options['multi_type'] == 'separator') { $output = ''; $separator = ''; $escaped_separator = Xss::filterAdmin($this->options['separator']); foreach ($items as $item) { $output .= $separator . SafeMarkup::escape($item); $separator = $escaped_separator; } return SafeMarkup::set($output); } else { $item_list = array('#theme' => 'item_list', '#items' => $items, '#title' => NULL, '#list_type' => $this->options['multi_type']); return drupal_render($item_list); } } }
/** * {@inheritdoc} */ public function execute() { $results = array(); if (!$this->isSearchExecutable()) { return $results; } $keys = $this->keywords; // Build matching conditions. $query = $this->database->select('search_index', 'i', array('target' => 'replica'))->extend('Drupal\\search\\SearchQuery')->extend('Drupal\\Core\\Database\\Query\\PagerSelectExtender'); $query->join('node_field_data', 'n', 'n.nid = i.sid'); $query->condition('n.status', 1)->addTag('node_access')->searchExpression($keys, $this->getPluginId()); // Handle advanced search filters in the f query string. // \Drupal::request()->query->get('f') is an array that looks like this in // the URL: ?f[]=type:page&f[]=term:27&f[]=term:13&f[]=langcode:en // So $parameters['f'] looks like: // array('type:page', 'term:27', 'term:13', 'langcode:en'); // We need to parse this out into query conditions, some of which go into // the keywords string, and some of which are separate conditions. $parameters = $this->getParameters(); if (!empty($parameters['f']) && is_array($parameters['f'])) { $filters = array(); // Match any query value that is an expected option and a value // separated by ':' like 'term:27'. $pattern = '/^(' . implode('|', array_keys($this->advanced)) . '):([^ ]*)/i'; foreach ($parameters['f'] as $item) { if (preg_match($pattern, $item, $m)) { // Use the matched value as the array key to eliminate duplicates. $filters[$m[1]][$m[2]] = $m[2]; } } // Now turn these into query conditions. This assumes that everything in // $filters is a known type of advanced search. foreach ($filters as $option => $matched) { $info = $this->advanced[$option]; // Insert additional conditions. By default, all use the OR operator. $operator = empty($info['operator']) ? 'OR' : $info['operator']; $where = new Condition($operator); foreach ($matched as $value) { $where->condition($info['column'], $value); } $query->condition($where); if (!empty($info['join'])) { $query->join($info['join']['table'], $info['join']['alias'], $info['join']['condition']); } } } // Add the ranking expressions. $this->addNodeRankings($query); // Run the query and load results. $find = $query->fields('i', array('langcode'))->groupBy('i.langcode')->limit(10)->execute(); // Check query status and set messages if needed. $status = $query->getStatus(); if ($status & SearchQuery::EXPRESSIONS_IGNORED) { drupal_set_message($this->t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => $this->searchSettings->get('and_or_limit'))), 'warning'); } if ($status & SearchQuery::LOWER_CASE_OR) { drupal_set_message($this->t('Search for either of the two terms with uppercase <strong>OR</strong>. For example, <strong>cats OR dogs</strong>.'), 'warning'); } if ($status & SearchQuery::NO_POSITIVE_KEYWORDS) { drupal_set_message(\Drupal::translation()->formatPlural($this->searchSettings->get('index.minimum_word_size'), 'You must include at least one positive keyword with 1 character or more.', 'You must include at least one positive keyword with @count characters or more.'), 'warning'); } $node_storage = $this->entityManager->getStorage('node'); $node_render = $this->entityManager->getViewBuilder('node'); foreach ($find as $item) { // Render the node. /** @var \Drupal\node\NodeInterface $node */ $node = $node_storage->load($item->sid)->getTranslation($item->langcode); $build = $node_render->view($node, 'search_result', $item->langcode); unset($build['#theme']); // Fetch comment count for snippet. $node->rendered = SafeMarkup::set(drupal_render($build) . ' ' . SafeMarkup::escape($this->moduleHandler->invoke('comment', 'node_update_index', array($node, $item->langcode)))); $extra = $this->moduleHandler->invokeAll('node_search_result', array($node, $item->langcode)); $language = language_load($item->langcode); $username = array('#theme' => 'username', '#account' => $node->getOwner()); $results[] = array('link' => $node->url('canonical', array('absolute' => TRUE, 'language' => $language)), 'type' => String::checkPlain($this->entityManager->getStorage('node_type')->load($node->bundle())->label()), 'title' => $node->label(), 'user' => drupal_render($username), 'date' => $node->getChangedTime(), 'node' => $node, 'extra' => $extra, 'score' => $item->calculated_score, 'snippet' => search_excerpt($keys, $node->rendered, $item->langcode), 'langcode' => $node->language()->getId()); } return $results; }
/** * Gather full SPI data and send to Acquia Network. * * @return mixed FALSE if data not sent else NSPI result array */ public function send(Request $request) { // Mark this page as being uncacheable. \Drupal::service('page_cache_kill_switch')->trigger(); $method = ACQUIA_SPI_METHOD_CALLBACK; // Insight's set variable feature will pass method insight. if ($request->query->has('method') && $request->query->get('method') === ACQUIA_SPI_METHOD_INSIGHT) { $method = ACQUIA_SPI_METHOD_INSIGHT; } $response = $this->sendFullSpi($method); if ($request->get('destination')) { if (!empty($response['body'])) { if (isset($response['body']['spi_data_received']) && $response['body']['spi_data_received'] === TRUE) { drupal_set_message($this->t('SPI data sent.')); } if (!empty($response['body']['nspi_messages'])) { drupal_set_message($this->t('Acquia Subscription returned the following messages. Further information may be in the logs.')); foreach ($response['body']['nspi_messages'] as $nspi_message) { drupal_set_message(SafeMarkup::escape($nspi_message)); } } } else { drupal_set_message($this->t('Error sending SPI data. Consult the logs for more information.'), 'error'); } $route_match = $route = RouteMatch::createFromRequest($request); return $this->redirect($route_match->getRouteName(), $route_match->getRawParameters()->all()); } $headers = ['Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT', 'Content-Type' => 'text/plain', 'Cache-Control' => 'no-cache', 'Pragma' => 'no-cache']; if (empty($response['body'])) { return new Response('', Response::HTTP_BAD_REQUEST, $headers); } return new Response('', Response::HTTP_OK, $headers); }
/** * Builds the table row structure for a single field. * * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition * The field definition. * @param \Drupal\Core\Entity\Display\EntityDisplayInterface $entity_display * The entity display. * @param array $form * An associative array containing the structure of the form. * @param array $form_state * A reference to a keyed array containing the current state of the form. * * @return array * A table row array. */ protected function buildFieldRow(FieldDefinitionInterface $field_definition, EntityDisplayInterface $entity_display, array $form, array &$form_state) { $field_name = $field_definition->getName(); $display_options = $entity_display->getComponent($field_name); $label = $field_definition->getLabel(); $regions = array_keys($this->getRegions()); $field_row = array('#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), '#row_type' => 'field', '#region_callback' => array($this, 'getRowRegion'), '#js_settings' => array('rowHandler' => 'field', 'defaultPlugin' => $this->getDefaultPlugin($field_definition->getType())), 'human_name' => array('#markup' => String::checkPlain($label)), 'weight' => array('#type' => 'textfield', '#title' => $this->t('Weight for @title', array('@title' => $label)), '#title_display' => 'invisible', '#default_value' => $display_options ? $display_options['weight'] : '0', '#size' => 3, '#attributes' => array('class' => array('field-weight'))), 'parent_wrapper' => array('parent' => array('#type' => 'select', '#title' => $this->t('Label display for @title', array('@title' => $label)), '#title_display' => 'invisible', '#options' => array_combine($regions, $regions), '#empty_value' => '', '#attributes' => array('class' => array('field-parent')), '#parents' => array('fields', $field_name, 'parent')), 'hidden_name' => array('#type' => 'hidden', '#default_value' => $field_name, '#attributes' => array('class' => array('field-name'))))); $field_row['plugin'] = array('type' => array('#type' => 'select', '#title' => $this->t('Plugin for @title', array('@title' => $label)), '#title_display' => 'invisible', '#options' => $this->getPluginOptions($field_definition->getType()), '#default_value' => $display_options ? $display_options['type'] : 'hidden', '#parents' => array('fields', $field_name, 'type'), '#attributes' => array('class' => array('field-plugin-type'))), 'settings_edit_form' => array()); // Check the currently selected plugin, and merge persisted values for its // settings. if (isset($form_state['values']['fields'][$field_name]['type'])) { $display_options['type'] = $form_state['values']['fields'][$field_name]['type']; } if (isset($form_state['plugin_settings'][$field_name]['settings'])) { $display_options['settings'] = $form_state['plugin_settings'][$field_name]['settings']; } if (isset($form_state['plugin_settings'][$field_name]['third_party_settings'])) { $display_options['third_party_settings'] = $form_state['plugin_settings'][$field_name]['third_party_settings']; } // Get the corresponding plugin object. $plugin = $this->getPlugin($field_definition, $display_options); // Base button element for the various plugin settings actions. $base_button = array('#submit' => array(array($this, 'multistepSubmit')), '#ajax' => array('callback' => array($this, 'multistepAjax'), 'wrapper' => 'field-display-overview-wrapper', 'effect' => 'fade'), '#field_name' => $field_name); if ($form_state['plugin_settings_edit'] == $field_name) { // We are currently editing this field's plugin settings. Display the // settings form and submit buttons. $field_row['plugin']['settings_edit_form'] = array(); if ($plugin) { // Generate the settings form and allow other modules to alter it. $settings_form = $plugin->settingsForm($form, $form_state); $third_party_settings_form = $this->thirdPartySettingsForm($plugin, $field_definition, $form, $form_state); if ($settings_form || $third_party_settings_form) { $field_row['plugin']['#cell_attributes'] = array('colspan' => 3); $field_row['plugin']['settings_edit_form'] = array('#type' => 'container', '#attributes' => array('class' => array('field-plugin-settings-edit-form')), '#parents' => array('fields', $field_name, 'settings_edit_form'), 'label' => array('#markup' => $this->t('Plugin settings')), 'settings' => $settings_form, 'third_party_settings' => $third_party_settings_form, 'actions' => array('#type' => 'actions', 'save_settings' => $base_button + array('#type' => 'submit', '#button_type' => 'primary', '#name' => $field_name . '_plugin_settings_update', '#value' => $this->t('Update'), '#op' => 'update'), 'cancel_settings' => $base_button + array('#type' => 'submit', '#name' => $field_name . '_plugin_settings_cancel', '#value' => $this->t('Cancel'), '#op' => 'cancel', '#limit_validation_errors' => array(array('fields', $field_name, 'type'))))); $field_row['#attributes']['class'][] = 'field-plugin-settings-editing'; } } } else { $field_row['settings_summary'] = array(); $field_row['settings_edit'] = array(); if ($plugin) { // Display a summary of the current plugin settings, and (if the // summary is not empty) a button to edit them. $summary = $plugin->settingsSummary(); // Allow other modules to alter the summary. $this->alterSettingsSummary($summary, $plugin, $field_definition); if (!empty($summary)) { $summary_escaped = ''; $separator = ''; foreach ($summary as $summary_item) { $summary_escaped .= $separator . SafeMarkup::escape($summary_item); $separator = '<br />'; } $field_row['settings_summary'] = array('#markup' => SafeMarkup::set('<div class="field-plugin-summary">' . $summary_escaped . '</div>'), '#cell_attributes' => array('class' => array('field-plugin-summary-cell'))); } // Check selected plugin settings to display edit link or not. $settings_form = $plugin->settingsForm($form, $form_state); $third_party_settings_form = $this->thirdPartySettingsForm($plugin, $field_definition, $form, $form_state); if (!empty($settings_form) || !empty($third_party_settings_form)) { $field_row['settings_edit'] = $base_button + array('#type' => 'image_button', '#name' => $field_name . '_settings_edit', '#src' => 'core/misc/configure-dark.png', '#attributes' => array('class' => array('field-plugin-settings-edit'), 'alt' => $this->t('Edit')), '#op' => 'edit', '#limit_validation_errors' => array(array('fields', $field_name, 'type')), '#prefix' => '<div class="field-plugin-settings-edit-wrapper">', '#suffix' => '</div>'); } } } return $field_row; }
/** * Tests the interaction between the safe list and XSS filtering. * * @covers ::escape */ public function testAdminXss() { // Mark the string as safe. This is for test purposes only. $text = '<marquee>text</marquee>'; SafeMarkup::set($text); // SafeMarkup::escape() will not escape the markup tag since the string was // marked safe above. $this->assertEquals('<marquee>text</marquee>', SafeMarkup::escape($text)); // SafeMarkup::checkPlain() will escape the markup tag even though the // string was marked safe above. $this->assertEquals('<marquee>text</marquee>', SafeMarkup::checkPlain($text)); }