      1: <?php
      2:
      3: AlphaAutoLoader::loadLib('markdown.php');
      4:
      5: /**
      6:  *
      7:  * A custom version of the markdown class which uses the geshi library for rendering code
      8:  *
      9:  * @package alpha::util
     10:  * @since 1.0
     11:  * @author John Collins <dev@alphaframework.org>
     12:  * @version $Id: AlphaMarkdown.inc 1624 2012-12-21 12:17:55Z alphadevx $
     13:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
     14:  * @copyright Copyright (c) 2012, John Collins (founder of Alpha Framework).
     15:  * All rights reserved.
     16:  *
     17:  * <pre>
     18:  * Redistribution and use in source and binary forms, with or
     19:  * without modification, are permitted provided that the
     20:  * following conditions are met:
     21:  *
     22:  * * Redistributions of source code must retain the above
     23:  *   copyright notice, this list of conditions and the
     24:  *   following disclaimer.
     25:  * * Redistributions in binary form must reproduce the above
     26:  *   copyright notice, this list of conditions and the
     27:  *   following disclaimer in the documentation and/or other
     28:  *   materials provided with the distribution.
     29:  * * Neither the name of the Alpha Framework nor the names
     30:  *   of its contributors may be used to endorse or promote
     31:  *   products derived from this software without specific
     32:  *   prior written permission.
     33:  *
     34:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
     35:  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
     36:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     37:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     38:  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
     39:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     40:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     41:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     42:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     43:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     44:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
     45:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     46:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     47:  * </pre>
     48:  *
     49:  */
     50: class AlphaMarkdown extends MarkdownExtra_Parser {
     51:
     52:     /**
     53:      * Custom version of the _doCodeBlocks_callback method which invokes a Gheshi
     54:      * object to render code.
     55:      *
     56:      * @since 1.0
     57:      */
     58:     public function _doCodeBlocks_callback($matches) {
     59:         global $config;
     60:
     61:         $codeblock = $matches[1];
     62:
     63:         $codeblock = $this->outdent($codeblock);
     64:
     65:         // trim leading newlines and trailing whitespace
     66:         $codeblock = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $codeblock);
     67:
     68:         // find the code block and replace it with a blank
     69:         $codeTypeTag = array();
     70:         preg_match('/codeType=\[.*\]/', $codeblock, $codeTypeTag);
     71:         $codeblock = preg_replace('/codeType=\[.*\]\n/', '', $codeblock);
     72:
     73:         if(isset($codeTypeTag[0])) {
     74:             $start = strpos($codeTypeTag[0], '[');
     75:             $end = strpos($codeTypeTag[0], ']');
     76:             $language = substr($codeTypeTag[0], $start+1, $end-($start+1));
     77:         }else{
     78:             // will use php as a defualt language type when none is provided
     79:             $language = 'php';
     80:         }
     81:
     82:         if($config->get('cms.highlight.provider.name') != '') {
     83:             $highlighter = AlphaHighlightProviderFactory::getInstance($config->get('cms.highlight.provider.name'));
     84:             $codeblock = $highlighter->highlight($codeblock, $language);
     85:         }
     86:
     87:         $result = "\n\n".$this->hashBlock("<pre><code>" . $codeblock . "\n</code></pre>")."\n\n";
     88:
     89:         return $result;
     90:     }
     91:
     92:     /**
     93:      * Custom _doAnchors_inline_callback that renders links to external sites with a
     94:      * target attribute and an icon.
     95:      *
     96:      * @since 1.0
     97:      */
     98:     public function _doAnchors_inline_callback($matches) {
     99:         global $config;
    100:
    101:         $whole_match    =  $matches[1];
    102:         $link_text      =  $this->runSpanGamut($matches[2]);
    103:         $url            =  $matches[3];
    104:         $title          =& $matches[6];
    105:
    106:         $external = false;
    107:
    108:         $parts = parse_url($url);
    109:
    110:         /*
    111:          * Only an external link if:
    112:          *
    113:          * 1. $url parses to a valid URL
    114:          * 2. $url has a host part
    115:          * 3. $url does not contain $config->get('app.url'), i.e. points to a local resource.
    116:          */
    117:         if(is_array($parts) && isset($parts['host']) && strpos($url, $config->get('app.url')) === false)
    118:             $external = true;
    119:
    120:         $url = $this->encodeAmpsAndAngles($url);
    121:
    122:         $result = "<a href=\"$url\"";
    123:         if (isset($title)) {
    124:             $title = str_replace('"', '&quot;', $title);
    125:             $title = $this->encodeAmpsAndAngles($title);
    126:             $result .=  " title=\"$title\"";
    127:         }
    128:         if ($external) {
    129:             $result .= " target=\"$url\"";
    130:         }
    131:
    132:         $link_text = $this->runSpanGamut($link_text);
    133:         if (!$external) {
    134:             $result .= ">$link_text</a>";
    135:         }else{
    136:             $result .= '>'.$link_text.'<img src="'.$config->get('app.url').'alpha/images/icons/page_go.png'.'" class="externalLink"/></a>';
    137:         }
    138:
    139:         return $this->hashSpan($result);
    140:     }
    141:
    142:     /**
    143:      * Custom version of the _doTable_callback(...) method which sets the table border and CSS style
    144:      *
    145:      * @since 1.0
    146:      */
    147:     public function _doTable_callback($matches) {
    148:         $head       = $matches[1];
    149:         $underline  = $matches[2];
    150:         $content    = $matches[3];
    151:
    152:         # Remove any tailing pipes for each line.
    153:         $head       = preg_replace('/[|] *$/m', '', $head);
    154:         $underline  = preg_replace('/[|] *$/m', '', $underline);
    155:         $content    = preg_replace('/[|] *$/m', '', $content);
    156:
    157:         # Reading alignement from header underline.
    158:         $separators = preg_split('/ *[|] */', $underline);
    159:         foreach ($separators as $n => $s) {
    160:             if (preg_match('/^ *-+: *$/', $s))      $attr[$n] = ' align="right"';
    161:             else if (preg_match('/^ *:-+: *$/', $s))$attr[$n] = ' align="center"';
    162:             else if (preg_match('/^ *:-+ *$/', $s)) $attr[$n] = ' align="left"';
    163:             else                                    $attr[$n] = '';
    164:         }
    165:
    166:         # Creating code spans before splitting the row is an easy way to
    167:         # handle a code span containg pipes.
    168:         $head   = $this->doCodeSpans($head);
    169:         $headers    = preg_split('/ *[|] */', $head);
    170:         $col_count  = count($headers);
    171:
    172:         # Write column headers.
    173:         $text = "<table class=\"bordered\" border=\"1\">\n";
    174:         $text .= "<thead>\n";
    175:         $text .= "<tr>\n";
    176:         foreach ($headers as $n => $header)
    177:             $text .= "  <th$attr[$n]>".$this->runSpanGamut(trim($header))."</th>\n";
    178:         $text .= "</tr>\n";
    179:         $text .= "</thead>\n";
    180:
    181:         # Split content by row.
    182:         $rows = explode("\n", trim($content, "\n"));
    183:
    184:         $text .= "<tbody>\n";
    185:         foreach ($rows as $row) {
    186:             # Creating code spans before splitting the row is an easy way to
    187:             # handle a code span containg pipes.
    188:             $row = $this->doCodeSpans($row);
    189:
    190:             # Split row by cell.
    191:             $row_cells = preg_split('/ *[|] */', $row, $col_count);
    192:             $row_cells = array_pad($row_cells, $col_count, '');
    193:
    194:             $text .= "<tr>\n";
    195:             foreach ($row_cells as $n => $cell)
    196:                 $text .= "  <td$attr[$n]>".$this->runSpanGamut(trim($cell))."</td>\n";
    197:             $text .= "</tr>\n";
    198:         }
    199:         $text .= "</tbody>\n";
    200:         $text .= "</table>";
    201:
    202:         return $this->hashBlock($text) . "\n";
    203:     }
    204: }
    205:
    206: ?>