<?php
/*
* Copyright (C) 2017 Karmabunny Pty Ltd.
*
* This file is a part of SproutCMS.
*
* SproutCMS is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation, either
* version 2 of the License, or (at your option) any later version.
*
* For more information, visit <http://getsproutcms.com>.
*/
namespace Sprout\Helpers;
use DOMDocument;
use karmabunny\pdb\Exceptions\RowMissingException;
/**
* No description yet.
**/
class ContentReplace
{
private static $temp;
public static
$preloaded_widgets = array();
/**
* Execute a content replace chain
*
* @example
* $html = Blogs::getBlogPost($post_id);
* $html = ContentReplace::executeChain('inner_html', $html);
*
* @param string $chain The name of the chain to execute
* @param string $html The incoming HTML
*/
public static function executeChain($chain, $html)
{
$methods = Register::getContentReplaceMethods($chain);
foreach ($methods as $callable) {
}
return $html;
}
/**
* Do all the replacements normally required for HTML content
* Internal links, inline addons, the local anchor fix, etc.
*
* @param string $html
* @return string HTML
**/
public static function html($html)
{
return self::executeChain('inner_html', $html);
}
/**
* Replace links to internal pages in the format page/view_by_id/... with the page's friendly url
**/
public static function intlinks($text)
{
}
private static function _intlinks($matches)
{
$matches[2] = (int) $matches[2];
$root = Navigation::getRootNode();
$node = $root->findNodeValue('id', $matches[2]);
if (! $node) {
return '<a ' . $matches[1] . 'href="page/view_by_id/' . $matches[2] . '"';
}
return '<a ' . $matches[1] . 'href="' . $node->getFriendlyUrl() . '"';
}
/**
* Add some preloaded widgets, e.g. generated by a preview
* @param array $widgets Format to match $_POST['widgets'] on
* admin/edit/page/{id}
* @param array $settings Format to match $_POST['widget_settings']
* on admin/edit/page/{id}
*/
public static
function preloadWidgets
(array $widgets, array $settings) {
foreach ($widgets as $area_name => $widget_list) {
foreach ($widget_list as $widget) {
list($record_order, $type, $widget_id) = explode(',', $widget); $widget_settings = array(); if (isset($settings[$area_name][$record_order])) { $widget_settings = $settings[$area_name][$record_order];
}
$area_id = WidgetArea::findAreaByName($area_name)->getIndex();
Widgets::add($area_id, $type, $widget_settings);
self::$preloaded_widgets[] = array( 'type' => $type,
'settings' => $widget_settings,
'embed_key' => $widget_id
);
}
}
}
/**
* Replace embed code with the actual widgets.
*
* Embed code is in the format ((WIDGET code))
*
* @param string $text The text which contains the widget embed codes
* @param string $widget_table The host table of the widgets, e.g. 'page'
* @param int $widget_record The record id
* @return string $text
**/
public static function embedWidgets($text, $widget_table, $widget_record)
{
$widgets = self::$preloaded_widgets;
Pdb::validateIdentifier($widget_table);
$widget_record = (int) $widget_record;
// Find widget references in text
'!<p>(?:\s| |<code>)*\(\(WIDGET [a-zA-Z]*? ?([0-9A-Za-z]+)\)\)(?:\s| |</code>)*</p>!',
$text,
$matches
);
if (count($matches) == 0) return $text;
// Map embed codes => widget refs
foreach ($matches[0] as $key => $match) {
$embeds[$match] = $matches[1][$key];
}
// Replace preloaded widgets first
foreach ($embeds as $pattern => $embed_key) {
foreach ($widgets as $widget) {
if ($embed_key == $widget['embed_key']) {
$text = self::embedWidget($text, $pattern, $widget);
unset($embeds[$pattern]); break;
}
}
}
if (count($embeds) == 0) return $text;
// If any embed codes remain, do a DB lookup and replace them
$widgets = self::lookupWidgets($widget_table, $widget_record, $embeds);
foreach ($embeds as $pattern => $embed_key) {
foreach ($widgets as $widget) {
if ($embed_key == $widget['embed_key']) {
$text = self::embedWidget($text, $pattern, $widget);
unset($embeds[$pattern]); break;
}
}
}
return $text;
}
/**
* @param string $widget_table The host table of the widgets, e.g. 'page'
* @param int $widget_record The record id
* @param array $embed_keys The embed keys
* @return array Keys are the widget reference codes, values are arrays
* with keys and values matching the DB fields: 'type' (string),
* 'settings' (array), and 'embed_key' (string)
*/
public static
function lookupWidgets
($widget_table, $widget_record, array $embed_keys) {
Pdb::validateIdentifier($widget_table);
$widget_record = (int) $widget_record;
$conditions = ["{$widget_table}_id" => $widget_record];
$conditions[] = ['embed_key', 'IN', $embed_keys];
$params = [];
$where = Pdb::buildClause($conditions, $params);
$q = "SELECT type, settings, embed_key
FROM ~{$widget_table}_widgets
WHERE {$where}";
$res = Pdb::q($q, $params, 'arr');
foreach ($res as $row) {
$row['settings'] = json_decode($row['settings'], true); $widgets[$row['embed_key']] = $row;
}
return $widgets;
}
/**
* Embeds a widget into some text
* @param string $text The text containing the widget embed
* @param string $pattern The widget embed to replace
* @param array $widget as per {@link lookupWidgets()}, has keys and values
* matching the DB fields: 'type' (string), 'settings' (array),
* and 'embed_key' (string)
* @return string The text with the rendered widget
*/
public static
function embedWidget
($text, $pattern, array $widget) {
$rendered = Widgets::render(
WidgetArea::ORIENTATION_WIDE,
$widget['type'],
$widget['settings']
);
}
/**
* Remove widget embed code from the specified HTML
* Embed code is in the format ((WIDGET code))
**/
public static function removeWidgets($text)
{
'!<p>(?:\s| |<code>)*\(\(WIDGET [a-zA-Z]*? ?([0-9A-Za-z]+)\)\)(?:\s| |</code>)*</p>!',
'',
$text
);
}
/**
* Replace embed code with the actual widget. This is a tweaked version of embedWidgets, designed for email
*
* Embed code is in the format ((WIDGET code))
*
* @param string $widget_table The host table of the widgets, e.g. 'page'
* @param string $widget_record The record id
**/
public static function emailWidgets($text, $widget_table, $widget_record, $pre_html, $post_html)
{
Pdb::validateIdentifier($widget_table);
$widget_record = (int) $widget_record;
self::$temp = array($widget_table, $widget_record, $pre_html, $post_html);
return preg_replace_callback('!<p>(?:<code>)?\(\(WIDGET [a-zA-Z]*? ?([0-9A-Za-z]+)\)\)(?:</code>)?</p>!', array('Sprout\Helpers\ContentReplace', '_email_widgets'), $text); }
private static function _emailWidgets($matches)
{
list($widget_table, $widget_record, $pre_html, $post_html) = self::$temp; $widget_record = (int) $widget_record;
$q = "SELECT type, settings
FROM ~{$widget_table}_widgets
WHERE {$widget_table}_id = ? AND embed_key = ?
LIMIT 1";
try {
$widget = Pdb::query($q, [$widget_record, $matches[1]], 'row');
} catch (RowMissingException $ex) {
return;
}
return Widgets::render(
WidgetArea::ORIENTATION_EMAIL,
$widget['type'],
$pre_html,
$post_html
);
}
/**
* Replace database fields with database values
*
* Replacement code is in the format ((REPLACE field_name))
*
* @param string $data An array of field data
**/
public static function dbFields($text, $data)
{
'!\(\(REPLACE ([a-z_]+)\)\)!',
function($matches) use ($data) {
return $data[$matches[1]];
},
$text
);
}
/**
* Remove tables that don't contain any content
**/
public static function emptyTables($text)
{
}
private static function _emptyTables($matches)
{
if (strpos($matches[1], '<img') !== false and
strpos($matches[1], 'class="deco"') === false) return $matches[0]; return '';
}
/**
* Replaces an expando with a link to the specified URL
*
* This method might not preserve whitespace in the input html.
* The link is only appended if the content actually contained expandos.
* The lunk url is optional. If not provided, no link gets appended.
*
* @param string $html The HTML which contains expandos.
* @param string $url A url for the link which gets appended to the end.
**/
public static function expandolink($html, $url = null)
{
if ($html == '') return '';
// Create a DOMDocument obj
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = true;
$dom->formatOutput = false;
$dom->strictErrorChecking = false;
// Load html string into a DOMDocument
@$dom->loadHTML('<meta http-equiv="Content-type" content="text/html; charset=UTF-8">' . $html);
// Find expandos
$has_expando = false;
$divs = $dom->getElementsByTagName('div');
foreach ($divs as $elem) {
if (strpos($elem->getAttribute('class'), 'expando') !== false) { $has_expando = true;
$rem[] = $elem;
}
}
// No expandos? No worries
if (! $has_expando) {
return $html;
}
// Remove expandos
foreach ($rem as $elem) {
$elem->parentNode->removeChild($elem);
}
// Save HTML back to string
$html = $dom->saveHTML();
$html = preg_replace('!.+<body>(.+?)</body>.+!s', '$1', $html);
// Append link if specified
if ($url) {
$html .= "<p><a href=\"{$url}\">More information</a></p>";
}
return $html;
}
/**
* Replace anchors without a URL (e.g. href="#top") to include the page URL to fix issues with BASE HREF
**/
public static function localAnchor($text)
{
}
/**
* Use Sprout::specialFileLinks instead, it's got better unit tests etc.
**/
public static function documentLinks($html)
{
return Sprout::specialFileLinks($html);
}
/**
* Replace file URLs that refer to IDs with URLs which refer to filenames
*
* @param string $html
* @return string
*/
public static function fileDownload($html)
{
$ids = [];
$pattern = '#<(?:a|img)\s[^>]*(?:href|src)="file/download/([0-9]+)#';
$matches = [];
return $html;
}
foreach ($matches[1] as $match) {
$id = (int) $match;
if ($id <= 0) continue;
}
if (count($ids) == 0) return $html;
$params = [];
$where = Pdb::buildClause([['id', 'IN', $ids]], $params);
$q = "SELECT id, filename FROM ~files WHERE {$where}";
$filenames = Pdb::q($q, $params, 'map');
// $matches:
// [0] Everything from start of tag to end of file download URL
// [1] Everything from the start of the tag up to the " at the start of the URL
// E.g. '<a href="' or '<img class="some-class" src="'
// [2] File ID
// [3] File size variant, e.g. 'small', 'medium', ...
$id_replace = function($matches) use ($filenames) {
$id = $matches[2];
$filename = $filenames[$id];
if (!empty($matches[3])) { $filename = File::getResizeFilename($filename, $matches[3]); }
return $matches[1] . File::url($filename); };
$pattern = '#(<(?:a|img)\s[^>]*(?:href|src)=")file/download/([0-9]+)(?:/([a-zA-Z0-9_]+))?#';
}
}