      1: <?php
      2:
      3: /**
      4:  *
      5:  * A facade class for the Markdown library
      6:  *
      7:  * @package alpha::util
      8:  * @since 1.0
      9:  * @author John Collins <dev@alphaframework.org>
     10:  * @version $Id: MarkdownFacade.inc 1624 2012-12-21 12:17:55Z alphadevx $
     11:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
     12:  * @copyright Copyright (c) 2012, John Collins (founder of Alpha Framework).
     13:  * All rights reserved.
     14:  *
     15:  * <pre>
     16:  * Redistribution and use in source and binary forms, with or
     17:  * without modification, are permitted provided that the
     18:  * following conditions are met:
     19:  *
     20:  * * Redistributions of source code must retain the above
     21:  *   copyright notice, this list of conditions and the
     22:  *   following disclaimer.
     23:  * * Redistributions in binary form must reproduce the above
     24:  *   copyright notice, this list of conditions and the
     25:  *   following disclaimer in the documentation and/or other
     26:  *   materials provided with the distribution.
     27:  * * Neither the name of the Alpha Framework nor the names
     28:  *   of its contributors may be used to endorse or promote
     29:  *   products derived from this software without specific
     30:  *   prior written permission.
     31:  *
     32:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
     33:  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
     34:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     35:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     36:  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
     37:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     38:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     39:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     40:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     41:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     42:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
     43:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     44:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     45:  * </pre>
     46:  *
     47:  */
     48: class MarkdownFacade {
     49:     /**
     50:      * The markdown-format content that we will render
     51:      *
     52:      * @var string
     53:      * @since 1.0
     54:      */
     55:     private $content;
     56:
     57:     /**
     58:      * The business object that stores the content will be rendered to Markdown
     59:      *
     60:      * @var AlphaDAO
     61:      * @since 1.0
     62:      */
     63:     private $BO = null;
     64:
     65:     /**
     66:      * The auto-generated name of the Markdown HTML cache file for the BO
     67:      *
     68:      * @var string
     69:      * @since 1.0
     70:      */
     71:     private $filename;
     72:
     73:     /**
     74:      * The constructor
     75:      *
     76:      * @param AlphaDAO $BO
     77:      * @param boolean $useCache
     78:      * @since 1.0
     79:      */
     80:     public function __construct($BO, $useCache = true) {
     81:         global $config;
     82:
     83:         $this->BO = $BO;
     84:         if($this->BO instanceof ArticleObject && $this->BO->isLoadedFromFile()) {
     85:             $underscoreTimeStamp = str_replace(array('-',' ',':'), '_', $this->BO->getContentFileDate());
     86:             $this->filename = $config->get('app.file.store.dir').'cache/html/'.get_class($this->BO).'_'.$this->BO->get('title').'_'.$underscoreTimeStamp.'.html';
     87:         }else{
     88:             $this->filename = $config->get('app.file.store.dir').'cache/html/'.get_class($this->BO).'_'.$this->BO->getID().'_'.$this->BO->getVersion().'.html';
     89:         }
     90:
     91:         if(!$useCache) {
     92:             $this->content = $this->markdown($this->BO->get('content', true));
     93:         }else{
     94:             if ($this->checkCache()) {
     95:                 $this->loadCache();
     96:             }else{
     97:                 if($this->BO->get('content', true) == '') {
     98:                     // the content may not be loaded from the DB at this stage due to a previous soft-load
     99:                     $this->BO->reload();
    100:                 }
    101:
    102:                 $this->content = $this->markdown($this->BO->get('content', true));
    103:
    104:                 $this->cache();
    105:             }
    106:         }
    107:
    108:         // Replace all instances of $attachURL in link tags to links to the ViewAttachment controller
    109:         $attachments = array();
    110:         preg_match_all('/href\=\"\$attachURL\/.*\"/', $this->content, $attachments);
    111:
    112:         foreach($attachments[0] as $attachmentURL) {
    113:             $start = strpos($attachmentURL, '/');
    114:             $end = strrpos($attachmentURL, '"');
    115:             $fileName = substr($attachmentURL, $start+1, $end-($start+1));
    116:
    117:             if(method_exists($this->BO, 'getAttachmentSecureURL')) {
    118:                 $this->content = str_replace($attachmentURL, 'href="'.$this->BO->getAttachmentSecureURL($fileName).'" rel="nofollow"', $this->content);
    119:             }
    120:         }
    121:
    122:         // Handle image attachments
    123:         $attachments = array();
    124:         preg_match_all('/\<img\ src\=\"\$attachURL\/.*\".*\>/', $this->content, $attachments);
    125:
    126:         foreach($attachments[0] as $attachmentURL) {
    127:             $start = strpos($attachmentURL, '/');
    128:             $end = strrpos($attachmentURL, '" alt');
    129:             $fileName = substr($attachmentURL, $start+1, $end-($start+1));
    130:
    131:             if($config->get('cms.images.widget')) {
    132:                 // get the details of the source image
    133:                 $path = $this->BO->getAttachmentsLocation().'/'.$fileName;
    134:                 $image_details = getimagesize($path);
    135:                 $imgType = $image_details[2];
    136:                 if($imgType == 1)
    137:                     $type = 'gif';
    138:                 elseif($imgType == 2)
    139:                     $type = 'jpg';
    140:                 elseif($imgType == 3)
    141:                     $type = 'png';
    142:
    143:                 $img = new Image($path, $image_details[0], $image_details[1], $type, 0.95, false, (boolean)$config->get('cms.images.widget.secure'));
    144:
    145:                 $this->content = str_replace($attachmentURL, $img->renderHTMLLink(), $this->content);
    146:             }else{
    147:                 // render a normal image link to the ViewAttachment controller
    148:                 if(method_exists($this->BO, 'getAttachmentSecureURL')) {
    149:                     $this->content = str_replace($attachmentURL, '<img src="'.$this->BO->getAttachmentSecureURL($fileName).'">', $this->content);
    150:                 }
    151:             }
    152:         }
    153:     }
    154:
    155:     /**
    156:      * Facade method which will invoke our custom markdown class rather than the standard one
    157:      *
    158:      * @return string
    159:      * @since 1.0
    160:      */
    161:     public function markdown($text) {
    162:         global $config;
    163:
    164:         // Initialize the parser and return the result of its transform method.
    165:         static $parser;
    166:
    167:         if (!isset($parser)) {
    168:             $parser_class = 'AlphaMarkdown';
    169:             $parser = new $parser_class;
    170:         }
    171:
    172:         /*
    173:          * Replace all instances of $sysURL in the text with the app.url setting from config
    174:          */
    175:         $text = str_replace('$sysURL', $config->get('app.url'), $text);
    176:
    177:         // transform text using parser.
    178:         return $parser->transform($text);
    179:     }
    180:
    181:     /**
    182:      * Getter for the content
    183:      *
    184:      * @return string
    185:      * @since 1.0
    186:      */
    187:     public function getContent() {
    188:         return $this->content;
    189:     }
    190:
    191:     /**
    192:      * Saves the HTML generated by Markdown to the cache directory
    193:      *
    194:      * @since 1.0
    195:      */
    196:     private function cache() {
    197:         // check to ensure that the article is not transient before caching it
    198:         if ($this->BO->getID() != '00000000000' || $this->BO->isLoadedFromFile()) {
    199:             $fp=fopen($this->filename,"w");
    200:             if (!$fp) {
    201:                 throw new AlphaException('Failed to open the cache file for writing, directory permissions my not be set correctly!');
    202:             }else{
    203:                 flock($fp,2); // locks the file for writting
    204:                 fwrite($fp,$this->content);
    205:                 flock($fp,3); // unlocks the file
    206:                 fclose($fp); //closes the file
    207:             }
    208:         }
    209:     }
    210:
    211:     /**
    212:      * Used to check the HTML cache for the BO cache file
    213:      *
    214:      * @return boolean
    215:      * @since 1.0
    216:      */
    217:     public function checkCache() {
    218:         return file_exists($this->filename);
    219:     }
    220:
    221:     /**
    222:      * Method to load the content of the cache file to the $content attribute of this object
    223:      *
    224:      * @since 1.0
    225:      */
    226:     public function loadCache() {
    227:         $fp = fopen($this->filename,"r");
    228:
    229:         if (!$fp) {
    230:             throw new AlphaException('Failed to open the cache file for reading, directory permissions my not be set correctly!');
    231:         }else{
    232:             $this->content = fread($fp, filesize($this->filename));
    233:             fclose($fp); //closes the file
    234:         }
    235:     }
    236: }
    237: ?>