public function transform($data) { $txtElement = new XMLElement('data'); $txtElement->setValue(General::wrapInCDATA($data)); $data = $txtElement->generate(); return $data; }
/** * Creates a new exception, and logs the error. * * @param string $message * @param int $code * @param Exception $previous * The previous exception, if nested. See * http://www.php.net/manual/en/language.exceptions.extending.php * @return void */ public function __construct($message, $code = 0, $previous = null) { $trace = parent::getTrace(); // Best-guess to retrieve classname of email-gateway. // Might fail in non-standard uses, will then return an // empty string. $gateway_class = $trace[1]['class'] ? ' (' . $trace[1]['class'] . ')' : ''; Symphony::Log()->pushToLog(__('Email Gateway Error') . $gateway_class . ': ' . $message, $code, true); // CDATA the $message: Do not trust input from others $message = General::wrapInCDATA(trim($message)); parent::__construct($message); }
public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null) { if (is_null($data['value'])) { return; } if ($mode == 'unformatted') { $value = trim($data['value']); } else { $mode = 'formatted'; $value = trim($data['value_formatted']); } if ($mode == 'unformatted' || $this->get('text_cdata') == 'yes') { $value = General::wrapInCDATA($value); } else { $value = $this->repairEntities($value); } $attributes = array('mode' => $mode, 'handle' => $data['handle'], 'word-count' => $data['word_count']); if ($this->get('text_handle') != 'yes') { unset($attributes['handle']); } $wrapper->appendChild(new XMLElement($this->get('element_name'), $value, $attributes)); }
public function execute(array &$param_pool = null) { $result = new XMLElement($this->dsParamROOTELEMENT); // When DS is called out of the Frontend context, this will enable // {$root} and {$workspace} parameters to be evaluated if (empty($this->_env)) { $this->_env['env']['pool'] = array('root' => URL, 'workspace' => WORKSPACE); } try { require_once TOOLKIT . '/class.gateway.php'; require_once TOOLKIT . '/class.xsltprocess.php'; require_once CORE . '/class.cacheable.php'; $this->dsParamURL = $this->parseParamURL($this->dsParamURL); if (isset($this->dsParamXPATH)) { $this->dsParamXPATH = $this->__processParametersInString(stripslashes($this->dsParamXPATH), $this->_env); } // Builds a Default Stylesheet to transform the resulting XML with $stylesheet = new XMLElement('xsl:stylesheet'); $stylesheet->setAttributeArray(array('version' => '1.0', 'xmlns:xsl' => 'http://www.w3.org/1999/XSL/Transform')); $output = new XMLElement('xsl:output'); $output->setAttributeArray(array('method' => 'xml', 'version' => '1.0', 'encoding' => 'utf-8', 'indent' => 'yes', 'omit-xml-declaration' => 'yes')); $stylesheet->appendChild($output); $template = new XMLElement('xsl:template'); $template->setAttribute('match', '/'); $instruction = new XMLElement('xsl:copy-of'); // Namespaces if (isset($this->dsParamNAMESPACES) && is_array($this->dsParamNAMESPACES)) { foreach ($this->dsParamNAMESPACES as $name => $uri) { $instruction->setAttribute('xmlns' . ($name ? ":{$name}" : null), $uri); } } // XPath $instruction->setAttribute('select', $this->dsParamXPATH); $template->appendChild($instruction); $stylesheet->appendChild($template); $stylesheet->setIncludeHeader(true); $xsl = $stylesheet->generate(true); // Check for an existing Cache for this Datasource $cache_id = self::buildCacheID($this); $cache = Symphony::ExtensionManager()->getCacheProvider('remotedatasource'); $cachedData = $cache->check($cache_id); $writeToCache = null; $isCacheValid = true; $creation = DateTimeObj::get('c'); // Execute if the cache doesn't exist, or if it is old. if (!is_array($cachedData) || empty($cachedData) || time() - $cachedData['creation'] > $this->dsParamCACHE * 60) { if (Mutex::acquire($cache_id, $this->dsParamTIMEOUT, TMP)) { $ch = new Gateway(); $ch->init($this->dsParamURL); $ch->setopt('TIMEOUT', $this->dsParamTIMEOUT); // Set the approtiate Accept: headers depending on the format of the URL. if ($this->dsParamFORMAT == 'xml') { $ch->setopt('HTTPHEADER', array('Accept: text/xml, */*')); } elseif ($this->dsParamFORMAT == 'json') { $ch->setopt('HTTPHEADER', array('Accept: application/json, */*')); } elseif ($this->dsParamFORMAT == 'csv') { $ch->setopt('HTTPHEADER', array('Accept: text/csv, */*')); } self::prepareGateway($ch); $data = $ch->exec(); $info = $ch->getInfoLast(); Mutex::release($cache_id, TMP); $data = trim($data); $writeToCache = true; // Handle any response that is not a 200, or the content type does not include XML, JSON, plain or text if ((int) $info['http_code'] != 200 || !preg_match('/(xml|json|csv|plain|text)/i', $info['content_type'])) { $writeToCache = false; $result->setAttribute('valid', 'false'); // 28 is CURLE_OPERATION_TIMEOUTED if ($info['curl_error'] == 28) { $result->appendChild(new XMLElement('error', sprintf('Request timed out. %d second limit reached.', $timeout))); } else { $result->appendChild(new XMLElement('error', sprintf('Status code %d was returned. Content-type: %s', $info['http_code'], $info['content_type']))); } return $result; } else { if (strlen($data) > 0) { // Handle where there is `$data` // If it's JSON, convert it to XML if ($this->dsParamFORMAT == 'json') { try { require_once TOOLKIT . '/class.json.php'; $data = JSON::convertToXML($data); } catch (Exception $ex) { $writeToCache = false; $errors = array(array('message' => $ex->getMessage())); } } elseif ($this->dsParamFORMAT == 'csv') { try { require_once EXTENSIONS . '/remote_datasource/lib/class.csv.php'; $data = CSV::convertToXML($data); } catch (Exception $ex) { $writeToCache = false; $errors = array(array('message' => $ex->getMessage())); } } elseif ($this->dsParamFORMAT == 'txt') { $txtElement = new XMLElement('entry'); $txtElement->setValue(General::wrapInCDATA($data)); $data = $txtElement->generate(); $txtElement = null; } else { if (!General::validateXML($data, $errors, false, new XsltProcess())) { // If the XML doesn't validate.. $writeToCache = false; } } // If the `$data` is invalid, return a result explaining why if ($writeToCache === false) { $error = new XMLElement('errors'); $error->setAttribute('valid', 'false'); $error->appendChild(new XMLElement('error', __('Data returned is invalid.'))); foreach ($errors as $e) { if (strlen(trim($e['message'])) == 0) { continue; } $error->appendChild(new XMLElement('item', General::sanitize($e['message']))); } $result->appendChild($error); return $result; } } elseif (strlen($data) == 0) { // If `$data` is empty, set the `force_empty_result` to true. $this->_force_empty_result = true; } } } else { // Failed to acquire a lock $result->appendChild(new XMLElement('error', __('The %s class failed to acquire a lock.', array('<code>Mutex</code>')))); } } else { // The cache is good, use it! $data = trim($cachedData['data']); $creation = DateTimeObj::get('c', $cachedData['creation']); } // Visit the data $this->exposeData($data); // If `$writeToCache` is set to false, invalidate the old cache if it existed. if (is_array($cachedData) && !empty($cachedData) && $writeToCache === false) { $data = trim($cachedData['data']); $isCacheValid = false; $creation = DateTimeObj::get('c', $cachedData['creation']); if (empty($data)) { $this->_force_empty_result = true; } } // If `force_empty_result` is false and `$result` is an instance of // XMLElement, build the `$result`. if (!$this->_force_empty_result && is_object($result)) { $proc = new XsltProcess(); $ret = $proc->process($data, $xsl); if ($proc->isErrors()) { $result->setAttribute('valid', 'false'); $error = new XMLElement('error', __('Transformed XML is invalid.')); $result->appendChild($error); $errors = new XMLElement('errors'); foreach ($proc->getError() as $e) { if (strlen(trim($e['message'])) == 0) { continue; } $errors->appendChild(new XMLElement('item', General::sanitize($e['message']))); } $result->appendChild($errors); $result->appendChild(new XMLElement('raw-data', General::wrapInCDATA($data))); } elseif (strlen(trim($ret)) == 0) { $this->_force_empty_result = true; } else { if ($this->dsParamCACHE > 0 && $writeToCache) { $cache->write($cache_id, $data, $this->dsParamCACHE); } $result->setValue(PHP_EOL . str_repeat("\t", 2) . preg_replace('/([\\r\\n]+)/', "\$1\t", $ret)); $result->setAttribute('status', $isCacheValid === true ? 'fresh' : 'stale'); $result->setAttribute('cache-id', $cache_id); $result->setAttribute('creation', $creation); } } } catch (Exception $e) { $result->appendChild(new XMLElement('error', $e->getMessage())); } if ($this->_force_empty_result) { $result = $this->emptyXMLSet(); } $result->setAttribute('url', General::sanitize($this->dsParamURL)); return $result; }
/** * This function sets the page's parameters, processes the Datasources and * Events and sets the `$xml` and `$xsl` variables. This functions resolves the `$page` * by calling the `resolvePage()` function. If a page is not found, it attempts * to locate the Symphony 404 page set in the backend otherwise it throws * the default Symphony 404 page. If the page is found, the page's XSL utility * is found, and the system parameters are set, including any URL parameters, * params from the Symphony cookies. Events and Datasources are executed and * any parameters generated by them are appended to the existing parameters * before setting the Page's XML and XSL variables are set to the be the * generated XML (from the Datasources and Events) and the XSLT (from the * file attached to this Page) * * @uses FrontendPageResolved * @uses FrontendParamsResolve * @uses FrontendParamsPostResolve * @see resolvePage() */ private function __buildPage() { $start = precision_timer(); if (!($page = $this->resolvePage())) { throw new FrontendPageNotFoundException(); } /** * Just after having resolved the page, but prior to any commencement of output creation * @delegate FrontendPageResolved * @param string $context * '/frontend/' * @param FrontendPage $page * An instance of this class, passed by reference * @param array $page_data * An associative array of page data, which is a combination from `tbl_pages` and * the path of the page on the filesystem. Passed by reference */ Symphony::ExtensionManager()->notifyMembers('FrontendPageResolved', '/frontend/', array('page' => &$this, 'page_data' => &$page)); $this->_pageData = $page; $path = explode('/', $page['path']); $root_page = is_array($path) ? array_shift($path) : $path; $current_path = explode(dirname($_SERVER['SCRIPT_NAME']), $_SERVER['REQUEST_URI'], 2); $current_path = '/' . ltrim(end($current_path), '/'); $split_path = explode('?', $current_path, 3); $current_path = rtrim(current($split_path), '/'); $querystring = '?' . next($split_path); // Get max upload size from php and symphony config then choose the smallest $upload_size_php = ini_size_to_bytes(ini_get('upload_max_filesize')); $upload_size_sym = Symphony::Configuration()->get('max_upload_size', 'admin'); $date = new DateTime(); $this->_param = array('today' => $date->format('Y-m-d'), 'current-time' => $date->format('H:i'), 'this-year' => $date->format('Y'), 'this-month' => $date->format('m'), 'this-day' => $date->format('d'), 'timezone' => $date->format('P'), 'website-name' => Symphony::Configuration()->get('sitename', 'general'), 'page-title' => $page['title'], 'root' => URL, 'workspace' => URL . '/workspace', 'root-page' => $root_page ? $root_page : $page['handle'], 'current-page' => $page['handle'], 'current-page-id' => $page['id'], 'current-path' => $current_path == '' ? '/' : $current_path, 'parent-path' => '/' . $page['path'], 'current-query-string' => self::sanitizeParameter($querystring), 'current-url' => URL . $current_path, 'upload-limit' => min($upload_size_php, $upload_size_sym), 'symphony-version' => Symphony::Configuration()->get('version', 'symphony')); if (isset($this->_env['url']) && is_array($this->_env['url'])) { foreach ($this->_env['url'] as $key => $val) { $this->_param[$key] = $val; } } if (is_array($_GET) && !empty($_GET)) { foreach ($_GET as $key => $val) { if (in_array($key, array('symphony-page', 'debug', 'profile'))) { continue; } // If the browser sends encoded entities for &, ie. a=1&b=2 // this causes the $_GET to output they key as amp;b, which results in // $url-amp;b. This pattern will remove amp; allow the correct param // to be used, $url-b $key = preg_replace('/(^amp;|\\/)/', null, $key); // If the key gets replaced out then it will break the XML so prevent // the parameter being set. if (!General::createHandle($key)) { continue; } // Handle ?foo[bar]=hi as well as straight ?foo=hi RE: #1348 if (is_array($val)) { $val = General::array_map_recursive(array('FrontendPage', 'sanitizeParameter'), $val); } else { $val = self::sanitizeParameter($val); } $this->_param['url-' . $key] = $val; } } if (is_array($_COOKIE[__SYM_COOKIE_PREFIX__]) && !empty($_COOKIE[__SYM_COOKIE_PREFIX__])) { foreach ($_COOKIE[__SYM_COOKIE_PREFIX__] as $key => $val) { $this->_param['cookie-' . $key] = $val; } } // Flatten parameters: General::flattenArray($this->_param); // Add Page Types to parameters so they are not flattened too early $this->_param['page-types'] = $page['type']; /** * Just after having resolved the page params, but prior to any commencement of output creation * @delegate FrontendParamsResolve * @param string $context * '/frontend/' * @param array $params * An associative array of this page's parameters */ Symphony::ExtensionManager()->notifyMembers('FrontendParamsResolve', '/frontend/', array('params' => &$this->_param)); $xml_build_start = precision_timer(); $xml = new XMLElement('data'); $xml->setIncludeHeader(true); $events = new XMLElement('events'); $this->processEvents($page['events'], $events); $xml->appendChild($events); $this->_events_xml = clone $events; $this->processDatasources($page['data_sources'], $xml); Symphony::Profiler()->seed($xml_build_start); Symphony::Profiler()->sample('XML Built', PROFILE_LAP); if (isset($this->_env['pool']) && is_array($this->_env['pool']) && !empty($this->_env['pool'])) { foreach ($this->_env['pool'] as $handle => $p) { if (!is_array($p)) { $p = array($p); } foreach ($p as $key => $value) { if (is_array($value) && !empty($value)) { foreach ($value as $kk => $vv) { $this->_param[$handle] .= @implode(', ', $vv) . ','; } } else { $this->_param[$handle] = @implode(', ', $p); } } $this->_param[$handle] = trim($this->_param[$handle], ','); } } /** * Access to the resolved param pool, including additional parameters provided by Data Source outputs * @delegate FrontendParamsPostResolve * @param string $context * '/frontend/' * @param array $params * An associative array of this page's parameters */ Symphony::ExtensionManager()->notifyMembers('FrontendParamsPostResolve', '/frontend/', array('params' => &$this->_param)); $params = new XMLElement('params'); foreach ($this->_param as $key => $value) { // To support multiple parameters using the 'datasource.field' // we will pop off the field handle prior to sanitizing the // key. This is because of a limitation where General::createHandle // will strip '.' as it's technically punctuation. if (strpos($key, '.') !== false) { $parts = explode('.', $key); $field_handle = '.' . array_pop($parts); $key = implode('', $parts); } else { $field_handle = ''; } $key = Lang::createHandle($key) . $field_handle; $param = new XMLElement($key); // DS output params get flattened to a string, so get the original pre-flattened array if (isset($this->_env['pool'][$key])) { $value = $this->_env['pool'][$key]; } if (is_array($value) && !(count($value) == 1 && empty($value[0]))) { foreach ($value as $key => $value) { $item = new XMLElement('item', General::sanitize($value)); $item->setAttribute('handle', Lang::createHandle($value)); $param->appendChild($item); } } else { if (is_array($value)) { $param->setValue(General::sanitize($value[0])); } else { if ($key == 'current-query-string') { $param->setValue(General::wrapInCDATA($value)); } else { $param->setValue(General::sanitize($value)); } } } $params->appendChild($param); } $xml->prependChild($params); Symphony::Profiler()->seed(); $this->setXML($xml->generate(true, 0)); Symphony::Profiler()->sample('XML Generation', PROFILE_LAP); $xsl = '<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:import href="./workspace/pages/' . basename($page['filelocation']) . '"/> </xsl:stylesheet>'; $this->setXSL($xsl, false); $this->setRuntimeParam($this->_param); Symphony::Profiler()->seed($start); Symphony::Profiler()->sample('Page Built', PROFILE_LAP); }
/** * Given a DOMNode, this function will help replicate it as an * XMLElement object * * @since Symphony 2.5.2 * @param XMLElement $element * @param DOMNode $node */ private static function convertNode(XMLElement $element, DOMNode $node) { if ($node->hasAttributes()) { foreach ($node->attributes as $name => $attrEl) { $element->setAttribute($name, General::sanitize($attrEl->value)); } } if ($node->hasChildNodes()) { foreach ($node->childNodes as $childNode) { if ($childNode instanceof DOMCdataSection) { $element->setValue(General::wrapInCDATA($childNode->data)); } elseif ($childNode instanceof DOMText) { if ($childNode->isWhitespaceInElementContent() === false) { $element->setValue(General::sanitize($childNode->data)); } } elseif ($childNode instanceof DOMElement) { self::convert($element, $childNode); } } } }
protected function __trigger() { $api = new MailChimp($this->_driver->getKey()); $result = new XMLElement("mailchimp"); $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL); $list = isset($_POST['list']) ? $_POST['list'] : $this->_driver->getList(); // For post values $fields = $_POST; unset($fields['action']); // Valid email? if (!$email) { $error = new XMLElement('error', 'E-mail is invalid.'); $error->setAttribute("handle", 'email'); $result->appendChild($error); $result->setAttribute("result", "error"); return $result; } // Default subscribe parameters $params = array('email' => array('email' => $email), 'id' => $list, 'merge_vars' => array(), 'email_type' => $fields['email_type'] ? $fields['email_type'] : 'html', 'double_optin' => $fields['double_optin'] ? $fields['double_optin'] == 'yes' : true, 'update_existing' => $fields['update_existing'] ? $fields['update_existing'] == 'yes' : false, 'replace_interests' => $fields['replace_interests'] ? $fields['replace_interests'] == 'yes' : true, 'send_welcome' => $fields['send_welcome'] ? $fields['send_welcome'] == 'yes' : false); // Are we merging? try { $mergeVars = $api->call('lists/merge-vars', array('id' => array($list))); $mergeVars = $mergeVars['success_count'] ? $mergeVars['data'][0]['merge_vars'] : array(); if (count($mergeVars) > 1 && isset($fields['merge'])) { $merge = $fields['merge']; foreach ($merge as $key => $val) { if (!empty($val)) { $params['merge_vars'][$key] = $val; } else { unset($fields['merge'][$key]); } } } // Subscribe the user $api_result = $api->call('lists/subscribe', $params); if ($api_result['status'] == 'error') { $result->setAttribute("result", "error"); // try to match mergeVars with error if (count($mergeVars) > 1) { // replace foreach ($mergeVars as $var) { $errorMessage = str_replace($var['tag'], $var['name'], $api_result['error'], $count); if ($count == 1) { $error = new XMLElement("message", $errorMessage); break; } } } // no error message found with merge vars in it if ($error == null) { $msg = General::sanitize($api_result['error']); $error = new XMLElement("message", strlen($msg) > 0 ? $msg : 'Unknown error', array('code' => $api_result['code'], 'name' => $api_result['name'])); } $result->appendChild($error); } else { if (isset($_REQUEST['redirect'])) { redirect($_REQUEST['redirect']); } else { $result->setAttribute("result", "success"); $result->appendChild(new XMLElement('message', __('Subscriber added to list successfully'))); } } // Set the post values $post_values = new XMLElement("post-values"); General::array_to_xml($post_values, $fields); $result->appendChild($post_values); } catch (Exception $ex) { $error = new XMLElement('error', General::wrapInCDATA($ex->getMessage())); $result->appendChild($error); } return $result; }