public function testHtmlentitiesDecode() { // setup $input = 'Ik heb géén bananen vandaag'; $expectedResult = 'Ik heb géén bananen vandaag'; // perform test $this->assertEquals(utf8_decode($expectedResult), SpoonFilter::htmlentitiesDecode(utf8_decode($input), 'iso-8859-1')); $this->assertEquals($expectedResult, SpoonFilter::htmlentitiesDecode($input, 'utf-8')); }
/** * Fetch data for this form from the database and reformat to csv rows. */ private function setItems() { // init header labels $lblSessionId = SpoonFilter::ucfirst(BL::lbl('SessionId')); $lblSentOn = SpoonFilter::ucfirst(BL::lbl('SentOn')); $this->columnHeaders = array($lblSessionId, $lblSentOn); // fetch query and parameters list($query, $parameters) = $this->buildQuery(); // get the data $records = (array) BackendModel::getDB()->getRecords($query, $parameters); $data = array(); // reformat data foreach ($records as $row) { // first row of a submission if (!isset($data[$row['data_id']])) { $data[$row['data_id']][$lblSessionId] = $row['session_id']; $data[$row['data_id']][$lblSentOn] = SpoonDate::getDate('Y-m-d H:i:s', $row['sent_on'], BackendLanguage::getWorkingLanguage()); } // value is serialized $value = unserialize($row['value']); // flatten arrays if (is_array($value)) { $value = implode(', ', $value); } // group submissions $data[$row['data_id']][$row['label']] = SpoonFilter::htmlentitiesDecode($value, null, ENT_QUOTES); // add into headers if not yet added if (!in_array($row['label'], $this->columnHeaders)) { $this->columnHeaders[] = $row['label']; } } // reorder data so they are in the correct column foreach ($data as $id => $row) { foreach ($this->columnHeaders as $header) { // submission has this field so add it if (isset($row[$header])) { $this->rows[$id][] = $row[$header]; } else { $this->rows[$id][] = ''; } } } // remove the keys $this->rows = array_values($this->rows); }
/** * Strips HTML from a string * * @return string A string with all HTML elements stripped. * @param string $string The string with HTML in it. * @param mixed[optional] $exceptions The HTML elements you want to exclude from stripping. Notation example: '<table><tr><td>' * @param bool[optional] $replaceAnchorsWithURL If this is true it will replace all anchor elements with their href value. * @param bool[optional] $replaceImagesWithAltText If this is true it will replace all img elements with their alt text. * @param bool[optional] $preserveParagraphLinebreaks If this is true it will generate an additional EOL for paragraphs. * @param bool[optional] $stripTabs If this is true it will strip all tabs from the string. */ public static function stripHTML($string, $exceptions = null, $replaceAnchorsWithURL = false, $replaceImagesWithAltText = false, $preserveParagraphLinebreaks = false, $stripTabs = true) { // redefine $string = (string) $string; // check input if (is_array($exceptions)) { $exceptions = implode('', $exceptions); } // remove ugly and mac endlines $string = preg_replace('/\\r\\n/', PHP_EOL, $string); $string = preg_replace('/\\r/', PHP_EOL, $string); // remove tabs if ($stripTabs) { $string = preg_replace("/\t/", '', $string); } // remove the style- and head-tags and all their contents $string = preg_replace('|\\<style.*\\>(.*\\n*)\\</style\\>|is', '', $string); $string = preg_replace('|\\<head.*\\>(.*\\n*)\\</head\\>|is', '', $string); // replace images with their alternative content // eg. <img src="path/to/the/image.jpg" alt="My image" /> => My image if ($replaceImagesWithAltText) { $string = preg_replace('|\\<img[^>]*alt="(.*)".*/\\>|isU', '$1', $string); } // replace links with the inner html of the link with the url between () // eg.: <a href="http://site.domain.com">My site</a> => My site (http://site.domain.com) if ($replaceAnchorsWithURL) { $string = preg_replace('|<a.*href="(.*)".*>(.*)</a>|isU', '$2 ($1)', $string); } // check if we need to preserve paragraphs and/or breaks $exceptions = $preserveParagraphLinebreaks ? $exceptions . '<p>' : $exceptions; // strip HTML tags and preserve paragraphs $string = strip_tags($string, $exceptions); // remove multiple with a single one $string = preg_replace('/\\n\\s/', PHP_EOL, $string); $string = preg_replace('/\\n{2,}/', PHP_EOL, $string); // for each linebreak, table row or- paragraph end we want an additional linebreak at the end if ($preserveParagraphLinebreaks) { $string = preg_replace('|<p>|', '', $string); $string = preg_replace('|</p>|', PHP_EOL, $string); } // trim whitespace and strip HTML tags $string = trim($string); // replace html entities that aren't replaced by SpoonFilter::htmlentitiesDecode (should be solved when using a newer Spoon Library) $string = str_replace('€', 'EUR', $string); $string = str_replace('€', 'EUR', $string); $string = str_replace('–', '-', $string); $string = str_replace('…', '...', $string); // decode html entities $string = SpoonFilter::htmlentitiesDecode($string); // return the plain text return $string; }
/** * Truncate a string * syntax: {$var|truncate:max-length[:append-hellip][:closest-word]} * * @param string $var The string passed from the template. * @param int $length The maximum length of the truncated string. * @param bool $useHellip Should a hellip be appended if the length exceeds the requested length? * @param bool $closestWord Truncate on exact length or on closest word? * @return string */ public static function truncate($var = null, $length, $useHellip = true, $closestWord = false) { // init vars $charset = BackendModel::getContainer()->getParameter('kernel.charset'); // remove special chars, all of them, also the ones that shouldn't be there. $var = \SpoonFilter::htmlentitiesDecode($var, null, ENT_QUOTES); // remove HTML $var = strip_tags($var); // less characters if (mb_strlen($var) <= $length) { return \SpoonFilter::htmlspecialchars($var); } else { // more characters // hellip is seen as 1 char, so remove it from length if ($useHellip) { $length = $length - 1; } // truncate if ($closestWord) { $var = mb_substr($var, 0, strrpos(substr($var, 0, $length + 1), ' '), $charset); } else { $var = mb_substr($var, 0, $length, $charset); } // add hellip if ($useHellip) { $var .= '…'; } // return return \SpoonFilter::htmlspecialchars($var, ENT_QUOTES); } }
/** * Process the XML and treat it as a blogpost * * @param SimpleXMLElement $xml The XML to process. * @return bool */ private function processXMLAsPost(SimpleXMLElement $xml) { // init var $postID = substr((string) $xml->id, mb_strpos((string) $xml->id, 'post-') + 5); // validate if ($postID == '') { return false; } if ((string) $xml->title == '') { return false; } // build item $item['id'] = (int) BackendBlogModel::getMaximumId() + 1; $item['user_id'] = BackendAuthentication::getUser()->getUserId(); $item['hidden'] = 'N'; $item['allow_comments'] = 'Y'; $item['num_comments'] = 0; $item['status'] = 'active'; $item['language'] = BL::getWorkingLanguage(); $item['publish_on'] = BackendModel::getUTCDate(null, strtotime((string) $xml->published)); $item['created_on'] = BackendModel::getUTCDate(null, strtotime((string) $xml->published)); $item['edited_on'] = BackendModel::getUTCDate(null, strtotime((string) $xml->updated)); $item['category_id'] = 1; $item['title'] = (string) $xml->title; $item['text'] = (string) $xml->content; // set drafts hidden if (strtotime((string) $xml->published) > time()) { $item['hidden'] = 'Y'; $item['status'] = 'draft'; } // build meta $meta = array(); $meta['keywords'] = $item['title']; $meta['keywords_overwrite'] = 'N'; $meta['description'] = $item['title']; $meta['description_overwrite'] = 'N'; $meta['title'] = $item['title']; $meta['title_overwrite'] = 'N'; $meta['url'] = BackendBlogModel::getURL($item['title']); $meta['url_overwrite'] = 'N'; // replace f****d up links $item['text'] = preg_replace('|<a(.*)onblur="(.*)"(.*)>|Ui', '<a$1$3>', $item['text']); // fix images $item['text'] = preg_replace('|<img(.*)border="(.*)"(.*)>|Ui', '<img$1$3>', $item['text']); // remove inline styles $item['text'] = preg_replace('|<(.*)style="(.*)"(.*)>|Ui', '<$1$3>', $item['text']); // whitespace $item['text'] = preg_replace('|\\s{2,}|', ' ', $item['text']); // cleanup $search = array('<br /><br />', '<div><br /></div>', '<div>', '</div>', '<i>', '</i>', '<b>', '</b>', '<p><object', '</object></p>', '<p><p>', '</p></p>', '...'); $replace = array('</p><p>', '</p><p>', '', '', '<em>', '</em>', '<strong>', '</strong>', '<object', '</object>', '<p>', '</p>', '…'); // cleanup $item['text'] = '<p>' . str_replace($search, $replace, SpoonFilter::htmlentitiesDecode($item['text'])) . '</p>'; // get images $matches = array(); preg_match_all('/<img.*src="(.*)".*\\/>/Ui', $item['text'], $matches); // any images? if (isset($matches[1]) && !empty($matches[1])) { // init var $imagesPath = FRONTEND_FILES_PATH . '/userfiles/images/blog'; $imagesURL = FRONTEND_FILES_URL . '/userfiles/images/blog'; // create dir if needed if (!SpoonDirectory::exists($imagesPath)) { SpoonDirectory::create($imagesPath); } // loop matches foreach ($matches[1] as $key => $file) { // get file info $fileInfo = SpoonFile::getInfo($file); // init var $destinationFile = $item['id'] . '_' . $fileInfo['basename']; try { // download SpoonFile::download($file, $imagesPath . '/' . $destinationFile); // replace the old URL with the new one $item['text'] = str_replace($file, $imagesURL . '/' . $destinationFile, $item['text']); } catch (Exception $e) { // ignore } } } // get links $matches = array(); preg_match_all('/<a.*href="(.*)".*\\/>/Ui', $item['text'], $matches); // any images? if (isset($matches[1]) && !empty($matches[1])) { // loop matches foreach ($matches[1] as $key => $file) { // get new link $replaceWith = self::download($file, $item['id']); // should we replace? if ($replaceWith !== false) { // replace the old URL with the new one $item['text'] = str_replace($file, $replaceWith, $item['text']); } } } // insert meta $item['meta_id'] = BackendModel::getDB(true)->insert('meta', $meta); // insert BackendBlogModel::insert($item); // store the post $this->newIds[$postID] = $item['id']; // get tags $tags = array(); // loop categories foreach ($xml->category as $category) { // is this a tag? if so add it if ((string) $category['scheme'] == 'http://www.blogger.com/atom/ns#') { $tags[] = (string) $category['term']; } } // any tags? if (!empty($tags)) { BackendTagsModel::saveTags($item['id'], implode(',', $tags), $this->getModule()); } // return return true; }
/** * Generate an url, using the predefined callback. * * @param string $URL The base-url to start from. * @return string * @throws Exception When the function does not exist */ public function generateURL($URL) { // validate (check if the function exists) if (!is_callable(array($this->callback['class'], $this->callback['method']))) { throw new Exception('The callback-method doesn\'t exist.'); } // when using ->getValue() in SpoonFormText fields the function is using htmlentities(), // so we must decode it again first! $URL = \SpoonFilter::htmlentitiesDecode($URL); // build parameters for use in the callback $parameters[] = CommonUri::getUrl($URL); // add parameters set by user if (!empty($this->callback['parameters'])) { foreach ($this->callback['parameters'] as $parameter) { $parameters[] = $parameter; } } // get the real url return call_user_func_array(array($this->callback['class'], $this->callback['method']), $parameters); }
/** * Adds an email to the queue. * * @param string $subject The subject for the email. * @param string $template The template to use. * @param array[optional] $variables Variables that should be assigned in the email. * @param string[optional] $toEmail The to-address for the email. * @param string[optional] $toName The to-name for the email. * @param string[optional] $fromEmail The from-address for the mail. * @param string[optional] $fromName The from-name for the mail. * @param string[optional] $replyToEmail The replyto-address for the mail. * @param string[optional] $replyToName The replyto-name for the mail. * @param bool[optional] $queue Should the mail be queued? * @param int[optional] $sendOn When should the email be send, only used when $queue is true. * @param bool[optional] $isRawHTML If this is true $template will be handled as raw HTML, so no parsing of $variables is done. * @param string[optional] $plainText The plain text version. * @param array[optional] $attachments Paths to attachments to include. * @return int The id of the inserted mail. */ public static function addEmail($subject, $template, array $variables = null, $toEmail = null, $toName = null, $fromEmail = null, $fromName = null, $replyToEmail = null, $replyToName = null, $queue = false, $sendOn = null, $isRawHTML = false, $plainText = null, array $attachments = null) { $subject = (string) strip_tags($subject); $template = (string) $template; // set defaults $to = BackendModel::getModuleSetting('core', 'mailer_to'); $from = BackendModel::getModuleSetting('core', 'mailer_from'); $replyTo = BackendModel::getModuleSetting('core', 'mailer_reply_to'); // set recipient/sender headers $email['to_email'] = $toEmail === null ? (string) $to['email'] : $toEmail; $email['to_name'] = $toName === null ? (string) $to['name'] : $toName; $email['from_email'] = $fromEmail === null ? (string) $from['email'] : $fromEmail; $email['from_name'] = $fromName === null ? (string) $from['name'] : $fromName; $email['reply_to_email'] = $replyToEmail === null ? (string) $replyTo['email'] : $replyToEmail; $email['reply_to_name'] = $replyToName === null ? (string) $replyTo['name'] : $replyToName; // validate if (!SpoonFilter::isEmail($email['to_email'])) { throw new BackendException('Invalid e-mail address for recipient.'); } if (!SpoonFilter::isEmail($email['from_email'])) { throw new BackendException('Invalid e-mail address for sender.'); } if (!SpoonFilter::isEmail($email['reply_to_email'])) { throw new BackendException('Invalid e-mail address for reply-to address.'); } // build array $email['subject'] = SpoonFilter::htmlentitiesDecode($subject); if ($isRawHTML) { $email['html'] = $template; } else { $email['html'] = self::getTemplateContent($template, $variables); } if ($plainText !== null) { $email['plain_text'] = $plainText; } $email['created_on'] = BackendModel::getUTCDate(); // init var $matches = array(); // get internal links preg_match_all('|href="/(.*)"|i', $email['html'], $matches); // any links? if (!empty($matches[0])) { // init vars $search = array(); $replace = array(); // loop the links foreach ($matches[0] as $key => $link) { $search[] = $link; $replace[] = 'href="' . SITE_URL . '/' . $matches[1][$key] . '"'; } // replace $email['html'] = str_replace($search, $replace, $email['html']); } // init var $matches = array(); // get internal urls preg_match_all('|src="/(.*)"|i', $email['html'], $matches); // any links? if (!empty($matches[0])) { // init vars $search = array(); $replace = array(); // loop the links foreach ($matches[0] as $key => $link) { $search[] = $link; $replace[] = 'src="' . SITE_URL . '/' . $matches[1][$key] . '"'; } // replace $email['html'] = str_replace($search, $replace, $email['html']); } // attachments added if (!empty($attachments)) { // add attachments one by one foreach ($attachments as $attachment) { // only add existing files if (SpoonFile::exists($attachment)) { $email['attachments'][] = $attachment; } } // serialize :) if (!empty($email['attachments'])) { $email['attachments'] = serialize($email['attachments']); } } // set send date if ($queue) { if ($sendOn === null) { $email['send_on'] = BackendModel::getUTCDate('Y-m-d H') . ':00:00'; } else { $email['send_on'] = BackendModel::getUTCDate('Y-m-d H:i:s', (int) $sendOn); } } // insert the email into the database $id = BackendModel::getDB(true)->insert('emails', $email); // trigger event BackendModel::triggerEvent('core', 'after_email_queued', array('id' => $id)); // if queue was not enabled, send this mail right away if (!$queue) { self::send($id); } // return return $id; }
/** * Truncate a string * syntax: {{ $string|truncate($max-length, $append-hellip, $closest-word) }}. * * @param string $string The string passed from the template. * @param int $length The maximum length of the truncated string. * @param bool $useHellip Should a hellip be appended if the length exceeds the requested length? * @param bool $closestWord Truncate on exact length or on closest word? * * @return string */ public static function truncate($string, $length, $useHellip = true, $closestWord = false) { // remove special chars, all of them, also the ones that shouldn't be there. $string = \SpoonFilter::htmlentitiesDecode($string, null, ENT_QUOTES); // remove HTML $string = strip_tags($string); // less characters if (mb_strlen($string) <= $length) { return \SpoonFilter::htmlspecialchars($string); } else { // more characters // hellip is seen as 1 char, so remove it from length if ($useHellip) { --$length; } // truncate if ($closestWord) { $string = mb_substr($string, 0, strrpos(substr($string, 0, $length + 1), ' '), 'UTF-8'); } else { $string = mb_substr($string, 0, $length, 'UTF8'); } // add hellip if ($useHellip) { $string .= '…'; } // return return \SpoonFilter::htmlspecialchars($string, ENT_QUOTES); } }