123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- <?php
- /*
- * TODO
- *
- * strip_shortcodes - remove all shortcodes from the processed text
- *
- * correct element cascading
- *
- */
- /**
- * Build a list of all shortcodes (for filter).
- * Calls the shortcode hook with the list parameter on all module
- */
- function shortcode_list_all($reset = FALSE) {
- $shortcodes = &drupal_static(__FUNCTION__);
- if(!isset($shortcodes) || $reset) {
- $shortcodes = array();
- $shortcodes += module_invoke_all('shortcode_info');
- }
-
- return $shortcodes;
- }
- /**
- * Returns only enabled shortcodes for a format
- */
- function shortcode_list_all_enabled($format, $reset = FALSE) {
- if (is_string($format)) {
- $format = filter_format_load($format);
- }
- $shortcodes_enabled = &drupal_static(__FUNCTION__, array());
- if (isset($shortcodes_enabled[$format->format]) && !$reset) {
- return $shortcodes_enabled[$format->format];
- }
- $shortcodes_enabled[$format->format] = array();
-
- $shortcodes = shortcode_list_all($reset);
- $filters = filter_list_format($format->format);
- if (empty($filters['shortcode'])) {
- return array();
- }
-
- foreach($filters['shortcode']->settings as $name => $enabled) { // Run through all shortcodes
- if ($enabled) {
- $shortcodes_enabled[$format->format][$name] = $shortcodes[$name];
- }
- }
- return $shortcodes_enabled[$format->format];
- }
- /**
- * Implementation of hook_filter_info().
- */
- function shortcode_filter_info() {
- $filters['shortcode'] = array(
- 'title' => t('Shortcodes'),
- 'description' => t('Provides WP like shortcodes to this text format.'),
- 'process callback' => '_shortcode_process',
- 'settings callback' => '_shortcode_settings_form',
- 'tips callback' => '_shortcode_filter_tips',
- );
- $filters['shortcode_text_corrector'] = array(
- 'title' => t('Shortcodes - html corrector'),
- 'description' => t('Trying to correct the html around shortcodes. Enable only if you using wysiwyg editor.'),
- 'process callback' => '_shortcode_postprocess_text',
- );
-
- return $filters;
- }
- /**
- * Filter tips callback
- */
- function _shortcode_filter_tips($filter, $format, $long = FALSE) {
- $shortcodes = shortcode_list_all_enabled($format);
- $tips = array();
- $args = func_get_args();
- foreach($filter->settings as $name => $enabled) { // Run through all shortcodes
- if($enabled && !empty($shortcodes[$name]['tips callback']) && function_exists($shortcodes[$name]['tips callback'])) {
- $tips[] = call_user_func_array($shortcodes[$name]['tips callback'], array($format, $long));
- }
- }
- return theme('item_list',
- array(
- 'title' => t('Shortcodes usage'),
- 'items' => $tips,
- 'type' => 'ol',
- )
- );
- }
- /**
- * Settings form
- */
- function _shortcode_settings_form($form, &$form_state, $filter, $format, $defaults) {
- $settings = array();
- $filter->settings += $defaults;
- $shortcodes = shortcode_list_all();
- foreach ($shortcodes as $key => $shortcode) {
- $settings[$key] = array(
- '#type' => 'checkbox',
- '#title' => t('Enable %name shortcode', array('%name' => $shortcode['title'])),
- '#default_value' => array(),
- '#description' => 'Enable or disable this shortcode in this input format',
- );
- if (!empty($filter->settings[$key])) {
- $settings[$key]['#default_value'] = $filter->settings[$key];
- }
- elseif( !empty($defaults[$key])) {
- $settings[$key]['#default_value'] = $defaults[$key];
- }
- }
- return $settings;
- }
- /**
- * Tags cache
- * @param $tags
- *
- * @access private
- */
- function _shortcode_tags($tags = NULL) {
- $shortcodes = &drupal_static(__FUNCTION__, array());
- if ($tags) {
- $shortcodes = $tags;
- return TRUE;
- }
- return $shortcodes;
- }
- /**
- * Process the shortcodes according to the text and the text format.
- */
- function _shortcode_process($text, $filter) {
- // TODO: need cache for list_all for the given filter!
- $shortcodes = shortcode_list_all();
- $shortcodes_enabled = array();
- foreach($filter->settings as $name => $value) { // run through all shortcodes
- if($value && $shortcodes[$name]['process callback']) {
- $shortcodes_enabled[$name] = array(
- 'function' => $shortcodes[$name]['process callback'],
- );
- }
- }
- if (empty($shortcodes_enabled)) {
- return $text;
- }
- // save the shortcodes
- _shortcode_tags($shortcodes_enabled);
- // improved version - recursive processing - embed tags within other tags is supported!
- $chunks = preg_split('!(\[.*?\])!', $text, -1, PREG_SPLIT_DELIM_CAPTURE );
- //dpr($chunks);
- $heap = array();
- $heap_index = array();
- foreach ($chunks as $c) {
- if (!$c) {
- continue;
- }
- // shortcode or not
- if (($c[0] == '[') && (substr($c, -1, 1) == ']')) {
- // $c contains shortcode
- // self-closing tag or not
- $c = substr($c, 1, -1);
- //dpr('process: ' . $c);
- if (substr($c, -1, 1) == '/') {
- // process a self closing tag - it has / at the end!
- //dpr('self closing: ' . $c);
- /*
- * 0 - the full tag text?
- * 1/5 - An extra [ or ] to allow for escaping shortcodes with double [[]]
- * 2 - The shortcode name
- * 3 - The shortcode argument list
- * 4 - The content of a shortcode when it wraps some content.
- * */
- $ts = explode(' ', trim($c));
- $tag = array_shift($ts);
- $m = array(
- $c,
- '',
- $tag,
- implode(' ', $ts),
- NULL,
- ''
- );
- array_unshift($heap_index, '_string_');
- array_unshift($heap, _shortcode_process_tag($m));
- }
- elseif ($c[0] == '/') {
- // closing tag - process the heap
- $closing_tag = substr($c, 1);
- //dpr('closing tag: ' . $closing_tag );
- $process_heap = array();
- $process_heap_index = array();
- $found = FALSE;
- // get elements from heap and process
- do {
- $tag = array_shift($heap_index);
- $heap_text = array_shift($heap);
- if($closing_tag == $tag) {
- // process the whole tag
- $m = array(
- $tag . ' ' . $heap_text,
- '',
- $tag,
- $heap_text,
- implode('', $process_heap),
- ''
- );
- $str = _shortcode_process_tag($m);
- array_unshift($heap_index, '_string_');
- array_unshift($heap, $str);
- $found = TRUE;
- }
- else {
- array_unshift($process_heap, $heap_text);
- array_unshift($process_heap_index, $tag);
- }
- } while(!$found && $heap);
- if(!$found) {
- foreach($process_heap as $val) {
- array_unshift($heap, $val);
- }
- foreach($process_heap_index as $val) {
- array_unshift($heap_index, $val);
- }
- }
- }
- else {
- // starting tag. put to the heap
- //dpr('tag pattern: ' . $c);
- $ts = explode(' ', trim($c));
- $tag = array_shift($ts);
- // dpr('start tag: ' . $tag);
- array_unshift($heap_index, $tag);
- array_unshift($heap, implode(' ', $ts));
- }
- }
- else {
- // not found a pair?
- array_unshift($heap_index, '_string_');
- array_unshift($heap, $c);
- }
- }
- return(implode('', array_reverse($heap)));
- }
- /*
- * Html corrector for wysiwyg editors
- *
- * Correcting p elements around the divs. No div are allowed in p so remove them.
- *
- */
- function _shortcode_postprocess_text($text, $filter) {
- $patterns = array(
- '|#!#|is',
- '!<p>( |\s)*(<\/*div>)!is',
- '!<p>( |\s)*(<div)!is',
- '!(<\/div.*?>)\s*</p>!is',
- '!(<div.*?>)\s*</p>!is',
- );
- //$replacements = array('!!\\2', '###\\2', '@@@\\1');
- $replacements = array('', '\\2', '\\2', '\\1', '\\1');
- return preg_replace($patterns, $replacements, $text);
- }
- /**
- * Regular Expression callable for do_shortcode() for calling shortcode hook.
- * @see get_shortcode_regex for details of the match array contents.
- *
- * @since 2.5
- * @access private
- * @uses $shortcode_tags
- *
- * @param array $m Regular expression match array
- * @return mixed False on failure.
- */
- function _shortcode_process_tag($m) {
- // get tags from static cache
- $shortcodes = _shortcode_tags();
- // allow [[foo]] syntax for escaping a tag
- if ($m[1] == '[' && $m[5] == ']') {
- return substr($m[0], 1, -1);
- }
- $tag = $m[2];
- if (!empty($shortcodes[$tag])) {
- // tag exists (enabled)
- $attr = _shortcode_parse_attrs($m[3]);
- /*
- * 0 - the full tag text?
- * 1/5 - An extra [ or ] to allow for escaping shortcodes with double [[]]
- * 2 - The shortcode name
- * 3 - The shortcode argument list
- * 4 - The content of a shortcode when it wraps some content.
- * */
- if (! is_null($m[4]) ) {
- // enclosing tag - extra parameter
- return $m[1] . call_user_func($shortcodes[$tag]['function'], $attr, $m[4], $m[2]) . $m[5];
- }
- else {
- // self-closing tag
- //dpr('fv self closing: ' . $shortcodes[$tag]->function);
- return $m[1] . call_user_func($shortcodes[$tag]['function'], $attr, NULL, $m[2]) . $m[5];
- }
- }
- elseif(is_null($m[4])) {
- return $m[4];
- }
- return '';
- }
- /**
- * Retrieve all attributes from the shortcodes tag.
- *
- * The attributes list has the attribute name as the key and the value of the
- * attribute as the value in the key/value pair. This allows for easier
- * retrieval of the attributes, since all attributes have to be known.
- *
- * @since 2.5
- *
- * @param string $text
- * @return array List of attributes and their value.
- */
- function _shortcode_parse_attrs($text) {
- $atts = array();
- $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';
- $text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text);
- if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) ) {
- foreach ($match as $m) {
- if (!empty($m[1]))
- $atts[strtolower($m[1])] = stripcslashes($m[2]);
- elseif (!empty($m[3]))
- $atts[strtolower($m[3])] = stripcslashes($m[4]);
- elseif (!empty($m[5]))
- $atts[strtolower($m[5])] = stripcslashes($m[6]);
- elseif (isset($m[7]) and strlen($m[7]))
- $atts[] = stripcslashes($m[7]);
- elseif (isset($m[8]))
- $atts[] = stripcslashes($m[8]);
- }
- }
- else {
- $atts = ltrim($text);
- }
- return $atts;
- }
- /**
- * Retrieve the shortcode regular expression for searching.
- *
- * The regular expression combines the shortcode tags in the regular expression
- * in a regex class.
- *
- * The regular expresion contains 6 different sub matches to help with parsing.
- *
- * 1/6 - An extra [ or ] to allow for escaping shortcodes with double [[]]
- * 2 - The shortcode name
- * 3 - The shortcode argument list
- * 4 - The self closing /
- * 5 - The content of a shortcode when it wraps some content.
- *
- * @return string The shortcode search regular expression
- */
- function _shortcode_get_shortcode_regex($names) {
- $tagregexp = join( '|', array_map('preg_quote', $names) );
- // WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcodes()
- return '(.?)\[('.$tagregexp.')\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?(.?)';
- }
- /**
- * Combine user attributes with known attributes and fill in defaults when needed.
- *
- * The pairs should be considered to be all of the attributes which are
- * supported by the caller and given as a list. The returned attributes will
- * only contain the attributes in the $pairs list.
- *
- * If the $atts list has unsupported attributes, then they will be ignored and
- * removed from the final returned list.
- *
- * @since 2.5
- *
- * @param array $pairs Entire list of supported attributes and their defaults.
- * @param array $atts User defined attributes in shortcode tag.
- * @return array Combined and filtered attribute list.
- */
- function shortcode_attrs($pairs, $attrs) {
- $attrs = (array)$attrs;
- $out = array();
- foreach ($pairs as $name => $default) {
- if (array_key_exists($name, $attrs)) {
- $out[$name] = $attrs[$name];
- }
- else {
- $out[$name] = $default;
- }
- }
- return $out;
- }
- /**
- * Helper function to decide the given param is a bool value
- * @param mixed $var
- *
- * @return bool
- */
- function shortcode_bool($var) {
- switch (strtolower($var)) {
- case false:
- case 'false':
- case 'no':
- case '0':
- $res = FALSE;
- break;
- default:
- $res = TRUE;
- break;
- }
- return $res;
- }
- /**
- * Class parameter helper function
- * @param $class
- * @param $default
- */
- function shortcode_add_class($class='', $default='') {
- if ($class) {
- if (! is_array($class)) {
- $class = explode(' ', $class);
- }
- array_unshift($class, $default);
- $class = array_unique($class);
- }
- else {
- $class[] = $default;
- }
- return implode(' ', $class);
- } //shortcode_add_class
- // shortcode implementations
- /**
- * Generates a random code
- *
- * Calling
- * [random length=X /]
- *
- * Where X is the length of the random text.
- * If the length empty or invalid, between 1-99, the length will be 8
- *
- */
- function shortcode_shortcode_random($attrs, $text) {
- extract( shortcode_attrs( array(
- 'length' => 8,
- ), $attrs ));
- $length = intval($length);
- if (($length < 0) || ($length > 99)) {
- $length = 8;
- }
- $text = '';
- for ($i=0; $i < $length; ++$i) {
- $text .= chr(rand(32, 126));
- }
- return $text;
- }
|