*/ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Code notes: * - Local, non-boolean variables start with "the" as in $the_output * and $the_version. Local boolean variables start with words such * as "is" or "has" to indicate their meaning as in $is_installed * and $has_title. Short-lived loop variables use standard, short * names as in $i and $x. * * - Function parameters are prefixed with "in", "out", or "io" to * indicate their use as input, output, or input-output, * respectively, as in $in_title. * * - All functions are prefixed with "textile_" and words are * separated with underscores. * * - I make extensive use of heredoc syntax and multiline strings. * It looks much nicer than dozens of "$var .= ..." lines and is * easier to read and format -- especially the HTML tags. It helps * in outputting HTML pages that are well-formatted and readable. */ // update automatically _textile_install(); require_once(drupal_get_path('module','textile') . '/textilephp/Textile.php'); require_once(drupal_get_path('module','textile') . '/smartypants-php/SmartyPants-PHP.inc'); /******************************************************************** * Drupal Hooks ********************************************************************/ /** * Implementation of hook_filter(). */ function textile_filter($in_op, $in_delta = 0, $in_format = -1, $in_text = '') { switch ($in_op) { case 'list': return array(t("Textile")); case 'description': return t('Allows content to be submitted using Textile, a simple, plain text syntax that is filtered into valid XHTML.'); case 'process': if (variable_get("textile_tags_$in_format", 1)) { return preg_replace_callback('{\[textile\](.*?)(\[/textile\]|$)}is', '_textile_process', $in_text); } else { return _textile_process(array(NULL, $in_text)); } case 'settings': return form_group(t('Textile filter'), form_checkbox(t('Use tags'), "textile_tags_$in_format", 1, variable_get("textile_tags_$in_format", 1), t('If enabled, only text between [textile] and optional [/textile] tags will be processed. Otherwise, all text will be processed as Textile markup.'))); default: return $in_text; } } // function textile_filter /** * Implementation of hook_filter_tips(). */ function textile_filter_tips($in_delta, $in_format, $in_is_long = FALSE) { if ($in_is_long) { return t('
CSS attributes can be applied to blocks (paragraphes, headers,
etc.). CSS classes are specifed with "(class)";
CSS IDs are specified with "(#id)"; both can be
specified with "(class#id)". An arbtirary
CSS style can be applied by using
"{style}". Finally, language attributes are
applied using "[language]".
Additionally, alignment and indentation shorthands are provided. To
left-align, right-align, center, and justify text, use
"<", ">", "=", and
"<>", respectively. "(" left-indents
a block 1em for each occurrence, and ")" right-indents
similarly.
Tables have additional options. "^", "-",
and "~" specify top, middle, and bottom vertical
alignment. The "_" attribute on a cell indicates that
it is a table header.
The examples below illustrate these attributes.
| textile input | output | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Headingshx. (where x is 1 - 6)
|
|||||||||||||
| h1. Heading | Heading |
||||||||||||
| h2(class). Heading with class | Heading with class |
||||||||||||
Paragraphs |
|||||||||||||
| p=. Centered text | Centered text |
||||||||||||
| p())(#id). Indented text with ID |
Indented text with ID |
||||||||||||
Block quotes |
|||||||||||||
| bq(class#id). Quote with class and ID |
Quote with class and ID |
||||||||||||
| bq[en]. English quote |
English quote
|
||||||||||||
Ordered lists |
|||||||||||||
|
{color: blue}# Attributes specified # before the first item # affect the whole list |
|
||||||||||||
Unordered lists |
|||||||||||||
|
* Lists can have ## subitems or ## sublists * too |
|
||||||||||||
Footnotesfnx. (where x is 1 - 100)
|
|||||||||||||
| fn17. Footnote | 17 Footnote |
||||||||||||
Tables |
|||||||||||||
|
|_. A|_. B|_. C| (dark). |very|simple|table| |<. left|=. center|>. right| |^{height:3em}. top|-. middle|~. bottom| |
|
||||||||||||
The class, ID, style, and language attributes described above also apply to the span phrase modifier as shown below.
| textile input | output |
|---|---|
| _emphasis_ | emphasis |
| __italic__ | italic |
| *strong* | strong |
| **bold** | bold |
| ??citation?? | citation |
| -delete text- | |
| +inserted text+ | inserted text |
| ^superscript^ | superscript |
| ~subscript~ | subscript |
| @code@ | code |
| %(class)span% | span |
| %{color:red;}span% | span |
| ==no textile== | no textile |
| "link text":url | link text |
| "link text(title)":url | link text |
| !imageurl! | |
| !imageurl(alt text)! | |
| !imageurl!:url | |
| ABC(Always Be Closing) | ABC |
| Footnote reference[17] | Footnote reference17 |
The Textile module allows users to enter content using Textile, a simple, plain text syntax that is filtered into valid XHTML. The filter tips page provides syntax descriptions and examples.
'); } } // function textile_help /******************************************************************** * Module Functions ********************************************************************/ /** * Returns the version of this release of the Textile module. * * @return * Anarray with keys 'text' and 'build' containing the
* text version and build ID of this release, respectively.
*
* @private
*/
function _textile_version() {
/* Why text and an ID? Well, the text is easier for the user to
* read and understand while the build ID, being a number (a date
* with a serial, specifically), is easier for the developer to use
* to determine newer/older versions for upgrade and installation
* purposes.
*/
return array('text' => '2.1.1', 'build' => 2004092301);
} // function _textile_version
/**
* Indicates whether or not the textile module is installed and
* up-to-date, transparently upgrading compatible versions when
* possible.
*
* @return
* TRUE if the module is up to date;
* FALSE, otherwise.
*
* @private
*/
function _textile_is_installed() {
$the_current_ver = _textile_version();
$the_installed_ver = variable_get('textile_version', NULL);
// Handle versions that can be safely/transparently updated.
if ($the_installed_ver && ($the_installed_ver['build'] < $the_current_ver['build'])) {
variable_set('textile_version', $the_current_ver);
return TRUE;
}
return ($the_current_ver['build'] == $the_installed_ver['build']);
} // function _textile_is_installed
/**
* Installs the textile module, updating existing Textile-formatted
* nodes if needed and creating the textile_version
* variable.
*
* @private
*/
function _textile_install() {
if (_textile_is_installed()) {
return;
}
// get version information
$the_current_ver = _textile_version();
$the_installed_ver = variable_get('textile_version', NULL);
if ($the_installed_ver['build'] > $the_current_ver['build']) {
// don't install an older version
return;
}
// update old tags
db_query("UPDATE {node} SET body = REPLACE(body, '\001textile1\001\n', '[textile]\n\n') WHERE body LIKE '%%\001textile1\001\n%%'");
db_query("UPDATE {node} SET teaser = REPLACE(teaser, '\001textile1\001\n', '[textile]\n\n') WHERE teaser LIKE '%%\001textile1\001\n%%'");
db_query("UPDATE {node} SET body = REPLACE(body, '\001textile2\001\n', '[textile]\n\n') WHERE body LIKE '%%\001textile2\001\n%%'");
db_query("UPDATE {node} SET teaser = REPLACE(teaser, '\001textile2\001\n', '[textile]\n\n') WHERE teaser LIKE '%%\001textile2\001\n%%'");
// installed successfully
variable_set('textile_version', $the_current_ver);
} // function _textile_install
/**
* Performs the appropriate Textile filtering on the provided text.
*
* @param $in_matches
* The array specifying the text to be filtered at
* index 1.
*
* @return
* A string containing the filtered text.
*
* @private
*/
function _textile_process($in_matches) {
static $s_textile = NULL;
if ($s_textile === NULL) {
$s_textile = new DrupalTextile(array('smarty_mode' => DEFAULT_OPERATION_MODE));
}
return $s_textile->process($in_matches[1]);
} // function _textile_process
/**
* DrupalTextile is a subclass of MTLikeTextile that properly handles
* Textile footnote URL fragments in a Drupal context.
*/
class DrupalTextile extends MTLikeTextile {
/**
* Creates a new DrupalTextile object using the provided options.
*
* @param $in_options
* The array specifying the options for this object.
*/
function DrupalTextile($in_options = array()) {
parent::MTLikeTextile($in_options);
} // function DrupalTextile
/**
* Processes the provided Textile-formatted text, properly handling
* URL fragments in a Drupal context.
*
* @param $in_string
* The string specifying the text to be processed.
*
* @return
* A string containing the processed text.
*/
function process($in_string) {
$this->charset('utf8'); // Changed as described in http://drupal.org/node/11357
return preg_replace('{(