Source for file document.parser.class.inc.php

Documentation is available at document.parser.class.inc.php

  1. <?php
  2. require_once('core.class.inc.php');
  3.  
  4. /**
  5.  * ClipperCMS Document Parser
  6.  *
  7.  * This class contains the main document parsing functions.
  8.  *
  9.  */
  10. class DocumentParser extends Core {
  11.     var $event$Event// event object
  12.     var $pluginEvent;
  13.     var $confignull;
  14.     var $rs;
  15.     var $result;
  16.     var $sql;
  17.     var $table_prefix;
  18.     var $debug;
  19.     var $documentIdentifier;
  20.     var $documentMethod;
  21.     var $documentGenerated;
  22.     var $documentContent;
  23.     var $tstart;
  24.     var $minParserPasses;
  25.     var $maxParserPasses;
  26.     var $documentObject;
  27.     var $templateObject;
  28.     var $snippetObjects;
  29.     var $stopOnNotice;
  30.     var $currentSnippet;
  31.     var $documentName;
  32.     var $aliases;
  33.     var $visitor;
  34.     var $entrypage;
  35.     var $documentListing;
  36.     var $dumpSnippets;
  37.     var $chunkCache;
  38.     var $snippetCache;
  39.     var $contentTypes;
  40.     var $virtualDir;
  41.     var $placeholders;
  42.     var $sjscripts;
  43.     var $jscripts;
  44.     var $loadedjscripts;
  45.     var $documentMap;
  46.     var $forwards3;
  47.  
  48.     /**
  49.      * @var is this an RSS feed request?
  50.      */
  51.     var $is_rss = false;
  52.  
  53.     /**
  54.      * @var array Map forked snippet names to names of earlier compatible snippets.
  55.      *  Note that keys are all lowercase.
  56.      *
  57.      * @todo Construct an API and/or config system for this. Currently only applies to core/bundled snippets.
  58.      */
  59.     var $snippetMap = array('ditto'=>'List''webloginpe'=>'WebUsers');
  60.     
  61.     /**
  62.      * @var hold type of code being eval'd
  63.      */
  64.     private $eval_type null;
  65.     
  66.     /**
  67.      * @var hold name of code being eval'd
  68.      */
  69.     private $eval_name null;
  70.  
  71.     /**
  72.      * @var stack for nested eval'd elements using registerEvalInfo()
  73.      */
  74.     private $eval_stack array();
  75.  
  76.     /**
  77.      * Document constructor
  78.      *
  79.      * @return DocumentParser 
  80.      */
  81.     function __construct({
  82.         $this->loadExtension('DBAPI'or die('Could not load DBAPI class.')// load DBAPI class
  83.         $this->dbConfig$this->db->config// alias for backward compatibility
  84.         $this->jscriptsarray ();
  85.         $this->sjscriptsarray ();
  86.         $this->loadedjscriptsarray ();
  87.         // events
  88.         $this->eventnew SystemEvent();
  89.         $this->Event$this->event//alias for backward compatibility
  90.         $this->pluginEventarray ();
  91.         // set track_errors ini variable
  92.         // @ini_set("track_errors", "1"); // enable error tracking in $php_errormsg
  93.     }
  94.  
  95.     /**
  96.      * Loads an extension from the extenders folder.
  97.      * Currently of limited use - can only load the DBAPI and ManagerAPI.
  98.      *
  99.      * @param string $extnamegetAllChildren 
  100.      * @return boolean 
  101.      */
  102.     function loadExtension($extname{
  103.         global $database_type;
  104.  
  105.         switch ($extname{
  106.             // Database API
  107.             case 'DBAPI' :
  108.                 if (!include_once MODX_BASE_PATH 'manager/includes/extenders/dbapi.' $database_type '.class.inc.php')
  109.                     return false;
  110.                 $this->dbnew DBAPI($this);
  111.                 return true;
  112.                 break;
  113.  
  114.                 // Manager API
  115.             case 'ManagerAPI' :
  116.                 if (!include_once MODX_BASE_PATH 'manager/includes/extenders/manager.api.class.inc.php')
  117.                     return false;
  118.                 $this->managernew ManagerAPI;
  119.                 return true;
  120.                 break;
  121.  
  122.             default :
  123.                 return false;
  124.         }
  125.     }
  126.  
  127.     /**
  128.      * Redirect
  129.      *
  130.      * @param string $url 
  131.      * @param int $count_attempts 
  132.      * @param type $type 
  133.      * @param type $responseCode 
  134.      * @return boolean 
  135.      */
  136.     function sendRedirect($url$count_attempts0$type''$responseCode''{
  137.     
  138.         global $base_url$site_url;
  139.     
  140.         if (empty ($url)) {
  141.             return false;
  142.         else {
  143.             if ($count_attempts == 1{
  144.                 // append the redirect count string to the url
  145.                 $currentNumberOfRedirects= isset ($_REQUEST['err']$_REQUEST['err'0;
  146.                 if ($currentNumberOfRedirects 3{
  147.                     $this->messageQuit('Redirection attempt failed - please ensure the document you\'re trying to redirect to exists. <p>Redirection URL: <i>' $url '</i></p>');
  148.                 else {
  149.                     $currentNumberOfRedirects += 1;
  150.                     if (strpos($url"?"0{
  151.                         $url .= "&err=$currentNumberOfRedirects";
  152.                     else {
  153.                         $url .= "?err=$currentNumberOfRedirects";
  154.                     }
  155.                 }
  156.             }
  157.             if ($type == 'REDIRECT_REFRESH'{
  158.                 $header'Refresh: 0;URL=' $url;
  159.             }
  160.             elseif ($type == 'REDIRECT_META'{
  161.                 $header'<META HTTP-EQUIV="Refresh" CONTENT="0; URL=' $url '" />';
  162.                 echo $header;
  163.                 exit;
  164.             }
  165.             elseif ($type == 'REDIRECT_HEADER' || empty ($type)) {
  166.                 // check if url has /$base_url
  167.                 if (substr($url0strlen($base_url)) == $base_url{
  168.                     // append $site_url to make it work with Location:
  169.                     $url$site_url substr($urlstrlen($base_url));
  170.                 }
  171.                 if (strpos($url"\n"=== false{
  172.                     $header'Location: ' $url;
  173.                 else {
  174.                     $this->messageQuit('No newline allowed in redirect url.');
  175.                 }
  176.             }
  177.             if ($responseCode && (strpos($responseCode'30'!== false)) {
  178.                 header($responseCode);
  179.             }
  180.             header($header);
  181.             exit();
  182.         }
  183.     }
  184.  
  185.     /**
  186.      * Forward to another page
  187.      *
  188.      * @param int $id 
  189.      * @param string $responseCode 
  190.      */
  191.     function sendForward($id$responseCode''{
  192.         if ($this->forwards > 0{
  193.             $this->forwards$this->forwards - 1;
  194.             $this->documentIdentifier$id;
  195.             $this->documentMethod'id';
  196.             $this->documentObject$this->getDocumentObject('id'$id);
  197.             if ($responseCode{
  198.                 header($responseCode);
  199.             }
  200.             $this->prepareResponse();
  201.             exit();
  202.         else {
  203.             header('HTTP/1.0 500 Internal Server Error');
  204.             die('<h1>ERROR: Too many forward attempts!</h1><p>The request could not be completed due to too many unsuccessful forward attempts.</p>');
  205.         }
  206.     }
  207.  
  208.     /**
  209.      * Redirect to the error page, by calling sendForward(). This is called for example when the page was not found.
  210.      */
  211.     function sendErrorPage({
  212.         // invoke OnPageNotFound event
  213.         $this->invokeEvent('OnPageNotFound');
  214. //        $this->sendRedirect($this->makeUrl($this->config['error_page'], '', '&refurl=' . urlencode($_SERVER['PHP_SELF'] . '?' . $_SERVER['QUERY_STRING'])), 1);
  215.         $this->sendForward($this->config['error_page'$this->config['error_page'$this->config['site_start']'HTTP/1.0 404 Not Found');
  216.         exit();
  217.     }
  218.  
  219.     /**
  220.      * Redirect to the unauthorized page, for example on calling a page without having the permissions to see this page.
  221.      */
  222.     function sendUnauthorizedPage({
  223.         // invoke OnPageUnauthorized event
  224.         $_REQUEST['refurl'$this->documentIdentifier;
  225.         $this->invokeEvent('OnPageUnauthorized');
  226.         if ($this->config['unauthorized_page']{
  227.             $unauthorizedPage$this->config['unauthorized_page'];
  228.         elseif ($this->config['error_page']{
  229.             $unauthorizedPage$this->config['error_page'];
  230.         else {
  231.             $unauthorizedPage$this->config['site_start'];
  232.         }
  233.         $this->sendForward($unauthorizedPage'HTTP/1.1 403 Forbidden');    // Changed by TimGS 22/6/2012. Originally was a 401 but this HTTP code appears intended for situations
  234.                                             // where the client can authenticate via HTTP authentication and send a www-authenticate header.
  235.         exit();
  236.     }
  237.  
  238.     /**
  239.      * Connect to the database
  240.      *
  241.      * @deprecated use $modx->db->connect()
  242.      */
  243.     function dbConnect({
  244.         $this->db->connect();
  245.         $this->rs$this->db->conn// for compatibility
  246.     }
  247.  
  248.     /**
  249.      * Query the database
  250.      *
  251.      * @deprecated use $modx->db->query()
  252.      * @param string $sql The SQL statement to execute
  253.      * @return resource|bool
  254.      */
  255.     function dbQuery($sql{
  256.         return $this->db->query($sql);
  257.     }
  258.  
  259.     /**
  260.      * Count the number of rows in a record set
  261.      *
  262.      * @deprecated use $modx->db->getRecordCount($rs)
  263.      * @param resource 
  264.      * @return int 
  265.      */
  266.     function recordCount($rs{
  267.         return $this->db->getRecordCount($rs);
  268.     }
  269.  
  270.     /**
  271.      * Get a result row
  272.      * 
  273.      * @deprecated use $modx->db->getRow()
  274.      * @param array $rs 
  275.      * @param string $mode 
  276.      * @return array 
  277.      */
  278.     function fetchRow($rs$mode'assoc'{
  279.         return $this->db->getRow($rs$mode);
  280.     }
  281.  
  282.     /**
  283.      * Get the number of rows affected in the last db operation
  284.      * 
  285.      * @deprecated use $modx->db->getAffectedRows()
  286.      * @param array $rs 
  287.      * @return int 
  288.      */
  289.     function affectedRows($rs{
  290.         return $this->db->getAffectedRows($rs);
  291.     }
  292.  
  293.     /**
  294.      * Get the ID generated in the last query
  295.      * 
  296.      * @deprecated use $modx->db->getInsertId()
  297.      * @param array $rs 
  298.      * @return int 
  299.      */
  300.     function insertId($rs{
  301.         return $this->db->getInsertId($rs);
  302.     }
  303.  
  304.     /**
  305.      * Close a database connection
  306.      *
  307.      * @deprecated use $modx->db->disconnect()
  308.      */
  309.     function dbClose({
  310.         $this->db->disconnect();
  311.     }
  312.  
  313.     /**
  314.      * Get MODx settings including, but not limited to, the system_settings table
  315.      */
  316.     function getSettings({
  317.         if (!is_array($this->config|| empty ($this->config)) {
  318.             if ($includedfile_exists(MODX_BASE_PATH 'assets/cache/siteCache.idx.php')) {
  319.                 $includedinclude_once (MODX_BASE_PATH 'assets/cache/siteCache.idx.php');
  320.             }
  321.             if (!$included || !is_array($this->config|| empty ($this->config)) {
  322.                 include_once MODX_BASE_PATH "/manager/processors/cache_sync.class.processor.php";
  323.                 $cache new synccache();
  324.                 $cache->setCachepath(MODX_BASE_PATH "/assets/cache/");
  325.                 $cache->setReport(false);
  326.                 $rebuilt $cache->buildCache($this);
  327.                 $included false;
  328.                 if($rebuilt && $includedfile_exists(MODX_BASE_PATH 'assets/cache/siteCache.idx.php')) {
  329.                     $includedinclude MODX_BASE_PATH 'assets/cache/siteCache.idx.php';
  330.                 }
  331.                 if(!$included{
  332.                     $result$this->db->query('SELECT setting_name, setting_value FROM ' $this->getFullTableName('system_settings'));
  333.                     while ($row$this->db->getRow($result'both')) {
  334.                         $this->config[$row[0]]$row[1];
  335.                     }
  336.                 }
  337.             }
  338.  
  339.             // added for backwards compatibility - garry FS#104
  340.             $this->config['etomite_charset'$this->config['modx_charset'];
  341.  
  342.             // store base_url and base_path inside config array
  343.             $this->config['base_url']MODX_BASE_URL;
  344.             $this->config['base_path']MODX_BASE_PATH;
  345.             $this->config['site_url']MODX_SITE_URL;
  346.             
  347.             $this->getUserSettings();
  348.         }
  349.     }
  350.  
  351.     /**
  352.      * Load user settings if user is logged in
  353.      */
  354.     function getUserSettings({
  355.         $usrSettingsarray ();
  356.         if ($id$this->getLoginUserID()) {
  357.             $usrType$this->getLoginUserType();
  358.             if (isset ($usrType&& $usrType == 'manager')
  359.                 $usrType'mgr';
  360.  
  361.             if ($usrType == 'mgr' && $this->isBackend()) {
  362.                 // invoke the OnBeforeManagerPageInit event, only if in backend
  363.                 $this->invokeEvent("OnBeforeManagerPageInit");
  364.             }
  365.  
  366.             if (isset ($_SESSION[$usrType 'UsrConfigSet'])) {
  367.                 $usrSettings$_SESSION[$usrType 'UsrConfigSet'];
  368.             else {
  369.                 if ($usrType == 'web')
  370.                     $query$this->getFullTableName('web_user_settings'' WHERE webuser=\'' $id '\'';
  371.                 else
  372.                     $query$this->getFullTableName('user_settings'' WHERE user=\'' $id '\'';
  373.                 $result$this->db->query('SELECT setting_name, setting_value FROM ' $query);
  374.                 while ($row$this->db->getRow($result'both'))
  375.                     $usrSettings[$row[0]]$row[1];
  376.                 if (isset ($usrType))
  377.                     $_SESSION[$usrType 'UsrConfigSet']$usrSettings// store user settings in session
  378.             }
  379.         }
  380.         if ($this->isFrontend(&& $mgrid$this->getLoginUserID('mgr')) {
  381.             $musrSettingsarray ();
  382.             if (isset ($_SESSION['mgrUsrConfigSet'])) {
  383.                 $musrSettings$_SESSION['mgrUsrConfigSet'];
  384.             else {
  385.                 $query$this->getFullTableName('user_settings'' WHERE user=\'' $mgrid '\'';
  386.                 if ($result$this->db->query('SELECT setting_name, setting_value FROM ' $query)) {
  387.                     while ($row$this->db->getRow($result'both')) {
  388.                         $usrSettings[$row[0]]$row[1];
  389.                     }
  390.                     $_SESSION['mgrUsrConfigSet']$musrSettings// store user settings in session
  391.                 }
  392.             }
  393.             if (!empty ($musrSettings)) {
  394.                 $usrSettingsarray_merge($musrSettings$usrSettings);
  395.             }
  396.         }
  397.         $this->configarray_merge($this->config$usrSettings);
  398.     }
  399.  
  400.     /**
  401.      * Get the method by which the current document/resource was requested
  402.      *
  403.      * @return string 'alias' (friendly url alias), 'rss' (friendly url alias with rss/ at the start of $_REQUEST['q']) or 'id' (may or may not be an RSS request).
  404.      */
  405.     function getDocumentMethod({
  406.         // function to test the query and find the retrieval method
  407.         if (isset ($_REQUEST['q'])) {
  408.             return preg_match('/^\/?rss\//'$_REQUEST['q']'rss' 'alias';
  409.         }
  410.         elseif (isset ($_REQUEST['id'])) {
  411.             return "id";
  412.         else {
  413.             return "none";
  414.         }
  415.     }
  416.  
  417.     /**
  418.      * Returns the document identifier of the current request
  419.      *
  420.      * @param string $method id and alias are allowed
  421.      * @return int 
  422.      */
  423.     function getDocumentIdentifier($method{
  424.         // function to test the query and find the retrieval method
  425.         $docIdentifier$this->config['site_start'];
  426.         switch ($method{
  427.             case 'rss':
  428.                 if (!is_string($_REQUEST['q'])) // If an array is passed (TimGS)
  429.                     $this->sendErrorPage();
  430.                 }
  431.                 $q preg_replace('/^\/?rss\//'''$_REQUEST['q']);
  432.                 if ($q{
  433.                     $docIdentifier $this->db->escape($q);
  434.                 else {
  435.                     $docIdentifier $this->config['site_start'];
  436.                 }
  437.                 break;
  438.             case 'alias' :
  439.                 if (!is_string($_REQUEST['q'])) // If an array is passed (TimGS)
  440.                     $this->sendErrorPage();
  441.                 }
  442.                 $docIdentifier$this->db->escape($_REQUEST['q']);
  443.                 break;
  444.             case 'id' :
  445.                 if (!is_numeric($_REQUEST['id'])) {
  446.                     $this->sendErrorPage();
  447.                 else {
  448.                     $docIdentifierintval($_REQUEST['id']);
  449.                 }
  450.                 break;
  451.         }
  452.         return $docIdentifier;
  453.     }
  454.  
  455.     /**
  456.      * Check for manager login session
  457.      *
  458.      * @return boolean 
  459.      */
  460.     function checkSession({
  461.         if (isset ($_SESSION['mgrValidated'])) {
  462.             return true;
  463.         else {
  464.             return false;
  465.         }
  466.     }
  467.  
  468.     /**
  469.      * Checks, if a the result is a preview
  470.      *
  471.      * @return boolean 
  472.      */
  473.     function checkPreview({
  474.         if ($this->checkSession(== true{
  475.             if (isset ($_REQUEST['z']&& $_REQUEST['z'== 'manprev'{
  476.                 return true;
  477.             else {
  478.                 return false;
  479.             }
  480.         else {
  481.             return false;
  482.         }
  483.     }
  484.  
  485.     /**
  486.      * check if site is offline
  487.      *
  488.      * @return boolean 
  489.      */
  490.     function checkSiteStatus({
  491.         $siteStatus$this->config['site_status'];
  492.         if ($siteStatus == 1{
  493.             // site online
  494.             return true;
  495.         }
  496.         elseif ($siteStatus == && $this->checkSession()) {
  497.             // site offline but launched via the manager
  498.             return true;
  499.         else {
  500.             // site is offline
  501.             return false;
  502.         }
  503.     }
  504.  
  505.     /**
  506.      * Create a 'clean' document identifier with path information, friendly URL suffix and prefix.
  507.      *
  508.      * @param string $qOrig 
  509.      * @return string 
  510.      */
  511.     function cleanDocumentIdentifier($qOrig{
  512.         (!empty($qOrig)) or $qOrig $this->config['site_start'];
  513.         $q$qOrig;
  514.         /* First remove any / before or after */
  515.         if ($q[strlen($q1== '/')
  516.             $qsubstr($q0-1);
  517.         if ($q[0== '/')
  518.             $qsubstr($q1);
  519.         /* Save path if any */
  520.         /* FS#476 and FS#308: only return virtualDir if friendly paths are enabled */
  521.         if ($this->config['use_alias_path'== 1{
  522.             $this->virtualDirdirname($q);
  523.             $this->virtualDir($this->virtualDir == '.' '' $this->virtualDir);
  524.             $qbasename($q);
  525.         else {
  526.             $this->virtualDir'';
  527.         }
  528.         $qstr_replace($this->config['friendly_url_prefix']""$q);
  529.         $qstr_replace($this->config['friendly_url_suffix']""$q);
  530.         if (is_numeric($q&& !$this->documentListing[$q]/* we got an ID returned, check to make sure it's not an alias */
  531.             /* FS#476 and FS#308: check that id is valid in terms of virtualDir structure */
  532.             if ($this->config['use_alias_path'== 1{
  533.                 if ((($this->virtualDir != '' && !$this->documentListing[$this->virtualDir . '/' $q]|| ($this->virtualDir == '' && !$this->documentListing[$q])) && (($this->virtualDir != '' && in_array($q$this->getChildIds($this->documentListing[$this->virtualDir]1))) || ($this->virtualDir == '' && in_array($q$this->getChildIds(01))))) {
  534.                     $this->documentMethod'id';
  535.                     return $q;
  536.                 else /* not a valid id in terms of virtualDir, treat as alias */
  537.                     $this->documentMethod'alias';
  538.                     return $q;
  539.                 }
  540.             else {
  541.                 $this->documentMethod'id';
  542.                 return $q;
  543.             }
  544.         else /* we didn't get an ID back, so instead we assume it's an alias */
  545.             if ($this->config['friendly_alias_urls'!= 1{
  546.                 $q$qOrig;
  547.             }
  548.             $this->documentMethod'alias';
  549.             return $q;
  550.         }
  551.     }
  552.  
  553.     /**
  554.      * Check the cache for a specific document/resource
  555.      *
  556.      * @param int $id 
  557.      * @return string 
  558.      */
  559.     function checkCache($id{
  560.         $cacheFile"assets/cache/docid_" $id ".pageCache.php";
  561.         if (file_exists($cacheFile)) {
  562.             $this->documentGenerated0;
  563.             $flContent file_get_contents($cacheFilefalse);
  564.             $flContentsubstr($flContent37)// remove php header
  565.             $aexplode("<!--__MODxCacheSpliter__-->"$flContent2);
  566.             if (count($a== 1)
  567.                 return $a[0]// return only document content
  568.             else {
  569.                 $docObjunserialize($a[0])// rebuild document object
  570.                 // check page security
  571.                 if ($docObj['privateweb'&& isset ($docObj['__MODxDocGroups__'])) {
  572.                     $passfalse;
  573.                     $usrGrps$this->getUserDocGroups();
  574.                     $docGrpsexplode(","$docObj['__MODxDocGroups__']);
  575.                     // check is user has access to doc groups
  576.                     if (is_array($usrGrps)) {
  577.                         foreach ($usrGrps as $k => $v)
  578.                             if (in_array($v$docGrps)) {
  579.                                 $passtrue;
  580.                                 break;
  581.                             }
  582.                     }
  583.                     // diplay error pages if user has no access to cached doc
  584.                     if (!$pass{
  585.                         if ($this->config['unauthorized_page']{
  586.                             // check if file is not public
  587.                             $tbldg$this->getFullTableName("document_groups");
  588.                             $secrs$this->db->query("SELECT id FROM $tbldg WHERE document = '$id "' LIMIT 1;");
  589.                             if ($secrs)
  590.                                 $seclimit$this->db->getRecordCount($secrs);
  591.                         }
  592.                         if ($seclimit 0{
  593.                             // match found but not publicly accessible, send the visitor to the unauthorized_page
  594.                             $this->sendUnauthorizedPage();
  595.                             exit// stop here
  596.                         else {
  597.                             // no match found, send the visitor to the error_page
  598.                             $this->sendErrorPage();
  599.                             exit// stop here
  600.                         }
  601.                     }
  602.                 }
  603.                 // Grab the Scripts
  604.                 if (isset($docObj['__MODxSJScripts__'])) $this->sjscripts = $docObj['__MODxSJScripts__'];
  605.                 if (isset($docObj['__MODxJScripts__']))  $this->jscripts = $docObj['__MODxJScripts__'];
  606.  
  607.                 // Remove intermediate variables
  608.                 unset($docObj['__MODxDocGroups__']$docObj['__MODxSJScripts__']$docObj['__MODxJScripts__']);
  609.  
  610.                 $this->documentObject$docObj;
  611.                 return $a[1]// return document content
  612.             }
  613.         else {
  614.             $this->documentGenerated1;
  615.             return "";
  616.         }
  617.     }
  618.  
  619.     /**
  620.      * Final processing and output of the document/resource.
  621.      * 
  622.      * - runs uncached snippets
  623.      * - add javascript to <head>
  624.      * - removes unused placeholders
  625.      * - converts URL tags [~...~] to URLs
  626.      *
  627.      * @param boolean $noEvent Default: false
  628.      */
  629.     function outputContent($noEventfalse{
  630.  
  631.         $this->documentOutput$this->documentContent;
  632.  
  633.         if ($this->documentGenerated == && $this->documentObject['cacheable'== && $this->documentObject['type'== 'document' && $this->documentObject['published'== 1{
  634.             if (!empty($this->sjscripts)) $this->documentObject['__MODxSJScripts__'$this->sjscripts;
  635.             if (!empty($this->jscripts)) $this->documentObject['__MODxJScripts__'$this->jscripts;
  636.         }
  637.  
  638.         // check for non-cached snippet output
  639.         if (strpos($this->documentOutput'[!'!== false{
  640.             $this->documentOutput$this->parseDocumentSource($this->documentOutputtrue);
  641.         }
  642.  
  643.         // Moved from prepareResponse() by sirlancelot
  644.         // Insert Startup jscripts & CSS scripts into template - template must have a <head> tag
  645.         if ($js$this->getRegisteredClientStartupScripts()) {
  646.             // change to just before closing </head>
  647.             // $this->documentContent = preg_replace("/(<head[^>]*>)/i", "\\1\n".$js, $this->documentContent);
  648.             $this->documentOutputpreg_replace("/(<\/head>)/i"$js "\n\\1"$this->documentOutput);
  649.         }
  650.  
  651.         // Insert jscripts & html block into template - template must have a </body> tag
  652.         if ($js$this->getRegisteredClientScripts()) {
  653.             $this->documentOutputpreg_replace("/(<\/body>)/i"$js "\n\\1"$this->documentOutput);
  654.         }
  655.         // End fix by sirlancelot
  656.  
  657.         // remove all unused placeholders
  658.         if (strpos($this->documentOutput'[+'> -1{
  659.             $matchesarray ();
  660.             preg_match_all('~\[\+(.*?)\+\]~'$this->documentOutput$matches);
  661.             if ($matches[0])
  662.                 $this->documentOutputstr_replace($matches[0]''$this->documentOutput);
  663.         }
  664.  
  665.         $this->documentOutput$this->rewriteUrls($this->documentOutput);
  666.         
  667.         // In RSS feeds change relative URLs to absolute URLs.
  668.         if ($this->is_rss{
  669.             $this->documentOutput preg_replace('/href="(?!http)/''href="'.$this->config['site_url']$this->documentOutput);
  670.         }
  671.  
  672.         // send out content-type and content-disposition headers
  673.         if (IN_PARSER_MODE == "true"{
  674.             if ($this->is_rss{
  675.                 header('Content-Type: application/rss+xml; charset='.$this->config['modx_charset']);
  676.             else {
  677.                 $type!empty ($this->contentTypes[$this->documentIdentifier]$this->contentTypes[$this->documentIdentifier"text/html";
  678.                 header('Content-Type: ' $type '; charset=' $this->config['modx_charset']);
  679.             }
  680.  
  681.             if (!$this->checkPreview(&& $this->documentObject['content_dispo'== 1{
  682.                 if ($this->documentObject['alias'])
  683.                     $name$this->documentObject['alias'];
  684.                 else {
  685.                     // strip title of special characters
  686.                     $name$this->documentObject['pagetitle'];
  687.                     $namestrip_tags($name);
  688.                     $namestrtolower($name);
  689.                     $namepreg_replace('/&.+?;/'''$name)// kill entities
  690.                     $namepreg_replace('/[^\.%a-z0-9 _-]/'''$name);
  691.                     $namepreg_replace('/\s+/''-'$name);
  692.                     $namepreg_replace('|-+|''-'$name);
  693.                     $nametrim($name'-');
  694.                 }
  695.                 $header'Content-Disposition: attachment; filename=' $name;
  696.                 header($header);
  697.             }
  698.         }
  699.  
  700.         $totalTime($this->getMicroTime($this->tstart);
  701.         $queryTime$this->queryTime;
  702.         $phpTime$totalTime $queryTime;
  703.  
  704.         $queryTimesprintf("%2.4f s"$queryTime);
  705.         $totalTimesprintf("%2.4f s"$totalTime);
  706.         $phpTimesprintf("%2.4f s"$phpTime);
  707.         $source$this->documentGenerated == "database" "cache";
  708.         $queries= isset ($this->executedQueries$this->executedQueries : 0;
  709.  
  710.         $out =$this->documentOutput;
  711.         if ($this->dumpSQL{
  712.             $out .= $this->queryCode;
  713.         }
  714.         $outstr_replace("[^q^]"$queries$out);
  715.         $outstr_replace("[^qt^]"$queryTime$out);
  716.         $outstr_replace("[^p^]"$phpTime$out);
  717.         $outstr_replace("[^t^]"$totalTime$out);
  718.         $outstr_replace("[^s^]"$source$out);
  719.         //$this->documentOutput= $out;
  720.  
  721.         // invoke OnWebPagePrerender event
  722.         if (!$noEvent{
  723.             $this->invokeEvent("OnWebPagePrerender");
  724.         }
  725.  
  726.         echo $this->documentOutput;
  727.         ob_end_flush();
  728.     }
  729.  
  730.     /**
  731.      * Checks the publish state of page
  732.      */
  733.     function checkPublishStatus({
  734.         $cacheRefreshTime0;
  735.         @include $this->config["base_path""assets/cache/sitePublishing.idx.php";
  736.         $timeNowtime($this->config['server_offset_time'];
  737.         if ($cacheRefreshTime <= $timeNow && $cacheRefreshTime != 0{
  738.             // now, check for documents that need publishing
  739.             $sql "UPDATE ".$this->getFullTableName("site_content")." SET published=1, publishedon=".time()." WHERE ".$this->getFullTableName("site_content").".pub_date <= $timeNow AND ".$this->getFullTableName("site_content").".pub_date!=0 AND published=0";
  740.             if (!$result$this->db->query($sql)) {
  741.                 $this->messageQuit("Execution of a query to the database failed"$sql);
  742.             }
  743.  
  744.             // now, check for documents that need un-publishing
  745.             $sql"UPDATE " $this->getFullTableName("site_content"" SET published=0, publishedon=0 WHERE " $this->getFullTableName("site_content"".unpub_date <= $timeNow AND $this->getFullTableName("site_content"".unpub_date!=0 AND published=1";
  746.             if (!$result$this->db->query($sql)) {
  747.                 $this->messageQuit("Execution of a query to the database failed"$sql);
  748.             }
  749.  
  750.             // clear the cache
  751.             $basepath$this->config["base_path""assets/cache/";
  752.             if ($handleopendir($basepath)) {
  753.                 $filesincache0;
  754.                 $deletedfilesincache0;
  755.                 while (false !== ($filereaddir($handle))) {
  756.                     if ($file != "." && $file != ".."{
  757.                         $filesincache += 1;
  758.                         if (preg_match("/\.pageCache/"$file)) {
  759.                             $deletedfilesincache += 1;
  760.                             while (!unlink($basepath "/" $file));
  761.                         }
  762.                     }
  763.                 }
  764.                 closedir($handle);
  765.             }
  766.  
  767.             // update publish time file
  768.             $timesArrarray ();
  769.             $sql"SELECT MIN(pub_date) AS minpub FROM " $this->getFullTableName("site_content"" WHERE pub_date>$timeNow";
  770.             if (!$result$this->db->query($sql)) {
  771.                 $this->messageQuit("Failed to find publishing timestamps"$sql);
  772.             }
  773.             $tmpRow$this->db->getRow($result);
  774.             $minpub$tmpRow['minpub'];
  775.             if ($minpub != NULL{
  776.                 $timesArr[]$minpub;
  777.             }
  778.  
  779.             $sql"SELECT MIN(unpub_date) AS minunpub FROM " $this->getFullTableName("site_content"" WHERE unpub_date>$timeNow";
  780.             if (!$result$this->db->query($sql)) {
  781.                 $this->messageQuit("Failed to find publishing timestamps"$sql);
  782.             }
  783.             $tmpRow$this->db->getRow($result);
  784.             $minunpub$tmpRow['minunpub'];
  785.             if ($minunpub != NULL{
  786.                 $timesArr[]$minunpub;
  787.             }
  788.  
  789.             if (count($timesArr0{
  790.                 $nexteventmin($timesArr);
  791.             else {
  792.                 $nextevent0;
  793.             }
  794.  
  795.             $basepath$this->config["base_path""assets/cache";
  796.             $fpfopen($basepath "/sitePublishing.idx.php""wb");
  797.             if ($fp{
  798.                 flock($fpLOCK_EX);
  799.                 fwrite($fp"<?php \$cacheRefreshTime=$nextevent; ?>");
  800.                 flock($fpLOCK_UN);
  801.                 fclose($fp);
  802.             }
  803.         }
  804.     }
  805.  
  806.     /** 
  807.      * Check for and log fatal errors
  808.      *
  809.      * @return void 
  810.      */
  811.      function fatalErrorCheck({
  812.          // Log fatal errors
  813.         $error error_get_last();
  814.         if ($error['type'== E_ERROR || $error['type'== E_USER_ERROR || $error['type'== E_PARSE{
  815.         
  816.             $file $error['file'];
  817.             if (strpos($file'/document.parser.class.inc.php'!== false{
  818.                 $file 'DocumentParser'.(strpos($file'eval()\'d code'=== false '' ' eval\'d code').($this->eval_type " in {$this->eval_type{$this->eval_name}" : '');
  819.             }
  820.     
  821.             if ($this->eval_type{
  822.                 $this->messageQuitFromElement("{$this->eval_type{$this->eval_name}", 'Fatal '.($error['type'] == 'E_USER_ERROR' ? '(user) ' : '')."error: {$error['message']}", '', true, $error['type'], $file, '', $error['message'], $error['line']);
  823.             } else {
  824.                 $this->messageQuit('Fatal '.($error['type'== 'E_USER_ERROR' '(user) ' '')."error: {$error['message']}", '', true, $error['type'], $file, '', $error['message'], $error['line']);
  825.             }
  826.         }
  827.     }
  828.  
  829.     /**
  830.      * Final jobs.
  831.      *
  832.      * - cache page
  833.      */
  834.     function postProcess() {
  835.         // if the current document was generated, cache it!
  836.         if ($this->documentGenerated == && $this->documentObject['cacheable'== && $this->documentObject['type'== 'document' && $this->documentObject['published'== 1{
  837.             $basepath$this->config["base_path""assets/cache";
  838.             // invoke OnBeforeSaveWebPageCache event
  839.             $this->invokeEvent("OnBeforeSaveWebPageCache");
  840.             if ($fpfopen($basepath "/docid_" $this->documentIdentifier . ".pageCache.php""w")) {
  841.                 // get and store document groups inside document object. Document groups will be used to check security on cache pages
  842.                 $sql"SELECT document_group FROM " . $this->getFullTableName("document_groups"" WHERE document='" $this->documentIdentifier . "'";
  843.                 $docGroups$this->db->getColumn("document_group"$sql);
  844.  
  845.                 // Attach Document Groups and Scripts
  846.                 if (is_array($docGroups)) $this->documentObject['__MODxDocGroups__'implode(","$docGroups);
  847.  
  848.                 $docObjSerialserialize($this->documentObject);
  849.                 $cacheContent$docObjSerial "<!--__MODxCacheSpliter__-->" $this->documentContent;
  850.                 fputs($fp"<?php die('Unauthorized access.'); ?>$cacheContent");
  851.                 fclose($fp);
  852.             }
  853.         }
  854.  
  855.         // Useful for example to external page counters/stats packages
  856.         $this->invokeEvent('OnWebPageComplete');
  857.  
  858.         // end post processing
  859.     }
  860.  
  861.     /**
  862.      * Merge meta tags
  863.      *
  864.      * @param string $template
  865.      * @return string
  866.      * @deprecated
  867.      */
  868.     function mergeDocumentMETATags($template) {
  869.         //content removed as this function is deprecated
  870.         return $template;
  871.     }
  872.  
  873.     /**
  874.      * Merge content fields and TVs
  875.      *
  876.      * @param string $template
  877.      * @return string
  878.      */
  879.     function mergeDocumentContent($template) {
  880.     
  881.         static $documentObjects = array(); // Could be improved by use of the modx cache. TODO below
  882.         $replacearray ();
  883.         preg_match_all('~\[\*(.*?)\*\]~', $template, $matches);
  884.         $variableCountcount($matches[1]);
  885.         $basepath$this->config["base_path""manager/includes";
  886.         for ($i0$i $variableCount$i++{
  887.             $key$matches[1][$i];
  888.             $keysubstr($key, 0, 1) == '#' ? substr($key, 1) : $key; // remove # for QuickEdit format
  889.             // Detect output modifiers
  890.             if (strpos($key, ';') != false) {
  891.                 $modifiers = explode(';', $key);
  892.                 $key = $modifiers[0];
  893.             } else {
  894.                 $modifiers = null;
  895.             }
  896.  
  897.             if (($sep_pos = strpos($key, ':')) !== false) {
  898.                 // Handle [*<docid>:<fieldname/TVname>*]
  899.                 // Identify the docid first.
  900.                 // <docid> can be any id, 'parent', 'ultimateparent', or contain site settings placeholders e.g. [(site_start)]
  901.                 $other_docid = null;
  902.                 if (ctype_digit($other_docid = substr($key, 0, $sep_pos))) {
  903.                     $other_docid = (int)$other_docid;
  904.                 } else {
  905.                     switch ($other_docid) {
  906.                         case 'parent':
  907.                             $other_docid = $this->documentObject['parent'];
  908.                             break;
  909.                         case 'ultimateparent':
  910.                             $other_docid $this->getUltimateParentId($this->documentIdentifier);
  911.                             break;
  912.                         default:
  913.                             if (array_key_exists($other_docid$this->config)) {
  914.                                 $other_docid = $this->config[$other_docid];
  915.                             }
  916.                             if (ctype_digit($other_docid)) {
  917.                                 $other_docid = (int)$other_docid;
  918.                             } else {
  919.                                 $other_docid = null;
  920.                             }
  921.                             break;
  922.                     }
  923.                 }
  924.  
  925.                 if ($other_docid) {
  926.                     if ($other_docid != $this->documentIdentifier{
  927.                         // Another docid is found, is valid, is not zero and is not the current document.
  928.                         // TODO: cache handling. May need to modify checkCache()
  929.                         if (!isset($documentObjects[$other_docid])) {
  930.                             $documentObjects[$other_docid] = $this->getDocumentObject('id'$other_docid);
  931.                         }
  932.                         $value = $documentObjects[$other_docid][substr($key, $sep_pos+1)];
  933.                     } else {
  934.                         // Using the current document.
  935.                         $value$this->documentObject[substr($key$sep_pos+1)];
  936.                     }
  937.                 } else {
  938.                     // Invalid $other_docid
  939.                     $value = '';
  940.                 }
  941.             } else {
  942.                 // Using the current document.
  943.                 $value$this->documentObject[$key];
  944.             }
  945.  
  946.             if (is_array($value)) {
  947.                 include_once $basepath . "/tmplvars.format.inc.php";
  948.                 include_once $basepath . "/tmplvars.commands.inc.php";
  949.                 $w"100%";
  950.                 $h"300";
  951.                 $valuegetTVDisplayFormat($value[0], $value[1], $value[2], $value[3], $value[4]);
  952.             }
  953.  
  954.             // Process output modifiers
  955.             if (is_array($modifiers)) {
  956.                 foreach(array_slice($modifiers, 1) as $modifier) {
  957.                     $value = $this->modifyOutput($value$modifier);
  958.                 }
  959.             }
  960.  
  961.             $replace[$i] = $value;
  962.         }
  963.         $templatestr_replace($matches[0], $replace, $template);
  964.  
  965.         return $template;
  966.     }
  967.  
  968.    /** 
  969.      * Modifies output
  970.      *
  971.      * @param string $string 
  972.      * @param string $modifier in the form 'modifier' or 'modifier(argument)'
  973.      * @return string
  974.      */
  975.     function modifyOutput($string, $modifier) {
  976.  
  977.         if (($pos = strpos($modifier, '(')) !== false) {
  978.             $arg = substr(substr($modifier, $pos + 1), 0, -1);
  979.             $modifier = substr($modifier, 0, $pos);
  980.         } else {
  981.             $arg = null;
  982.         }
  983.  
  984.         switch ($modifier) {
  985.             case 'strtolower':
  986.             case 'strtoupper':
  987.             case 'ucwords':
  988.             case 'ucfirst':
  989.             case 'strip_tags':
  990.             case 'urlencode':
  991.             case 'rawurlencode':
  992.                 $string = $modifier($string);
  993.                 break;
  994.                 
  995.             case 'html':
  996.                 $string = htmlentities($string, ENT_QUOTES, $this->config['modx_charset']);
  997.                 break;
  998.             
  999.             case 'limit':
  1000.                 if (ctype_digit($arg)) {
  1001.                     $string = substr($string, 0, $arg);
  1002.                 }
  1003.                 break;
  1004.  
  1005.             case 'ellipsis':
  1006.                 if (ctype_digit($arg) && strlen($string) > $arg) {
  1007.                     $string = substr($string, 0, $arg).'&hellip;';
  1008.                 }
  1009.                 break;
  1010.         }
  1011.  
  1012.     return $string;
  1013.     }
  1014.  
  1015.     /**
  1016.      * Merge system settings
  1017.      *
  1018.      * @param string $template
  1019.      * @return string
  1020.      */
  1021.     function mergeSettingsContent($template) {
  1022.         $replacearray ();
  1023.         $matchesarray ();
  1024.         if (preg_match_all('~\[\(([a-z\_]*?)\)\]~', $template, $matches)) {
  1025.             $settingsCountcount($matches[1]);
  1026.             for ($i0; $i < $settingsCount; $i++) {
  1027.                 if (array_key_exists($matches[1][$i], $this->config))
  1028.                     $replace[$i]$this->config[$matches[1][$i]];
  1029.             }
  1030.  
  1031.             $templatestr_replace($matches[0], $replace, $template);
  1032.         }
  1033.         return $template;
  1034.     }
  1035.  
  1036.     /**
  1037.      * Merge chunks
  1038.      *
  1039.      * @param string $content
  1040.      * @return string
  1041.      */
  1042.     function mergeChunkContent($content) {
  1043.         $replacearray ();
  1044.         $matchesarray ();
  1045.         if (preg_match_all('~{{(.*?)}}~', $content, $matches)) {
  1046.             $settingsCountcount($matches[1]);
  1047.             for ($i0; $i < $settingsCount; $i++) {
  1048.                 if (isset ($this->chunkCache[$matches[1][$i]])) {
  1049.                     $replace[$i]$this->chunkCache[$matches[1][$i]];
  1050.                 } else {
  1051.                     $sql"SELECT `snippet` FROM " . $this->getFullTableName("site_htmlsnippets"" WHERE " $this->getFullTableName("site_htmlsnippets"".`name`='" $this->db->escape($matches[1][$i]"';";
  1052.                     $result$this->db->query($sql);
  1053.                     $limit$this->db->getRecordCount($result);
  1054.                     if ($limit 1{
  1055.                         $this->chunkCache[$matches[1][$i]]"";
  1056.                         $replace[$i]"";
  1057.                     } else {
  1058.                         $row$this->db->getRow($result);
  1059.                         $this->chunkCache[$matches[1][$i]]$row['snippet'];
  1060.                         $replace[$i]$row['snippet'];
  1061.                     }
  1062.                 }
  1063.             }
  1064.             $contentstr_replace($matches[0], $replace, $content);
  1065.         }
  1066.         return $content;
  1067.     }
  1068.  
  1069.     /**
  1070.      * Merge placeholder values
  1071.      *
  1072.      * @param string $content
  1073.      * @return string
  1074.      */
  1075.     function mergePlaceholderContent($content) {
  1076.         $replacearray ();
  1077.         $matchesarray ();
  1078.         if (preg_match_all('~\[\+(.*?)\+\]~', $content, $matches)) {
  1079.             $cntcount($matches[1]);
  1080.             for ($i0; $i < $cnt; $i++) {
  1081.                 $v'';
  1082.                 $key$matches[1][$i];
  1083.                 if (is_array($this->placeholders&& array_key_exists($key$this->placeholders))
  1084.                     $v$this->placeholders[$key];
  1085.                 if ($v === '')
  1086.                     unset ($matches[0][$i])// here we'll leave empty placeholders for last.
  1087.                 else
  1088.                     $replace[$i]$v;
  1089.             }
  1090.             $contentstr_replace($matches[0], $replace, $content);
  1091.         }
  1092.         return $content;
  1093.     }
  1094.  
  1095.     /**
  1096.      * Set eval type and name
  1097.      * Used by the fatal error handler.
  1098.      * After the eval'd code is run, call unregisterEvalInfo().
  1099.      *
  1100.      * @param string $type
  1101.      * @param string $name
  1102.      * @return void
  1103.      */
  1104.     function registerEvalInfo($type, $name) {
  1105.         $this->eval_stack[array($this->eval_type$this->eval_name);
  1106.         $this->eval_type $type;
  1107.         $this->eval_name $name;
  1108.     }
  1109.     
  1110.     /**
  1111.      * Unset eval type and name
  1112.      *
  1113.      * @return void
  1114.      */
  1115.     function unregisterEvalInfo() {
  1116.         list($this->eval_type$this->eval_namearray_pop($this->eval_stack);
  1117.     }
  1118.  
  1119.     /**
  1120.      * Run a plugin
  1121.      *
  1122.      * @param string $pluginCode Code to run
  1123.      * @param array $params
  1124.      */
  1125.     function evalPlugin($___plugin_code, $___params) {
  1126.         global $modx; // For eval'd code
  1127.         if ($___plugin_code) {
  1128.             $this->event->params&$___params// store params inside event object
  1129.             if (is_array($___params)) {
  1130.                 extract($___params, EXTR_SKIP);
  1131.             }
  1132.             $this->registerEvalInfo('plugin'$this->event->activePlugin);
  1133.             ob_start();
  1134.             $___plug = eval ($___plugin_code);
  1135.             $___msg ob_get_contents();
  1136.             ob_end_clean();
  1137.             $this->unregisterEvalInfo();
  1138.             if ($___plug === false{
  1139.                 $this->messageQuitFromElement("Plugin {$this->event->activePlugin}", "PHP Parse error in plugin {$this->event->activePlugin}");
  1140.             }
  1141.             unset ($this->event->params);
  1142.         } else {
  1143.             $this->logEvent(03"Plugin {$___name} missing or empty");
  1144.         }
  1145.     }
  1146.  
  1147.     /**
  1148.      * Run a snippet
  1149.      *
  1150.      * @param string $snippet Code to run
  1151.      * @param array $params
  1152.      * @param string $name Snippet name. Optional but advised.
  1153.      * @return string
  1154.      */
  1155.     function evalSnippet($___snippet_code, $___params, $___name = null) {
  1156.         global $modx; // For eval'd code
  1157.         if ($___snippet_code) {
  1158.             $this->event->params$___params// store params inside event object
  1159.             if (is_array($___params)) {
  1160.                 extract($___params, EXTR_SKIP);
  1161.             }
  1162.             $this->registerEvalInfo('snippet'$___name);
  1163.             ob_start();
  1164.             $___snip = eval ($___snippet_code);
  1165.             $___msg ob_get_contents();
  1166.             ob_end_clean();
  1167.             $this->unregisterEvalInfo();
  1168.             if ($___snip === false{
  1169.                 $this->messageQuitFromElement("Snippet {$___name}", "PHP Parse error in snippet {$___name}");
  1170.             }
  1171.             unset ($this->event->params);
  1172.             return $___msg $___snip;
  1173.         } else {
  1174.             $this->logEvent(03"Snippet {$___name} missing or empty");
  1175.         }
  1176.     }
  1177.  
  1178.     /**
  1179.      * Run snippets as per the tags in $documentSource and replace the tags with the returned values.
  1180.      *
  1181.      * @param string $documentSource
  1182.      * @return string
  1183.      */
  1184.     function evalSnippets($documentSource) {
  1185.         // preg_match_all('~\[\[(.*?)\]\]~ms', $documentSource, $matches);
  1186.     preg_match_all('~\[\[((.(?!\[[[!]))*?)\]\]~ms', $documentSource, $matches); // Nested snippets now possible (TimGS)
  1187.         $etomite& $this;
  1188.  
  1189.     $snippets = array();
  1190.  
  1191.         if ($matchCountcount($matches[1])) {
  1192.             for ($i0; $i < $matchCount; $i++) {
  1193.                 $sposstrpos($matches[1][$i], '?', 0);
  1194.                 if ($spos !== false) {
  1195.                     $paramssubstr($matches[1][$i], $spos);
  1196.                     $matches[1][$i]substr($matches[1][$i], 0, $spos);
  1197.                 } else {
  1198.                     $params'';
  1199.                 }
  1200.  
  1201.                 if (isset($this->snippetMap[strtolower($matches[1][$i])])) {
  1202.                      $snippets[$i]['oldname'] = $matches[1][$i]; // Store old name as it appears in the source
  1203.                      $matches[1][$i] = $this->snippetMap[strtolower($matches[1][$i])]// Map old name to new
  1204.                 }
  1205.  
  1206.                 $snippetParams[$i]$params;
  1207.             }
  1208.             $nrSnippetsToGet$matchCount;
  1209.             for ($i0; $i < $nrSnippetsToGet; $i++) { // Raymond: Mod for Snippet props
  1210.                 if (isset ($this->snippetCache[$matches[1][$i]])) {
  1211.                     $snippets[$i]['name']$matches[1][$i];
  1212.                     $snippets[$i]['snippet']$this->snippetCache[$matches[1][$i]];
  1213.                     if (array_key_exists($matches[1][$i"Props"$this->snippetCache))
  1214.                         $snippets[$i]['properties']$this->snippetCache[$matches[1][$i"Props"];
  1215.                 } else {
  1216.                     // get from db and store a copy inside cache
  1217.                     $sql"SELECT `name`, `snippet`, `properties` FROM " . $this->getFullTableName("site_snippets"" WHERE " $this->getFullTableName("site_snippets"".`name`='" $this->db->escape($matches[1][$i]"';";
  1218.                     $result$this->db->query($sql);
  1219.                     $added false;
  1220.                     if ($this->db->getRecordCount($result== 1{
  1221.                         $row$this->db->getRow($result);
  1222.                         if($row['name'== $matches[1][$i]{
  1223.                             $snippets[$i]['name']$row['name'];
  1224.                             $snippets[$i]['snippet']$this->snippetCache[$row['name']]$row['snippet'];
  1225.                             $snippets[$i]['properties']$this->snippetCache[$row['name'"Props"]$row['properties'];
  1226.                             $added true;
  1227.                         }
  1228.                     }
  1229.                     if(!$added) {
  1230.                         $snippets[$i]['name']$matches[1][$i];
  1231.                         $snippets[$i]['snippet']$this->snippetCache[$matches[1][$i]]null;
  1232.                         $snippets[$i]['properties']'';
  1233.                     }
  1234.                 }
  1235.             }
  1236.  
  1237.             for ($i0; $i < $nrSnippetsToGet; $i++) {
  1238.                 $parameterarray ();
  1239.                 $snippetName$this->currentSnippet$snippets[$i]['name'];
  1240.                 // FIXME Undefined index: properties
  1241.                 if (array_key_exists('properties'$snippets[$i])) {
  1242.                     $snippetProperties$snippets[$i]['properties'];
  1243.                 } else {
  1244.                     $snippetProperties'';
  1245.                 }
  1246.                 
  1247.                 // load default params/properties - Raymond
  1248.                 $parameter$this->parseProperties($snippetProperties);
  1249.                 
  1250.                 // This snippet's parameters.
  1251.                 // NOTE 1: &amp; and & situation non-ideal, but needed to avoid breaking sites that use snippet calls in richtext fields!
  1252.                 // NOTE 2: For backwards compatability the first parameter name need not be prefixed with '&', but this behaviour is deprecated.
  1253.                 $params_to_process str_replace('&amp;''&'trim(substr($snippetParams[$i]1)));
  1254.                 if ($params_to_process[0!= '&'$params_to_process '&'.$params_to_process;
  1255.                 preg_match_all('/(^|[`\s])&([^=]+)\=`([^`]*)`/'$params_to_process$tempSnippetParamsPREG_SET_ORDER);
  1256.                 foreach ($tempSnippetParams as $tempSnippetParam{
  1257.                     $parameter[$tempSnippetParam[2]] = $tempSnippetParam[3];
  1258.                 }
  1259.  
  1260.                 $executedSnippets[$i]$this->evalSnippet($snippets[$i]['snippet']$parameter$snippets[$i]['name']);
  1261.                 if ($this->dumpSnippets == 1{
  1262.                     echo "<fieldset><legend><b>$snippetName</b></legend><textarea style='width:60%; height:200px'>" . htmlentities($executedSnippets[$i]) . "</textarea></fieldset><br />";
  1263.                 }
  1264.  
  1265.                 // Replace snippet call with snippet return value
  1266.                 $documentSourcestr_replace('[[' . $snippetName . $snippetParams[$i] . ']]', $executedSnippets[$i], $documentSource);
  1267.                 
  1268.                 if (isset($snippets[$i]['oldname'])) {
  1269.                     // And again for old mapped snippets
  1270.                     $documentSourcestr_replace('[[' . $snippets[$i]['oldname'] . $snippetParams[$i] . ']]', $executedSnippets[$i], $documentSource);
  1271.                     }
  1272.  
  1273.             }
  1274.         }
  1275.         return $documentSource;
  1276.     }
  1277.  
  1278.     /**
  1279.      * Create a friendly URL
  1280.      *
  1281.      * @param string $pre
  1282.      * @param string $suff
  1283.      * @param string $alias
  1284.      * @return string
  1285.      */
  1286.     function makeFriendlyURL($pre, $suff, $alias) {
  1287.         $Alias = explode('/',$alias);
  1288.         $alias = array_pop($Alias);
  1289.         $dir = implode('/', $Alias);
  1290.         unset($Alias);
  1291.         return ($dir != '' ? "$dir/" : '') . $pre . $alias . $suff;
  1292.     }
  1293.  
  1294.     /** 
  1295.      * Convert URL tags [~...~] to URLs
  1296.      *
  1297.      * @param string $documentSource
  1298.      * @return string
  1299.      */
  1300.     function rewriteUrls($documentSource) {
  1301.         // rewrite the urls
  1302.         if ($this->config['friendly_urls'== 1{
  1303.             $aliasesarray ();
  1304.             foreach ($this->aliasListing as $item{
  1305.                 $aliases[$item['id']](strlen($item['path']) > 0 ? $item['path'] . '/' : '') . $item['alias'];
  1306.             }
  1307.             $in'!\[\~([0-9]+)\~\]!ise'; // Use preg_replace with /e to make it evaluate PHP
  1308.             $isfriendly($this->config['friendly_alias_urls'== 0);
  1309.             $pref$this->config['friendly_url_prefix'];
  1310.             $suff$this->config['friendly_url_suffix'];
  1311.             $thealias'$aliases[\\1]';
  1312.             $found_friendlyurl"\$this->makeFriendlyURL('$pref','$suff',$thealias)";
  1313.             $not_found_friendlyurl"\$this->makeFriendlyURL('$pref','$suff','" . '\\1' . "')";
  1314.             $out"({$isfriendly} && isset({$thealias}) ? {$found_friendlyurl} : {$not_found_friendlyurl})";
  1315.             $documentSourcepreg_replace($in, $out, $documentSource);
  1316.         } else {
  1317.             $in'!\[\~([0-9]+)\~\]!is';
  1318.             $out"index.php?id=" . '\1';
  1319.             $documentSourcepreg_replace($in, $out, $documentSource);
  1320.         }
  1321.         return $documentSource;
  1322.     }
  1323.  
  1324.     /**
  1325.      * Get all db fields and TVs for a document/resource
  1326.      *
  1327.      * @param type $method
  1328.      * @param type $identifier
  1329.      * @return array
  1330.      */
  1331.     function getDocumentObject($method, $identifier) {
  1332.         $tblsc$this->getFullTableName("site_content");
  1333.         $tbldg$this->getFullTableName("document_groups");
  1334.  
  1335.         // allow alias to be full path
  1336.         if($method == 'alias'{
  1337.             $identifier = $this->cleanDocumentIdentifier($identifier);
  1338.             $method $this->documentMethod;
  1339.         }
  1340.         if($method == 'alias' && $this->config['use_alias_path'&& array_key_exists($identifier$this->documentListing)) {
  1341.             $method = 'id';
  1342.             $identifier = $this->documentListing[$identifier];
  1343.         }
  1344.         // get document groups for current user
  1345.         if ($docgrp$this->getUserDocGroups())
  1346.             $docgrpimplode(","$docgrp);
  1347.         // get document
  1348.         $access($this->isFrontend("sc.privateweb=0" "1='" $_SESSION['mgrRole'"' OR sc.privatemgr=0".
  1349.          (!$docgrp "" " OR dg.document_group IN ($docgrp)");
  1350.         $sql"SELECT sc.*
  1351.               FROM $tblsc sc
  1352.               LEFT JOIN $tbldg dg ON dg.document = sc.id
  1353.               WHERE sc." . $method . " = '" . $identifier . "'
  1354.               AND ($access) LIMIT 1;";
  1355.         $result$this->db->query($sql);
  1356.         $rowCount$this->db->getRecordCount($result);
  1357.         if ($rowCount 1{
  1358.             if ($this->config['unauthorized_page']{
  1359.                 // method may still be alias, while identifier is not full path alias, e.g. id not found above
  1360.                 if ($method === 'alias') {
  1361.                     $q = "SELECT dg.id FROM $tbldg dg, $tblsc sc WHERE dg.document = sc.id AND sc.alias = '{$identifier}' LIMIT 1;";
  1362.                 } else {
  1363.                     $q = "SELECT id FROM $tbldg WHERE document = '{$identifier}' LIMIT 1;";
  1364.                 }
  1365.                 // check if file is not public
  1366.                 $secrs$this->db->query($q);
  1367.                 if ($secrs)
  1368.                     $seclimit$this->db->getRecordCount($secrs);
  1369.             }
  1370.             if ($seclimit > 0) {
  1371.                 // match found but not publicly accessible, send the visitor to the unauthorized_page
  1372.                 $this->sendUnauthorizedPage();
  1373.                 exit// stop here
  1374.             } else {
  1375.                 $this->sendErrorPage();
  1376.                 exit;
  1377.             }
  1378.         }
  1379.  
  1380.         # this is now the document :) #
  1381.         $documentObject$this->db->getRow($result);
  1382.  
  1383.         if ($documentObject['template']{
  1384.             // load TVs and merge with document - Orig by Apodigm - Docvars
  1385.             $sql"SELECT tv.*, IF(tvc.value!='',tvc.value,tv.default_text) as value ";
  1386.             $sql .= "FROM " . $this->getFullTableName("site_tmplvars"" tv ";
  1387.             $sql .= "INNER JOIN " $this->getFullTableName("site_tmplvar_templates")." tvtpl ON tvtpl.tmplvarid = tv.id ";
  1388.             $sql .= "LEFT JOIN " $this->getFullTableName("site_tmplvar_contentvalues")." tvc ON tvc.tmplvarid=tv.id AND tvc.contentid = '" $documentObject['id'"' ";
  1389.             $sql .= "WHERE tvtpl.templateid = '" $documentObject['template'"'";
  1390.             $rs$this->db->query($sql);
  1391.             $rowCount$this->db->getRecordCount($rs);
  1392.             if ($rowCount 0{
  1393.                 for ($i0; $i < $rowCount; $i++) {
  1394.                     $row$this->db->getRow($rs);
  1395.                     $tmplvars[$row['name']]array (
  1396.                         $row['name'],
  1397.                         $row['value'],
  1398.                         $row['display'],
  1399.                         $row['display_params'],
  1400.                         $row['type']
  1401.                     );
  1402.                 }
  1403.                 $documentObjectarray_merge($documentObject, $tmplvars);
  1404.             }
  1405.         }
  1406.         
  1407.         return $documentObject;
  1408.     }
  1409.  
  1410.     /**
  1411.      * Parse a source string.
  1412.      *
  1413.      * Handles most MODx tags. Exceptions include:
  1414.      *   - URL tags [~...~]
  1415.      *
  1416.      * @param string $source
  1417.      * @param bool $uncached_snippets
  1418.      * @return string
  1419.      */
  1420.     function parseDocumentSource($source, $uncached_snippets = false) {
  1421.         // set the number of times we are to parse the document source
  1422.         $this->minParserPassesempty ($this->minParserPasses$this->minParserPasses;
  1423.         $this->maxParserPassesempty ($this->maxParserPasses10 $this->maxParserPasses;
  1424.         $passes$this->minParserPasses;
  1425.         for ($i0$i $passes$i++{
  1426.             // get source length if this is the final pass
  1427.             if ($i == ($passes -1))
  1428.                 $ststrlen($source);
  1429.             if ($this->dumpSnippets == 1{
  1430.                 echo "<fieldset><legend><b style='color: #821517;'>PARSE PASS " . ($i +1) . "</b></legend>The following snippets (if any) were parsed during this pass.<div style='width:100%' align='center'>";
  1431.             }
  1432.  
  1433.             // invoke OnParseDocument event
  1434.             $this->documentOutput$source// store source code so plugins can
  1435.             $this->invokeEvent("OnParseDocument")// work on it via $modx->documentOutput
  1436.             $source$this->documentOutput;
  1437.  
  1438.             // combine template and document variables
  1439.             $source$this->mergeDocumentContent($source);
  1440.             // replace settings referenced in document
  1441.             $source$this->mergeSettingsContent($source);
  1442.             // replace HTMLSnippets in document
  1443.             $source$this->mergeChunkContent($source);
  1444.             
  1445.             if ($uncached_snippets{
  1446.                 $source = str_replace(array('[!', '!]'), array('[[', ']]'), $source);
  1447.             }
  1448.             
  1449.             // find and merge snippets
  1450.             $source$this->evalSnippets($source);
  1451.             // find and replace Placeholders (must be parsed last) - Added by Raymond
  1452.             $source$this->mergePlaceholderContent($source);
  1453.             if ($this->dumpSnippets == 1{
  1454.                 echo "</div></fieldset><br />";
  1455.             }
  1456.             if ($i == ($passes -1) && $i < ($this->maxParserPasses - 1)) {
  1457.                 // check if source length was changed
  1458.                 $etstrlen($source);
  1459.                 if ($st != $et)
  1460.                     $passes++; // if content change then increase passes because
  1461.             } // we have not yet reached maxParserPasses
  1462.         }
  1463.         return $source;
  1464.     }
  1465.  
  1466.     /**
  1467.      * Starts the parsing operations.
  1468.      * 
  1469.      * - connects to the db
  1470.      * - gets the settings (including system_settings)
  1471.      * - gets the document/resource identifier as in the query string
  1472.      * - finally calls prepareResponse()
  1473.      */
  1474.     function executeParser() {
  1475.  
  1476.         $this->set_error_handler();
  1477.  
  1478.         $this->db->connect();
  1479.  
  1480.         // get the settings
  1481.         if (empty ($this->config)) {
  1482.             $this->getSettings();
  1483.         }
  1484.  
  1485.         // IIS friendly url fix
  1486.         if ($this->config['friendly_urls'== && strpos($_SERVER['SERVER_SOFTWARE']'Microsoft-IIS'!== false{
  1487.             $url$_SERVER['QUERY_STRING'];
  1488.             $errsubstr($url, 0, 3);
  1489.             if ($err == '404' || $err == '405') {
  1490.                 $karray_keys($_GET);
  1491.                 unset ($_GET[$k[0]]);
  1492.                 unset ($_REQUEST[$k[0]]); // remove 404,405 entry
  1493.                 $_SERVER['QUERY_STRING']$qp['query'];
  1494.                 $qpparse_url(str_replace($this->config['site_url']''substr($url4)));
  1495.                 if (!empty ($qp['query'])) {
  1496.                     parse_str($qp['query'], $qv);
  1497.                     foreach ($qv as $n => $v)
  1498.                         $_REQUEST[$n]$_GET[$n]$v;
  1499.                 }
  1500.                 $_SERVER['PHP_SELF']$this->config['base_url'$qp['path'];
  1501.                 $_REQUEST['q']$_GET['q']$qp['path'];
  1502.             }
  1503.         }
  1504.  
  1505.         // check site settings
  1506.         if (!$this->checkSiteStatus()) {
  1507.             header('HTTP/1.0 503 Service Unavailable');
  1508.             if (!$this->config['site_unavailable_page']{
  1509.                 // display offline message
  1510.                 $this->documentContent$this->config['site_unavailable_message'];
  1511.                 $this->outputContent();
  1512.                 exit// stop processing here, as the site's offline
  1513.             } else {
  1514.                 // setup offline page document settings
  1515.                 $this->documentMethod"id";
  1516.                 $this->documentIdentifier$this->config['site_unavailable_page'];
  1517.             }
  1518.         } else {
  1519.             // make sure the cache doesn't need updating
  1520.             $this->checkPublishStatus();
  1521.  
  1522.             // find out which document we need to display
  1523.             $this->documentMethod$this->getDocumentMethod();
  1524.             $this->documentIdentifier$this->getDocumentIdentifier($this->documentMethod);
  1525.  
  1526.             $this->is_rss = ($this->documentMethod == 'rss' || !empty($_GET['rss']));
  1527.  
  1528.             if (is_int($this->documentIdentifier)) {
  1529.                 $this->documentMethod = 'id';
  1530.             }
  1531.         }
  1532.  
  1533.         if ($this->documentMethod == "none"{
  1534.             $this->documentMethod"id"// now we know the site_start, change the none method to id
  1535.         }
  1536.  
  1537.         if ($this->documentMethod == 'alias' || $this->documentMethod == 'rss'{
  1538.             $this->documentIdentifier$this->cleanDocumentIdentifier($this->documentIdentifier);
  1539.         }
  1540.  
  1541.         if ($this->documentMethod == 'alias' || $this->documentMethod == 'rss'{
  1542.             // Check use_alias_path and check if $this->virtualDir is set to anything, then parse the path
  1543.             if ($this->config['use_alias_path'== 1{
  1544.                 $alias(strlen($this->virtualDir$this->virtualDir . '/' ''$this->documentIdentifier;
  1545.                 if (array_key_exists($alias$this->documentListing)) {
  1546.                     $this->documentIdentifier$this->documentListing[$alias];
  1547.                 } else {
  1548.                     $this->sendErrorPage();
  1549.                 }
  1550.             } else {
  1551.                 $this->documentIdentifier$this->documentListing[$this->documentIdentifier];
  1552.             }
  1553.             $this->documentMethod'id';
  1554.         }
  1555.  
  1556.         // invoke OnWebPageInit event
  1557.         $this->invokeEvent("OnWebPageInit");
  1558.  
  1559.         // invoke OnLogPageView event
  1560.         if ($this->config['track_visitors'== 1{
  1561.             $this->invokeEvent("OnLogPageHit");
  1562.         }
  1563.  
  1564.         $this->prepareResponse();
  1565.     }
  1566.  
  1567.     /**
  1568.      * The next step called at the end of executeParser()
  1569.      *
  1570.      * - checks cache
  1571.      * - checks if document/resource is deleted/unpublished
  1572.      * - checks if resource is a weblink and redirects if so
  1573.      * - gets template and parses it
  1574.      * - ensures that postProcess is called when PHP is finished
  1575.      */
  1576.     function prepareResponse() {
  1577.         if ($this->is_rss{
  1578.             $this->documentContent = ''// RSS is not cached
  1579.         } else {
  1580.             // we now know the method and identifier, let's check the cache
  1581.             $this->documentContent$this->checkCache($this->documentIdentifier);
  1582.         }
  1583.         
  1584.         if ($this->documentContent != ""{
  1585.             // invoke OnLoadWebPageCache  event
  1586.             $this->invokeEvent("OnLoadWebPageCache");
  1587.         } else {
  1588.             // get document object
  1589.             $this->documentObject$this->getDocumentObject($this->documentMethod$this->documentIdentifier);
  1590.  
  1591.             // write the documentName to the object
  1592.             $this->documentName$this->documentObject['pagetitle'];
  1593.  
  1594.             // validation routines
  1595.             if ($this->documentObject['deleted'== 1{
  1596.                 $this->sendErrorPage();
  1597.             }
  1598.             //  && !$this->checkPreview()
  1599.             if ($this->documentObject['published'== 0{
  1600.  
  1601.                 // Can't view unpublished pages
  1602.                 if (!$this->hasPermission('view_unpublished')) {
  1603.                     $this->sendErrorPage();
  1604.                 } else {
  1605.                     // Inculde the necessary files to check document permissions
  1606.                     require_once('user_documents_permissions.class.php');
  1607.                     $udpermsnew udperms();
  1608.                     $udperms->user$this->getLoginUserID();
  1609.                     $udperms->document$this->documentIdentifier;
  1610.                     $udperms->role$_SESSION['mgrRole'];
  1611.                     // Doesn't have access to this document
  1612.                     if (!$udperms->checkPermissions()) {
  1613.                         $this->sendErrorPage();
  1614.                     }
  1615.  
  1616.                 }
  1617.  
  1618.             }
  1619.  
  1620.             // check whether it's a reference
  1621.             if ($this->documentObject['type'== "reference"{
  1622.                 if (is_numeric($this->documentObject['content'])) {
  1623.                     // if it's a bare document id
  1624.                     $this->documentObject['content']$this->makeUrl($this->documentObject['content']);
  1625.                 }
  1626.                 elseif (strpos($this->documentObject['content']'[~'!== false{
  1627.                     // if it's an internal docid tag, process it
  1628.                     $this->documentObject['content']$this->rewriteUrls($this->documentObject['content']);
  1629.                 }
  1630.                 $this->sendRedirect($this->documentObject['content']0'''HTTP/1.0 301 Moved Permanently');
  1631.             }
  1632.  
  1633.             // check if we should not hit this document
  1634.             if ($this->documentObject['donthit'== 1{
  1635.                 $this->config['track_visitors']0;
  1636.             }
  1637.  
  1638.             if ($this->is_rss{
  1639.                 // The following line could be a config option
  1640.                 $this->documentContent = '[[List? &format=`rss` &depth=`0` &display=`'.$this->config['rss_len'].'` &summarize=`'.$this->config['rss_len'].'` &parents=`'.($this->documentIdentifier == $this->config['site_start'$this->documentIdentifier).'`]]';
  1641.             } else {
  1642.                 // get the template and start parsing!
  1643.                 if (!$this->documentObject['template'])
  1644.                     $this->documentContent"[*content*]"// use blank template
  1645.                 else {
  1646.                     $sql"SELECT `content` FROM " . $this->getFullTableName("site_templates"" WHERE " $this->getFullTableName("site_templates"".`id` = '" $this->documentObject['template'"';";
  1647.                     $result$this->db->query($sql);
  1648.                     $rowCount$this->db->getRecordCount($result);
  1649.                     if ($rowCount 1{
  1650.                         $this->messageQuit("Incorrect number of templates returned from database"$sql);
  1651.                     }
  1652.                     elseif ($rowCount == 1) {
  1653.                         $row$this->db->getRow($result);
  1654.                         $this->documentContent$row['content'];
  1655.                     }
  1656.                 }
  1657.             }
  1658.  
  1659.             // invoke OnLoadWebDocument event
  1660.             $this->invokeEvent("OnLoadWebDocument");
  1661.  
  1662.             // Parse document source
  1663.             $this->documentContent$this->parseDocumentSource($this->documentContent);
  1664.  
  1665.             // setup <base> tag for friendly urls
  1666.             //            if($this->config['friendly_urls']==1 && $this->config['use_alias_path']==1) {
  1667.             //                $this->regClientStartupHTMLBlock('<base href="'.$this->config['site_url'].'" />');
  1668.             //            }
  1669.         }
  1670.         register_shutdown_function(array (
  1671.             & $this,
  1672.             "postProcess"
  1673.         )); // tell PHP to call postProcess when it shuts down
  1674.         $this->outputContent();
  1675.         //$this->postProcess();
  1676.     }
  1677.  
  1678.     /**
  1679.      * Returns an array of all parent record IDs for the id passed.
  1680.      *
  1681.      * @param int $id Docid to get parents for.
  1682.      * @param int $height The maximum number of levels to go up, default 10.
  1683.      * @return array
  1684.      */
  1685.     function getParentIds($id, $height10) {
  1686.         $parentsarray ();
  1687.         while ( $id && $height-- ) {
  1688.             $thisid = $id;
  1689.             $id = $this->aliasListing[$id]['parent'];
  1690.             if (!$idbreak;
  1691.             $pkey strlen($this->aliasListing[$thisid]['path']$this->aliasListing[$thisid]['path'$this->aliasListing[$id]['alias'];
  1692.             if (!strlen($pkey)) $pkey "{$id}";
  1693.             $parents[$pkey] = $id;
  1694.         }
  1695.         return $parents;
  1696.     }
  1697.  
  1698.     /**
  1699.      * Returns the ultimate parent of a document
  1700.      *
  1701.      * @param int $id Docid to get ultimate parent.
  1702.      * @return int
  1703.      */
  1704.     function getUltimateParentId($id) {
  1705.         while ($id) {
  1706.             $last_id = $id;
  1707.             $id = $this->aliasListing[$id]['parent'];
  1708.         }
  1709.         return $last_id;
  1710.     }
  1711.  
  1712.     /**
  1713.      * Returns an array of child IDs belonging to the specified parent.
  1714.      *
  1715.      * @param int $id The parent resource/document to start from
  1716.      * @param int $depth How many levels deep to search for children, default: 10
  1717.      * @param array $children Optional array of docids to merge with the result.
  1718.      * @return array Contains the document Listing (tree) like the sitemap
  1719.      */
  1720.     function getChildIds($id, $depth10, $childrenarray ()) {
  1721.  
  1722.         // Initialise a static array to index parents->children
  1723.         static $documentMap_cache = array();
  1724.         if (!count($documentMap_cache)) {
  1725.             foreach ($this->documentMap as $document{
  1726.                 foreach ($document as $p => $c) {
  1727.                     $documentMap_cache[$p][] = $c;
  1728.                 }
  1729.             }
  1730.         }
  1731.  
  1732.         // Get all the children for this parent node
  1733.         if (isset($documentMap_cache[$id])) {
  1734.             $depth--;
  1735.  
  1736.             foreach ($documentMap_cache[$id] as $childId) {
  1737.                 $pkey = (strlen($this->aliasListing[$childId]['path']"{$this->aliasListing[$childId]['path']}/" : '') . $this->aliasListing[$childId]['alias'];
  1738.                 if (!strlen($pkey)) $pkey "{$childId}";
  1739.                     $children[$pkey] = $childId;
  1740.  
  1741.                 if ($depth) {
  1742.                     $children += $this->getChildIds($childId$depth);
  1743.                 }
  1744.             }
  1745.         }
  1746.         return $children;
  1747.     }
  1748.  
  1749.     /**
  1750.      * Displays a javascript alert message in the web browser
  1751.      *
  1752.      * @param string $msg Message to show
  1753.      * @param string $url URL to redirect to
  1754.      */
  1755.     function webAlert($msg, $url"") {
  1756.         $msgaddslashes($this->db->escape($msg));
  1757.         if (substr(strtolower($url)011== "javascript:"{
  1758.             $act"__WebAlert();";
  1759.             $fnc"function __WebAlert(){" . substr($url, 11) . "};";
  1760.         } else {
  1761.             $act($url ? "window.location.href='" . addslashes($url) . "';" : "");
  1762.         }
  1763.         $html"<script>$fnc window.setTimeout(\"alert('$msg');$act\",100);</script>";
  1764.         if ($this->isFrontend())
  1765.             $this->regClientScript($html);
  1766.         else {
  1767.             echo $html;
  1768.         }
  1769.     }
  1770.  
  1771.     /**
  1772.      * Returns true if user has the currect permission
  1773.      *
  1774.      * @param string $pm Permission name
  1775.      * @return int
  1776.      */
  1777.     function hasPermission($pm) {
  1778.         $statefalse;
  1779.         $pms$_SESSION['mgrPermissions'];
  1780.         if ($pms)
  1781.             $state($pms[$pm] == 1);
  1782.         return $state;
  1783.     }
  1784.  
  1785.     /**
  1786.      * Add an a alert message to the system event log
  1787.      *
  1788.      * @param int $evtid Event ID
  1789.      * @param int $type Types: 1 = information, 2 = warning, 3 = error
  1790.      * @param string $msg Message to be logged
  1791.      * @param string $source source of the event (module, snippet name, etc.)
  1792.      *                       Default: Parser
  1793.      */
  1794.     function logEvent($evtid, $type, $msg, $source'Parser') {
  1795.         $msg$this->db->escape($msg);
  1796.         $source$this->db->escape($source);
  1797.         if ($GLOBALS['database_connection_charset'== 'utf8' && extension_loaded('mbstring')) {
  1798.             $source = mb_substr($source, 0, 50 , "UTF-8");
  1799.         } else {
  1800.             $source = substr($source, 0, 50);
  1801.         }
  1802.         $LoginUserID = $this->getLoginUserID();
  1803.         if ($LoginUserID == ''$LoginUserID 0;
  1804.         $evtidintval($evtid);
  1805.         $type intval($type);
  1806.         if ($type 1{
  1807.             $type1;
  1808.         }
  1809.         elseif ($type > 3) {
  1810.             $type3; // Types: 1 = information, 2 = warning, 3 = error
  1811.         }
  1812.         $sql"INSERT INTO " . $this->getFullTableName("event_log"" (eventid,type,createdon,source,description,user) " .
  1813.                 "VALUES($evtid,$type," . time() . ",'$source','$msg','" . $LoginUserID . "')";
  1814.         $ds@$this->db->query($sql);
  1815.         if (!$ds{
  1816.             echo "Error while inserting event log into database.";
  1817.             exit();
  1818.         }
  1819.     }
  1820.  
  1821.     /**
  1822.      * Returns true if we are currently in the manager/backend
  1823.      *
  1824.      * @return boolean
  1825.      */
  1826.     function isBackend() {
  1827.         return $this->insideManager(true false;
  1828.     }
  1829.  
  1830.     /**
  1831.      * Returns true if we are currently in the frontend
  1832.      *
  1833.      * @return boolean
  1834.      */
  1835.     function isFrontend() {
  1836.         return !$this->insideManager(true false;
  1837.     }
  1838.  
  1839.     /**
  1840.      * Gets all child documents of the specified document, including those which are unpublished or deleted.
  1841.      *
  1842.      * @param int $id The Document identifier to start with
  1843.      * @param string $sort Sort field
  1844.      *                     Default: menuindex
  1845.      * @param string $dir Sort direction, ASC and DESC is possible
  1846.      *                    Default: ASC
  1847.      * @param string $fields Default: id, pagetitle, description, parent, alias, menutitle
  1848.      * @return array
  1849.      */
  1850.     function getAllChildren($id0, $sort'menuindex', $dir'ASC', $fields'id, pagetitle, description, parent, alias, menutitle') {
  1851.         $tblsc$this->getFullTableName("site_content");
  1852.         $tbldg$this->getFullTableName("document_groups");
  1853.         // modify field names to use sc. table reference
  1854.         $fields'sc.' implode(',sc.'preg_replace("/^\s/i"""explode(','$fields)));
  1855.         $sort'sc.' implode(',sc.'preg_replace("/^\s/i"""explode(','$sort)));
  1856.         // get document groups for current user
  1857.         if ($docgrp$this->getUserDocGroups())
  1858.             $docgrpimplode(","$docgrp);
  1859.         // build query
  1860.         $access($this->isFrontend("sc.privateweb=0" "1='" $_SESSION['mgrRole'"' OR sc.privatemgr=0".
  1861.          (!$docgrp "" " OR dg.document_group IN ($docgrp)");
  1862.         $sql"SELECT DISTINCT $fields FROM $tblsc sc
  1863.               LEFT JOIN $tbldg dg on dg.document = sc.id
  1864.               WHERE sc.parent = '$id'
  1865.               AND ($access)
  1866.               GROUP BY sc.id
  1867.               ORDER BY $sort $dir;";
  1868.         $result$this->db->query($sql);
  1869.         $resourceArrayarray ();
  1870.         for ($i0$i $this->db->getRecordCount($result)$i++{
  1871.             array_push($resourceArray, @ $this->db->getRow($result));
  1872.         }
  1873.         return $resourceArray;
  1874.     }
  1875.  
  1876.     /**
  1877.      * Gets all active child documents of the specified document, i.e. those which published and not deleted.
  1878.      *
  1879.      * @param int $id The Document identifier to start with
  1880.      * @param string $sort Sort field
  1881.      *                     Default: menuindex
  1882.      * @param string $dir Sort direction, ASC and DESC is possible
  1883.      *                    Default: ASC
  1884.      * @param string $fields Default: id, pagetitle, description, parent, alias, menutitle
  1885.      * @return array
  1886.      */
  1887.     function getActiveChildren($id0, $sort'menuindex', $dir'ASC', $fields'id, pagetitle, description, parent, alias, menutitle') {
  1888.         $tblsc$this->getFullTableName("site_content");
  1889.         $tbldg$this->getFullTableName("document_groups");
  1890.  
  1891.         // modify field names to use sc. table reference
  1892.         $fields'sc.' implode(',sc.'preg_replace("/^\s/i"""explode(','$fields)));
  1893.         $sort'sc.' implode(',sc.'preg_replace("/^\s/i"""explode(','$sort)));
  1894.         // get document groups for current user
  1895.         if ($docgrp$this->getUserDocGroups())
  1896.             $docgrpimplode(","$docgrp);
  1897.         // build query
  1898.         $access($this->isFrontend("sc.privateweb=0" "1='" $_SESSION['mgrRole'"' OR sc.privatemgr=0".
  1899.          (!$docgrp "" " OR dg.document_group IN ($docgrp)");
  1900.         $sql"SELECT DISTINCT $fields FROM $tblsc sc
  1901.               LEFT JOIN $tbldg dg on dg.document = sc.id
  1902.               WHERE sc.parent = '$id' AND sc.published=1 AND sc.deleted=0
  1903.               AND ($access)
  1904.               GROUP BY sc.id
  1905.               ORDER BY $sort $dir;";
  1906.         $result$this->db->query($sql);
  1907.         $resourceArrayarray ();
  1908.         for ($i0$i $this->db->getRecordCount($result)$i++{
  1909.             array_push($resourceArray, @ $this->db->getRow($result));
  1910.         }
  1911.         return $resourceArray;
  1912.     }
  1913.  
  1914.     /**
  1915.      * Returns the children of the selected document/folder.
  1916.      *
  1917.      * @param int $parentid The parent document identifier
  1918.      *                      Default: 0 (site root)
  1919.      * @param int $published Whether published or unpublished documents are in the result
  1920.      *                      Default: 1
  1921.      * @param int $deleted Whether deleted or undeleted documents are in the result
  1922.      *                      Default: 0 (undeleted)
  1923.      * @param string $fields List of fields
  1924.      *                       Default: * (all fields)
  1925.      * @param string $where Where condition in SQL style. Should include a leading 'AND '
  1926.      *                      Default: Empty string
  1927.      * @param type $sort Should be a comma-separated list of field names on which to sort
  1928.      *                    Default: menuindex
  1929.      * @param string $dir Sort direction, ASC and DESC is possible
  1930.      *                    Default: ASC
  1931.      * @param string|int $limit Should be a valid SQL LIMIT clause without the 'LIMIT' i.e. just include the numbers as a string.
  1932.      *                          Default: Empty string (no limit)
  1933.      * @return array
  1934.      */
  1935.     function getDocumentChildren($parentid0, $published1, $deleted0, $fields"*", $where'', $sort"menuindex", $dir"ASC", $limit"") {
  1936.         $limit($limit != "") ? "LIMIT $limit" : "";
  1937.         $tblsc$this->getFullTableName("site_content");
  1938.         $tbldg$this->getFullTableName("document_groups");
  1939.         // modify field names to use sc. table reference
  1940.         $fields'sc.' implode(',sc.'preg_replace("/^\s/i"""explode(','$fields)));
  1941.         $sort($sort == """" 'sc.' implode(',sc.'preg_replace("/^\s/i"""explode(','$sort)));
  1942.         if ($where != '')
  1943.             $where'AND ' $where;
  1944.         // get document groups for current user
  1945.         if ($docgrp$this->getUserDocGroups())
  1946.             $docgrpimplode(","$docgrp);
  1947.         // build query
  1948.         $access($this->isFrontend("sc.privateweb=0" "1='" $_SESSION['mgrRole'"' OR sc.privatemgr=0".
  1949.          (!$docgrp "" " OR dg.document_group IN ($docgrp)");
  1950.         $sql"SELECT DISTINCT $fields
  1951.               FROM $tblsc sc
  1952.               LEFT JOIN $tbldg dg on dg.document = sc.id
  1953.               WHERE sc.parent = '$parentid' AND sc.published=$published AND sc.deleted=$deleted $where
  1954.               AND ($access)
  1955.               GROUP BY sc.id " .
  1956.          ($sort ? " ORDER BY $sort $dir " : "") . " $limit ";
  1957.         $result$this->db->query($sql);
  1958.         $resourceArrayarray ();
  1959.         for ($i0$i $this->db->getRecordCount($result)$i++{
  1960.             array_push($resourceArray, @ $this->db->getRow($result));
  1961.         }
  1962.         return $resourceArray;
  1963.     }
  1964.  
  1965.     /**
  1966.      * Returns multiple documents/resources
  1967.      *
  1968.      * @category API-Function
  1969.      * @param array $ids Documents to fetch by docid
  1970.      *                   Default: Empty array
  1971.      * @param int $published Whether published or unpublished documents are in the result
  1972.      *                      Default: 1
  1973.      * @param int $deleted Whether deleted or undeleted documents are in the result
  1974.      *                      Default: 0 (undeleted)
  1975.      * @param string $fields List of fields
  1976.      *                       Default: * (all fields)
  1977.      * @param string $where Where condition in SQL style. Should include a leading 'AND '.
  1978.      *                      Default: Empty string
  1979.      * @param type $sort Should be a comma-separated list of field names on which to sort
  1980.      *                    Default: menuindex
  1981.      * @param string $dir Sort direction, ASC and DESC is possible
  1982.      *                    Default: ASC
  1983.      * @param string|int $limit Should be a valid SQL LIMIT clause without the 'LIMIT' i.e. just include the numbers as a string.
  1984.      *                          Default: Empty string (no limit)
  1985.      * @return array|boolean Result array with documents, or false
  1986.      */
  1987.     function getDocuments($idsarray (), $published1, $deleted0, $fields"*", $where'', $sort"menuindex", $dir"ASC", $limit"") {
  1988.         if (count($ids) == 0) {
  1989.             return false;
  1990.         } else {
  1991.             $limit($limit != "") ? "LIMIT $limit" : ""; // LIMIT capabilities - rad14701
  1992.             $tblsc$this->getFullTableName("site_content");
  1993.             $tbldg$this->getFullTableName("document_groups");
  1994.             // modify field names to use sc. table reference
  1995.             $fields'sc.' implode(',sc.'preg_replace("/^\s/i"""explode(','$fields)));
  1996.             $sort($sort == """" 'sc.' implode(',sc.'preg_replace("/^\s/i"""explode(','$sort)));
  1997.             if ($where != '')
  1998.                 $where'AND ' $where;
  1999.             // get document groups for current user
  2000.             if ($docgrp$this->getUserDocGroups())
  2001.                 $docgrpimplode(","$docgrp);
  2002.             $access($this->isFrontend("sc.privateweb=0" "1='" $_SESSION['mgrRole'"' OR sc.privatemgr=0".
  2003.              (!$docgrp "" " OR dg.document_group IN ($docgrp)");
  2004.             $sql"SELECT DISTINCT $fields FROM $tblsc sc
  2005.                     LEFT JOIN $tbldg dg on dg.document = sc.id
  2006.                     WHERE (sc.id IN (" . implode(",",$ids) . ") AND sc.published=$published AND sc.deleted=$deleted $where)
  2007.                     AND ($access)
  2008.                     GROUP BY sc.id " .
  2009.              ($sort ? " ORDER BY $sort $dir" : "") . " $limit ";
  2010.             $result$this->db->query($sql);
  2011.             $resourceArrayarray ();
  2012.             for ($i0$i $this->db->getRecordCount($result)$i++{
  2013.                 array_push($resourceArray, @ $this->db->getRow($result));
  2014.             }
  2015.             return $resourceArray;
  2016.         }
  2017.     }
  2018.  
  2019.     /**
  2020.      * Returns one document/resource
  2021.      *
  2022.      * @category API-Function
  2023.      * @param int $id docid
  2024.      *                Default: 0 (no documents)
  2025.      * @param string $fields List of fields
  2026.      *                       Default: * (all fields)
  2027.      * @param int $published Whether published or unpublished documents are in the result
  2028.      *                      Default: 1
  2029.      * @param int $deleted Whether deleted or undeleted documents are in the result
  2030.      *                      Default: 0 (undeleted)
  2031.      * @return boolean|string
  2032.      */
  2033.     function getDocument($id0, $fields"*", $published1, $deleted0) {
  2034.         if ($id == 0) {
  2035.             return false;
  2036.         } else {
  2037.             $tmpArr[]$id;
  2038.             $docs$this->getDocuments($tmpArr$published$deleted$fields""""""1);
  2039.             if ($docs != false{
  2040.                 return $docs[0];
  2041.             } else {
  2042.                 return false;
  2043.             }
  2044.         }
  2045.     }
  2046.  
  2047.     /**
  2048.      * Returns the page information as database row, the type of result is
  2049.      * defined with the parameter $rowMode
  2050.      *
  2051.      * @param int $pageid The parent document identifier
  2052.      *                    Default: -1 (no result)
  2053.      * @param int $active Should we fetch only published and undeleted documents/resources?
  2054.      *                     1 = yes, 0 = no
  2055.      *                     Default: 1
  2056.      * @param string $fields List of fields
  2057.      *                       Default: id, pagetitle, description, alias
  2058.      * @return boolean|array
  2059.      */
  2060.     function getPageInfo($pageid= -1, $active1, $fields'id, pagetitle, description, alias') {
  2061.         if ($pageid == 0) {
  2062.             return false;
  2063.         } else {
  2064.             $tblsc$this->getFullTableName("site_content");
  2065.             $tbldg$this->getFullTableName("document_groups");
  2066.             $activeSql$active == "AND sc.published=1 AND sc.deleted=0" "";
  2067.             // modify field names to use sc. table reference
  2068.             $fields'sc.' implode(',sc.'preg_replace("/^\s/i"""explode(','$fields)));
  2069.             // get document groups for current user
  2070.             if ($docgrp$this->getUserDocGroups())
  2071.                 $docgrpimplode(","$docgrp);
  2072.             $access($this->isFrontend("sc.privateweb=0" "1='" $_SESSION['mgrRole'"' OR sc.privatemgr=0".
  2073.              (!$docgrp "" " OR dg.document_group IN ($docgrp)");
  2074.             $sql"SELECT $fields
  2075.                     FROM $tblsc sc
  2076.                     LEFT JOIN $tbldg dg on dg.document = sc.id
  2077.                     WHERE (sc.id=$pageid $activeSql)
  2078.                     AND ($access)
  2079.                     LIMIT 1 ";
  2080.             $result$this->db->query($sql);
  2081.             $pageInfo$this->db->getRow($result);
  2082.             return $pageInfo;
  2083.         }
  2084.     }
  2085.  
  2086.     /**
  2087.      * Returns the parent document/resource of the given docid
  2088.      *
  2089.      * @param int $pid The parent docid. If -1, then fetch the current document/resource's parent
  2090.      *                 Default: -1
  2091.      * @param int $active Should we fetch only published and undeleted documents/resources?
  2092.      *                     1 = yes, 0 = no
  2093.      *                     Default: 1
  2094.      * @param string $fields List of fields
  2095.      *                       Default: id, pagetitle, description, alias
  2096.      * @return boolean|array
  2097.      */
  2098.     function getParent($pid= -1, $active1, $fields'id, pagetitle, description, alias, parent') {
  2099.         if ($pid == -1) {
  2100.             $pid$this->documentObject['parent'];
  2101.             return ($pid == 0false $this->getPageInfo($pid$active$fields);
  2102.         } else
  2103.             if ($pid == 0) {
  2104.                 return false;
  2105.             } else {
  2106.                 // first get the child document
  2107.                 $child$this->getPageInfo($pid$active"parent");
  2108.                 // now return the child's parent
  2109.                 $pid($child['parent']$child['parent'0;
  2110.                 return ($pid == 0false $this->getPageInfo($pid$active$fields);
  2111.             }
  2112.     }
  2113.  
  2114.     /**
  2115.      * Returns the id of the current snippet.
  2116.      *
  2117.      * @return int
  2118.      */
  2119.     function getSnippetId() {
  2120.         if ($this->currentSnippet{
  2121.             $tbl$this->getFullTableName("site_snippets");
  2122.             $rs$this->db->query("SELECT id FROM $tbl WHERE name='" . $this->db->escape($this->currentSnippet"' LIMIT 1");
  2123.             $row$this->db->getRow($rs);
  2124.             if ($row['id'])
  2125.                 return $row['id'];
  2126.         }
  2127.         return 0;
  2128.     }
  2129.  
  2130.     /**
  2131.      * Returns the name of the current snippet.
  2132.      *
  2133.      * @return string
  2134.      */
  2135.     function getSnippetName() {
  2136.         return $this->currentSnippet;
  2137.     }
  2138.  
  2139.     /**
  2140.      * Clear the cache of MODX.
  2141.      *
  2142.      * @return boolean 
  2143.      */
  2144.     function clearCache() {
  2145.         $basepath$this->config["base_path""assets/cache";
  2146.         if ($handleopendir($basepath)) {
  2147.             $filesincache0;
  2148.             $deletedfilesincache0;
  2149.             while (false !== ($filereaddir($handle))) {
  2150.                 if ($file != "." && $file != "..") {
  2151.                     $filesincache += 1;
  2152.                     if (preg_match("/\.pageCache/", $file)) {
  2153.                         $deletedfilesincache += 1;
  2154.                         unlink($basepath . "/" . $file);
  2155.                     }
  2156.                 }
  2157.             }
  2158.             closedir($handle);
  2159.             return true;
  2160.         } else {
  2161.             return false;
  2162.         }
  2163.     }
  2164.  
  2165.     /**
  2166.      * Create an URL for the given document identifier. The url prefix and
  2167.      * postfix are used, when friendly_url is active.
  2168.      *
  2169.      * @param int $id The document identifier
  2170.      * @param string $alias The alias name for the document
  2171.      *                      Default: Empty string
  2172.      * @param string $args The paramaters to add to the URL
  2173.      *                     Default: Empty string
  2174.      * @param string $scheme With full as valus, the site url configuration is
  2175.      *                       used
  2176.      *                       Default: Empty string
  2177.      * @return string
  2178.      */
  2179.      function makeUrl($id, $alias'', $args'', $scheme'') {
  2180.         $url'';
  2181.         $virtualDir'';
  2182.         $f_url_prefix = $this->config['friendly_url_prefix'];
  2183.         $f_url_suffix $this->config['friendly_url_suffix'];
  2184.         if (!is_numeric($id)) {
  2185.             $this->messageQuit('`' $id '` is not numeric and may not be passed to makeUrl()');
  2186.         }
  2187.         if ($args != '' && $this->config['friendly_urls'== 1{
  2188.             // add ? to $args if missing
  2189.             $csubstr($args, 0, 1);
  2190.             if (strpos($f_url_prefix, '?') === false) {
  2191.                 if ($c == '&')
  2192.                     $args'?' . substr($args, 1);
  2193.                 elseif ($c != '?') $args'?' . $args;
  2194.             } else {
  2195.                 if ($c == '?')
  2196.                     $args'&' . substr($args, 1);
  2197.                 elseif ($c != '&') $args'&' . $args;
  2198.             }
  2199.         }
  2200.         elseif ($args != '') {
  2201.             // add & to $args if missing
  2202.             $csubstr($args, 0, 1);
  2203.             if ($c == '?')
  2204.                 $args'&' . substr($args, 1);
  2205.             elseif ($c != '&') $args'&' . $args;
  2206.         }
  2207.         if ($this->config['friendly_urls'== && $alias != ''{
  2208.             $url$f_url_prefix . $alias . $f_url_suffix . $args;
  2209.         }
  2210.         elseif ($this->config['friendly_urls'== && $alias == ''{
  2211.             $alias$id;
  2212.             if ($this->config['friendly_alias_urls'== 1{
  2213.                 $al$this->aliasListing[$id];
  2214.                 $alPath!empty ($al['path']$al['path''/' '';
  2215.                 if ($al && $al['alias'])
  2216.                     $alias$al['alias'];
  2217.             }
  2218.             $alias$alPath . $f_url_prefix . $alias . $f_url_suffix;
  2219.             $url$alias . $args;
  2220.         } else {
  2221.             $url'index.php?id=' . $id . $args;
  2222.         }
  2223.  
  2224.         $host$this->config['base_url'];
  2225.         // check if scheme argument has been set
  2226.         if ($scheme != ''{
  2227.             // for backward compatibility - check if the desired scheme is different than the current scheme
  2228.             if (is_numeric($scheme) && $scheme != $_SERVER['HTTPS']) {
  2229.                 $scheme($_SERVER['HTTPS'] ? 'http' : 'https');
  2230.             }
  2231.  
  2232.             // to-do: check to make sure that $site_url incudes the url :port (e.g. :8080)
  2233.             $host$scheme == 'full' ? $this->config['site_url'$scheme '://' $_SERVER['HTTP_HOST'$host;
  2234.         }
  2235.  
  2236.         if ($this->config['xhtml_urls']{
  2237.             return preg_replace("/&(?!amp;)/","&amp;", $host . $virtualDir . $url);
  2238.         } else {
  2239.             return $host . $virtualDir . $url;
  2240.         }
  2241.     }
  2242.  
  2243.     /**
  2244.      * Returns an entry from the config
  2245.      *
  2246.      * Note: most code accesses the config array directly and we will continue to support this.
  2247.      *
  2248.      * @return boolean|string
  2249.      */
  2250.     function getConfig($name'') {
  2251.         if (!empty ($this->config[$name])) {
  2252.             return $this->config[$name];
  2253.         } else {
  2254.             return false;
  2255.         }
  2256.     }
  2257.  
  2258.     /**
  2259.      * Returns the ClipperCMS version information as version, branch, release date and full application name.
  2260.      *
  2261.      * @return array
  2262.      */
  2263.     function getVersionData() {
  2264.         require_once($this->config["base_path""manager/includes/version.inc.php");
  2265.         $varray ();
  2266.         $v['version']CMS_RELEASE_VERSION;
  2267.         $v['branch']CMS_NAME;
  2268.         $v['release_date']CMS_RELEASE_DATE;
  2269.         $v['full_appname']CMS_FULL_APPNAME;
  2270.         return $v;
  2271.     }
  2272.  
  2273.     /**
  2274.      * Returns an ordered or unordered HTML list.
  2275.      *
  2276.      * @param array $array
  2277.      * @param string $ulroot Default: root
  2278.      * @param string $ulprefix Default: sub_
  2279.      * @param string $type Default: Empty string
  2280.      * @param boolean $ordered Default: false
  2281.      * @param int $tablevel Default: 0
  2282.      * @return string
  2283.      */
  2284.     function makeList($array, $ulroot'root', $ulprefix'sub_', $type'', $orderedfalse, $tablevel0) {
  2285.         // first find out whether the value passed is an array
  2286.         if (!is_array($array)) {
  2287.             return "<ul><li>Bad list</li></ul>";
  2288.         }
  2289.         if (!empty ($type)) {
  2290.             $typestr" style='list-style-type: $type'";
  2291.         } else {
  2292.             $typestr"";
  2293.         }
  2294.         $tabs"";
  2295.         for ($i0; $i < $tablevel; $i++) {
  2296.             $tabs .= "\t";
  2297.         }
  2298.         $listhtml$ordered == true ? $tabs . "<ol class='$ulroot'$typestr>\n" : $tabs . "<ul class='$ulroot'$typestr>\n";
  2299.         foreach ($array as $key => $value) {
  2300.             if (is_array($value)) {
  2301.                 $listhtml .= $tabs . "\t<li>" . $key . "\n" . $this->makeList($value$ulprefix $ulroot$ulprefix$type$ordered$tablevel +2$tabs "\t</li>\n";
  2302.             } else {
  2303.                 $listhtml .= $tabs . "\t<li>" . $value . "</li>\n";
  2304.             }
  2305.         }
  2306.         $listhtml .= $ordered == true ? $tabs . "</ol>\n" : $tabs . "</ul>\n";
  2307.         return $listhtml;
  2308.     }
  2309.  
  2310.     /**
  2311.      * Returns user login information, as loggedIn (true or false), internal key, username and usertype (web or manager).
  2312.      *
  2313.      * @return boolean|array
  2314.      */
  2315.     function userLoggedIn() {
  2316.         $userdetailsarray ();
  2317.         if ($this->isFrontend(&& isset ($_SESSION['webValidated'])) {
  2318.             // web user
  2319.             $userdetails['loggedIn']true;
  2320.             $userdetails['id']$_SESSION['webInternalKey'];
  2321.             $userdetails['username']$_SESSION['webShortname'];
  2322.             $userdetails['usertype']'web'; // added by Raymond
  2323.             return $userdetails;
  2324.         } else
  2325.             if ($this->isBackend(&& isset ($_SESSION['mgrValidated'])) {
  2326.                 // manager user
  2327.                 $userdetails['loggedIn']true;
  2328.                 $userdetails['id']$_SESSION['mgrInternalKey'];
  2329.                 $userdetails['username']$_SESSION['mgrShortname'];
  2330.                 $userdetails['usertype']'manager'; // added by Raymond
  2331.                 return $userdetails;
  2332.             } else {
  2333.                 return false;
  2334.             }
  2335.     }
  2336.  
  2337.     /**
  2338.      * Returns an array with keywords for the current document, or a document with a given docid
  2339.      *
  2340.      * @param int $id The docid, 0 means the current document
  2341.      *                Default: 0
  2342.      * @return array
  2343.      * @deprecated
  2344.      */
  2345.     function getKeywords($id0) {
  2346.         //content removed as this function is deprecated
  2347.         $keywordsarray ();
  2348.         return $keywords;
  2349.     }
  2350.  
  2351.     /**
  2352.      * Returns an array with meta tags for the current document, or a document with a given docid.
  2353.      *
  2354.      * @param int $id The document identifier, 0 means the current document
  2355.      *                Default: 0
  2356.      * @return array
  2357.      * @deprecated
  2358.      */
  2359.     function getMETATags($id0) {
  2360.         //content removed as this function is deprecated
  2361.         $metatagsarray ();
  2362.         return $metatags;
  2363.     }
  2364.  
  2365.     /**
  2366.      * Executes a snippet.
  2367.      *
  2368.      * @param string $snippetName
  2369.      * @param array $params Default: Empty array
  2370.      * @return string
  2371.      */
  2372.     function runSnippet($snippetName, $paramsarray ()) {
  2373.  
  2374.         if (isset($this->snippetMap[strtolower($snippetName)])) {
  2375.              $snippetName = $this->snippetMap[strtolower($snippetName)];
  2376.         }
  2377.  
  2378.         if (isset ($this->snippetCache[$snippetName])) {
  2379.             $snippet$this->snippetCache[$snippetName];
  2380.             $properties$this->snippetCache[$snippetName "Props"];
  2381.         } else { // not in cache so let's check the db
  2382.             $sql"SELECT `name`, `snippet`, `properties` FROM " . $this->getFullTableName("site_snippets"" WHERE " $this->getFullTableName("site_snippets"".`name`='" $this->db->escape($snippetName"';";
  2383.             $result$this->db->query($sql);
  2384.             if ($this->db->getRecordCount($result== 1{
  2385.                 $row$this->db->getRow($result);
  2386.                 $snippet$this->snippetCache[$row['name']]$row['snippet'];
  2387.                 $properties$this->snippetCache[$row['name'"Props"]$row['properties'];
  2388.             } else {
  2389.                 $snippet$this->snippetCache[$snippetName]null;
  2390.                 $properties'';
  2391.             }
  2392.         }
  2393.         // load default params/properties
  2394.         $parameters$this->parseProperties($properties);
  2395.         $parametersarray_merge($parameters$params);
  2396.         // run snippet
  2397.         return $this->evalSnippet($snippet$parameters$snippetName);
  2398.     }
  2399.  
  2400.     /**
  2401.      * Returns the chunk content for the given chunk name
  2402.      * 
  2403.      * @param string $chunkName
  2404.      * @return boolean|string
  2405.      */
  2406.    function getChunk($chunkName) {
  2407.         $t$this->chunkCache[$chunkName];
  2408.         return $t;
  2409.     }
  2410.  
  2411.     /**
  2412.      * Old method that just calls getChunk()
  2413.      * 
  2414.      * @deprecated Use getChunk
  2415.      * @param string $chunkName
  2416.      * @return boolean|string
  2417.      */
  2418.     function putChunk($chunkName) { // alias name >.<
  2419.         return $this->getChunk($chunkName);
  2420.     }
  2421.  
  2422.     /**
  2423.      * Parse a chunk for placeholders
  2424.      *
  2425.      * @param string $chunkname Name of chunk to get from db
  2426.      * @param string $chunkArr Array of placeholder names (array keys) and replacements (array values)
  2427.      * @param string $prefix Placeholder prefix. Defaults to [+
  2428.      * @param string $suffix Placeholder suffix. Defaults to +]
  2429.      * @return string
  2430.      */
  2431.     function parseChunk($chunkName, $chunkArr, $prefix"[+", $suffix"+]") {
  2432.         if (!is_array($chunkArr)) {
  2433.             return false;
  2434.         }
  2435.         $chunk$this->getChunk($chunkName);
  2436.         foreach ($chunkArr as $key => $value{
  2437.             $chunkstr_replace($prefix . $key . $suffix, $value, $chunk);
  2438.         }
  2439.         return $chunk;
  2440.     }
  2441.  
  2442.     /**
  2443.      * Get data from phpSniff
  2444.      *
  2445.      * @category API-Function
  2446.      * @return array
  2447.      */
  2448.     function getUserData() {
  2449.         include $this->config["base_path""manager/includes/extenders/getUserData.extender.php";
  2450.         return $tmpArray;
  2451.     }
  2452.  
  2453.     /**
  2454.      * Returns the timestamp in the date format defined in $this->config['date_format']
  2455.      *
  2456.      * @param int $timestamp Default: 0
  2457.      * @param string $mode Default: Empty string (adds the time as below). Can also be 'dateOnly' for no time or 'formatOnly' to get the date_format string.
  2458.      * @return string
  2459.      */
  2460.     function toDateFormat($timestamp = 0, $mode = '') {
  2461.         $timestamp = trim($timestamp);
  2462.         $timestamp = intval($timestamp);
  2463.  
  2464.         switch($this->config['date_format']{
  2465.             case 'dd-mm-yy':
  2466.                 $dateFormat = '%d-%m-%Y';
  2467.                 break;
  2468.             case 'mm/dd/yy':
  2469.                 $dateFormat = '%m/%d/%Y';
  2470.                 break;
  2471.             case 'yy/mm/dd':
  2472.                 $dateFormat = '%Y/%m/%d';
  2473.                 break;
  2474.         }
  2475.         
  2476.         switch($this->config['time_format']{
  2477.             case 'HH:mm:ss':
  2478.                 $timeFormat = '%H:%M:%S';
  2479.                 break;
  2480.         }
  2481.  
  2482.         if (empty($mode)) {
  2483.             $strTime = strftime($dateFormat . " " . $timeFormat, $timestamp);
  2484.         } elseif ($mode == 'dateOnly') {
  2485.             $strTime = strftime($dateFormat, $timestamp);
  2486.         } elseif ($mode == 'formatOnly') {
  2487.             $strTime = $dateFormat;
  2488.         }
  2489.         return $strTime;
  2490.     }
  2491.  
  2492.     /**
  2493.      * Make a timestamp from a string corresponding to the format in $this->config['date_format']
  2494.      *
  2495.      * @param string $str
  2496.      * @return string
  2497.      */
  2498.     function toTimeStamp($str) {
  2499.         $str = trim($str);
  2500.         if (empty($str)) {return '';}
  2501.  
  2502.         switch($this->config['date_format']{
  2503.             case 'dd-mm-yy':
  2504.                 if (!preg_match('/^[0-9]{2}-[0-9]{2}-[0-9]{4}[0-9 :]*$/', $str)) {return '';}
  2505.                 list ($d, $m, $Y, $H, $M, $S) = sscanf($str, '%2d-%2d-%4d %2d:%2d:%2d');
  2506.                 break;
  2507.             case 'mm/dd/yy':
  2508.                 if (!preg_match('/^[0-9]{2}\/[0-9]{2}\/[0-9]{4}[0-9 :]*$/', $str)) {return '';}
  2509.                 list ($m, $d, $Y, $H, $M, $S) = sscanf($str, '%2d/%2d/%4d %2d:%2d:%2d');
  2510.                 break;
  2511.             case 'yy/mm/dd':
  2512.                 if (!preg_match('/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}[0-9 :]*$/', $str)) {return '';}
  2513.                 list ($Y, $m, $d, $H, $M, $S) = sscanf($str, '%4d/%2d/%2d %2d:%2d:%2d');
  2514.                 break;
  2515.         }
  2516.         
  2517.         if (!$H && !$M && !$S) {$H = 0; $M = 0; $S = 0;}
  2518.         $timeStamp = mktime($H, $M, $S, $m, $d, $Y);
  2519.         $timeStamp = intval($timeStamp);
  2520.         return $timeStamp;
  2521.     }
  2522.  
  2523.     /**
  2524.      * Get the TVs of a document's children. Returns an array where each element represents one child doc.
  2525.      *
  2526.      * Ignores deleted children. Gets all children - there is no where clause available.
  2527.      *
  2528.      * @param int $parentid The parent docid
  2529.      *                 Default: 0 (site root)
  2530.      * @param array $tvidnames. Which TVs to fetch - Can relate to the TV ids in the db (array elements should be numeric only)
  2531.      *                                               or the TV names (array elements should be names only)
  2532.      *                      Default: Empty array
  2533.      * @param int $published Whether published or unpublished documents are in the result
  2534.      *                      Default: 1
  2535.      * @param string $docsort How to sort the result array (field)
  2536.      *                      Default: menuindex
  2537.      * @param ASC $docsortdir How to sort the result array (direction)
  2538.      *                      Default: ASC
  2539.      * @param string $tvfields Fields to fetch from site_tmplvars, default '*'
  2540.      *                      Default: *
  2541.      * @param string $tvsort How to sort each element of the result array i.e. how to sort the TVs (field)
  2542.      *                      Default: rank
  2543.      * @param string  $tvsortdir How to sort each element of the result array i.e. how to sort the TVs (direction)
  2544.      *                      Default: ASC
  2545.      * @return boolean|array
  2546.      */
  2547.     function getDocumentChildrenTVars($parentid0, $tvidnamesarray (), $published1, $docsort"menuindex", $docsortdir"ASC", $tvfields"*", $tvsort"rank", $tvsortdir"ASC") {
  2548.         $docs$this->getDocumentChildren($parentid$published0'*'''$docsort$docsortdir);
  2549.         if (!$docs)
  2550.             return false;
  2551.         else {
  2552.             $resultarray ();
  2553.             // get user defined template variables
  2554.             $fields($tvfields == "") ? "tv.*" : 'tv.' . implode(',tv.', preg_replace("/^\s/i", "", explode(',', $tvfields)));
  2555.             $tvsort($tvsort == "") ? "" : 'tv.' . implode(',tv.', preg_replace("/^\s/i", "", explode(',', $tvsort)));
  2556.             if ($tvidnames == "*")
  2557.                 $query"tv.id<>0";
  2558.             else
  2559.                 $query(is_numeric($tvidnames[0]) ? "tv.id" : "tv.name") . " IN ('" . implode("','", $tvidnames) . "')";
  2560.             if ($docgrp$this->getUserDocGroups())
  2561.                 $docgrpimplode(","$docgrp);
  2562.  
  2563.             $docCountcount($docs);
  2564.             for ($i0$i $docCount$i++{
  2565.  
  2566.                 $tvsarray ();
  2567.                 $docRow$docs[$i];
  2568.                 $docid$docRow['id'];
  2569.  
  2570.                 $sql"SELECT $fields, IF(tvc.value!='',tvc.value,tv.default_text) as value ";
  2571.                 $sql .= "FROM " . $this->getFullTableName('site_tmplvars'" tv ";
  2572.                 $sql .= "INNER JOIN " $this->getFullTableName('site_tmplvar_templates')." tvtpl ON tvtpl.tmplvarid = tv.id ";
  2573.                 $sql .= "LEFT JOIN " $this->getFullTableName('site_tmplvar_contentvalues')." tvc ON tvc.tmplvarid=tv.id AND tvc.contentid = '" $docid "' ";
  2574.                 $sql .= "WHERE " $query " AND tvtpl.templateid = " $docRow['template'];
  2575.                 if ($tvsort)
  2576.                     $sql .= " ORDER BY $tvsort $tvsortdir ";
  2577.                 $rs$this->db->query($sql);
  2578.                 $limit$this->db->getRecordCount($rs);
  2579.                 for ($x0$x $limit$x++{
  2580.                     array_push($tvs, @ $this->db->getRow($rs));
  2581.                 }
  2582.  
  2583.                 // get default/built-in template variables
  2584.                 ksort($docRow);
  2585.                 foreach ($docRow as $key => $value) {
  2586.                     if ($tvidnames == "*" || in_array($key, $tvidnames))
  2587.                         array_push($tvs, array (
  2588.                             "name" => $key,
  2589.                             "value" => $value
  2590.                         ));
  2591.                 }
  2592.  
  2593.                 if (count($tvs))
  2594.                     array_push($result, $tvs);
  2595.             }
  2596.             return $result;
  2597.         }
  2598.     }
  2599.  
  2600.     /**
  2601.      * Get the TV outputs of a document's children.
  2602.      * 
  2603.      * Returns an array where each element represents one child doc and contains the result from getTemplateVarOutput()
  2604.      *
  2605.      * Ignores deleted children. Gets all children - there is no where clause available.
  2606.      *
  2607.      * @param int $parentid The parent docid
  2608.      *                        Default: 0 (site root)
  2609.      * @param array $tvidnames. Which TVs to fetch. In the form expected by getTemplateVarOutput().
  2610.      *                        Default: Empty array
  2611.      * @param int $published Whether published or unpublished documents are in the result
  2612.      *                        Default: 1
  2613.      * @param string $docsort How to sort the result array (field)
  2614.      *                        Default: menuindex
  2615.      * @param ASC $docsortdir How to sort the result array (direction)
  2616.      *                        Default: ASC
  2617.      * @return boolean|array
  2618.      */
  2619.     function getDocumentChildrenTVarOutput($parentid0, $tvidnamesarray (), $published1, $docsort"menuindex", $docsortdir"ASC") {
  2620.         $docs$this->getDocumentChildren($parentid$published0'*'''$docsort$docsortdir);
  2621.         if (!$docs)
  2622.             return false;
  2623.         else {
  2624.             $resultarray ();
  2625.             for ($i0; $i < count($docs); $i++) {
  2626.                 $tvs$this->getTemplateVarOutput($tvidnames$docs[$i]["id"]$published);
  2627.                 if ($tvs)
  2628.                     $result[$docs[$i]['id']]$tvs// Use docid as key - netnoise 2006/08/14
  2629.             }
  2630.             return $result;
  2631.         }
  2632.     }
  2633.  
  2634.     /**
  2635.      * Modified by Raymond for TV - Orig Modified by Apodigm - DocVars
  2636.      * Returns a single site_content field or TV record from the db.
  2637.      *
  2638.      * If a site content field the result is an associative array of 'name' and 'value'.
  2639.      *
  2640.      * If a TV the result is an array representing a db row including the fields specified in $fields.
  2641.      *
  2642.      * @param string $idname Can be a TV id or name
  2643.      * @param string $fields Fields to fetch from site_tmplvars. Default: *
  2644.      * @param type $docid Docid. Defaults to empty string which indicates the current document.
  2645.      * @param int $published Whether published or unpublished documents are in the result
  2646.      *                        Default: 1
  2647.      * @return boolean
  2648.      */
  2649.     function getTemplateVar($idname"", $fields"*", $docid"", $published1) {
  2650.         if ($idname == "") {
  2651.             return false;
  2652.         } else {
  2653.             $result$this->getTemplateVars(array ($idname)$fields$docid$published"""")//remove sorting for speed
  2654.             return ($result != false$result[0false;
  2655.         }
  2656.     }
  2657.  
  2658.     /**
  2659.      * Returns an array of site_content field fields and/or TV records from the db
  2660.      *
  2661.      * Elements representing a site content field consist of an associative array of 'name' and 'value'.
  2662.      *
  2663.      * Elements representing a TV consist of an array representing a db row including the fields specified in $fields.
  2664.      *
  2665.      * @param array $idnames Which TVs to fetch - Can relate to the TV ids in the db (array elements should be numeric only)
  2666.      *                                               or the TV names (array elements should be names only)
  2667.      *                        Default: Empty array
  2668.      * @param string $fields Fields to fetch from site_tmplvars.
  2669.      *                        Default: *
  2670.      * @param string $docid Docid. Defaults to empty string which indicates the current document.
  2671.      * @param int $published Whether published or unpublished documents are in the result
  2672.      *                        Default: 1
  2673.      * @param string $sort How to sort the result array (field)
  2674.      *                        Default: rank
  2675.      * @param string $dir How to sort the result array (direction)
  2676.      *                        Default: ASC
  2677.      * @return boolean|array
  2678.      */
  2679.     function getTemplateVars($idnamesarray (), $fields"*", $docid"", $published1, $sort"rank", $dir"ASC") {
  2680.         if (($idnames != '*' && !is_array($idnames)) || count($idnames) == 0) {
  2681.             return false;
  2682.         } else {
  2683.             $resultarray ();
  2684.  
  2685.             // get document record
  2686.             if ($docid == "") {
  2687.                 $docid$this->documentIdentifier;
  2688.                 $docRow$this->documentObject;
  2689.             } else {
  2690.                 $docRow$this->getDocument($docid'*'$published);
  2691.                 if (!$docRow)
  2692.                     return false;
  2693.             }
  2694.  
  2695.             // Get TVs
  2696.             $fields($fields == "") ? "tv.*" : 'tv.' . implode(',tv.', preg_replace("/^\s/i", "", explode(',', $fields)));
  2697.             $sort($sort == "") ? "" : 'tv.' . implode(',tv.', preg_replace("/^\s/i", "", explode(',', $sort)));
  2698.             if ($idnames == "*")
  2699.                 $query"tv.id<>0";
  2700.             else
  2701.                 $query(is_numeric($idnames[0]) ? "tv.id" : "tv.name") . " IN ('" . implode("','", $idnames) . "')";
  2702.  
  2703.             $sql"SELECT $fields, IF(tvc.value!='',tvc.value,tv.default_text) as value ";
  2704.             $sql .= "FROM " . $this->getFullTableName('site_tmplvars')." tv ";
  2705.             $sql .= "INNER JOIN " $this->getFullTableName('site_tmplvar_templates')." tvtpl ON tvtpl.tmplvarid = tv.id ";
  2706.             $sql .= "LEFT JOIN " $this->getFullTableName('site_tmplvar_contentvalues')." tvc ON tvc.tmplvarid=tv.id AND tvc.contentid = '" $docid "' ";
  2707.             $sql .= "WHERE " $query " AND tvtpl.templateid = " $docRow['template'];
  2708.             if ($sort)
  2709.                 $sql .= " ORDER BY $sort $dir ";
  2710.             $rs$this->db->query($sql);
  2711.             for ($i0$i $this->db->getRecordCount($rs)$i++{
  2712.                 array_push($result, @ $this->db->getRow($rs));
  2713.             }
  2714.  
  2715.             // Get fields from site_content
  2716.             ksort($docRow);
  2717.             foreach ($docRow as $key => $value) {
  2718.                 if ($idnames == "*" || in_array($key, $idnames))
  2719.                     array_push($result, array (
  2720.                         "name" => $key,
  2721.                         "value" => $value
  2722.                     ));
  2723.             }
  2724.  
  2725.             return $result;
  2726.         }
  2727.     }
  2728.  
  2729.     /**
  2730.      * Returns an associative array containing TV rendered output values.
  2731.      *
  2732.      * @param type $idnames Which TVs to fetch - Can relate to the TV ids in the db (array elements should be numeric only)
  2733.      *                                               or the TV names (array elements should be names only)
  2734.      *                        Default: Empty array
  2735.      * @param string $docid Docid. Defaults to empty string which indicates the current document.
  2736.      * @param int $published Whether published or unpublished documents are in the result
  2737.      *                        Default: 1
  2738.      * @param string $sep
  2739.      * @return boolean|array
  2740.      */
  2741.     function getTemplateVarOutput($idnamesarray (), $docid"", $published1, $sep='') {
  2742.         if (count($idnames) == 0) {
  2743.             return false;
  2744.         } else {
  2745.             $outputarray ();
  2746.             $vars($idnames == '*' || is_array($idnames)) ? $idnames : array ($idnames);
  2747.             $docidintval($docid) ? intval($docid) : $this->documentIdentifier;
  2748.             $result$this->getTemplateVars($vars"*"$docid$published""""$sep)// remove sort for speed
  2749.             if ($result == false)
  2750.                 return false;
  2751.             else {
  2752.         $baspath$this->config["base_path""manager/includes";
  2753.         include_once $baspath "/tmplvars.format.inc.php";
  2754.         include_once $baspath "/tmplvars.commands.inc.php";
  2755.         for ($i0$i count($result)$i++{
  2756.             $row$result[$i];
  2757.             if (!$row['id'])
  2758.                 $output[$row['name']]$row['value'];
  2759.             else    $output[$row['name']]getTVDisplayFormat($row['name'], $row['value'], $row['display'], $row['display_params'], $row['type'], $docid, $sep);
  2760.         }
  2761.         return $output;
  2762.             }
  2763.         }
  2764.     }
  2765.  
  2766.     /**
  2767.      * Returns the placeholder value
  2768.      *
  2769.      * @param string $name Placeholder name
  2770.      * @return string Placeholder value
  2771.      */
  2772.     function getPlaceholder($name) {
  2773.         return $this->placeholders[$name];
  2774.     }
  2775.  
  2776.     /**
  2777.      * Sets a value for a placeholder
  2778.      *
  2779.      * @param string $name The name of the placeholder
  2780.      * @param string $value The value of the placeholder
  2781.      */
  2782.     function setPlaceholder($name, $value) {
  2783.         $this->placeholders[$name]$value;
  2784.     }
  2785.  
  2786.     /**
  2787.      * Set placeholders en masse via an array or object.
  2788.      *
  2789.      * @param object|array $subject
  2790.      * @param string $prefix
  2791.      */
  2792.     function toPlaceholders($subject, $prefix'') {
  2793.         if (is_object($subject)) {
  2794.             $subjectget_object_vars($subject);
  2795.         }
  2796.         if (is_array($subject)) {
  2797.             foreach ($subject as $key => $value) {
  2798.                 $this->toPlaceholder($key$value$prefix);
  2799.             }
  2800.         }
  2801.     }
  2802.  
  2803.     /**
  2804.      * For use by toPlaceholders(); For setting an array or object element as placeholder.
  2805.      *
  2806.      * @param string $key
  2807.      * @param object|array $value
  2808.      * @param string $prefix
  2809.      */
  2810.     function toPlaceholder($key, $value, $prefix'') {
  2811.         if (is_array($value) || is_object($value)) {
  2812.             $this->toPlaceholders($value"{$prefix}{$key}.");
  2813.         } else {
  2814.             $this->setPlaceholder("{$prefix}{$key}", $value);
  2815.         }
  2816.     }
  2817.  
  2818.     /**
  2819.      * Returns the manager relative URL/path with respect to the site root.
  2820.      *
  2821.      * @return string The complete URL to the manager folder
  2822.      */
  2823.     function getManagerPath() {
  2824.         global $base_url;
  2825.         $pth$base_url . 'manager/';
  2826.         return $pth;
  2827.     }
  2828.  
  2829.     /**
  2830.      * Returns the cache relative URL/path with respect to the site root.
  2831.      *
  2832.      * @return string The complete URL to the cache folder
  2833.      */
  2834.     function getCachePath() {
  2835.         global $base_url;
  2836.         $pth$base_url . 'assets/cache/';
  2837.         return $pth;
  2838.     }
  2839.  
  2840.     /**
  2841.      * Sends a message to a user's message box.
  2842.      *
  2843.      * @param string $type Type of the message
  2844.      * @param string $to The recipient of the message
  2845.      * @param string $from The sender of the message
  2846.      * @param string $subject The subject of the message
  2847.      * @param string $msg The message body
  2848.      * @param int $private Whether it is a private message, or not
  2849.      *                     Default : 0
  2850.      */
  2851.     function sendAlert($type, $to, $from, $subject, $msg, $private0) {
  2852.         $private($private) ? 1 : 0;
  2853.         if (!is_numeric($to)) {
  2854.             // Query for the To ID
  2855.             $sql"SELECT id FROM " . $this->getFullTableName("manager_users"" WHERE username='$to';";
  2856.             $rs$this->db->query($sql);
  2857.             if ($this->db->getRecordCount($rs)) {
  2858.                 $rs$this->db->getRow($rs);
  2859.                 $to$rs['id'];
  2860.             }
  2861.         }
  2862.         if (!is_numeric($from)) {
  2863.             // Query for the From ID
  2864.             $sql"SELECT id FROM " . $this->getFullTableName("manager_users"" WHERE username='$from';";
  2865.             $rs$this->db->query($sql);
  2866.             if ($this->db->getRecordCount($rs)) {
  2867.                 $rs$this->db->getRow($rs);
  2868.                 $from$rs['id'];
  2869.             }
  2870.         }
  2871.         // insert a new message into user_messages
  2872.         $sql"INSERT INTO " . $this->getFullTableName("user_messages"" ( id , type , subject , message , sender , recipient , private , postdate , messageread ) VALUES ( '', '$type', '$subject', '$msg', '$from', '$to', '$private', '" . time() . "', '0' );";
  2873.         $rs$this->db->query($sql);
  2874.     }
  2875.  
  2876.     /**
  2877.      * Returns true, install or interact when inside manager.
  2878.      *
  2879.      * @deprecated
  2880.      * @return string
  2881.      */
  2882.     function insideManager() {
  2883.         $mfalse;
  2884.         if (defined('IN_MANAGER_MODE') && IN_MANAGER_MODE == 'true') {
  2885.             $mtrue;
  2886.             if (defined('SNIPPET_INTERACTIVE_MODE') && SNIPPET_INTERACTIVE_MODE == 'true')
  2887.                 $m"interact";
  2888.             else
  2889.                 if (defined('SNIPPET_INSTALL_MODE') && SNIPPET_INSTALL_MODE == 'true')
  2890.                     $m"install";
  2891.         }
  2892.         return $m;
  2893.     }
  2894.  
  2895.     /**
  2896.      * Returns current user id.
  2897.      *
  2898.      * @param string $context. Default is an empty string which indicates the method should automatically pick 'web (frontend) or 'mgr' (backend)
  2899.      * @return string
  2900.      */
  2901.     function getLoginUserID($context'') {
  2902.         if ($context && isset ($_SESSION[$context . 'Validated'])) {
  2903.             return $_SESSION[$context . 'InternalKey'];
  2904.         }
  2905.         elseif ($this->isFrontend(&& isset ($_SESSION['webValidated'])) {
  2906.             return $_SESSION['webInternalKey'];
  2907.         }
  2908.         elseif ($this->isBackend(&& isset ($_SESSION['mgrValidated'])) {
  2909.             return $_SESSION['mgrInternalKey'];
  2910.         }
  2911.     }
  2912.  
  2913.     /**
  2914.      * Returns current user name
  2915.      *
  2916.      * @param string $context. Default is an empty string which indicates the method should automatically pick 'web (frontend) or 'mgr' (backend)
  2917.      * @return string
  2918.      */
  2919.     function getLoginUserName($context'') {
  2920.         if (!empty($context) && isset ($_SESSION[$context . 'Validated'])) {
  2921.             return $_SESSION[$context . 'Shortname'];
  2922.         }
  2923.         elseif ($this->isFrontend(&& isset ($_SESSION['webValidated'])) {
  2924.             return $_SESSION['webShortname'];
  2925.         }
  2926.         elseif ($this->isBackend(&& isset ($_SESSION['mgrValidated'])) {
  2927.             return $_SESSION['mgrShortname'];
  2928.         }
  2929.     }
  2930.  
  2931.     /**
  2932.      * Returns current login user type - web or manager
  2933.      *
  2934.      * @return string
  2935.      */
  2936.     function getLoginUserType() {
  2937.         if ($this->isFrontend(&& isset ($_SESSION['webValidated'])) {
  2938.             return 'web';
  2939.         }
  2940.         elseif ($this->isBackend(&& isset ($_SESSION['mgrValidated'])) {
  2941.             return 'manager';
  2942.         } else {
  2943.             return '';
  2944.         }
  2945.     }
  2946.  
  2947.     /**
  2948.      * Returns a user info record for the given manager user
  2949.      *
  2950.      * @param int $uid
  2951.      * @return boolean|string
  2952.      */
  2953.     function getUserInfo($uid) {
  2954.         $sql"
  2955.               SELECT mu.username, mu.password, mua.*
  2956.               FROM " . $this->getFullTableName("manager_users"" mu
  2957.               INNER JOIN " $this->getFullTableName("user_attributes"" mua ON mua.internalkey=mu.id
  2958.               WHERE mu.id = '$uid'
  2959.               ";
  2960.         $rs$this->db->query($sql);
  2961.         $limit$this->db->getRecordCount($rs);
  2962.         if ($limit == 1{
  2963.             $row$this->db->getRow($rs);
  2964.             if (!$row["usertype"])
  2965.                 $row["usertype"]"manager";
  2966.             return $row;
  2967.         }
  2968.     }
  2969.  
  2970.     /**
  2971.      * Returns a record for the web user
  2972.      *
  2973.      * @param int $uid
  2974.      * @return boolean|string
  2975.      */
  2976.     function getWebUserInfo($uid) {
  2977.         $sql"
  2978.               SELECT wu.username, wu.password, wua.*
  2979.               FROM " . $this->getFullTableName("web_users"" wu
  2980.               INNER JOIN " $this->getFullTableName("web_user_attributes"" wua ON wua.internalkey=wu.id
  2981.               WHERE wu.id='$uid'
  2982.               ";
  2983.         $rs$this->db->query($sql);
  2984.         $limit$this->db->getRecordCount($rs);
  2985.         if ($limit == 1{
  2986.             $row$this->db->getRow($rs);
  2987.             if (!$row["usertype"])
  2988.                 $row["usertype"]"web";
  2989.             return $row;
  2990.         }
  2991.     }
  2992.  
  2993.     /**
  2994.      * Returns an array of document groups that current user is assigned to.
  2995.      * This function will first return the web user doc groups when running from
  2996.      * frontend otherwise it will return manager user's docgroup.
  2997.      *
  2998.      * @param boolean $resolveIds Set to true to return the document group names
  2999.      *                            Default: false
  3000.      * @return string|array
  3001.      */
  3002.      function getUserDocGroups($resolveIdsfalse) {
  3003.         if ($this->isFrontend(&& isset ($_SESSION['webDocgroups']&& isset ($_SESSION['webValidated'])) {
  3004.             $dg$_SESSION['webDocgroups'];
  3005.             $dgn= isset ($_SESSION['webDocgrpNames']) ? $_SESSION['webDocgrpNames'] : false;
  3006.         } else
  3007.             if ($this->isBackend(&& isset ($_SESSION['mgrDocgroups']&& isset ($_SESSION['mgrValidated'])) {
  3008.                 $dg$_SESSION['mgrDocgroups'];
  3009.                 $dgn$_SESSION['mgrDocgrpNames'];
  3010.             } else {
  3011.                 $dg'';
  3012.             }
  3013.         if (!$resolveIds)
  3014.             return $dg;
  3015.         else
  3016.             if (is_array($dgn))
  3017.                 return $dgn;
  3018.             else
  3019.                 if (is_array($dg)) {
  3020.                     // resolve ids to names
  3021.                     $dgnarray ();
  3022.                     $tbl$this->getFullTableName("documentgroup_names");
  3023.                     $ds$this->db->query("SELECT name FROM $tbl WHERE id IN (" . implode(",", $dg) . ")");
  3024.                     while ($row$this->db->getRow($ds))
  3025.                         $dgn[count($dgn)]$row['name'];
  3026.                     // cache docgroup names to session
  3027.                     if ($this->isFrontend())
  3028.                         $_SESSION['webDocgrpNames']$dgn;
  3029.                     else
  3030.                         $_SESSION['mgrDocgrpNames']$dgn;
  3031.                     return $dgn;
  3032.                 }
  3033.     }
  3034.  
  3035.     /**
  3036.      * Returns an array of document groups that current user is assigned to.
  3037.      * This function will first return the web user doc groups when running from
  3038.      * frontend otherwise it will return manager user's docgroup.
  3039.      *
  3040.      * @deprecated
  3041.      * @return string|array
  3042.      */
  3043.     function getDocGroups() {
  3044.         return $this->getUserDocGroups();
  3045.     } // deprecated
  3046.     /**
  3047.      * Change current web user's password
  3048.      *
  3049.      * @todo Make password length configurable, allow rules for passwords and translation of messages
  3050.      * @param string $oldPwd
  3051.      * @param string $newPwd
  3052.      * @return string|boolean Returns true if successful, oterhwise return error
  3053.      *                        message
  3054.      */
  3055.     function changeWebUserPassword($oldPwd, $newPwd) {
  3056.         $rtfalse;
  3057.         if ($_SESSION["webValidated"] == 1) {
  3058.             $tbl$this->getFullTableName("web_users");
  3059.             $ds$this->db->query("SELECT `id`, `username`, `password` FROM $tbl WHERE `id`='" . $this->getLoginUserID("'");
  3060.             $limit$this->db->getRecordCount($ds);
  3061.             if ($limit == 1{
  3062.                 $row$this->db->getRow($ds);
  3063.                 if ($row["password"== md5($oldPwd)) {
  3064.                     if (strlen($newPwd) < 6) {
  3065.                         return "Password is too short!";
  3066.                     }
  3067.                     elseif ($newPwd == "") {
  3068.                         return "You didn't specify a password for this user!";
  3069.                     } else {
  3070.                         $this->db->query("UPDATE $tbl SET password = md5('" . $this->db->escape($newPwd"') WHERE id='" $this->getLoginUserID("'");
  3071.                         // invoke OnWebChangePassword event
  3072.                         $this->invokeEvent("OnWebChangePassword"array (
  3073.                             "userid" => $row["id"],
  3074.                             "username" => $row["username"],
  3075.                             "userpassword" => $newPwd
  3076.                         ));
  3077.                         return true;
  3078.                     }
  3079.                 } else {
  3080.                     return "Incorrect password.";
  3081.                 }
  3082.             }
  3083.         }
  3084.     }
  3085.  
  3086.     /**
  3087.      * Change current web user's password
  3088.      *
  3089.      * @deprecated
  3090.      * @param string $o
  3091.      * @param string $n
  3092.      * @return string|boolean
  3093.      */
  3094.     function changePassword($o, $n) {
  3095.         return changeWebUserPassword($o, $n);
  3096.     } // deprecated
  3097.     /**
  3098.      * Returns true if the current web user is a member the specified groups
  3099.      *
  3100.      * @param array $groupNames
  3101.      * @return boolean
  3102.      */
  3103.     function isMemberOfWebGroup($groupNamesarray ()) {
  3104.         if (!is_array($groupNames))
  3105.             return false;
  3106.         // check cache
  3107.         $grpNames= isset ($_SESSION['webUserGroupNames']) ? $_SESSION['webUserGroupNames'] : false;
  3108.         if (!is_array($grpNames)) {
  3109.             $tbl$this->getFullTableName("webgroup_names");
  3110.             $tbl2$this->getFullTableName("web_groups");
  3111.             $sql"SELECT wgn.name
  3112.                     FROM $tbl wgn
  3113.                     INNER JOIN $tbl2 wg ON wg.webgroup=wgn.id AND wg.webuser='" . $this->getLoginUserID("'";
  3114.             $grpNames$this->db->getColumn("name"$sql);
  3115.             // save to cache
  3116.             $_SESSION['webUserGroupNames']$grpNames;
  3117.         }
  3118.         foreach ($groupNames as $k => $v)
  3119.             if (in_array(trim($v), $grpNames))
  3120.                 return true;
  3121.         return false;
  3122.     }
  3123.  
  3124.     /**
  3125.      * Registers Client-side CSS scripts - these scripts are loaded at inside
  3126.      * the <head> tag
  3127.      *
  3128.      * @param string $src
  3129.      * @param string $media Default: Empty string
  3130.      */
  3131.     function regClientCSS($src, $media='') {
  3132.         if (empty($src) || isset ($this->loadedjscripts[$src]))
  3133.             return '';
  3134.         $nextposmax(array_merge(array(0),array_keys($this->sjscripts)))+1;
  3135.         $this->loadedjscripts[$src]['startup']true;
  3136.         $this->loadedjscripts[$src]['version']'0';
  3137.         $this->loadedjscripts[$src]['pos']$nextpos;
  3138.         if (strpos(strtolower($src)"<style"!== false || strpos(strtolower($src)"<link"!== false{
  3139.             $this->sjscripts[$nextpos]$src;
  3140.         } else {
  3141.             $this->sjscripts[$nextpos]"\t" '<link rel="stylesheet" type="text/css" href="'.$src.'" '.($media 'media="'.$media.'" ' '').'/>';
  3142.         }
  3143.     }
  3144.  
  3145.     /**
  3146.      * Registers Startup Client-side JavaScript - these scripts are loaded at inside the <head> tag
  3147.      *
  3148.      * @param string $src
  3149.      * @param array $options Default: 'name'=>'', 'version'=>'0', 'plaintext'=>false
  3150.      */
  3151.     function regClientStartupScript($src, $optionsarray('name'=>'', 'version'=>'0', 'plaintext'=>false)) {
  3152.         $this->regClientScript($src$optionstrue);
  3153.     }
  3154.  
  3155.     /**
  3156.      * Registers Client-side JavaScript these scripts are loaded at the end of the page unless $startup is true
  3157.      *
  3158.      * @param string $src
  3159.      * @param array $options Default: 'name'=>'', 'version'=>'0', 'plaintext'=>false
  3160.      * @param boolean $startup Default: false
  3161.      * @return string
  3162.      */
  3163.     function regClientScript($src, $optionsarray('name'=>'', 'version'=>'0', 'plaintext'=>false), $startupfalse) {
  3164.         if (empty($src))
  3165.             return ''; // nothing to register
  3166.         if (!is_array($options)) {
  3167.             if (is_bool($options))  // backward compatibility with old plaintext parameter
  3168.                 $options=array('plaintext'=>$options);
  3169.             elseif (is_string($options)) // Also allow script name as 2nd param
  3170.                 $options=array('name'=>$options);
  3171.             else
  3172.                 $options=array();
  3173.         }
  3174.         $name= isset($options['name']) ? strtolower($options['name']) : '';
  3175.         $version= isset($options['version']) ? $options['version'] : '0';
  3176.         $plaintext= isset($options['plaintext']) ? $options['plaintext'] : false;
  3177.         $key!empty($name) ? $name : $src;
  3178.         unset($overwritepos); // probably unnecessary--just making sure
  3179.         $useThisVertrue;
  3180.         if (isset($this->loadedjscripts[$key])) { // a matching script was found
  3181.             // if existing script is a startup script, make sure the candidate is also a startup script
  3182.             if ($this->loadedjscripts[$key]['startup'])
  3183.                 $startuptrue;
  3184.  
  3185.             if (empty($name)) {
  3186.                 $useThisVerfalse; // if the match was based on identical source code, no need to replace the old one
  3187.             } else {
  3188.                 $useThisVer = version_compare($this->loadedjscripts[$key]['version']$version'<');
  3189.             }
  3190.  
  3191.             if ($useThisVer) {
  3192.                 if ($startup==true && $this->loadedjscripts[$key]['startup']==false{
  3193.                     // remove old script from the bottom of the page (new one will be at the top)
  3194.                     unset($this->jscripts[$this->loadedjscripts[$key]['pos']]);
  3195.                 } else {
  3196.                     // overwrite the old script (the position may be important for dependent scripts)
  3197.                     $overwritepos$this->loadedjscripts[$key]['pos'];
  3198.                 }
  3199.             } else { // Use the original version
  3200.                 if ($startup==true && $this->loadedjscripts[$key]['startup']==false{
  3201.                     // need to move the exisiting script to the head
  3202.                     $version$this->loadedjscripts[$key][$version];
  3203.                     $src$this->jscripts[$this->loadedjscripts[$key]['pos']];
  3204.                     unset($this->jscripts[$this->loadedjscripts[$key]['pos']]);
  3205.                 } else {
  3206.                     return ''; // the script is already in the right place
  3207.                 }
  3208.             }
  3209.         }
  3210.  
  3211.         if ($useThisVer && $plaintext!=true && (strpos(strtolower($src), "<script") === false))
  3212.             $src"\t" . '<script type="text/javascript" src="' . $src . '"></script>';
  3213.         if ($startup) {
  3214.             $pos= isset($overwritepos) ? $overwritepos : max(array_merge(array(0),array_keys($this->sjscripts)))+1;
  3215.             $this->sjscripts[$pos]$src;
  3216.         } else {
  3217.             $pos= isset($overwritepos) ? $overwritepos : max(array_merge(array(0),array_keys($this->jscripts)))+1;
  3218.             $this->jscripts[$pos]$src;
  3219.         }
  3220.         $this->loadedjscripts[$key]['version']$version;
  3221.         $this->loadedjscripts[$key]['startup']$startup;
  3222.         $this->loadedjscripts[$key]['pos']$pos;
  3223.     }
  3224.     
  3225.     /**
  3226.      * Register jQuery core script
  3227.      */
  3228.     function regClientJquery() {
  3229.     
  3230.         static $jquery_included = false;
  3231.         
  3232.         if (!$jquery_included) {    
  3233.              $this->regClientStartupScript($this->config['jquery_url']);
  3234.              if ($this->config['jquery_noconflict']{
  3235.                  $this->regClientStartupScript('<script type="text/javascript">jQuery.noConflict()</script>');
  3236.              }
  3237.              $jquery_included = true;
  3238.          }
  3239.     }
  3240.     
  3241.     /**
  3242.      * Register jquery plugin
  3243.      *
  3244.      * @param string $plugin_name Plugin name, use the name most likely to be used by other scripts (case insensitive)
  3245.      * @param string $plugin_file Plugin URL. Relative to plugin directory if $use_plugin_dir is true
  3246.      * @param string $plugin_version
  3247.      * @param bool $use_plugin_dir See above, defaults to true
  3248.      */
  3249.    function regClientJqueryPlugin($plugin_name, $plugin_file, $plugin_version = 0, $use_plugin_dir = true) {
  3250.            if ($use_plugin_dir) {
  3251.                $plugin_file = $this->config['jquery_plugin_dir'].$plugin_file;
  3252.            }
  3253.            $this->regClientStartupScript($plugin_filearray('name'=>$plugin_name$plugin_version'plaintext'=>false));
  3254.    }
  3255.    
  3256.    /**
  3257.     * Get jquery <script> tag as HTML.
  3258.     *
  3259.     * Intended for use in the backend. Use the above methods for the frontend.
  3260.     *
  3261.     * Returns script tag with full absolute URL, so suitable for all manager pages including any without a <base> tag.
  3262.     *
  3263.     * @param bool $only_once If true, only return the script tag if we haven't already done so
  3264.     */
  3265.    function getJqueryTag($only_once = true) {
  3266.    
  3267.            static $run_once = false;
  3268.            
  3269.            if (!$run_once || !$only_once) {
  3270.                $jq_url = $this->config['jquery_url'];
  3271.                if ($jq_url[0== '/'{
  3272.                    $jq_url = $this->config['site_url'].substr($this->config['jquery_url']1);
  3273.                } elseif (substr($jq_url, 0, 4) != 'http') {
  3274.                    $jq_url = $this->config['site_url'].$jq_url;
  3275.                }
  3276.                $script_tag = '<script type="text/javascript" src="'.str_replace('&', '&amp;', $jq_url)."\"></script>\n";
  3277.            } else {
  3278.                $script_tag = '';
  3279.            }
  3280.  
  3281.         $run_once = true;
  3282.         
  3283.         return $script_tag;         
  3284.    }
  3285.  
  3286.     /**
  3287.      * Get jquery plugin <script> tag as HTML.
  3288.      *
  3289.      * Currently used plugin names:
  3290.      *    - jquery-ui-custom-clippermanager The custom jquery-ui file for the Clipper manager
  3291.      *  - jquery-ui-timepicker
  3292.      *
  3293.      * @param string $plugin_name Plugin name, use the name most likely to be used by other scripts (case insensitive)
  3294.      * @param string $plugin_file Plugin URL. Relative to plugin directory if $use_plugin_dir is true
  3295.      * @param bool $use_plugin_dir See above, defaults to true
  3296.      */
  3297.    function getJqueryPluginTag($plugin_name, $plugin_file, $use_plugin_dir = true, $only_once = true) {
  3298.  
  3299.            static $plugin_names = array();
  3300.            
  3301.            if (!in_array($plugin_name, $plugin_names) || !$only_once) {
  3302.                $plugin_names[] = $plugin_name;
  3303.                if ($use_plugin_dir) {
  3304.                    $plugin_file = $this->config['site_url'].$this->config['jquery_plugin_dir'].$plugin_file// Need [(site_url)] because <base> tags are not present in the backend.
  3305.             }
  3306.             $plugin_file = ($plugin_file[0] == '/') ? $this->config['site_url'].substr($plugin_file1$plugin_file;
  3307.             $script_tag '<script type="text/javascript" src="'.str_replace('&''&amp;'$plugin_file)."\"></script>\n";
  3308.            } else {
  3309.                $script_tag = '';
  3310.            }
  3311.  
  3312.         return $script_tag;         
  3313.    }
  3314.  
  3315.     /**
  3316.      * Registers Client-side Startup HTML block
  3317.      *
  3318.      * @param string $html
  3319.      */
  3320.     function regClientStartupHTMLBlock($html) {
  3321.         $this->regClientScript($htmltruetrue);
  3322.     }
  3323.  
  3324.     /**
  3325.      * Registers Client-side HTML block
  3326.      *
  3327.      * @param string $html
  3328.      */
  3329.     function regClientHTMLBlock($html) {
  3330.         $this->regClientScript($htmltrue);
  3331.     }
  3332.  
  3333.     /**
  3334.      * Returns all registered JavaScripts
  3335.      *
  3336.      * @return string
  3337.      */
  3338.     function getRegisteredClientScripts() {
  3339.         return implode("\n", $this->jscripts);
  3340.     }
  3341.  
  3342.     /**
  3343.      * Returns all registered startup scripts
  3344.      *
  3345.      * @return string
  3346.      */
  3347.     function getRegisteredClientStartupScripts() {
  3348.         return implode("\n", $this->sjscripts);
  3349.     }
  3350.     
  3351.     /**
  3352.      * Remove unwanted html tags and snippet, settings and tags
  3353.      *
  3354.      * @param string $html
  3355.      * @param string $allowed Default: Empty string
  3356.      * @return string
  3357.      */
  3358.     function stripTags($html, $allowed"") {
  3359.         $tstrip_tags($html, $allowed);
  3360.         $tpreg_replace('~\[\*(.*?)\*\]~', "", $t); //tv
  3361.         $tpreg_replace('~\[\[(.*?)\]\]~', "", $t); //snippet
  3362.         $tpreg_replace('~\[\!(.*?)\!\]~', "", $t); //snippet
  3363.         $tpreg_replace('~\[\((.*?)\)\]~', "", $t); //settings
  3364.         $tpreg_replace('~\[\+(.*?)\+\]~', "", $t); //placeholders
  3365.         $tpreg_replace('~{{(.*?)}}~', "", $t); //chunks
  3366.         $tpreg_replace('/(\[\*|\[\[|\[\!|\[\(|\[\+|\{\{|\*\]|\]\]|\!\]|\)\]|\}\})/', '', $t); // All half tags (TimGS)
  3367.         
  3368.         return $t;
  3369.     }
  3370.  
  3371.     /**
  3372.      * Format alias to be URL-safe. Strip invalid characters.
  3373.      *
  3374.      * @param string Alias to be formatted
  3375.      * @return string Safe alias
  3376.      */
  3377.     function stripAlias($alias) {
  3378.         // let add-ons overwrite the default behavior
  3379.         $results = $this->invokeEvent('OnStripAlias'array ('alias'=>$alias));
  3380.         if (!empty($results)) {
  3381.             // if multiple plugins are registered, only the last one is used
  3382.             return end($results);
  3383.         } else {
  3384.             // default behavior: strip invalid characters and replace spaces with dashes.
  3385.             $alias = strip_tags($alias); // strip HTML
  3386.             $alias = preg_replace('/[^\.A-Za-z0-9 _-]/', '', $alias); // strip non-alphanumeric characters
  3387.             $alias = preg_replace('/\s+/', '-', $alias); // convert white-space to dash
  3388.             $alias = preg_replace('/-+/', '-', $alias);  // convert multiple dashes to one
  3389.             $alias = trim($alias, '-'); // trim excess
  3390.             return $alias;
  3391.         }
  3392.     }
  3393.  
  3394.     /**
  3395.      * Add an event listner to a plugin - only for use within the current execution cycle
  3396.      *
  3397.      * @param string $evtName
  3398.      * @param string $pluginName
  3399.      * @return boolean|int
  3400.      */
  3401.     function addEventListener($evtName, $pluginName) {
  3402.         if (!$evtName || !$pluginName)
  3403.             return false;
  3404.         if (!array_key_exists($evtName,$this->pluginEvent))
  3405.             $this->pluginEvent[$evtNamearray();
  3406.         return array_push($this->pluginEvent[$evtName]$pluginName)// return array count
  3407.     }
  3408.  
  3409.     /**
  3410.      * Remove event listner - only for use within the current execution cycle
  3411.      *
  3412.      * @param string $evtName
  3413.      * @return boolean
  3414.      */
  3415.     function removeEventListener($evtName) {
  3416.         if (!$evtName)
  3417.             return false;
  3418.         unset ($this->pluginEvent[$evtName]);
  3419.     }
  3420.  
  3421.     /**
  3422.      * Remove all event listners - only for use within the current execution cycle
  3423.      */
  3424.     function removeAllEventListener() {
  3425.         unset ($this->pluginEvent);
  3426.         $this->pluginEventarray ();
  3427.     }
  3428.  
  3429.     /**
  3430.      * Invoke an event.
  3431.      *
  3432.      * @param string $evtName
  3433.      * @param array $extParams Parameters available to plugins. Each array key will be the PHP variable name, and the array value will be the variable value.
  3434.      * @return boolean|array
  3435.      */
  3436.     function invokeEvent($evtName, $extParamsarray ()) {
  3437.         if (!$evtName)
  3438.             return false;
  3439.         if (!isset ($this->pluginEvent[$evtName]))
  3440.             return false;
  3441.         $el$this->pluginEvent[$evtName];
  3442.         $resultsarray ();
  3443.         $numEventscount($el);
  3444.         if ($numEvents 0)
  3445.             for ($i0$i $numEvents$i++{ // start for loop
  3446.                 $pluginName$el[$i];
  3447.                 $pluginName = stripslashes($pluginName);
  3448.                 // reset event object
  3449.                 $e& $this->Event;
  3450.                 $e->_resetEventObject();
  3451.                 $e->name$evtName;
  3452.                 $e->activePlugin$pluginName;
  3453.  
  3454.                 // get plugin code
  3455.                 if (isset ($this->pluginCache[$pluginName])) {
  3456.                     $pluginCode$this->pluginCache[$pluginName];
  3457.                     $pluginProperties$this->pluginCache[$pluginName "Props"];
  3458.                 } else {
  3459.                     $sql"SELECT `name`, `plugincode`, `properties` FROM " . $this->getFullTableName("site_plugins"" WHERE `name`='" $pluginName "' AND `disabled`=0;";
  3460.                     $result$this->db->query($sql);
  3461.                     if ($this->db->getRecordCount($result== 1{
  3462.                         $row$this->db->getRow($result);
  3463.                         $pluginCode$this->pluginCache[$row['name']]$row['plugincode'];
  3464.                         $pluginProperties$this->pluginCache[$row['name'"Props"]$row['properties'];
  3465.                     } else {
  3466.                         $pluginCode$this->pluginCache[$pluginName]null;
  3467.                         $pluginProperties'';
  3468.                     }
  3469.                 }
  3470.  
  3471.                 // load default params/properties
  3472.                 $parameter$this->parseProperties($pluginProperties);
  3473.                 if (!empty ($extParams))
  3474.                     $parameterarray_merge($parameter$extParams);
  3475.  
  3476.                 // eval plugin
  3477.                 $this->evalPlugin($pluginCode$parameter);
  3478.                 if ($e->_output != "")
  3479.                     $results[]$e->_output;
  3480.                 if ($e->_propagate != true)
  3481.                     break;
  3482.             }
  3483.         $e->activePlugin"";
  3484.         return $results;
  3485.     }
  3486.  
  3487.     /**
  3488.      * Parses a resource property string and returns the result as an array
  3489.      *
  3490.      * @param string $propertyString
  3491.      * @return array Associative array in the form property name => property value
  3492.      */
  3493.     function parseProperties($propertyString) {
  3494.         $parameterarray ();
  3495.         if (!empty ($propertyString)) {
  3496.             $tmpParamsexplode("&", $propertyString);
  3497.             for ($x0; $x < count($tmpParams); $x++) {
  3498.                 if (strpos($tmpParams[$x], '=', 0)) {
  3499.                     $pTmpexplode("=", $tmpParams[$x]);
  3500.                     $pvTmpexplode(";", trim($pTmp[1]));
  3501.                     if ($pvTmp[1] == 'list' && $pvTmp[3] != "")
  3502.                         $parameter[trim($pTmp[0])]$pvTmp[3]; //list default
  3503.                     else
  3504.                         if ($pvTmp[1] != 'list' && $pvTmp[2] != "")
  3505.                             $parameter[trim($pTmp[0])]$pvTmp[2];
  3506.                 }
  3507.             }
  3508.         }
  3509.         return $parameter;
  3510.     }
  3511.  
  3512.     /**
  3513.      * Set PHP error handlers
  3514.      * 
  3515.      * @return void
  3516.      */
  3517.     function set_error_handler()
  3518.         {
  3519.         if (version_compare(PHP_VERSION, '5.3.0') >= 0)
  3520.             {
  3521.             // PHP 5.3
  3522.             set_error_handler(array (&$this, 'phpError'), (error_reporting() & ~E_DEPRECATED & ~E_USER_DEPRECATED) | ($this->config['error_handling_deprecated'E_DEPRECATED E_USER_DEPRECATED 0));
  3523.             }
  3524.         else
  3525.             {
  3526.             set_error_handler(array (&$this, 'phpError'), error_reporting());
  3527.             }
  3528.         
  3529.         register_shutdown_function(array(&$this, 'fatalErrorCheck'));
  3530.         }
  3531.  
  3532.     /**
  3533.      * PHP error handler set by http://www.php.net/manual/en/function.set-error-handler.php
  3534.      *
  3535.      * Checks the PHP error and calls messageQuit() unless:
  3536.      *    - error_reporting() returns 0, or
  3537.      *  - the PHP error level is 0, or
  3538.      *  - the PHP error level is 8 (E_NOTICE) and stopOnNotice is false
  3539.      *
  3540.      * @param int $nr The PHP error level as per http://www.php.net/manual/en/errorfunc.constants.php
  3541.      * @param string $text Error message
  3542.      * @param string $file File where the error was detected
  3543.      * @param string $line Line number within $file
  3544.      * @return boolean
  3545.      */
  3546.     function phpError($nr, $text, $file, $line) {
  3547.         if (error_reporting() == 0 || $nr == 0 || ($nr == 8 && $this->stopOnNotice == false)) {
  3548.             return true;
  3549.         }
  3550.         
  3551.         if (strpos($file, '/document.parser.class.inc.php') !== false) {
  3552.             $file = 'DocumentParser'.(strpos($file, 'eval()\'d code') === false ? '' : ' eval\'d code').($this->eval_type " in {$this->eval_type{$this->eval_name}" : '');
  3553.         }
  3554.         
  3555.         if (version_compare(PHP_VERSION, '5.3.0') >= 0 && ($nr & (E_DEPRECATED | E_USER_DEPRECATED))) { // TimGS. Handle deprecated functions according to config.
  3556.                 switch ($this->config['error_handling_deprecated']{
  3557.                         case 1:
  3558.                             if ($this->eval_type{
  3559.                                 $this->logEvent(29,2,$text.'; File: '.$file.'; Line: '.$line"{$this->eval_type{$this->eval_name}");
  3560.                             } else {
  3561.                                 $this->logEvent(29,2,$text.'; File: '.$file.'; Line: '.$line);
  3562.                             }
  3563.                         case 0:
  3564.                                 return true;
  3565.                 }
  3566.         }
  3567.         
  3568.         if (is_readable($file)) {
  3569.             $sourcefile($file);
  3570.             $sourcehtmlspecialchars($source[$line -1]);
  3571.         } else {
  3572.             $source"";
  3573.         } //Error $nr in $file at $line: <div><code>$source</code></div>
  3574.         if ($this->eval_type{
  3575.             $this->messageQuitFromElement("{$this->eval_type{$this->eval_name}", 'PHP Parse Error', '', true, $nr, $file, $source, $text, $line);
  3576.         } else {
  3577.             $this->messageQuit('PHP Parse Error'''true$nr$file$source$text$line);
  3578.         }
  3579.     }
  3580.  
  3581.     /**
  3582.      * Generate display body for messageQuit()
  3583.      * 
  3584.      * @param string $msg Default: unspecified error
  3585.      * @param string $query Default: Empty string
  3586.      * @param boolean $is_error Default: true
  3587.      * @param string $nr Default: Empty string
  3588.      * @param string $file Default: Empty string
  3589.      * @param string $source Default: Empty string
  3590.      * @param string $text Default: Empty string
  3591.      * @param string $line Default: Empty string
  3592.      */
  3593.      function messageQuitText($msg'unspecified error', $query'', $is_errortrue, $nr'', $file'', $source'', $text'', $line'') {
  3594.      
  3595.         $version= isset ($GLOBALS['version']) ? $GLOBALS['version'] : '';
  3596.         $release_date= isset ($GLOBALS['release_date']) ? $GLOBALS['release_date'] : '';
  3597.         $parsedMessageString"
  3598.               <html><head><title>".CMS_NAME." Content Manager $version &raquo; $release_date</title>
  3599.               <style>TD, BODY { font-size: 11px; font-family:verdana; }</style>
  3600.               </head><body>";
  3601.  
  3602.         if ($is_error) {
  3603.             $parsedMessageString .= "<h3 style='color:red'>&laquo; ".CMS_NAME." Parse Error &raquo;</h3>
  3604.                     <table border='0' cellpadding='1' cellspacing='0'>
  3605.                     <tr><td colspan='3'>".CMS_NAME." encountered the following error while attempting to parse the requested resource:</td></tr>
  3606.                     <tr><td colspan='3'><b style='color:red;'>&laquo; $msg &raquo;</b></td></tr>";
  3607.         } else {
  3608.             $parsedMessageString .= "<h3 style='color:#003399'>&laquo; ".CMS_NAME." Debug/ stop message &raquo;</h3>
  3609.                     <table border='0' cellpadding='1' cellspacing='0'>
  3610.                     <tr><td colspan='3'>The ".CMS_NAME." parser recieved the following debug/ stop message:</td></tr>
  3611.                     <tr><td colspan='3'><b style='color:#003399;'>&laquo; $msg &raquo;</b></td></tr>";
  3612.         }
  3613.  
  3614.         if (!empty ($query)) {
  3615.             $parsedMessageString .= "<tr><td colspan='3'><b style='color:#999;font-size: 9px;'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SQL:&nbsp;<span id='sqlHolder'>$query</span></b></td></tr>";
  3616.         }
  3617.  
  3618.         if ($text != '') {
  3619.  
  3620.             $errortypearray (
  3621.                 E_ERROR => "Error",
  3622.                 E_WARNING => "Warning",
  3623.                 E_PARSE => "Parsing Error",
  3624.                 E_NOTICE => "Notice",
  3625.                 E_CORE_ERROR => "Core Error",
  3626.                 E_CORE_WARNING => "Core Warning",
  3627.                 E_COMPILE_ERROR => "Compile Error",
  3628.                 E_COMPILE_WARNING => "Compile Warning",
  3629.                 E_USER_ERROR => "User Error",
  3630.                 E_USER_WARNING => "User Warning",
  3631.                 E_USER_NOTICE => "User Notice",
  3632.  
  3633.             );
  3634.  
  3635.             $parsedMessageString .= "<tr><td>&nbsp;</td></tr><tr><td colspan='3'><b>PHP error debug</b></td></tr>";
  3636.  
  3637.             $parsedMessageString .= "<tr><td valign='top'>&nbsp;&nbsp;Error: </td>";
  3638.             $parsedMessageString .= "<td colspan='2'>$text</td><td>&nbsp;</td>";
  3639.             $parsedMessageString .= "</tr>";
  3640.  
  3641.             $parsedMessageString .= "<tr><td valign='top'>&nbsp;&nbsp;Error type/ Nr.: </td>";
  3642.             $parsedMessageString .= "<td colspan='2'>" . $errortype[$nr] . " - $nr</b></td><td>&nbsp;</td>";
  3643.             $parsedMessageString .= "</tr>";
  3644.  
  3645.             $parsedMessageString .= "<tr><td>&nbsp;&nbsp;File: </td>";
  3646.             $parsedMessageString .= "<td colspan='2'>$file</td><td>&nbsp;</td>";
  3647.             $parsedMessageString .= "</tr>";
  3648.  
  3649.             $parsedMessageString .= "<tr><td>&nbsp;&nbsp;Line: </td>";
  3650.             $parsedMessageString .= "<td colspan='2'>$line</td><td>&nbsp;</td>";
  3651.             $parsedMessageString .= "</tr>";
  3652.             if ($source != '') {
  3653.                 $parsedMessageString .= "<tr><td valign='top'>&nbsp;&nbsp;Line $line source: </td>";
  3654.                 $parsedMessageString .= "<td colspan='2'>$source</td><td>&nbsp;</td>";
  3655.                 $parsedMessageString .= "</tr>";
  3656.             }
  3657.         }
  3658.  
  3659.         $parsedMessageString .= "<tr><td>&nbsp;</td></tr><tr><td colspan='3'><b>Parser timing</b></td></tr>";
  3660.  
  3661.         $parsedMessageString .= "<tr><td>&nbsp;&nbsp;MySQL: </td>";
  3662.         $parsedMessageString .= "<td><i>[^qt^]</i></td><td>(<i>[^q^] Requests</i>)</td>";
  3663.         $parsedMessageString .= "</tr>";
  3664.  
  3665.         $parsedMessageString .= "<tr><td>&nbsp;&nbsp;PHP: </td>";
  3666.         $parsedMessageString .= "<td><i>[^p^]</i></td><td>&nbsp;</td>";
  3667.         $parsedMessageString .= "</tr>";
  3668.  
  3669.         $parsedMessageString .= "<tr><td>&nbsp;&nbsp;Total: </td>";
  3670.         $parsedMessageString .= "<td><i>[^t^]</i></td><td>&nbsp;</td>";
  3671.         $parsedMessageString .= "</tr>";
  3672.  
  3673.         $parsedMessageString .= "</table>";
  3674.         $parsedMessageString .= "</body></html>";
  3675.  
  3676.         $totalTime($this->getMicroTime($this->tstart);
  3677.         $queryTime$this->queryTime;
  3678.         $phpTime$totalTime $queryTime;
  3679.         $queries= isset ($this->executedQueries$this->executedQueries : 0;
  3680.         $queryTimesprintf("%2.4f s"$queryTime);
  3681.         $totalTimesprintf("%2.4f s"$totalTime);
  3682.         $phpTimesprintf("%2.4f s"$phpTime);
  3683.  
  3684.         $parsedMessageStringstr_replace("[^q^]"$queries$parsedMessageString);
  3685.         $parsedMessageStringstr_replace("[^qt^]"$queryTime$parsedMessageString);
  3686.         $parsedMessageStringstr_replace("[^p^]"$phpTime$parsedMessageString);
  3687.         $parsedMessageStringstr_replace("[^t^]"$totalTime$parsedMessageString);
  3688.         
  3689.         return $parsedMessageString;
  3690.         }
  3691.  
  3692.     /**
  3693.      * Error logging and output.
  3694.      * 
  3695.      * If error_handling_silent is 0, outputs an error page with detailed informations about the error.
  3696.      * Always logs the error using logEvent()
  3697.      *
  3698.      * @param string $msg Default: unspecified error
  3699.      * @param string $query Default: Empty string
  3700.      * @param boolean $is_error Default: true
  3701.      * @param string $nr Default: Empty string
  3702.      * @param string $file Default: Empty string
  3703.      * @param string $source Default: Empty string
  3704.      * @param string $text Default: Empty string
  3705.      * @param string $line Default: Empty string
  3706.      */
  3707.     function messageQuit($msg'unspecified error', $query'', $is_errortrue, $nr'', $file'', $source'', $text'', $line'') {
  3708.  
  3709.         $parsedMessageString = $this->messageQuitText($msg$query$is_error$nr$file$source$text$line);
  3710.  
  3711.         // Set 500 response header
  3712.         header('HTTP/1.1 500 Internal Server Error');
  3713.  
  3714.         // Display error
  3715.         if (!$this->config['error_handling_silent']{
  3716.             echo $parsedMessageString;
  3717.         }
  3718.         ob_end_flush();
  3719.  
  3720.         // Log error if a connection to the db exists
  3721.         if ($this->db->conn{
  3722.              $this->logEvent(03$parsedMessageString'Parser');
  3723.         }
  3724.  
  3725.         // Make sure and die!
  3726.         exit();
  3727.     }
  3728.  
  3729.     /**
  3730.      * Error logging and output.
  3731.      * Takes an $element_name parameter (snippet or plugin name) for extra clarity in the System Events page.
  3732.      * 
  3733.      * If error_handling_silent is 0, outputs an error page with detailed informations about the error.
  3734.      * Always logs the error using logEvent()
  3735.      *
  3736.      * @param string $element_name Name of snippet or plugin
  3737.      * @param string $msg Default: unspecified error
  3738.      * @param string $query Default: Empty string
  3739.      * @param boolean $is_error Default: true
  3740.      * @param string $nr Default: Empty string
  3741.      * @param string $file Default: Empty string
  3742.      * @param string $source Default: Empty string
  3743.      * @param string $text Default: Empty string
  3744.      * @param string $line Default: Empty string
  3745.      */
  3746.     function messageQuitFromElement($element_name, $msg'unspecified error', $query'', $is_errortrue, $nr'', $file'', $source'', $text'', $line'') {
  3747.  
  3748.         if (is_null($element_name)) {
  3749.             $element_name = "{$this->eval_type{$this->eval_name}";
  3750.         }
  3751.  
  3752.         $parsedMessageString = $this->messageQuitText($msg$query$is_error$nr$file$source$text$line);
  3753.  
  3754.         // Set 500 response header
  3755.         header('HTTP/1.1 500 Internal Server Error');
  3756.  
  3757.         // Display error
  3758.         if (!$this->config['error_handling_silent']{
  3759.             echo $parsedMessageString;
  3760.         }
  3761.         ob_end_flush();
  3762.  
  3763.         // Log error if a connection to the db exists
  3764.         if ($this->db->conn{
  3765.              $this->logEvent(03$parsedMessageString$element_name);
  3766.         }
  3767.  
  3768.         // Make sure and die!
  3769.         exit();
  3770.     }
  3771.  
  3772.     // End of class.
  3773. }
  3774.  
  3775. /**
  3776.  * System Event Class
  3777.  */
  3778. class SystemEvent {
  3779.  
  3780.     var $name;
  3781.     var $_propagate;
  3782.     var $_output;
  3783.     var $activated;
  3784.     var $activePlugin;
  3785.  
  3786.     /**
  3787.      * @param string $name Name of the event
  3788.      */
  3789.     function __construct($name'') {
  3790.         $this->_resetEventObject();
  3791.         $this->name$name;
  3792.     }
  3793.  
  3794.     /**
  3795.      * Display a message to the user
  3796.      *
  3797.      * @param string $msg The message
  3798.      */
  3799.     function alert($msg) {
  3800.         global $SystemAlertMsgQueque;
  3801.         if ($msg == "")
  3802.             return;
  3803.         if (is_array($SystemAlertMsgQueque)) {
  3804.             if ($this->name && $this->activePlugin)
  3805.                 $title"<div><b>" $this->activePlugin "</b> - <span style='color:maroon;'>" $this->name "</span></div>";
  3806.             $SystemAlertMsgQueque[]"$title<div style='margin-left:10px;margin-top:3px;'>$msg</div>";
  3807.         }
  3808.     }
  3809.  
  3810.     /**
  3811.      * Output
  3812.      * 
  3813.      * @param string $msg 
  3814.      */
  3815.     function output($msg) {
  3816.         $this->_output .= $msg;
  3817.     }
  3818.  
  3819.  
  3820.     /** 
  3821.      * Stop event propogation
  3822.      */
  3823.     function stopPropagation() {
  3824.         $this->_propagatefalse;
  3825.     }
  3826.  
  3827.     function _resetEventObject() {
  3828.         unset ($this->returnedValues);
  3829.         $this->name"";
  3830.         $this->_output"";
  3831.         $this->_propagatetrue;
  3832.         $this->activatedfalse;
  3833.     }

Documentation generated on Fri, 21 Jun 2013 12:37:06 +0100 by phpDocumentor 1.4.4