Ejemplo n.º 1
0
 /**
  * Retrieves the plain-text content from the current raw content.
  */
 protected function getTextContent()
 {
     if (!isset($this->plainTextContent)) {
         $this->plainTextContent = Xss::filter($this->getRawContent(), array());
     }
     return $this->plainTextContent;
 }
Ejemplo n.º 2
0
 /**
  * Determines if a string of text is considered "simple".
  *
  * @param string $string
  *   The string of text to check "simple" criteria on.
  * @param int|FALSE $length
  *   The length of characters used to determine whether or not $string is
  *   considered "simple". Set explicitly to FALSE to disable this criteria.
  * @param array|FALSE $allowed_tags
  *   An array of allowed tag elements. Set explicitly to FALSE to disable this
  *   criteria.
  * @param bool $html
  *   A variable, passed by reference, that indicates whether or not the
  *   string contains HTML.
  *
  * @return bool
  *   Returns TRUE if the $string is considered "simple", FALSE otherwise.
  */
 public static function isSimple($string, $length = 250, $allowed_tags = NULL, &$html = FALSE)
 {
     // Typecast to a string (if an object).
     $string_clone = (string) $string;
     // Use the advanced drupal_static() pattern.
     static $drupal_static_fast;
     if (!isset($drupal_static_fast)) {
         $drupal_static_fast['strings'] =& drupal_static(__METHOD__);
     }
     $strings =& $drupal_static_fast['strings'];
     if (!isset($strings[$string_clone])) {
         $plain_string = strip_tags($string_clone);
         $simple = TRUE;
         if ($allowed_tags !== FALSE) {
             $filtered_string = Xss::filter($string_clone, $allowed_tags);
             $html = $filtered_string !== $plain_string;
             $simple = $simple && $string_clone === $filtered_string;
         }
         if ($length !== FALSE) {
             $simple = $simple && strlen($plain_string) <= intval($length);
         }
         $strings[$string_clone] = $simple;
     }
     return $strings[$string_clone];
 }
 /**
  * {@inheritdoc}
  */
 public function process($text, $langcode)
 {
     $allowed_tags = array_filter($this->settings['restrictions']['allowed'], function ($value) {
         return is_array($value) || (bool) $value !== FALSE;
     });
     return new FilterProcessResult(Xss::filter($text, array_keys($allowed_tags)));
 }
Ejemplo n.º 4
0
 /**
  * Retrieves the plain-text content from the current raw content.
  */
 protected function getTextContent() {
   if (!isset($this->plainTextContent)) {
     $raw_content = $this->getRawContent();
     // Strip everything between the HEAD tags.
     $raw_content = preg_replace('@<head>(.+?)</head>@si', '', $raw_content);
     $this->plainTextContent = Xss::filter($raw_content, array());
   }
   return $this->plainTextContent;
 }
Ejemplo n.º 5
0
 /**
  * Tests execution order of hook_form_alter() and hook_form_FORM_ID_alter().
  */
 function testExecutionOrder()
 {
     $this->drupalGet('form-test/alter');
     // Ensure that the order is first by module, then for a given module, the
     // id-specific one after the generic one.
     $expected = array('block_form_form_test_alter_form_alter() executed.', 'form_test_form_alter() executed.', 'form_test_form_form_test_alter_form_alter() executed.', 'system_form_form_test_alter_form_alter() executed.');
     $content = preg_replace('/\\s+/', ' ', Xss::filter($this->content, array()));
     $this->assert(strpos($content, implode(' ', $expected)) !== FALSE, 'Form alter hooks executed in the expected order.');
 }
 /**
  * Returns the array of recipient handler labels.
  * @todo documentation
  */
 public function getOptions()
 {
     $handlers = $this->getDefinitions();
     $allowed_values = array();
     foreach ($handlers as $handler => $settings) {
         $allowed_values[$handler] = Xss::filter($settings['title']);
     }
     return $allowed_values;
 }
Ejemplo n.º 7
0
 /**
  * Ensure that custom field content is XSS filtered.
  */
 public function testCustomFieldXss()
 {
     $view = Views::getView('test_view');
     $view->setDisplay();
     // Alter the text of the field to include XSS.
     $text = '<script>alert("kittens")</script>';
     $view->displayHandlers->get('default')->overrideOption('fields', array('name' => array('id' => 'name', 'table' => 'views_test_data', 'field' => 'name', 'relationship' => 'none', 'alter' => array('text' => $text))));
     $this->executeView($view);
     $this->assertEqual(Xss::filter($text), $view->style_plugin->getField(0, 'name'));
 }
Ejemplo n.º 8
0
function at_core_submit_custom_css($values, $generated_files_path)
{
    $custom_css = '';
    if (!empty($values['settings_custom_css'])) {
        // sanitize user entered data
        $custom_css = Xss::filter($values['settings_custom_css']);
    }
    $file_name = 'custom-css.css';
    $filepath = $generated_files_path . '/' . $file_name;
    file_unmanaged_save_data($custom_css, $filepath, FILE_EXISTS_REPLACE);
}
Ejemplo n.º 9
0
 /**
  * {@inheritdoc}
  */
 public function process($text, $langcode)
 {
     $restrictions = $this->getHtmlRestrictions();
     // Split the work into two parts. For filtering HTML tags out of the content
     // we rely on the well-tested Xss::filter() code. Since there is no '*' tag
     // that needs to be removed from the list.
     unset($restrictions['allowed']['*']);
     $text = Xss::filter($text, array_keys($restrictions['allowed']));
     // After we've done tag filtering, we do attribute and attribute value
     // filtering as the second part.
     return new FilterProcessResult($this->filterAttributes($text));
 }
Ejemplo n.º 10
0
 /**
  * {@inheritdoc}
  */
 public function checkoutInfo(JobInterface $job)
 {
     $tuid = $job->getSetting('translator');
     if ($tuid && ($translator = User::load($tuid))) {
         $form['job_status'] = array('#type' => 'item', '#title' => t('Job status'), '#markup' => t('Translation job is assigned to %name.', array('%name' => $translator->getUsername())));
     } else {
         $form['job_status'] = array('#type' => 'item', '#title' => t('Job status'), '#markup' => t('Translation job is not assigned to any user.'));
     }
     if ($job->getSetting('job_comment')) {
         $form['job_comment'] = array('#type' => 'item', '#title' => t('Job comment'), '#markup' => Xss::filter($job->getSetting('job_comment')));
     }
     return $form;
 }
Ejemplo n.º 11
0
 /**
  * Filters an HTML string to prevent XSS vulnerabilities.
  *
  * Like \Drupal\Component\Utility\Xss::filterAdmin(), but with a shorter list
  * of allowed tags.
  *
  * Used for items entered by administrators, like field descriptions, allowed
  * values, where some (mainly inline) mark-up may be desired (so
  * \Drupal\Component\Utility\SafeMarkup::checkPlain() is not acceptable).
  *
  * @param string $string
  *   The string with raw HTML in it.
  *
  * @return \Drupal\Component\Utility\SafeMarkup
  *   An XSS safe version of $string, or an empty string if $string is not
  *   valid UTF-8.
  */
 public function fieldFilterXss($string)
 {
     // All known XSS vectors are filtered out by
     // \Drupal\Component\Utility\Xss::filter(), all tags in the markup are
     // allowed intentionally by the trait, and no danger is added in by
     // \Drupal\Component\Utility\HTML::normalize(). Since the normalized value
     // is essentially the same markup, designate this string as safe as well.
     // This method is an internal part of field sanitization, so the resultant,
     // sanitized string should be printable as is.
     //
     // @todo Free this memory in https://www.drupal.org/node/2505963.
     return SafeMarkup::set(Html::normalize(Xss::filter($string, $this->allowedTags())));
 }
Ejemplo n.º 12
0
 /**
  * {@inheritdoc}
  */
 public function zen()
 {
     $principles = $this->principleManager->getAllPrinciples();
     $title = t('My mind is empty.');
     if (count($principles) > 0) {
         // Get a random item from the array of principles
         $k = array_rand($principles);
         $principle = $principles[$k];
         $title = Xss::filter($principle->title);
     }
     $build = array('#type' => 'markup', '#markup' => $title);
     return new Response(\Drupal::service('renderer')->renderRoot($build));
 }
 /**
  * Overrides \Drupal\Component\Utility\SafeStringTrait::create().
  *
  * @return string|\Drupal\Component\Utility\SafeStringInterface
  *   A safe string filtered with the allowed tag list and normalized.
  *
  * @see \Drupal\Core\Field\FieldFilteredString::allowedTags()
  * @see \Drupal\Component\Utility\Xss::filter()
  * @see \Drupal\Component\Utility\Html::normalize()
  */
 public static function create($string)
 {
     $string = (string) $string;
     if ($string === '') {
         return '';
     }
     $safe_string = new static();
     // All known XSS vectors are filtered out by
     // \Drupal\Component\Utility\Xss::filter(), all tags in the markup are
     // allowed intentionally by the trait, and no danger is added in by
     // \Drupal\Component\Utility\HTML::normalize(). Since the normalized value
     // is essentially the same markup, designate this string as safe as well.
     // This method is an internal part of field sanitization, so the resultant,
     // sanitized string should be printable as is.
     $safe_string->string = Html::normalize(Xss::filter($string, static::allowedTags()));
     return $safe_string;
 }
Ejemplo n.º 14
0
 /**
  * {@inheritdoc}
  */
 public function process($text, $langcode)
 {
     $result = new FilterProcessResult($text);
     if (stristr($text, 'data-caption') !== FALSE) {
         $dom = Html::load($text);
         $xpath = new \DOMXPath($dom);
         foreach ($xpath->query('//*[@data-caption]') as $node) {
             // Read the data-caption attribute's value, then delete it.
             $caption = Html::escape($node->getAttribute('data-caption'));
             $node->removeAttribute('data-caption');
             // Sanitize caption: decode HTML encoding, limit allowed HTML tags; only
             // allow inline tags that are allowed by default, plus <br>.
             $caption = Html::decodeEntities($caption);
             $caption = FilteredMarkup::create(Xss::filter($caption, array('a', 'em', 'strong', 'cite', 'code', 'br')));
             // The caption must be non-empty.
             if (Unicode::strlen($caption) === 0) {
                 continue;
             }
             // Given the updated node and caption: re-render it with a caption, but
             // bubble up the value of the class attribute of the captioned element,
             // this allows it to collaborate with e.g. the filter_align filter.
             $tag = $node->tagName;
             $classes = $node->getAttribute('class');
             $node->removeAttribute('class');
             $node = $node->parentNode->tagName === 'a' ? $node->parentNode : $node;
             $filter_caption = array('#theme' => 'filter_caption', '#node' => FilteredMarkup::create($node->C14N()), '#tag' => $tag, '#caption' => $caption, '#classes' => $classes);
             $altered_html = drupal_render($filter_caption);
             // Load the altered HTML into a new DOMDocument and retrieve the element.
             $updated_nodes = Html::load($altered_html)->getElementsByTagName('body')->item(0)->childNodes;
             foreach ($updated_nodes as $updated_node) {
                 // Import the updated node from the new DOMDocument into the original
                 // one, importing also the child nodes of the updated node.
                 $updated_node = $dom->importNode($updated_node, TRUE);
                 $node->parentNode->insertBefore($updated_node, $node);
             }
             // Finally, remove the original data-caption node.
             $node->parentNode->removeChild($node);
         }
         $result->setProcessedText(Html::serialize($dom))->addAttachments(array('library' => array('filter/caption')));
     }
     return $result;
 }
Ejemplo n.º 15
0
 /**
  * {@inheritdoc}
  */
 public function submitConfigurationForm(array &$form, FormStateInterface $form_state)
 {
     $this->configuration['wrappers'] = $form_state->getValue('region_wrapper');
     foreach (['outer_wrapper', 'attributes', 'link_attribute', 'link_custom'] as $name) {
         $this->configuration[$name] = $this->configuration['wrappers'][$name];
         unset($this->configuration['wrappers'][$name]);
     }
     // Apply Xss::filter to attributes.
     $this->configuration['attributes'] = Xss::filter($this->configuration['attributes']);
     // In case classes is missing entirely, use the defaults.
     $defaults = $this->defaultConfiguration();
     $this->configuration['classes'] = $form_state->getValue('ds_classes', $defaults['classes']);
     // Do not save empty classes.
     foreach ($this->configuration['classes'] as $region_name => &$classes) {
         foreach ($classes as $class) {
             if (empty($class)) {
                 unset($classes[$class]);
             }
         }
     }
 }
 /**
  * Outputs the data that is used for the Coffee autocompletion in JSON.
  */
 public function coffeeData()
 {
     $output = array();
     // Get configured menus from configuration.
     $menus = \Drupal::config('coffee.configuration')->get('coffee_menus');
     if ($menus !== NULL) {
         foreach ($menus as $v) {
             if ($v === '0') {
                 continue;
             }
             // Build the menu tree.
             $menu_tree_parameters = new MenuTreeParameters();
             $tree = \Drupal::menuTree()->load($v, $menu_tree_parameters);
             foreach ($tree as $key => $link) {
                 $command = $v == 'user-menu' ? ':user' : NULL;
                 $this->coffee_traverse_below($link, $output, $command);
             }
         }
     }
     module_load_include('inc', 'coffee', 'coffee.hooks');
     $commands = array();
     foreach (\Drupal::moduleHandler()->getImplementations('coffee_commands') as $module) {
         $commands = array_merge($commands, \Drupal::moduleHandler()->invoke($module, 'coffee_commands', array()));
     }
     if (!empty($commands)) {
         $output = array_merge($output, $commands);
     }
     foreach ($output as $k => $v) {
         if ($v['value'] == '<front>') {
             unset($output[$k]);
             continue;
         }
         // Filter out XSS.
         $output[$k]['label'] = Xss::filter($output[$k]['label']);
     }
     // Re-index the array.
     $output = array_values($output);
     return new JsonResponse($output);
 }
Ejemplo n.º 17
0
 /**
  * {@inheritdoc}
  */
 public function form(array $form, FormStateInterface $form_state)
 {
     $form = parent::form($form, $form_state);
     $task_item = $this->entity;
     $form['#title'] = $task_item->label();
     $job_item = $task_item->getJobItem();
     $job = $job_item->getJob();
     $form['info'] = array('#type' => 'container', '#attributes' => array('class' => array('tmgmt-local-task-info', 'clearfix')), '#weight' => 0);
     $url = $job_item->getSourceUrl();
     $form['info']['source'] = array('#type' => 'item', '#title' => t('Source'), '#markup' => $url ? Link::fromTextAndUrl($job_item->getSourceLabel(), $url)->toString() : $job_item->getSourceLabel(), '#prefix' => '<div class="tmgmt-ui-source tmgmt-ui-info-item">', '#suffix' => '</div>');
     $form['info']['sourcetype'] = array('#type' => 'item', '#title' => t('Source type'), '#markup' => $job_item->getSourceType(), '#prefix' => '<div class="tmgmt-ui-source-type tmgmt-ui-info-item">', '#suffix' => '</div>');
     $form['info']['source_language'] = array('#type' => 'item', '#title' => t('Source language'), '#markup' => $job_item->getJob()->getSourceLanguage()->getName(), '#prefix' => '<div class="tmgmt-ui-source-language tmgmt-ui-info-item">', '#suffix' => '</div>');
     $form['info']['target_language'] = array('#type' => 'item', '#title' => t('Target language'), '#markup' => $job_item->getJob()->getTargetLanguage()->getName(), '#prefix' => '<div class="tmgmt-ui-target-language tmgmt-ui-info-item">', '#suffix' => '</div>');
     $form['info']['changed'] = array('#type' => 'item', '#title' => t('Last change'), '#value' => $task_item->getChangedTime(), '#markup' => \Drupal::service('date.formatter')->format($task_item->getChangedTime()), '#prefix' => '<div class="tmgmt-ui-changed tmgmt-ui-info-item">', '#suffix' => '</div>');
     $statuses = LocalTaskItem::getStatuses();
     $form['info']['status'] = array('#type' => 'item', '#title' => t('Status'), '#markup' => $statuses[$task_item->getStatus()], '#prefix' => '<div class="tmgmt-ui-task-item-status tmgmt-ui-info-item">', '#suffix' => '</div>', '#value' => $task_item->getStatus());
     $task = $task_item->getTask();
     $url = $task->toUrl();
     $form['info']['task'] = array('#type' => 'item', '#title' => t('Task'), '#markup' => Link::fromTextAndUrl($task->label(), $url)->toString(), '#prefix' => '<div class="tmgmt-ui-task tmgmt-ui-info-item">', '#suffix' => '</div>');
     if ($job->getSetting('job_comment')) {
         $form['job_comment'] = array('#type' => 'item', '#title' => t('Job comment'), '#markup' => Xss::filter($job->getSetting('job_comment')));
     }
     $form['translation'] = array('#type' => 'container');
     // Build the translation form.
     $data = $task_item->getData();
     // Need to keep the first hierarchy. So flatten must take place inside
     // of the foreach loop.
     $zebra = 'even';
     foreach (Element::children($data) as $key) {
         $flattened = \Drupal::service('tmgmt.data')->flatten($data[$key], $key);
         $form['translation'][$key] = $this->formElement($flattened, $task_item, $zebra);
     }
     $form['footer'] = tmgmt_color_local_review_legend();
     $form['#attached']['library'][] = 'tmgmt/admin';
     return $form;
 }
Ejemplo n.º 18
0
 /**
  * Tests the generation of all system date tokens.
  */
 public function testSystemDateTokenReplacement()
 {
     // Set time to one hour before request.
     $date = REQUEST_TIME - 3600;
     // Generate and test tokens.
     $tests = array();
     $date_formatter = \Drupal::service('date.formatter');
     $tests['[date:short]'] = $date_formatter->format($date, 'short', '', NULL, $this->interfaceLanguage->getId());
     $tests['[date:medium]'] = $date_formatter->format($date, 'medium', '', NULL, $this->interfaceLanguage->getId());
     $tests['[date:long]'] = $date_formatter->format($date, 'long', '', NULL, $this->interfaceLanguage->getId());
     $tests['[date:custom:m/j/Y]'] = $date_formatter->format($date, 'custom', 'm/j/Y', NULL, $this->interfaceLanguage->getId());
     $tests['[date:since]'] = $date_formatter->formatTimeDiffSince($date, array('langcode' => $this->interfaceLanguage->getId()));
     $tests['[date:raw]'] = Xss::filter($date);
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
     foreach ($tests as $input => $expected) {
         $output = $this->tokenService->replace($input, array('date' => $date), array('langcode' => $this->interfaceLanguage->getId()));
         $this->assertEqual($output, $expected, format_string('Date token %token replaced.', array('%token' => $input)));
     }
 }
 /**
  * Creates some terms and a node, then tests the tokens generated from them.
  */
 function testTaxonomyTokenReplacement()
 {
     $token_service = \Drupal::token();
     $language_interface = \Drupal::languageManager()->getCurrentLanguage();
     // Create two taxonomy terms.
     $term1 = $this->createTerm($this->vocabulary);
     $term2 = $this->createTerm($this->vocabulary);
     // Edit $term2, setting $term1 as parent.
     $edit = array();
     $edit['name[0][value]'] = '<blink>Blinking Text</blink>';
     $edit['parent[]'] = array($term1->id());
     $this->drupalPostForm('taxonomy/term/' . $term2->id() . '/edit', $edit, t('Save'));
     // Create node with term2.
     $edit = array();
     $node = $this->drupalCreateNode(array('type' => 'article'));
     $edit[$this->field_name . '[]'] = $term2->id();
     $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
     // Generate and test sanitized tokens for term1.
     $tests = array();
     $tests['[term:tid]'] = $term1->id();
     $tests['[term:name]'] = String::checkPlain($term1->getName());
     $tests['[term:description]'] = $term1->description->processed;
     $tests['[term:url]'] = $term1->url('canonical', array('absolute' => TRUE));
     $tests['[term:node-count]'] = 0;
     $tests['[term:parent:name]'] = '[term:parent:name]';
     $tests['[term:vocabulary:name]'] = String::checkPlain($this->vocabulary->name);
     foreach ($tests as $input => $expected) {
         $output = $token_service->replace($input, array('term' => $term1), array('langcode' => $language_interface->getId()));
         $this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
     }
     // Generate and test sanitized tokens for term2.
     $tests = array();
     $tests['[term:tid]'] = $term2->id();
     $tests['[term:name]'] = String::checkPlain($term2->getName());
     $tests['[term:description]'] = $term2->description->processed;
     $tests['[term:url]'] = $term2->url('canonical', array('absolute' => TRUE));
     $tests['[term:node-count]'] = 1;
     $tests['[term:parent:name]'] = String::checkPlain($term1->getName());
     $tests['[term:parent:url]'] = $term1->url('canonical', array('absolute' => TRUE));
     $tests['[term:parent:parent:name]'] = '[term:parent:parent:name]';
     $tests['[term:vocabulary:name]'] = String::checkPlain($this->vocabulary->name);
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
     foreach ($tests as $input => $expected) {
         $output = $token_service->replace($input, array('term' => $term2), array('langcode' => $language_interface->getId()));
         $this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
     }
     // Generate and test unsanitized tokens.
     $tests['[term:name]'] = $term2->getName();
     $tests['[term:description]'] = $term2->getDescription();
     $tests['[term:parent:name]'] = $term1->getName();
     $tests['[term:vocabulary:name]'] = $this->vocabulary->name;
     foreach ($tests as $input => $expected) {
         $output = $token_service->replace($input, array('term' => $term2), array('langcode' => $language_interface->getId(), 'sanitize' => FALSE));
         $this->assertEqual($output, $expected, format_string('Unsanitized taxonomy term token %token replaced.', array('%token' => $input)));
     }
     // Generate and test sanitized tokens.
     $tests = array();
     $tests['[vocabulary:vid]'] = $this->vocabulary->id();
     $tests['[vocabulary:name]'] = String::checkPlain($this->vocabulary->name);
     $tests['[vocabulary:description]'] = Xss::filter($this->vocabulary->description);
     $tests['[vocabulary:node-count]'] = 1;
     $tests['[vocabulary:term-count]'] = 2;
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
     foreach ($tests as $input => $expected) {
         $output = $token_service->replace($input, array('vocabulary' => $this->vocabulary), array('langcode' => $language_interface->getId()));
         $this->assertEqual($output, $expected, format_string('Sanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
     }
     // Generate and test unsanitized tokens.
     $tests['[vocabulary:name]'] = $this->vocabulary->name;
     $tests['[vocabulary:description]'] = $this->vocabulary->description;
     foreach ($tests as $input => $expected) {
         $output = $token_service->replace($input, array('vocabulary' => $this->vocabulary), array('langcode' => $language_interface->getId(), 'sanitize' => FALSE));
         $this->assertEqual($output, $expected, format_string('Unsanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
     }
 }
Ejemplo n.º 20
0
 /**
  * Formats a backtrace into a plain-text string.
  *
  * The calls show values for scalar arguments and type names for complex ones.
  *
  * @param array $backtrace
  *   A standard PHP backtrace.
  *
  * @return string
  *   A plain-text line-wrapped string ready to be put inside <pre>.
  */
 public static function formatBacktrace(array $backtrace)
 {
     $return = '';
     foreach ($backtrace as $trace) {
         $call = array('function' => '', 'args' => array());
         if (isset($trace['class'])) {
             $call['function'] = $trace['class'] . $trace['type'] . $trace['function'];
         } elseif (isset($trace['function'])) {
             $call['function'] = $trace['function'];
         } else {
             $call['function'] = 'main';
         }
         if (isset($trace['args'])) {
             foreach ($trace['args'] as $arg) {
                 if (is_scalar($arg)) {
                     $call['args'][] = is_string($arg) ? '\'' . Xss::filter($arg) . '\'' : $arg;
                 } else {
                     $call['args'][] = ucfirst(gettype($arg));
                 }
             }
         }
         $return .= $call['function'] . '(' . implode(', ', $call['args']) . ")\n";
     }
     return $return;
 }
Ejemplo n.º 21
0
 /**
  * Route title callback.
  *
  * @param \Drupal\aggregator\FeedInterface $aggregator_feed
  *   The aggregator feed.
  *
  * @return string
  *   The feed label.
  */
 public function feedTitle(FeedInterface $aggregator_feed)
 {
     return Xss::filter($aggregator_feed->label());
 }
Ejemplo n.º 22
0
 /**
  * {@inheritdoc}
  */
 public function sanitizeValue($value, $type = NULL)
 {
     switch ($type) {
         case 'xss':
             $value = Xss::filter($value);
             break;
         case 'xss_admin':
             $value = Xss::filterAdmin($value);
             break;
         case 'url':
             $value = Html::escape(UrlHelper::stripDangerousProtocols($value));
             break;
         default:
             $value = Html::escape($value);
             break;
     }
     return ViewsRenderPipelineMarkup::create($value);
 }
Ejemplo n.º 23
0
 /**
  * Tests basic aggregator_item view.
  */
 public function testAggregatorItemView()
 {
     /** @var \Drupal\Core\Render\RendererInterface $renderer */
     $renderer = \Drupal::service('renderer');
     $feed = $this->feedStorage->create(array('title' => $this->randomMachineName(), 'url' => 'https://www.drupal.org/', 'refresh' => 900, 'checked' => 123543535, 'description' => $this->randomMachineName()));
     $feed->save();
     $items = array();
     $expected = array();
     for ($i = 0; $i < 10; $i++) {
         $values = array();
         $values['fid'] = $feed->id();
         $values['timestamp'] = mt_rand(REQUEST_TIME - 10, REQUEST_TIME + 10);
         $values['title'] = $this->randomMachineName();
         $values['description'] = $this->randomMachineName();
         // Add a image to ensure that the sanitizing can be tested below.
         $values['author'] = $this->randomMachineName() . '<img src="http://example.com/example.png" \\>"';
         $values['link'] = 'https://www.drupal.org/node/' . mt_rand(1000, 10000);
         $values['guid'] = $this->randomString();
         $aggregator_item = $this->itemStorage->create($values);
         $aggregator_item->save();
         $items[$aggregator_item->id()] = $aggregator_item;
         $values['iid'] = $aggregator_item->id();
         $expected[] = $values;
     }
     $view = Views::getView('test_aggregator_items');
     $this->executeView($view);
     $column_map = array('iid' => 'iid', 'title' => 'title', 'aggregator_item_timestamp' => 'timestamp', 'description' => 'description', 'aggregator_item_author' => 'author');
     $this->assertIdenticalResultset($view, $expected, $column_map);
     // Ensure that the rendering of the linked title works as expected.
     foreach ($view->result as $row) {
         $iid = $view->field['iid']->getValue($row);
         $expected_link = \Drupal::l($items[$iid]->getTitle(), Url::fromUri($items[$iid]->getLink(), ['absolute' => TRUE]));
         $output = $renderer->executeInRenderContext(new RenderContext(), function () use($view, $row) {
             return $view->field['title']->advancedRender($row);
         });
         $this->assertEqual($output, $expected_link->getGeneratedLink(), 'Ensure the right link is generated');
         $expected_author = Xss::filter($items[$iid]->getAuthor(), _aggregator_allowed_tags());
         $output = $renderer->executeInRenderContext(new RenderContext(), function () use($view, $row) {
             return $view->field['author']->advancedRender($row);
         });
         $this->assertEqual($output, $expected_author, 'Ensure the author got filtered');
         $expected_description = Xss::filter($items[$iid]->getDescription(), _aggregator_allowed_tags());
         $output = $renderer->executeInRenderContext(new RenderContext(), function () use($view, $row) {
             return $view->field['description']->advancedRender($row);
         });
         $this->assertEqual($output, $expected_description, 'Ensure the author got filtered');
     }
 }
Ejemplo n.º 24
0
 /**
  * {@inheritdoc}
  */
 public function sanitizeValue($value, $type = NULL)
 {
     switch ($type) {
         case 'xss':
             $value = Xss::filter($value);
             break;
         case 'xss_admin':
             $value = Xss::filterAdmin($value);
             break;
         case 'url':
             $value = SafeMarkup::checkPlain(UrlHelper::stripDangerousProtocols($value));
             break;
         default:
             $value = SafeMarkup::checkPlain($value);
             break;
     }
     return $value;
 }
Ejemplo n.º 25
0
 /**
  * Formats a date, using a date type or a custom date format string.
  *
  * @param int $timestamp
  *   A UNIX timestamp to format.
  * @param string $type
  *   (optional) The format to use, one of:
  *   - One of the built-in formats: 'short', 'medium',
  *     'long', 'html_datetime', 'html_date', 'html_time',
  *     'html_yearless_date', 'html_week', 'html_month', 'html_year'.
  *   - The name of a date type defined by a date format config entity.
  *   - The machine name of an administrator-defined date format.
  *   - 'custom', to use $format.
  *   Defaults to 'medium'.
  * @param string $format
  *   (optional) If $type is 'custom', a PHP date format string suitable for
  *   input to date(). Use a backslash to escape ordinary text, so it does not
  *   get interpreted as date format characters.
  * @param string|null $timezone
  *   (optional) Time zone identifier, as described at
  *   http://php.net/manual/timezones.php Defaults to the time zone used to
  *   display the page.
  * @param string|null $langcode
  *   (optional) Language code to translate to. NULL (default) means to use
  *   the user interface language for the page.
  *
  * @return string
  *   A translated date string in the requested format.
  */
 public function format($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL)
 {
     if (!isset($timezone)) {
         $timezone = date_default_timezone_get();
     }
     // Store DateTimeZone objects in an array rather than repeatedly
     // constructing identical objects over the life of a request.
     if (!isset($this->timezones[$timezone])) {
         $this->timezones[$timezone] = timezone_open($timezone);
     }
     if (empty($langcode)) {
         $langcode = $this->languageManager->getCurrentLanguage()->getId();
     }
     // Create a DrupalDateTime object from the timestamp and timezone.
     $create_settings = array('langcode' => $langcode, 'country' => $this->country());
     $date = DrupalDateTime::createFromTimestamp($timestamp, $this->timezones[$timezone], $create_settings);
     // If we have a non-custom date format use the provided date format pattern.
     if ($date_format = $this->dateFormat($type, $langcode)) {
         $format = $date_format->getPattern();
     }
     // Fall back to medium if a format was not found.
     if (empty($format)) {
         $format = $this->dateFormat('fallback', $langcode)->getPattern();
     }
     // Call $date->format().
     $settings = array('langcode' => $langcode);
     return Xss::filter($date->format($format, $settings));
 }
 /**
  * Tests execution order of theme suggestion alter hooks.
  *
  * hook_theme_suggestions_alter() should fire before
  * hook_theme_suggestions_HOOK_alter() within an extension (module or theme).
  */
 function testExecutionOrder()
 {
     // Enable our test theme and module.
     \Drupal::config('system.theme')->set('default', 'test_theme')->save();
     \Drupal::moduleHandler()->install(array('theme_suggestions_test'));
     $this->resetAll();
     // Send two requests so that we get all the messages we've set via
     // drupal_set_message().
     $this->drupalGet('theme-test/suggestion-alter');
     // Ensure that the order is first by extension, then for a given extension,
     // the hook-specific one after the generic one.
     $expected = array('theme_suggestions_test_theme_suggestions_alter() executed.', 'theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter() executed.', 'theme_test_theme_suggestions_alter() executed.', 'theme_test_theme_suggestions_theme_test_suggestions_alter() executed.', 'test_theme_theme_suggestions_alter() executed.', 'test_theme_theme_suggestions_theme_test_suggestions_alter() executed.');
     $content = preg_replace('/\\s+/', ' ', Xss::filter($this->content, array()));
     $this->assert(strpos($content, implode(' ', $expected)) !== FALSE, 'Suggestion alter hooks executed in the expected order.');
 }
Ejemplo n.º 27
0
function at_core_submit_fonts($values, $generated_files_path)
{
    // Websafe fonts.
    $websafe_fonts = $values['websafe_options'];
    // Elements to apply fonts to.
    $font_elements = font_elements();
    // Fallback family
    $fallback_font_family = 'sans-serif';
    if (isset($values['settings_font_fallback'])) {
        $fallback_font_family = str_replace('_', '-', $values['settings_font_fallback']);
    }
    // Initialize some variables.
    $fonts = array();
    $base_size = '16';
    // 16px default
    // Inject config settings for web-fonts.
    $values['settings_font_use_google_fonts'] = FALSE;
    $values['settings_font_use_typekit'] = FALSE;
    $font_styles = array();
    foreach ($font_elements as $font_key => $font_values) {
        // Get the selectors for each element.
        $fonts[$font_key]['selectors'] = $font_values['selector'];
        // Reset the selectors variable if we have custom selectors.
        if ($font_key == 'custom_selectors' && !empty($values['settings_font_custom_selectors']) && !empty($values['settings_custom_selectors'])) {
            $fonts[$font_key]['selectors'] = $values['settings_custom_selectors'];
            // ? $values['settings_custom_selectors'] : 'ruby ruby'
        }
        // Size/Line height.
        if (!empty($values['settings_font_size_' . $font_key])) {
            //$base_size = $values['settings_font_size_base'] ? $values['settings_font_size_base'] : $base_size;
            $px_size = $values['settings_font_size_' . $font_key];
            $rem_size = $values['settings_font_size_' . $font_key] / $base_size;
            // line-height multipliers are a bit magical, but "pretty good" defaults.
            $line_height_multiplier = $values['settings_font_line_height_multiplier_default'];
            if ($px_size >= $values['settings_font_line_height_multiplier_large_size']) {
                $line_height_multiplier = $values['settings_font_line_height_multiplier_large'];
            }
            $fonts[$font_key]['size'] = ' font-size: ' . ceil($px_size) . 'px; font-size: ' . round($rem_size, 3) . 'rem;';
            $fonts[$font_key]['line_height'] = ' line-height: ' . ceil($px_size * $line_height_multiplier) . 'px; line-height: ' . round($rem_size * $line_height_multiplier, 3) . 'rem;';
            //      if (isset($values['settings_font_size_base'])) {
            //        $rem_size =  $base_size / 16;
            //        $fonts['base']['size'] = ' font-size: ' . ceil($px_size) . 'px; font-size: ' . round($rem_size, 3) . 'rem;';
            //        $fonts['base']['line_height'] = ' line-height: ' . ceil($px_size * $line_height_multiplier) . 'px; line-height: ' . round($rem_size * $line_height_multiplier, 3) . 'rem;';
            //      }
        }
        // Set font family for each key.
        if (isset($values['settings_font_' . $font_key])) {
            // Websafe.
            if ($values['settings_font_' . $font_key] == 'websafe') {
                if (isset($values['settings_font_websafe_' . $font_key])) {
                    if (!empty($websafe_fonts[$values['settings_font_websafe_' . $font_key]])) {
                        $websafe_font = $websafe_fonts[$values['settings_font_websafe_' . $font_key]];
                        $fonts[$font_key]['family'] = 'font-family: ' . trim($websafe_font) . ';';
                    } else {
                        $fonts[$font_key]['family'] = 'font-family: inherit;';
                    }
                } else {
                    $fonts[$font_key]['family'] = 'font-family: inherit;';
                }
            }
            // Google.
            if ($values['settings_font_' . $font_key] == 'google') {
                if (isset($values['settings_font_google_' . $font_key])) {
                    $fonts[$font_key]['family'] = 'font-family: ' . $values['settings_font_google_' . $font_key] . ', ' . trim($fallback_font_family) . ';';
                    $values['settings_font_use_google_fonts'] = TRUE;
                } else {
                    $fonts[$font_key]['family'] = 'font-family: inherit;';
                }
            }
            // Typekit.
            if ($values['settings_font_' . $font_key] == 'typekit') {
                if (!empty($values['settings_font_typekit_' . $font_key])) {
                    $fonts[$font_key]['family'] = 'font-family: ' . $values['settings_font_typekit_' . $font_key] . ', ' . trim($fallback_font_family) . ';';
                    $values['settings_font_use_typekit'] = TRUE;
                } else {
                    $fonts[$font_key]['family'] = 'font-family: inherit;';
                }
            }
        }
        // Font smoothing.
        if (isset($values['settings_font_smoothing_' . $font_key]) && $values['settings_font_smoothing_' . $font_key] == 1) {
            $fonts[$font_key]['smoothing'] = ' -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;';
        }
    }
    // Output data to file.
    if (!empty($fonts)) {
        foreach ($fonts as $font_key => $font_values) {
            if (isset($font_values['family']) || isset($font_values['size'])) {
                $font_style = $font_values['selectors'] . ' { ';
                if (isset($font_values['family'])) {
                    $font_style .= str_replace(';;', ';', $font_values['family']);
                }
                if (isset($font_values['size'])) {
                    $font_style .= $font_values['size'];
                }
                if (isset($font_values['line_height'])) {
                    $font_style .= $font_values['line_height'];
                }
                if (isset($font_values['smoothing'])) {
                    $font_style .= $font_values['smoothing'];
                }
                $font_style .= ' }';
                $font_styles[] = $font_style;
            }
        }
        $output = implode("\n", $font_styles);
    }
    $output = $output ? Xss::filter($output) : '/** No fonts styles set **/';
    $file_name = 'fonts.css';
    $filepath = "{$generated_files_path}/{$file_name}";
    file_unmanaged_save_data($output, $filepath, FILE_EXISTS_REPLACE);
    // Return modified values to convert to config.
    return $values;
}
Ejemplo n.º 28
0
 /**
  * Displays a listing of database log messages.
  *
  * Messages are truncated at 56 chars.
  * Full-length messages can be viewed on the message details page.
  *
  * @return array
  *   A render array as expected by drupal_render().
  *
  * @see dblog_clear_log_form()
  * @see dblog_event()
  */
 public function overview()
 {
     $filter = $this->buildFilterQuery();
     $rows = array();
     $classes = static::getLogLevelClassMap();
     $this->moduleHandler->loadInclude('dblog', 'admin.inc');
     $build['dblog_filter_form'] = $this->formBuilder->getForm('Drupal\\dblog\\Form\\DblogFilterForm');
     $build['dblog_clear_log_form'] = $this->formBuilder->getForm('Drupal\\dblog\\Form\\DblogClearLogForm');
     $header = array('', array('data' => $this->t('Type'), 'field' => 'w.type', 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)), array('data' => $this->t('Date'), 'field' => 'w.wid', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)), $this->t('Message'), array('data' => $this->t('User'), 'field' => 'u.name', 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)), array('data' => $this->t('Operations'), 'class' => array(RESPONSIVE_PRIORITY_LOW)));
     $query = $this->database->select('watchdog', 'w')->extend('\\Drupal\\Core\\Database\\Query\\PagerSelectExtender')->extend('\\Drupal\\Core\\Database\\Query\\TableSortExtender');
     $query->fields('w', array('wid', 'uid', 'severity', 'type', 'timestamp', 'message', 'variables', 'link'));
     if (!empty($filter['where'])) {
         $query->where($filter['where'], $filter['args']);
     }
     $result = $query->limit(50)->orderByHeader($header)->execute();
     foreach ($result as $dblog) {
         $message = $this->formatMessage($dblog);
         if ($message && isset($dblog->wid)) {
             // Truncate link_text to 56 chars of message.
             $log_text = Unicode::truncate(Xss::filter($message, array()), 56, TRUE, TRUE);
             $message = $this->l($log_text, 'dblog.event', array('event_id' => $dblog->wid), array('html' => TRUE));
         }
         $username = array('#theme' => 'username', '#account' => user_load($dblog->uid));
         $rows[] = array('data' => array(array('class' => array('icon')), $this->t($dblog->type), $this->dateFormatter->format($dblog->timestamp, 'short'), $message, array('data' => $username), Xss::filter($dblog->link)), 'class' => array(drupal_html_class('dblog-' . $dblog->type), $classes[$dblog->severity]));
     }
     $build['dblog_table'] = array('#type' => 'table', '#header' => $header, '#rows' => $rows, '#attributes' => array('id' => 'admin-dblog', 'class' => array('admin-dblog')), '#empty' => $this->t('No log messages available.'), '#attached' => array('library' => array('dblog/drupal.dblog')));
     $build['dblog_pager'] = array('#theme' => 'pager');
     return $build;
 }
Ejemplo n.º 29
0
 /**
  * Helper function called from preg_replace_callback() above.
  *
  * Uses static vars to temporarily store footnotes found.
  * This is not threadsafe, but PHP isn't.
  *
  * @param array $matches
  *   Elements from array:
  *   - 0: complete matched string.
  *   - 1: tag name.
  *   - 2: tag attributes.
  *   - 3: tag content.
  * @param string $op
  *   Operation.
  *
  * @return string
  *   Return the string processed by geshi library.
  */
 protected function replaceCallback($matches, $op = '')
 {
     static $opt_collapse = 0;
     static $n = 0;
     static $store_matches = array();
     static $used_values = array();
     $str = '';
     if ($op == 'prepare') {
         // In the 'prepare' case, the first argument contains the options to use.
         // The name 'matches' is incorrect, we just use the variable anyway.
         $opt_collapse = $matches;
         return 0;
     }
     if ($op == 'output footer') {
         if (count($store_matches) > 0) {
             // Only if there are stored fn matches, pass the array of fns to be
             // themed as a list Drupal 7 requires we use "render element" which
             // just introduces a wrapper around the old array.
             // @FIXME
             // theme() has been renamed to _theme() and should NEVER be called
             // directly. Calling _theme() directly can alter the expected output and
             // potentially introduce  security issues
             // (see https://www.drupal.org/node/2195739). You should use renderable
             // arrays instead. @see https://www.drupal.org/node/2195739
             $markup = array('#theme' => 'footnote_list', '#footnotes' => $store_matches);
             $str = \Drupal::service('renderer')->render($markup, FALSE);
         }
         // Reset the static variables so they can be used again next time.
         $n = 0;
         $store_matches = array();
         $used_values = array();
         return $str;
     }
     // Default op: act as called by preg_replace_callback()
     // Random string used to ensure footnote id's are unique, even
     // when contents of multiple nodes reside on same page.
     // (fixes http://drupal.org/node/194558).
     $randstr = $this->randstr();
     $value = '';
     // Did the pattern match anything in the <fn> tag?
     if ($matches[1]) {
         // See if value attribute can parsed, either well-formed in quotes eg
         // <fn value="3">.
         if (preg_match('|value=["\'](.*?)["\']|', $matches[1], $value_match)) {
             $value = $value_match[1];
             // Or without quotes eg <fn value=8>.
         } elseif (preg_match('|value=(\\S*)|', $matches[1], $value_match)) {
             $value = $value_match[1];
         }
     }
     if ($value) {
         // A value label was found. If it is numeric, record it in $n so further
         // notes can increment from there.
         // After adding support for multiple references to same footnote in the
         // body (http://drupal.org/node/636808) also must check that $n is
         // monotonously increasing.
         if (is_numeric($value) && $n < $value) {
             $n = $value;
         }
     } elseif ($opt_collapse and $value_existing = $this->findFootnote($matches[2], $store_matches)) {
         // An identical footnote already exists. Set value to the previously
         // existing value.
         $value = $value_existing;
     } else {
         // No value label, either a plain <fn> or unparsable attributes. Increment
         // the footnote counter, set label equal to it.
         $n++;
         $value = $n;
     }
     // Remove illegal characters from $value so it can be used as an HTML id
     // attribute.
     $value_id = preg_replace('|[^\\w\\-]|', '', $value);
     // Create a sanitized version of $text that is suitable for using as HTML
     // attribute value. (In particular, as the title attribute to the footnote
     // link).
     $allowed_tags = array();
     $text_clean = Xss::filter($matches['2'], $allowed_tags);
     // HTML attribute cannot contain quotes.
     $text_clean = str_replace('"', "&quot;", $text_clean);
     // Remove newlines. Browsers don't support them anyway and they'll confuse
     // line break converter in filter.module.
     $text_clean = str_replace("\n", " ", $text_clean);
     $text_clean = str_replace("\r", "", $text_clean);
     // Create a footnote item as an array.
     $fn = array('value' => $value, 'text' => $matches[2], 'text_clean' => $text_clean, 'fn_id' => 'footnote' . $value_id . '_' . $randstr, 'ref_id' => 'footnoteref' . $value_id . '_' . $randstr);
     // We now allow to repeat the footnote value label, in which case the link
     // to the previously existing footnote is returned. Content of the current
     // footnote is ignored. See http://drupal.org/node/636808 .
     if (!in_array($value, $used_values)) {
         // This is the normal case, add the footnote to $store_matches.
         // Store the footnote item.
         array_push($store_matches, $fn);
         array_push($used_values, $value);
     } else {
         // A footnote with the same label already exists.
         // Use the text and id from the first footnote with this value.
         // Any text in this footnote is discarded.
         $i = array_search($value, $used_values);
         $fn['text'] = $store_matches[$i]['text'];
         $fn['text_clean'] = $store_matches[$i]['text_clean'];
         $fn['fn_id'] = $store_matches[$i]['fn_id'];
         // Push the new ref_id into the first occurence of this footnote label
         // The stored footnote thus holds a list of ref_id's rather than just one
         // id.
         $ref_array = is_array($store_matches[$i]['ref_id']) ? $store_matches[$i]['ref_id'] : array($store_matches[$i]['ref_id']);
         array_push($ref_array, $fn['ref_id']);
         $store_matches[$i]['ref_id'] = $ref_array;
     }
     // Return the item themed into a footnote link.
     // Drupal 7 requires we use "render element" which just introduces a wrapper
     // around the old array.
     $fn = array('#theme' => 'footnote_link', 'fn' => $fn);
     $result = \Drupal::service('renderer')->render($fn, FALSE);
     return $result;
 }
Ejemplo n.º 30
0
 /**
  * Generates an overview table of older revisions of a node.
  *
  * @param \Drupal\node\NodeInterface $node
  *   A node object.
  *
  * @return array
  *   An array as expected by drupal_render().
  */
 public function revisionOverview(NodeInterface $node)
 {
     $account = $this->currentUser();
     $node_storage = $this->entityManager()->getStorage('node');
     $type = $node->getType();
     $build = array();
     $build['#title'] = $this->t('Revisions for %title', array('%title' => $node->label()));
     $header = array($this->t('Revision'), $this->t('Operations'));
     $revert_permission = ($account->hasPermission("revert {$type} revisions") || $account->hasPermission('revert all revisions') || $account->hasPermission('administer nodes')) && $node->access('update');
     $delete_permission = ($account->hasPermission("delete {$type} revisions") || $account->hasPermission('delete all revisions') || $account->hasPermission('administer nodes')) && $node->access('delete');
     $rows = array();
     $vids = $node_storage->revisionIds($node);
     foreach (array_reverse($vids) as $vid) {
         if ($revision = $node_storage->loadRevision($vid)) {
             $row = array();
             $revision_author = $revision->uid->entity;
             if ($vid == $node->getRevisionId()) {
                 $username = array('#theme' => 'username', '#account' => $revision_author);
                 $row[] = array('data' => $this->t('!date by !username', array('!date' => $node->link($this->dateFormatter->format($revision->revision_timestamp->value, 'short')), '!username' => drupal_render($username))) . ($revision->revision_log->value != '' ? '<p class="revision-log">' . Xss::filter($revision->revision_log->value) . '</p>' : ''), 'class' => array('revision-current'));
                 $row[] = array('data' => SafeMarkup::placeholder($this->t('current revision')), 'class' => array('revision-current'));
             } else {
                 $username = array('#theme' => 'username', '#account' => $revision_author);
                 $row[] = $this->t('!date by !username', array('!date' => $this->l($this->dateFormatter->format($revision->revision_timestamp->value, 'short'), new Url('entity.node.revision', array('node' => $node->id(), 'node_revision' => $vid))), '!username' => drupal_render($username))) . ($revision->revision_log->value != '' ? '<p class="revision-log">' . Xss::filter($revision->revision_log->value) . '</p>' : '');
                 if ($revert_permission) {
                     $links['revert'] = array('title' => $this->t('Revert'), 'url' => Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid]));
                 }
                 if ($delete_permission) {
                     $links['delete'] = array('title' => $this->t('Delete'), 'url' => Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid]));
                 }
                 $row[] = array('data' => array('#type' => 'operations', '#links' => $links));
             }
             $rows[] = $row;
         }
     }
     $build['node_revisions_table'] = array('#theme' => 'table', '#rows' => $rows, '#header' => $header, '#attached' => array('library' => array('node/drupal.node.admin')));
     return $build;
 }