syntaxhighlighter.module in Syntax Highlighter 8
Same filename and directory in other branches
Syntax highlight code using the SyntaxHighlighter Javascript library.
See http://alexgorbatchev.com/SyntaxHighlighter for details.
@author: Matthew Young <www.hddigitalworks.com/contact>
File
syntaxhighlighter.moduleView source
<?php
/**
* @file
* Syntax highlight code using the SyntaxHighlighter Javascript library.
*
* See http://alexgorbatchev.com/SyntaxHighlighter for details.
*
* @author: Matthew Young <www.hddigitalworks.com/contact>
*/
use Drupal\Core\Config\Config;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\filter\Entity\FilterFormat;
/**
* Inject SyntaxHighlighter on every page except the listed pages.
*/
const SYNTAXHIGHLIGHTER_INJECT_EXCEPT_LISTED = 0;
/**
* Inject SyntaxHighlighter on only the listed pages.
*/
const SYNTAXHIGHLIGHTER_INJECT_IF_LISTED = 1;
/**
* Inject SyntaxHighlighter if the associated PHP code returns TRUE.
*/
const SYNTAXHIGHLIGHTER_INJECT_PHP = 2;
const SYNTAXHIGHLIGHTER_PHP_PERMISSION = 'use PHP for syntaxhighlighter js/css code inject control';
/**
* Define a placeholder for the syntaxhighlighter tag.
*
* Use a completely nonsense word for replacement when filtering so there is
* absolutely no chance this will conflict with something in the content text.
*/
const SYNTAXHIGHLIGHTER_TAG_STRING = '-_sYnTaXhIgHlIgHtEr_-';
/**
* Implements hook_help().
*/
function syntaxhighlighter_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.syntaxhighlighter':
return _syntaxhighlighter_filter_tips(0, 0, TRUE);
}
}
/**
* Implements hook_library_info_build().
*
* NOTE: the assets could have been defined in syntaxhighlighter.library.yml
* as well using an absolute path, but in that case the library location would
* have been to be hardcoded, e.g.:
*
* core:
* js:
* /libraries/syntaxhighlighter/scripts/shCore.js: { minified: true }
*/
function syntaxhighlighter_library_info_build() {
// Make the path absolute.
//
// This is needed because:
// 1. libraries_get_path('syntaxhighlighter') returns the path _relative_ to
// the site root path,
// 2. however hook_library_info_build assume that relative paths for the
// assets are relative to the path of the module they are defined in (the
// current module),
// 3. while the external library assets we are defining are NOT from the
// current module.
//
// So using an absolute path makes sure to look for the assets starting from
// the site root path.
$lib_location = '/' . libraries_get_path('syntaxhighlighter');
$styles_path = $lib_location . '/styles/';
$scripts_path = $lib_location . '/scripts/';
$libraries = [];
$config = \Drupal::config('syntaxhighlighter.settings');
$theme = $config
->get('theme');
$libraries['core'] = [
'css' => [
'theme' => [
$styles_path . 'shCore.css' => [],
$styles_path . $theme => [],
],
],
'js' => [
$scripts_path . 'shCore.js' => [
'minified' => TRUE,
],
],
];
$libraries['legacy'] = [
'js' => [
$scripts_path . 'shLegacy.js' => [
'minified' => TRUE,
],
],
];
$libraries['brushes'] = [
'js' => [],
];
$enabled_languages = $config
->get('enabled_languages');
foreach ($enabled_languages as $lang) {
if (!empty($lang)) {
$libraries['brushes']['js'] += [
$scripts_path . $lang => [],
];
}
}
// This also needs to be an absolute path, for the reasons stated above.
$libraries['autoloader'] = [
'js' => [
$scripts_path . 'shAutoloader.js' => [],
// PublicStream::basePath() does the same thing as the old D6
// file_directory_path() which is to give the default public 'files'
// directory path relative the the web root.
'/' . PublicStream::basePath() . '/js/syntaxhighlighter.autoloader.js' => [],
],
];
return $libraries;
}
/**
* Implements hook_page_attachments().
*/
function syntaxhighlighter_page_attachments(array &$attachments) {
if (!_syntaxhighlighter_page_match()) {
return;
}
$config = \Drupal::config('syntaxhighlighter.settings');
$attachments['#attached']['library'][] = 'syntaxhighlighter/core';
if ($config
->get('legacy_mode')) {
$attachments['#attached']['library'][] = 'syntaxhighlighter/legacy';
$settings['legacyMode'] = TRUE;
}
if ($config
->get('use_autoloader')) {
$attachments['#attached']['library'][] = 'syntaxhighlighter/autoloader';
$settings['useAutoloader'] = TRUE;
}
else {
$attachments['#attached']['library'][] = 'syntaxhighlighter/brushes';
}
$tag_name = $config
->get('tagname');
if ($tag_name !== 'pre') {
$settings['tagName'] = $tag_name;
}
$lib_location = libraries_get_path('syntaxhighlighter');
$scripts_path = $lib_location . '/scripts/';
if (file_exists($scripts_path . 'clipboard.swf')) {
$settings['clipboard'] = base_path() . $scripts_path . 'clipboard.swf';
}
$default_expressions = $config
->get('default_expressions');
if ($default_expressions) {
$settings['default_expressions'] = $default_expressions;
}
if (isset($settings)) {
$attachments['#attached']['drupalSettings']['syntaxhighlighter'] = $settings;
}
$attachments['#attached']['library'][] = 'syntaxhighlighter/syntaxhighlighter';
}
/**
* Page matching helper.
*/
function _syntaxhighlighter_page_match() {
$config = \Drupal::config('syntaxhighlighter.settings');
$inject = $config
->get('inject');
$pages = $config
->get('pages');
if ($inject != SYNTAXHIGHLIGHTER_INJECT_PHP) {
$current_path = \Drupal::service('path.current')
->getPath();
$path_alias = Drupal::service('path.alias_manager')
->getAliasByPath($current_path);
// Compare with the internal and path alias (if any).
$page_match = \Drupal::service('path.matcher')
->matchPath($path_alias, $pages);
if ($path_alias != $current_path) {
$page_match = $page_match || \Drupal::service('path.matcher')
->matchPath($current_path, $pages);
}
return !($inject xor $page_match);
}
else {
// If the PHP module is not enabled, we just return FALSE
// which just ends up disabling the syntaxhighlighter.
return function_exists('php_eval') && php_eval($pages);
}
}
/**
* Implements hook_modules_disabled().
*/
function syntaxhighlighter_modules_disabled($modules) {
$config = \Drupal::config('syntaxhighlighter.settings');
if (in_array('php', $modules) && $config
->get('inject') == SYNTAXHIGHLIGHTER_INJECT_PHP) {
drupal_set_message(t('The "%syntaxhighlighter" module is currently configured to use PHP for js/css code inject control, disabling the "%module_name" module will effectively turn off syntax highlighting on all pages.', [
'%syntaxhighlighter' => t('Syntax Highlighter'),
'%module_name' => t('PHP filter'),
]), 'warning');
}
}
/**
* Show usage tips for the syntaxhighlighter filter.
*/
function _syntaxhighlighter_filter_tips($filter, $format, $long = FALSE) {
$config = \Drupal::config('syntaxhighlighter.settings');
$tag_name = $config
->get('tagname');
$tip = t('Syntax highlight code surrounded by the <code>@ex0</code> tags, where @lang is one of the following language brushes: %brushes.', [
'@ex0' => "<{$tag_name} class=\"brush: LANG\">...</{$tag_name}>",
'@lang' => "LANG",
'%brushes' => implode(', ', _syntaxhighlighter_get_enabled_language_brushes()),
]);
if ($long) {
$tip .= '<br/>' . t('See <a href=":url0">the SyntaxHighlighter javascript library site</a> for additional help.', [
':url0' => 'http://alexgorbatchev.com/SyntaxHighlighter/',
]);
}
return $tip;
}
/**
* Get the enabled programming languages from brushes names.
*
* @return array
* An array of all enabled languages.
*/
function _syntaxhighlighter_get_enabled_language_brushes() {
$config = \Drupal::config('syntaxhighlighter.settings');
$brushes =& drupal_static(__FUNCTION__);
if (!isset($brushes)) {
$brushes = [];
foreach ($config
->get('enabled_languages') as $val) {
if ($val) {
$brushes[] = strtolower(substr(substr($val, 7), 0, -3));
}
}
}
return $brushes;
}
/**
* Implements hook_form_BASE_FORM_ID_alter().
*
* Install custom node validate function.
*/
function syntaxhighlighter_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['#validate'][] = '_syntaxhighlighter_node_validate';
}
/**
* Validate the node input text to be sure that there are no bad tags.
*/
function _syntaxhighlighter_node_validate($form, FormStateInterface $form_state) {
$body = $form_state
->getValue('body');
if (isset($body)) {
$format = isset($body[0]['format']) ? $body[0]['format'] : filter_fallback_format();
if (_syntaxhighlighter_format_has_syntaxhighlighter_filter($format)) {
if (!empty($body[0]['summary'])) {
_syntaxhighlighter_validate_input($form_state, "body][0][summary", $body[0]['summary']);
}
_syntaxhighlighter_validate_input($form_state, "body][0][value", $body[0]['value']);
}
}
}
/**
* Implements hook_form_BASE_FORM_ID_alter().
*
* Install custom comment validate function.
*/
function syntaxhighlighter_form_comment_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['#validate'][] = '_syntaxhighlighter_comment_validate';
}
/**
* Validate the comment input text to be sure that there are no bad tags.
*/
function _syntaxhighlighter_comment_validate($form, FormStateInterface $form_state) {
$comment_body = $form_state
->getValue('comment_body');
if (isset($comment_body)) {
$format = isset($comment_body[0]['format']) ? $comment_body[0]['format'] : filter_fallback_format();
if (_syntaxhighlighter_format_has_syntaxhighlighter_filter($format)) {
_syntaxhighlighter_validate_input($form_state, "comment_body][0][value", $comment_body[0]['value']);
}
}
}
/**
* Validate the input form text.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form being validated.
* @param string $field
* The input field being checked, used in the error message if any error is
* found.
* @param string $text
* The input text to validate.
*/
function _syntaxhighlighter_validate_input(FormStateInterface $form_state, $field, $text) {
$config = \Drupal::config('syntaxhighlighter.settings');
$tag_name = $config
->get('tagname');
// Check that the number of open and close tags is the same.
preg_match_all("#<{$tag_name}\\s*[^>]*>#", $text, $matches_open);
preg_match_all("#</\\s*{$tag_name}\\s*>#", $text, $matches_close);
if (count($matches_open[0]) != count($matches_close[0])) {
$error = t('Unbalanced @tag tags.', [
'@tag' => Markup::create("<{$tag_name}>"),
]);
$form_state
->setErrorByName($field, (string) $error);
}
else {
// If they are, make sure that the tags are also in the proper order.
preg_match_all("#<{$tag_name}\\s*[^>]*>.*</\\s*{$tag_name}\\s*>#sU", $text, $matches_pair);
if (count($matches_pair[0]) != count($matches_open[0]) || count($matches_pair[0]) != count($matches_close[0])) {
$error = t('@tag tags in the wrong order (e.g. unbalanced or nested).', [
'@tag' => Markup::create("<{$tag_name}>"),
]);
$form_state
->setErrorByName($field, (string) $error);
}
}
}
/**
* Create the autoload setup script file.
*
* Must call this whenever the lib location and/or the enable brushes change.
* Make sure never call this if the js lib is not found.
*/
function _syntaxhighlighter_setup_autoloader_script(Config $config) {
$dir = 'public://js';
$path = $dir . '/syntaxhighlighter.autoloader.js';
if ($config
->get('use_autoloader')) {
$script_path = base_path() . libraries_get_path('syntaxhighlighter') . '/scripts/';
$script_data = <<<__HERE__
/*
* This file is generated by the Syntax Highlighter module
*/
__HERE__;
$script_data .= "\nfunction syntaxhighlighterAutoloaderSetup() {\n SyntaxHighlighter.autoloader(\n";
$need_ending = FALSE;
$brushes = $config
->get('enabled_languages');
foreach ($brushes as $b) {
if ($b) {
if ($need_ending) {
$script_data .= ",\n";
}
$alias = strtolower(substr(substr($b, 7), 0, -3));
$script_data .= " '{$alias} {$script_path}{$b}'";
$need_ending = TRUE;
}
}
$script_data .= "\n);\n}\n";
file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
file_unmanaged_save_data($script_data, $path, FILE_EXISTS_REPLACE);
}
elseif (file_exists($path)) {
file_unmanaged_delete($path);
@\Drupal::service('file_system')
->rmdir($dir);
}
}
/**
* Check if the format uses the syntaxhighlighter filter.
*/
function _syntaxhighlighter_format_has_syntaxhighlighter_filter($format_id) {
return FilterFormat::load($format_id)
->filters()
->has('filter_syntaxhighlighter');
}
/**
* Implements hook_libraries_info().
*
* NOTE: this hook is deprecated and will be dropped in the stable Drupal 8
* release of the libraries module.
*/
function syntaxhighlighter_libraries_info() {
$libraries['syntaxhighlighter'] = [
// Only used in administrative UI of Libraries API.
'name' => 'SyntaxHighlighter',
'vendor url' => 'http://alexgorbatchev.com/SyntaxHighlighter/',
'download url' => 'https://github.com/syntaxhighlighter/SyntaxHighlighter-Site/tree/master/content/SyntaxHighlighter/download',
'path' => 'scripts',
'version arguments' => [
'file' => 'scripts/shCore.js',
'pattern' => '@([0-9]+.[0-9]+.[0-9]+)@',
'lines' => 15,
'cols' => 30,
],
];
return $libraries;
}
Functions
Constants
Name | Description |
---|---|
SYNTAXHIGHLIGHTER_INJECT_EXCEPT_LISTED | Inject SyntaxHighlighter on every page except the listed pages. |
SYNTAXHIGHLIGHTER_INJECT_IF_LISTED | Inject SyntaxHighlighter on only the listed pages. |
SYNTAXHIGHLIGHTER_INJECT_PHP | Inject SyntaxHighlighter if the associated PHP code returns TRUE. |
SYNTAXHIGHLIGHTER_PHP_PERMISSION | |
SYNTAXHIGHLIGHTER_TAG_STRING | Define a placeholder for the syntaxhighlighter tag. |