<?php
/**
* Code related to the template.lib.php interface.
*
* PHP version 5
*
* @category Library
* @package Sucuri
* @subpackage SucuriScanner
* @author Daniel Cid <
[email protected]>
* @copyright 2010-2018 Sucuri Inc.
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
* @link https://wordpress.org/plugins/sucuri-scanner
*/
if (!defined('SUCURISCAN_INIT') || SUCURISCAN_INIT !== true) {
if (!headers_sent()) {
/* Report invalid access if possible. */
header('HTTP/1.1 403 Forbidden');
}
exit(1);
}
/**
* Read, parse and handle everything related with the templates.
*
* A web template system uses a template processor to combine web templates to
* form finished web pages, possibly using some data source to customize the
* pages or present a large amount of content on similar-looking pages. It is a
* web publishing tool present in content management systems, web application
* frameworks, and HTML editors.
*
* Web templates can be used like the template of a form letter to either
* generate a large number of "static" (unchanging) web pages in advance, or to
* produce "dynamic" web pages on demand.
*
* @category Library
* @package Sucuri
* @subpackage SucuriScanner
* @author Daniel Cid <
[email protected]>
* @copyright 2010-2018 Sucuri Inc.
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL2
* @link https://wordpress.org/plugins/sucuri-scanner
*/
class SucuriScanTemplate extends SucuriScanRequest
{
/**
* Replace all pseudo-variables from a string of characters.
*
* @see http://php.net/manual/en/function.gettype.php
*
* @param string $content The content of a template file which contains pseudo-variables.
* @param array $params List of pseudo-variables that will be replaced in the template.
* @return string The content of the template with the pseudo-variables replated.
*/
private static function replacePseudoVars($content = '', $params = array())
{
$params = is_array($params) ? $params : array();
foreach ($params as $keyname => $kvalue) {
$tplkey = 'SUCURI.' . $keyname;
$with_escape = '%%' . $tplkey . '%%';
$wout_escape = '%%%' . $tplkey . '%%%';
if (is_bool($kvalue)) {
$kvalue = ($kvalue === true) ? 'True' : 'False';
} elseif (!is_string($kvalue) && !is_numeric($kvalue)) {
$kvalue = gettype($kvalue);
}
if (strpos($content, $wout_escape) !== false) {
$content = str_replace($wout_escape, $kvalue, $content);
continue;
}
if (strpos($content, $with_escape) !== false) {
$kvalue = SucuriScan::escape($kvalue);
$content = str_replace($with_escape, $kvalue, $content);
continue;
}
}
global $locale;
preg_match_all('~{{(.+?)}}~', $content, $matches);
if (!empty($matches[1])) {
foreach ($matches[1] as $index => $string) {
$search = $matches[0][$index];
$replace = ('en_US' !== $locale) ? translate($string, 'sucuri-scanner') : $string;
$content = str_replace($search, $replace, $content);
}
}
return $content;
}
/**
* Gather and generate the information required globally by all the template files.
*
* @param string $target Scenario where the params are going to be replaced.
* @param array $params Key-value array with variables shared with the template.
* @return array Additional list of variables for the template files.
*/
private static function sharedParams($target = null, $params = array())
{
$params = is_array($params) ? $params : array();
// Base parameters, required to render all the pages.
$params = self::linksAndNavbar($params);
// Global parameters, used through out all the pages.
$params['GenerateAPIKey.Modal'] = '';
$params['GenerateAPIKey.Visibility'] = 'hidden';
$params['PageNonce'] = wp_create_nonce('sucuriscan_page_nonce');
$params['WordPressVersion'] = self::siteVersion();
$params['PluginVersion'] = SUCURISCAN_VERSION;
$params['CleanDomain'] = self::getDomain();
$params['Year'] = date('Y');
if (!array_key_exists('PageStyleClass', $params)) {
$params['PageStyleClass'] = 'base';
}
if ($target === 'base'
&& current_user_can('manage_options')
&& !SucuriScanAPI::getPluginKey()
) {
$params['GenerateAPIKey.Visibility'] = 'visible';
$params['GenerateAPIKey.Modal'] = /* register-site */
SucuriScanTemplate::getModal(
'register-site',
array(
'Title' => __('Generate API Key', 'sucuri-scanner'),
'Identifier' => 'register-site',
'Visibility' => 'hidden',
)
);
}
// Get a list of admin users for the API key generation.
if ($target === 'modal' && !SucuriScanAPI::getPluginKey()) {
$admin_users = SucuriScan::getUsersForAPIKey();
$params['AdminEmails'] = self::selectOptions($admin_users);
}
return $params;
}
/**
* Return a string indicating the visibility of a HTML component.
*
* @param bool $visible Whether the condition executed returned a positive value or not.
* @return string A string indicating the visibility of a HTML component.
*/
public static function visibility($visible = false)
{
return ($visible === true ? 'visible' : 'hidden');
}
/**
* Generate an URL pointing to the page indicated in the method and that must
* be loaded through the administrator panel.
*
* @param string $page Short name of the page that will be generated.
* @param bool $ajax True if the URL should point to the Ajax handler.
* @return string Full string containing the link of the page.
*/
public static function getUrl($page = '', $ajax = false)
{
$suffix = ($ajax === true) ? 'admin-ajax' : 'admin';
$url_path = SucuriScan::adminURL($suffix . '.php?page=sucuriscan');
if (!empty($page)) {
$url_path .= '_' . strtolower($page);
}
/* convert URL to multisite format */
$networkURL = str_replace(
'wp-admin/network/admin-ajax.php',
'wp-admin/admin-ajax.php',
$url_path
);
return SucuriScan::isMultiSite() ? $networkURL : $url_path;
}
/**
* Generate an URL pointing to the page indicated in the method and that must
* be loaded through the Ajax handler of the administrator panel.
*
* @param string $page Short name of the page that will be generated.
* @return string Full string containing the link of the page.
*/
public static function getAjaxUrl($page = '')
{
return self::getUrl($page, true);
}
/**
* Complement the list of pseudo-variables that will be used in the base
* template files, this will also generate the navigation bar and detect which
* items in it are selected by the current page.
*
* @param array $params Key-value array with pseudo-variables shared with the template.
* @return array A complementary list of pseudo-variables for the template files.
*/
private static function linksAndNavbar($params = array())
{
$pages = sucuriscanMainPages();
$params = is_array($params) ? $params : array();
$sub_pages = is_array($pages) ? $pages : array();
foreach ($sub_pages as $sub_page_func => $sub_page_title) {
$func_parts = explode('_', $sub_page_func, 2);
if (isset($func_parts[1])) {
$unique_name = $func_parts[1];
$pseudo_var = 'URL.' . ucwords($unique_name);
} else {
$unique_name = '';
$pseudo_var = 'URL.Dashboard';
}
$params[$pseudo_var] = self::getUrl($unique_name);
// Copy URL variable and create an Ajax handler.
$pseudo_var_ajax = 'Ajax' . $pseudo_var;
$params[$pseudo_var_ajax] = self::getAjaxUrl($unique_name);
}
return $params;
}
/**
* Generate a HTML code using a template and replacing all the pseudo-variables
* by the dynamic variables provided by the developer through one of the parameters
* of the function.
*
* @param string $html The HTML content of a template file with its pseudo-variables parsed.
* @param array $params Key-value array with pseudo-variables shared with the template.
* @return string The formatted HTML content of the base template.
*/
public static function getBaseTemplate($html = '', $params = array())
{
$params = is_array($params) ? $params : array();
$params = self::sharedParams('base', $params);
$params['PageContent'] = $html;
return self::getTemplate('base', $params);
}
/**
* Generate a HTML code using a template and replacing all the pseudo-variables
* by the dynamic variables provided by the developer through one of the parameters
* of the function.
*
* @param string $template Filename of the template that will be used to generate the page.
* @param array $params Key-value array with pseudo-variables shared with the template.
* @param string $type Template type; either page, section or snippet.
* @return string Formatted HTML code after pseudo-variables replacement.
*/
public static function getTemplate($template = '', $params = array(), $type = 'page')
{
$params = is_array($params) ? $params : array();
$filenames = array(
'page' => '%s/inc/tpl/%s.html.tpl',
'section' => '%s/inc/tpl/%s.html.tpl',
'snippet' => '%s/inc/tpl/%s.snippet.tpl',
);
if (!array_key_exists($type, $filenames)) {
return (string)SucuriScan::throwException(__('Invalid template type', 'sucuri-scanner'));
}
$output = ''; /* initialize response */
$_page = self::get('page', '_page');
$params['PluginURL'] = SUCURISCAN_URL;
$trailing = $_page ? 'admin.php?page=' . $_page : '';
$params['CurrentURL'] = SucuriScan::adminURL($trailing);
$params['DashboardButtonVisibility'] = $_page == 'sucuriscan' ? 'hidden' : 'visible';
$params['SettingsButtonVisibility'] = $_page == 'sucuriscan_settings' ? 'hidden' : 'visible';
$params['FirewallButtonVisibility'] = $_page == 'sucuriscan_firewall' ? 'hidden' : 'visible';
/* load raw content from the specified template file */
$fpath = sprintf($filenames[$type], SUCURISCAN_PLUGIN_PATH, $template);
$output = SucuriScanFileInfo::fileContent($fpath);
/* replace the global pseudo-variables in the section/snippets templates. */
if ($template == 'base'
&& array_key_exists('PageContent', $params)
&& strpos($params['PageContent'], '%%SUCURI.') !== false
) {
$params['PageContent'] = self::replacePseudoVars($params['PageContent'], $params);
}
$output = self::replacePseudoVars($output, $params);
if ($template == 'base' || $type != 'page') {
return $output;
}
return self::getBaseTemplate($output, $params);
}
/**
* Generate a HTML code using a template and replacing all the pseudo-variables
* by the dynamic variables provided by the developer through one of the parameters
* of the function.
*
* @param string $template Filename of the template that will be used to generate the page.
* @param array $params Key-value array with pseudo-variables shared with the template.
* @return string The formatted HTML page after replace all the pseudo-variables.
*/
public static function getSection($template = '', $params = array())
{
$params = self::sharedParams('section', $params);
return self::getTemplate($template, $params, 'section');
}
/**
* Generate a HTML code using a template and replacing all the pseudo-variables
* by the dynamic variables provided by the developer through one of the parameters
* of the function.
*
* @param string $template Filename of the template that will be used to generate the page.
* @param array $params Key-value array with pseudo-variables shared with the template.
* @return string The formatted HTML page after replace all the pseudo-variables.
*/
public static function getModal($template = '', $params = array())
{
$required = array(
'Title' => 'Lorem ipsum dolor sit amet',
'Visibility' => 'visible',
'Identifier' => 'foobar',
'CssClass' => '',
'Content' => '<p>Lorem ipsum dolor sit amet, consectetur adipisici'
. 'ng elit, sed do eiusmod tempor incididunt ut labore et dolore m'
. 'agna aliqua. Ut enim ad minim veniam, quis nostrud exercitation'
. ' ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis '
. 'aute irure dolor in reprehenderit in voluptate velit esse cillu'
. 'm dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupi'
. 'datat non proident, sunt in culpa qui officia deserunt mollit a'
. 'nim id est laborum.</p>',
);
if (!empty($template) && $template !== 'none') {
$params['Content'] = self::getSection($template);
}
foreach ($required as $param_name => $param_value) {
if (!isset($params[$param_name])) {
$params[$param_name] = $param_value;
}
}
$params['Visibility'] = SUCURISCAN . '-' . $params['Visibility'];
$params['Identifier'] = SUCURISCAN . '-' . $params['Identifier'] . '-modal';
$params = self::sharedParams('modal', $params);
return self::getTemplate('modalwindow', $params, 'section');
}
/**
* Generate a HTML code using a template and replacing all the pseudo-variables
* by the dynamic variables provided by the developer through one of the parameters
* of the function.
*
* @param string $template Filename of the template that will be used to generate the page.
* @param array $params Key-value array with pseudo-variables shared with the template.
* @return string The formatted HTML page after replace all the pseudo-variables.
*/
public static function getSnippet($template = '', $params = array())
{
$params = self::sharedParams('snippet', $params);
return self::getTemplate($template, $params, 'snippet');
}
/**
* Generate the HTML code necessary to render a list of options in a form.
*
* @param array $allowed Key-value array with allowed options.
* @param string|int $selected Optional selected value from the list.
* @return string HTML code for the select box.
*/
public static function selectOptions($allowed = array(), $selected = '')
{
$options = '';
foreach ((array)$allowed as $option_name => $option_label) {
$selectedAttr = '';
if ($option_name === $selected) {
$selectedAttr = "\x20selected=\"selected\"";
}
$options .= sprintf(
"<option value=\"%s\"%s>%s</option>\n",
SucuriScan::escape($option_name),
$selectedAttr, /* do not escape HTML */
SucuriScan::escape($option_label)
);
}
return $options;
}
/**
* Detect which number in a pagination was clicked.
*
* @return int Page number of the link clicked in a pagination.
*/
public static function pageNumber()
{
$paged = self::get('paged', '[0-9]{1,5}');
return ($paged ? intval($paged) : 1);
}
/**
* Generate the HTML code to display a pagination.
*
* @param string $base_url Base URL for the links before the page number.
* @param int $total_items Total quantity of items retrieved from a query.
* @param int $max_per_page Maximum number of items that will be shown per page.
* @return string HTML code for a pagination generated using the provided data.
*/
public static function pagination($base_url = '', $total_items = 0, $max_per_page = 1, $query_params = array())
{
/* calculate the number of links for the pagination */
$html_links = '';
$page_number = self::pageNumber();
$max_pages = ceil($total_items / $max_per_page);
$final_page = $max_pages;
$start_page = 1;
$extra_url = '';
/* fix for inline anchor URLs */
$offset = strpos($base_url, '#');
if ($offset !== false) {
$clean_url = substr($base_url, 0, $offset);
$extra_url = substr($base_url, $offset);
$base_url = $clean_url;
}
/* keep the number of pagination buttons at limit */
if ($max_pages > SUCURISCAN_MAX_PAGINATION_BUTTONS) {
$middle = floor(SUCURISCAN_MAX_PAGINATION_BUTTONS / 2);
if ($page_number > $middle) {
$start_page = max(1, $page_number - $middle);
$final_page = min($max_pages, $page_number + $middle);
if ($final_page - $start_page + 1 < SUCURISCAN_MAX_PAGINATION_BUTTONS) {
$start_page = max(1, $final_page - SUCURISCAN_MAX_PAGINATION_BUTTONS + 1);
}
} else {
$final_page = SUCURISCAN_MAX_PAGINATION_BUTTONS;
}
} else {
$final_page = $max_pages;
}
$query_params_str = http_build_query($query_params);
if (!empty($query_params) && !empty($query_params_str)) {
$extra_url .= '&' . $query_params_str;
}
/* generate the HTML links for the pagination */
for ($j = $start_page; $j <= $final_page; $j++) {
$link_class = 'sucuriscan-pagination-link';
if ($page_number == $j) {
$link_class .= "\x20sucuriscan-pagination-active";
}
$html_links .= sprintf(
'<li><a href="%s&paged=%d%s" class="%s" data-page="%d">%s</a></li>',
$base_url,
$j,
$extra_url,
$link_class,
$j,
$j
);
}
return $html_links;
}
}