1094 lines
41 KiB
PHP
1094 lines
41 KiB
PHP
<?php
|
|
|
|
/* --------------------------------------------------------------------
|
|
|
|
This file is part of Chevereto Free.
|
|
https://chevereto.com/free
|
|
|
|
(c) Rodolfo Berrios <rodolfo@chevereto.com>
|
|
|
|
For the full copyright and license information, please view the LICENSE
|
|
file that was distributed with this source code.
|
|
|
|
--------------------------------------------------------------------- */
|
|
|
|
namespace CHV\Render;
|
|
|
|
use G;
|
|
use CHV;
|
|
|
|
if (!defined('access') or !access) {
|
|
die('This file cannot be directly accessed.');
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------------------------------------------- */
|
|
/*** STRING DATA FUNCTIONS ***/
|
|
|
|
function get_email_body_str($file)
|
|
{
|
|
ob_start();
|
|
G\Render\include_theme_file($file);
|
|
$mail_body = ob_get_contents();
|
|
ob_end_clean();
|
|
|
|
return $mail_body;
|
|
}
|
|
|
|
// For inline JS and CSS code from a given file
|
|
function get_theme_inline_code($file, $type = null)
|
|
{
|
|
if (!isset($type)) {
|
|
$type = pathinfo(rtrim($file, '.php'), PATHINFO_EXTENSION);
|
|
}
|
|
if (!CHV\getSetting('minify_enable') or !in_array($type, ['js', 'css'])) {
|
|
G\Render\include_theme_file($file);
|
|
} else {
|
|
$ob_start = ob_start();
|
|
G\Render\include_theme_file($file);
|
|
if ($ob_start) {
|
|
$code = ob_get_clean();
|
|
ob_flush();
|
|
if ($code) {
|
|
return get_cond_minified_code($code, $type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function show_theme_inline_code($file, $type = null)
|
|
{
|
|
G\Render\include_theme_file($file);
|
|
// echo get_theme_inline_code($file, $type); // Don't minify this (saves execution time and is not needed)
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------------------------------------------- */
|
|
/*** THEME DATA FUNCTIONS ***/
|
|
|
|
function get_theme_file_url($file, $options = [])
|
|
{
|
|
$filepath = G_APP_PATH_THEME . $file;
|
|
$filepath_override = G_APP_PATH_THEME . 'overrides/' . $file;
|
|
if (file_exists($filepath_override)) {
|
|
$filepath = $filepath_override;
|
|
}
|
|
|
|
return get_static_url($filepath, $options);
|
|
}
|
|
|
|
function get_static_url($filepath, $options = [])
|
|
{
|
|
$options = array_merge(['versionize' => true, 'minify' => null], $options);
|
|
if ($options['minify'] !== false) {
|
|
$filepath = get_cond_minified_file($filepath, $options['forced']); // Handle the conditional minify
|
|
}
|
|
$return = G\absolute_to_url($filepath, defined('CHV_ROOT_URL_STATIC') ? CHV_ROOT_URL_STATIC : null);
|
|
if ($options['versionize']) {
|
|
$return = versionize_src($return);
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
function get_cond_minified_file($filepath, $forced = false)
|
|
{
|
|
// Check for theme override
|
|
if (G\starts_with(G_APP_PATH_THEME, $filepath)) {
|
|
$filepath_override = G\str_replace_first(G_APP_PATH_THEME, G_APP_PATH_THEME . 'overrides/', $filepath);
|
|
if (file_exists($filepath_override)) {
|
|
$filepath = $filepath_override;
|
|
}
|
|
}
|
|
if (!CHV\getSetting('minify_enable')) {
|
|
return $filepath;
|
|
}
|
|
|
|
return get_minified($filepath, ['forced' => $forced, 'output' => 'file']);
|
|
}
|
|
function get_cond_minified_code($code, $type = 'js')
|
|
{
|
|
if (!CHV\getSetting('minify_enable')) {
|
|
return $code;
|
|
}
|
|
|
|
return get_minified($code, ['source_method' => 'inline', 'source_type' => $type, 'output' => 'inline']);
|
|
}
|
|
|
|
function get_minified($var, $options = [])
|
|
{
|
|
$options = array_merge(['source_method' => 'file', 'forced' => false], (array) $options);
|
|
try {
|
|
$minify = new G\Minify(array_merge($options, [
|
|
'source' => $var,
|
|
]));
|
|
$minify->exec();
|
|
$var = $minify->result;
|
|
} catch (G\MinifyException $e) {
|
|
error_log($e->getMessage());
|
|
}
|
|
|
|
return $var;
|
|
}
|
|
|
|
function theme_file_exists($var)
|
|
{
|
|
return file_exists(G_APP_PATH_THEME . $var);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------------------------------------------- */
|
|
/*** HTML TAGS ***/
|
|
|
|
function get_html_tags()
|
|
{
|
|
$classes = 'device-' . (G\Handler::getCond('mobile_device') ? 'mobile' : 'nonmobile') . ' tone-' . G\Handler::getVar('theme_tone') . ' top-bar-' . G\Handler::getVar('theme_top_bar_color') . ' unsafe-blur-' . (CHV\getSetting('theme_nsfw_blur') ? 'on' : 'off');
|
|
|
|
return get_lang_html_tags() . ' class="' . $classes . '"';
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------------------------------------------- */
|
|
/*** LANGUAGE TAGS ***/
|
|
|
|
function get_lang_html_tags()
|
|
{
|
|
$lang = CHV\get_language_used();
|
|
|
|
return 'xml:lang="' . $lang['base'] . '" lang="' . $lang['base'] . '" dir="' . $lang['dir'] . '"';
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------------------------------------------- */
|
|
/*** FORM ASSETS ***/
|
|
|
|
function get_select_options_html($arr, $selected)
|
|
{
|
|
$html = '';
|
|
foreach ($arr as $k => $v) {
|
|
$selected = is_bool($selected) ? ($selected ? 1 : 0) : $selected;
|
|
$html .= '<option value="' . $k . '"' . ($selected == $k ? ' selected' : '') . '>' . $v . '</option>' . "\n";
|
|
}
|
|
|
|
return $html;
|
|
}
|
|
|
|
function get_checkbox_html($options = [])
|
|
{
|
|
if (!array_key_exists('name', $options)) {
|
|
return 'ERR:CHECKBOX_NAME_MISSING';
|
|
}
|
|
|
|
$options = array_merge([
|
|
'value_checked' => 1,
|
|
'value_unchecked' => 0,
|
|
'label' => $options['name'],
|
|
'checked' => false,
|
|
], $options);
|
|
|
|
$tooltip = $options['tooltip'] ? (' rel="tooltip" title="' . $options['tooltip'] . '"') : null;
|
|
|
|
$html = '<div class="checkbox-label">' . "\n" .
|
|
' <label for="' . $options['name'] . '"' . $tooltip . '>' . "\n" .
|
|
' <input type="hidden" name="' . $options['name'] . '" value="' . $options['value_unchecked'] . '">' . "\n" .
|
|
' <input type="checkbox" name="' . $options['name'] . '" id="' . $options['name'] . '" ' . ((bool) $options['checked'] ? ' checked' : null) . ' value="' . $options['value_checked'] . '">' . $options['label'] . "\n" .
|
|
' </label>' . "\n" .
|
|
'</div>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
function get_recaptcha_component($id = 'g-recaptcha')
|
|
{
|
|
if (CHV\getSetting('recaptcha_version') == 3) {
|
|
return ['recaptcha_invisible_html', get_recaptcha_invisible_html()];
|
|
}
|
|
|
|
return ['recaptcha_html', get_recaptcha_html($id)];
|
|
}
|
|
|
|
function get_recaptcha_invisible_html()
|
|
{
|
|
return '<script>
|
|
const recaptchaAction = "' . G\get_route_name() . '";
|
|
const recaptchaLocal = "' . G\get_base_url('recaptcha-verify') . '";
|
|
grecaptcha.ready(function() {
|
|
grecaptcha.execute("' . CHV\getSetting('recaptcha_public_key') . '", {action: recaptchaAction})
|
|
.then(function(token) {
|
|
fetch(recaptchaLocal + "/?action=" + recaptchaAction + "&token="+token).then(function(response) {
|
|
response.json().then(function(data) {
|
|
console.log(data);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
</script>';
|
|
}
|
|
|
|
function get_recaptcha_html($id = 'g-recaptcha')
|
|
{
|
|
return strtr('<div id="%id" data-recaptcha-element class="g-recaptcha"></div>', ['%id' => $id]);
|
|
}
|
|
|
|
/**
|
|
* ----------------------------------------------------------------------------------------------------------------------------------------
|
|
* ----------------------------------------------------------------------------------------------------------------------------------------
|
|
* ----------------------------------------------------------------------------------------------------------------------------------------.
|
|
*/
|
|
function get_share_links($share_element)
|
|
{
|
|
if (function_exists('get_share_links')) {
|
|
return \get_share_links($share_element);
|
|
}
|
|
|
|
if (!$share_element['twitter']) {
|
|
$share_element['twitter'] = CHV\getSetting('twitter_account');
|
|
}
|
|
|
|
$share_element['urlencoded'] = array();
|
|
|
|
foreach ($share_element as $key => $value) {
|
|
if ($key == 'urlencoded') {
|
|
continue;
|
|
}
|
|
$share_element['urlencoded'][$key] = rawurlencode($value);
|
|
}
|
|
|
|
global $share_links_networks;
|
|
G\Render\include_theme_file('custom_hooks/share_links');
|
|
|
|
if (!$share_links_networks) {
|
|
$share_links_networks = array(
|
|
'facebook' => array(
|
|
'url' => 'http://www.facebook.com/share.php?u=%URL%',
|
|
'label' => 'Facebook',
|
|
),
|
|
'twitter' => array(
|
|
'url' => 'https://twitter.com/intent/tweet?original_referer=%URL%&url=%URL%&text=%TITLE%' . ($share_element['twitter'] ? '&via=%TWITTER%' : null),
|
|
'label' => 'Twitter',
|
|
),
|
|
'whatsapp' => array(
|
|
'url' => 'whatsapp://send?text=%TITLE% - ' . _s('view on %s', G\safe_html(CHV\getSetting('website_name'))) . ': %URL%',
|
|
'label' => 'WhatsApp',
|
|
'mobileonly' => true,
|
|
),
|
|
'reddit' => array(
|
|
'url' => 'http://reddit.com/submit?url=%URL%',
|
|
'label' => 'reddit',
|
|
),
|
|
'vk' => array(
|
|
'url' => 'http://vk.com/share.php?url=%URL%',
|
|
'label' => 'VK',
|
|
),
|
|
'blogger' => array(
|
|
'url' => 'http://www.blogger.com/blog-this.g?n=%TITLE%&source=&b=%HTML%',
|
|
'label' => 'Blogger',
|
|
),
|
|
'tumblr' => array(
|
|
'url' => 'http://www.tumblr.com/share/photo?source=%PHOTO_URL%&caption=%TITLE%&clickthru=%URL%&title=%TITLE%',
|
|
'label' => 'Tumblr.',
|
|
),
|
|
'pinterest' => array(
|
|
'url' => 'http://www.pinterest.com/pin/create/bookmarklet/?media=%PHOTO_URL%&url=%URL%&is_video=false&description=%DESCRIPTION%&title=%TITLE%',
|
|
'label' => 'Pinterest',
|
|
),
|
|
'stumbleupon' => array(
|
|
'url' => 'http://www.stumbleupon.com/submit?url=%URL%',
|
|
'label' => 'StumbleUpon',
|
|
),
|
|
'mail' => array(
|
|
'url' => 'mailto:?subject=%TITLE%&body=%URL%',
|
|
'label' => 'Email',
|
|
),
|
|
);
|
|
}
|
|
|
|
$return = array();
|
|
|
|
foreach ($share_links_networks as $key => $value) {
|
|
$search = array('%URL%', '%TITLE%', '%DESCRIPTION%', '%HTML%', '%PHOTO_URL%', '%TWITTER%');
|
|
$replace = array('url', 'title', 'description', 'HTML', 'image', 'twitter');
|
|
|
|
for ($i = 0; $i < count($replace); ++$i) {
|
|
if (array_key_exists($replace[$i], $share_element['urlencoded'])) {
|
|
$replace[$i] = $share_element['urlencoded'][$replace[$i]];
|
|
}
|
|
}
|
|
|
|
$value['url'] = str_replace($search, $replace, $value['url']);
|
|
|
|
$return[] = '<li' . ($value['mobileonly'] ? ' class="hidden phone-show"' : null) . '><a data-href="' . $value['url'] . '" class="popup-link btn-32 btn-social btn-' . $key . '" rel="tooltip" data-tiptip="top" title="' . $value['label'] . '"><span class="btn-icon icon-' . $key . '"></span></a></li>';
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* PEAFOWL FRAMEWORK
|
|
* ----------------------------------------------------------------------------------------------------------------------------------------.
|
|
*/
|
|
function include_peafowl_head()
|
|
{
|
|
echo '<meta name="generator" content="' . G_APP_NAME . ' ' . CHV\get_chevereto_version() . '">' . "\n" .
|
|
'<link rel="stylesheet" href="' . get_static_url(CHV_PATH_PEAFOWL . 'peafowl.css') . '">' . "\n" .
|
|
'<link rel="stylesheet" href="' . get_theme_file_url('style.css') . '">' . "\n\n" .
|
|
'<script data-cfasync="false">document.documentElement.className+=" js";var devices=["phone","phablet","tablet","laptop","desktop","largescreen"],window_to_device=function(){for(var e=[480,768,992,1200,1880,2180],t=[],n="",d=document.documentElement.clientWidth||document.getElementsByTagName("body")[0].clientWidth||window.innerWidth,c=0;c<devices.length;++c)d>=e[c]&&t.push(devices[c]);for(0==t.length&&t.push(devices[0]),n=t[t.length-1],c=0;c<devices.length;++c)document.documentElement.className=document.documentElement.className.replace(devices[c],""),c==devices.length-1&&(document.documentElement.className+=" "+n),document.documentElement.className=document.documentElement.className.replace(/\s+/g," ");if("laptop"==n||"desktop"==n){var o=document.getElementById("pop-box-mask");null!==o&&o.parentNode.removeChild(o)}};window_to_device(),window.onresize=window_to_device;function jQueryLoaded(){!function(n,d){n.each(readyQ,function(d,e){n(e)}),n.each(bindReadyQ,function(e,i){n(d).bind("ready",i)})}(jQuery,document)}!function(n,d,e){function i(d,e){"ready"==d?n.bindReadyQ.push(e):n.readyQ.push(d)}n.readyQ=[],n.bindReadyQ=[];var u={ready:i,bind:i};n.$=n.jQuery=function(n){return n===d||void 0===n?u:void i(n)}}(window,document);
|
|
</script>' . "\n\n";
|
|
if (G\Handler::getCond('captcha_show') && CHV\getSetting('recaptcha_version') == 3) {
|
|
echo '<script src="https://www.google.com/recaptcha/api.js?render=' . CHV\getSetting('recaptcha_public_key') . '"></script>';
|
|
}
|
|
}
|
|
// Get cookie law banner
|
|
function get_cookie_law_banner()
|
|
{
|
|
return '<div id="cookie-law-banner" data-cookie="CHV_COOKIE_LAW_DISPLAY"><div class="c24 center-box position-relative"><p class="">' . _s('We use our own and third party cookies to improve your browsing experience and our services. If you continue using our website is understood that you accept this %cookie_policy_link.', ['%cookie_policy_link' => '<a href="' . G\get_base_url('page/privacy') . '">' . _s('cookie policy') . '</a>']) . '</p><a data-action="cookie-law-close" title="' . _s('I understand') . '" class="cookie-law-close"><span class="icon icon-close"></span></a></div></div>' . "\n\n";
|
|
}
|
|
|
|
// Sensitive Cookie law display
|
|
function display_cookie_law_banner()
|
|
{
|
|
if (!CHV\getSetting('enable_cookie_law') or CHV\Login::getUser()) {
|
|
return;
|
|
}
|
|
// No user logged in and cookie law has not been accepted
|
|
if (!isset($_COOKIE['CHV_COOKIE_LAW_DISPLAY']) or (bool) $_COOKIE['CHV_COOKIE_LAW_DISPLAY'] !== false) {
|
|
echo get_cookie_law_banner();
|
|
}
|
|
}
|
|
|
|
function include_peafowl_foot()
|
|
{
|
|
display_cookie_law_banner();
|
|
$resources = [
|
|
'peafowl' => CHV_PATH_PEAFOWL . 'peafowl.js',
|
|
'chevereto' => G_APP_PATH_LIB . 'chevereto.js',
|
|
];
|
|
foreach ($resources as $k => &$v) {
|
|
$v = get_static_url($v);
|
|
}
|
|
$resources['scripts'] = get_static_url(CHV_PATH_PEAFOWL . 'js/scripts.js');
|
|
$echo = [
|
|
'<script defer data-cfasync="false" src="' . $resources['scripts'] . '" id="jquery-js" onload="jQueryLoaded(this, event)"></script>',
|
|
'<script defer data-cfasync="false" src="' . $resources['peafowl'] . '" id="peafowl-js"></script>',
|
|
'<script defer data-cfasync="false" src="' . $resources['chevereto'] . '" id="chevereto-js"></script>',
|
|
];
|
|
if (G\Handler::getCond('captcha_needed') && CHV\getSetting('recaptcha_version') != 3) {
|
|
$echo[] = strtr('<script>
|
|
var PFrecaptchaCallback = function() {
|
|
$("[data-recaptcha-element]:empty:visible").each(function() {
|
|
var $this = $(this);
|
|
grecaptcha.render($this.attr("id"), {
|
|
sitekey: "%k",
|
|
theme: "%t"
|
|
});
|
|
});
|
|
};
|
|
</script>', [
|
|
'%k' => CHV\getSetting('recaptcha_public_key'),
|
|
'%t' => in_array(G\Handler::getVar('theme_tone'), ['light', 'dark']) ? G\Handler::getVar('theme_tone') : 'light', // Esto es MongoCodeQl (en camel case)
|
|
]);
|
|
$echo[] = '<script defer src="https://www.google.com/recaptcha/api.js?onload=PFrecaptchaCallback&render=explicit"></script>';
|
|
}
|
|
|
|
if (method_exists('CHV\Settings', 'getChevereto')) {
|
|
$echo[] = '<script data-cfasync="false">var CHEVERETO = ' . json_encode(CHV\Settings::getChevereto()) . '</script>';
|
|
}
|
|
echo implode("\n", $echo);
|
|
}
|
|
|
|
function get_peafowl_item_list($tpl = 'image', $item, $template, $requester = null, $tools)
|
|
{
|
|
if (empty($requester)) {
|
|
$requester = CHV\Login::getUser();
|
|
} elseif (!is_array($requester) and !is_null($requester)) {
|
|
$requester = CHV\User::getSingle($requester, 'id');
|
|
}
|
|
|
|
// Default
|
|
$stock_tpl = 'IMAGE';
|
|
|
|
if ($tpl == 'album' || $tpl == 'user/album') {
|
|
$stock_tpl = 'ALBUM';
|
|
}
|
|
if ($tpl == 'user' || $tpl == 'user/user') {
|
|
$stock_tpl = 'USER';
|
|
if ($item['is_private']) {
|
|
if ($requester['is_admin'] || $requester['is_manager']) {
|
|
$item['name'] = '🔒 ' . $item['name'];
|
|
} else {
|
|
unset($item);
|
|
$item = CHV\User::getPrivate();
|
|
}
|
|
}
|
|
} else {
|
|
if (array_key_exists('user', $item)) {
|
|
CHV\User::fill($item['user']);
|
|
}
|
|
}
|
|
|
|
if (in_array($stock_tpl, ['IMAGE', 'ALBUM'])) {
|
|
$item['liked'] = is_null($item['like']['user_id']) ? 0 : ($requester['id'] == $item['like']['user_id'] ? 1 : 0);
|
|
}
|
|
|
|
if ($stock_tpl == 'IMAGE') {
|
|
if (!$item['is_animated'] || !isset($item['file_resource']['chain']['image'])) {
|
|
$conditional_replaces['tpl_list_item/item_image_play_gif'] = null;
|
|
}
|
|
} elseif (!isset($item['images_slice'][0]['is_animated']) || $item['images_slice'][0]['is_animated'] == false) {
|
|
$conditional_replaces['tpl_list_item/item_image_play_gif'] = null;
|
|
}
|
|
|
|
$fill_tpl = $tpl;
|
|
|
|
if ($stock_tpl == 'ALBUM' && $requester['is_admin'] == false && $item['privacy'] == 'password' && !($item['user']['id'] && $item['user']['id'] == $requester['id']) && CHV\Album::checkSessionPassword($item) == false) {
|
|
$fill_tpl = 'album_password';
|
|
}
|
|
|
|
$filled_template = $template["tpl_list_item/$fill_tpl"]; // Stock the unfilled template
|
|
|
|
if (!CHV\getSetting('enable_likes') || $requester['is_private']/* || $item['user']['is_private']*/) {
|
|
$conditional_replaces['tpl_list_item/item_like'] = null;
|
|
}
|
|
|
|
if ($item['user']['is_private'] && !$requester['is_admin'] && $item['user']['id'] !== $requester['id']) {
|
|
unset($item['user']);
|
|
$item['user'] = CHV\User::getPrivate();
|
|
$conditional_replaces['tpl_list_item/image_description_user'] = null;
|
|
$conditional_replaces['tpl_list_item/image_description_guest'] = null;
|
|
} else {
|
|
$conditional_replaces['tpl_list_item/image_description_private'] = null;
|
|
}
|
|
|
|
if ($item['user']['is_private'] && ($requester['is_admin'] || $requester['is_manager'])) {
|
|
$item['user']['name'] = '🔒 ' . $item['user']['name'];
|
|
}
|
|
|
|
$conditional_replaces[$item['user']['id'] == null ? 'tpl_list_item/image_description_user' : 'tpl_list_item/image_description_guest'] = null;
|
|
$conditional_replaces[$item['user']['avatar'] == null ? 'tpl_list_item/image_description_user_avatar' : 'tpl_list_item/image_description_user_no_avatar'] = null;
|
|
|
|
if ($stock_tpl == 'IMAGE') {
|
|
$conditional_replaces['tpl_list_item/' . (!$item['file_resource']['chain']['image'] ? 'image_cover_image' : 'image_cover_empty')] = null;
|
|
}
|
|
|
|
if ($stock_tpl == 'ALBUM') {
|
|
if ($item['privacy'] !== 'password' || (!$requester['is_admin'] || $item['user']['id'] !== $requester['id'])) {
|
|
$item['password'] = null;
|
|
}
|
|
|
|
if ($fill_tpl == 'album_password') {
|
|
unset($item['images_slice']);
|
|
}
|
|
|
|
$conditional_replaces['tpl_list_item/' . (($item['image_count'] == 0 or !$item['images_slice'][0]['file_resource']) ? 'album_cover_image' : 'album_cover_empty')] = null;
|
|
|
|
for ($i = 1; $i < count((array) $item['images_slice']); ++$i) {
|
|
if (!$item['images_slice'][$i]['file_resource']['chain']['thumb']) {
|
|
continue;
|
|
}
|
|
$template['tpl_list_item/album_thumbs'] = str_replace("%$i", '', $template['tpl_list_item/album_thumbs']);
|
|
}
|
|
$template['tpl_list_item/album_thumbs'] = preg_replace('/%[0-9]+(.*)%[0-9]+/', '', $template['tpl_list_item/album_thumbs']);
|
|
}
|
|
|
|
if ($stock_tpl == 'USER') {
|
|
$conditional_replaces[$item['avatar'] ? 'tpl_list_item/user_no_avatar' : 'tpl_list_item/user_avatar'] = null;
|
|
foreach (array('twitter', 'facebook', 'website') as $social) {
|
|
if (!$item[$social]) {
|
|
$conditional_replaces['tpl_list_item/user_' . $social] = null;
|
|
}
|
|
}
|
|
$conditional_replaces[empty($item['avatar']['url']) ? 'tpl_list_item/user_cover_image' : 'tpl_list_item/user_cover_empty'] = null;
|
|
$conditional_replaces[empty($item['background']['url']) ? 'tpl_list_item/user_background_image' : 'tpl_list_item/user_background_empty'] = null;
|
|
}
|
|
|
|
if (is_null($requester)) {
|
|
$show_item_edit_tools = false;
|
|
$show_item_public_tools = false;
|
|
} else {
|
|
//$show_item_edit_tools = $item["user"]["id"] == $requester['id'];
|
|
if (!is_null($tools)) {
|
|
$show_item_edit_tools = !is_array($tools);
|
|
$show_item_public_tools = is_array($tools);
|
|
} else {
|
|
$show_item_edit_tools = false;
|
|
$show_item_public_tools = false;
|
|
}
|
|
|
|
if ($requester['is_admin'] || $requester['is_manager']) {
|
|
$show_item_edit_tools = true;
|
|
$show_item_public_tools = false;
|
|
}
|
|
}
|
|
|
|
if (!$show_item_public_tools) {
|
|
$template['tpl_list_item/item_' . strtolower($stock_tpl) . '_public_tools'] = null;
|
|
}
|
|
|
|
if (!$show_item_edit_tools) {
|
|
$template['tpl_list_item/item_' . strtolower($stock_tpl) . '_edit_tools'] = null;
|
|
}
|
|
|
|
if (!($requester['is_admin'] || $requester['is_manager'])) {
|
|
$template['tpl_list_item/item_' . strtolower($stock_tpl) . '_admin_tools'] = null;
|
|
}
|
|
|
|
foreach ($conditional_replaces as $k => $v) {
|
|
$template[$k] = $v;
|
|
}
|
|
|
|
preg_match_all('#%(tpl_list_item/.*)%#', $filled_template, $matches);
|
|
|
|
if (is_array($matches[1])) {
|
|
foreach ($matches[1] as $k => $v) {
|
|
$filled_template = replace_tpl_string($v, $template[$v], $filled_template);
|
|
}
|
|
}
|
|
|
|
foreach ($template as $k => $v) {
|
|
$filled_template = replace_tpl_string($k, $v, $filled_template);
|
|
}
|
|
|
|
// Get rid of the useless keys
|
|
unset($item['original_exifdata']);
|
|
|
|
// Now stock the item values
|
|
$replacements = array_change_key_case(flatten_array($item, $stock_tpl . '_'), CASE_UPPER);
|
|
|
|
unset($replacements['IMAGE_ORIGINAL_EXIFDATA']);
|
|
|
|
if ($stock_tpl == 'IMAGE' or $stock_tpl == 'ALBUM') {
|
|
$replacements['ITEM_URL_EDIT'] = ($stock_tpl == 'IMAGE' ? $item['url_viewer'] : $item['url']) . '#edit';
|
|
}
|
|
|
|
// Public for the guest
|
|
if (!array_key_exists('user', $item)) {
|
|
$replacements['IMAGE_ALBUM_PRIVACY'] = 'public';
|
|
}
|
|
|
|
if (in_array($stock_tpl, ['IMAGE', 'ALBUM'])) {
|
|
$nsfw = $stock_tpl == 'IMAGE' ? $item['nsfw'] : $item['images_slice'][0]['nsfw'];
|
|
$placeholder = $stock_tpl == 'IMAGE' ? 'IMAGE_FLAG' : 'ALBUM_COVER_FLAG';
|
|
$replacements[$placeholder] = $nsfw ? 'unsafe' : 'safe';
|
|
}
|
|
|
|
// $show_object = ($show_item_edit_tools || $show_item_public_tools) || ($requester['is_admin'] || (!is_null($requester) AND $item["user"]["id"] == $requester['id']));
|
|
$show_object = true;
|
|
|
|
if ($show_object) {
|
|
$object = G\array_filter_array($item, ['id_encoded', 'image', 'medium', 'thumb', 'name', 'title', 'display_url', 'extension', 'filename', 'height', 'how_long_ago', 'size_formatted', 'url', 'url_viewer', 'url_short', 'width']);
|
|
if ($item['user']) {
|
|
$object['user'] = [];
|
|
foreach (['avatar', 'url', 'username', 'name_short_html'] as $k) {
|
|
$object['user'][$k] = $item['user'][$k];
|
|
}
|
|
}
|
|
$replacements['DATA_OBJECT'] = "data-object='" . rawurlencode(json_encode(G\array_utf8encode($object))) . "'";
|
|
} else {
|
|
$replacements['DATA_OBJECT'] = null;
|
|
}
|
|
|
|
if ($stock_tpl == 'IMAGE') {
|
|
$replacements['SIZE_TYPE'] = CHV\getSetting('theme_image_listing_sizing') . '-size';
|
|
}
|
|
|
|
foreach ($replacements as $k => $v) {
|
|
$filled_template = replace_tpl_string($k, $v, $filled_template);
|
|
}
|
|
|
|
$column_sizes = array(
|
|
'image' => 8,
|
|
'album' => 8,
|
|
'user' => 8,
|
|
);
|
|
|
|
foreach ($column_sizes as $k => $v) {
|
|
$filled_template = replace_tpl_string('COLUMN_SIZE_' . strtoupper($k), $v, $filled_template);
|
|
}
|
|
|
|
return $filled_template;
|
|
}
|
|
|
|
function replace_tpl_string($search, $replace, $subject)
|
|
{
|
|
return str_replace('%' . $search . '%', is_null($replace) ? '' : $replace, $subject);
|
|
}
|
|
|
|
// http://stackoverflow.com/a/9546215
|
|
function flatten_array($array, $prefix = '')
|
|
{
|
|
$result = array();
|
|
foreach ($array as $key => $value) {
|
|
if (is_array($value)) {
|
|
$result = $result + flatten_array($value, $prefix . $key . '_');
|
|
} else {
|
|
$result[$prefix . $key] = $value;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
// This function is sort of an alias of php die() but with html error display
|
|
function chevereto_die($error_msg, $paragraph = null, $title = null)
|
|
{
|
|
if (!is_array($error_msg) && G\check_value($error_msg)) {
|
|
$error_msg = array($error_msg);
|
|
}
|
|
|
|
if (is_null($paragraph)) {
|
|
$paragraph = "The system has encountered errors that must be fixed to allow proper Chevereto functionality. Chevereto won't work until the following issues are solved:";
|
|
}
|
|
$solution = "Need help or questions about this? Go to <a href='http://chevereto.com/support' target='_blank'>Chevereto support<a/>.";
|
|
$title = (!is_null($title)) ? $title : 'System error';
|
|
$doctitle = $title . ' - Chevereto';
|
|
|
|
$handled_request = G_ROOT_PATH == '/' ? sanitize_path_slashes($_SERVER['REQUEST_URI']) : str_ireplace(G_ROOT_PATH_RELATIVE, '', G\add_trailing_slashes($_SERVER['REQUEST_URI']));
|
|
$base_request = explode('/', rtrim(str_replace('//', '/', str_replace('?', '/', $handled_request)), '/'))[0];
|
|
|
|
if ($base_request == 'json' || $base_request == 'api') {
|
|
$output = array(
|
|
'status_code' => 500,
|
|
'status_txt' => G\get_set_status_header_desc(500),
|
|
'error' => $title,
|
|
'errors' => $error_msg,
|
|
);
|
|
G\set_status_header(500);
|
|
G\json_prepare();
|
|
die(G\Render\json_output($output));
|
|
}
|
|
|
|
$html = [
|
|
'<h1>' . $title . '</h1>',
|
|
'<p>' . $paragraph . '</p>',
|
|
];
|
|
|
|
if (is_array($error_msg)) {
|
|
$html[] = '<ul class="errors">';
|
|
foreach ($error_msg as $error) {
|
|
$html[] = '<li>' . $error . '</li>';
|
|
}
|
|
$html[] = '</ul>';
|
|
}
|
|
|
|
$html[] = '<p>' . $solution . '</p>';
|
|
$html = join('', $html);
|
|
$template = CHV_APP_PATH_CONTENT_SYSTEM . 'template.php';
|
|
|
|
if (!require_once($template)) {
|
|
die("Can't find " . G\absolute_to_relative($template));
|
|
}
|
|
|
|
die();
|
|
}
|
|
|
|
function getFriendlyExif($Exif)
|
|
{
|
|
if (gettype($Exif) == 'string') {
|
|
$Exif = json_decode($Exif);
|
|
}
|
|
|
|
if ($Exif->Make) {
|
|
$exif_one_line = [];
|
|
if ($Exif->ExposureTime) {
|
|
$Exposure = $Exif->ExposureTime . 's';
|
|
$exif_one_line[] = $Exposure;
|
|
}
|
|
if ($Exif->FNumber or $Exif->COMPUTED->ApertureFNumber) {
|
|
$Aperture = 'ƒ/' . ($Exif->FNumber ? G\fraction_to_decimal($Exif->FNumber) : explode('/', $Exif->COMPUTED->ApertureFNumber)[1]);
|
|
$exif_one_line[] = $Aperture;
|
|
}
|
|
if ($Exif->ISOSpeedRatings) {
|
|
$ISO = 'ISO' . (is_array($Exif->ISOSpeedRatings) ? $Exif->ISOSpeedRatings[0] : $Exif->ISOSpeedRatings);
|
|
$exif_one_line[] = $ISO;
|
|
}
|
|
if ($Exif->FocalLength) {
|
|
$FocalLength = G\fraction_to_decimal($Exif->FocalLength) . 'mm';
|
|
$exif_one_line[] = $FocalLength;
|
|
}
|
|
|
|
$exif_relevant = [
|
|
'XResolution',
|
|
'YResolution',
|
|
'ResolutionUnit',
|
|
'ColorSpace',
|
|
'Orientation',
|
|
'Software',
|
|
'BrightnessValue',
|
|
'SensingMethod',
|
|
'SceneCaptureType',
|
|
'GainControl',
|
|
'ExposureBiasValue',
|
|
'MaxApertureValue',
|
|
'ExposureProgram',
|
|
'ExposureMode',
|
|
'MeteringMode',
|
|
'LightSource',
|
|
'Flash',
|
|
'WhiteBalance',
|
|
'DigitalZoomRatio',
|
|
'Contrast',
|
|
'Saturation',
|
|
'Sharpness',
|
|
'ExifVersion',
|
|
'DateTimeModified',
|
|
'DateTimeOriginal',
|
|
'DateTimeDigitized',
|
|
];
|
|
$ExifRelevant = [];
|
|
foreach ($exif_relevant as $k) {
|
|
if (property_exists($Exif, $k)) {
|
|
$exifReadableValue = exifReadableValue($Exif, $k);
|
|
if ($exifReadableValue !== null && !is_array($exifReadableValue)) { // Just make sure to avoid this array
|
|
$ExifRelevant[$k] = $exifReadableValue;
|
|
}
|
|
}
|
|
}
|
|
$return = (object) [
|
|
'Simple' => (object) [
|
|
'Camera' => $Exif->Make . ' ' . $Exif->Model,
|
|
'Capture' => implode(' ', $exif_one_line),
|
|
],
|
|
'Full' => (object) array_merge([
|
|
'Manufacturer' => $Exif->Make,
|
|
'Model' => $Exif->Model,
|
|
'ExposureTime' => $Exposure,
|
|
'Aperture' => $Aperture,
|
|
'ISO' => preg_replace('/iso/i', '', $ISO),
|
|
'FocalLength' => $FocalLength,
|
|
], $ExifRelevant),
|
|
];
|
|
// Clean all this stuff
|
|
foreach ($return as $k => &$v) {
|
|
if ($k == 'Full') {
|
|
$v = (object) array_filter((array) $v, 'strlen');
|
|
}
|
|
foreach ($v as $kk => $vv) {
|
|
$return->{$k}->{$kk} = G\safe_html(strip_tags($vv));
|
|
}
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function exifReadableValue($Exif, $key)
|
|
{
|
|
$table = [
|
|
'PhotometricInterpretation' => [
|
|
0 => 'WhiteIsZero',
|
|
1 => 'BlackIsZero',
|
|
2 => 'RGB',
|
|
3 => 'RGB Palette',
|
|
4 => 'Transparency Mask',
|
|
5 => 'CMYK',
|
|
6 => 'YCbCr',
|
|
8 => 'CIELab',
|
|
9 => 'ICCLab',
|
|
10 => 'ITULab',
|
|
32803 => 'Color Filter Array',
|
|
32844 => 'Pixar LogL',
|
|
32845 => 'Pixar LogLuv',
|
|
34892 => 'Linear Raw',
|
|
],
|
|
'ColorSpace' => [
|
|
1 => 'sRGB',
|
|
2 => 'Adobe RGB',
|
|
65533 => 'Wide Gamut RGB',
|
|
65534 => 'ICC Profile',
|
|
65535 => 'Uncalibrated',
|
|
],
|
|
'Orientation' => [
|
|
1 => 'Horizontal (normal)',
|
|
2 => 'Mirror horizontal',
|
|
3 => 'Rotate 180',
|
|
4 => 'Mirror vertical',
|
|
5 => 'Mirror horizontal and rotate 270 CW',
|
|
6 => 'Rotate 90 CW',
|
|
7 => 'Mirror horizontal and rotate 90 CW',
|
|
8 => 'Rotate 270 CW',
|
|
],
|
|
'ResolutionUnit' => [
|
|
1 => 'None',
|
|
2 => 'inches',
|
|
3 => 'cm',
|
|
],
|
|
'ExposureProgram' => [
|
|
0 => 'Not Defined',
|
|
1 => 'Manual',
|
|
2 => 'Program AE',
|
|
3 => 'Aperture-priority AE',
|
|
4 => 'Shutter speed priority AE',
|
|
5 => 'Creative (Slow speed)',
|
|
6 => 'Action (High speed)',
|
|
7 => 'Portrait',
|
|
8 => 'Landscape',
|
|
9 => 'Bulb',
|
|
],
|
|
'MeteringMode' => [
|
|
0 => 'Unknown',
|
|
1 => 'Average',
|
|
2 => 'Center-weighted average',
|
|
3 => 'Spot',
|
|
4 => 'Multi-spot',
|
|
5 => 'Multi-segment',
|
|
6 => 'Partial',
|
|
255 => 'Other',
|
|
],
|
|
'ExposureMode' => [
|
|
0 => 'Auto',
|
|
1 => 'Manual',
|
|
2 => 'Auto bracket',
|
|
],
|
|
'SensingMethod' => [
|
|
1 => 'Monochrome area',
|
|
2 => 'One-chip color area',
|
|
3 => 'Two-chip color area',
|
|
4 => 'Three-chip color area',
|
|
5 => 'Color sequential area',
|
|
6 => 'Monochrome linear',
|
|
7 => 'Trilinear',
|
|
8 => 'Color sequential linear',
|
|
],
|
|
'SceneCaptureType' => [
|
|
0 => 'Standard',
|
|
1 => 'Landscape',
|
|
2 => 'Portrait',
|
|
3 => 'Night',
|
|
],
|
|
'GainControl' => [
|
|
0 => 'None',
|
|
1 => 'Low gain up',
|
|
2 => 'High gain up',
|
|
3 => 'Low gain down',
|
|
4 => 'High gain down',
|
|
],
|
|
'Saturation' => [
|
|
0 => 'Normal',
|
|
1 => 'Low',
|
|
2 => 'High',
|
|
],
|
|
'Sharpness' => [
|
|
0 => 'Normal',
|
|
1 => 'Soft',
|
|
2 => 'Hard',
|
|
],
|
|
'Flash' => [
|
|
0 => 'No Flash',
|
|
1 => 'Fired',
|
|
5 => 'Fired, Return not detected',
|
|
7 => 'Fired, Return detected',
|
|
8 => 'On, Did not fire',
|
|
9 => 'On, Fired',
|
|
13 => 'On, Return not detected',
|
|
15 => 'On, Return detected',
|
|
16 => 'Off, Did not fire',
|
|
20 => 'Off, Did not fire, Return not detected',
|
|
24 => 'Auto, Did not fire',
|
|
25 => 'Auto, Fired',
|
|
29 => 'Auto, Fired, Return not detected',
|
|
31 => 'Auto, Fired, Return detected',
|
|
32 => 'No flash function',
|
|
48 => 'Off, No flash function',
|
|
65 => 'Fired, Red-eye reduction',
|
|
69 => 'Fired, Red-eye reduction, Return not detected',
|
|
71 => 'Fired, Red-eye reduction, Return detected',
|
|
73 => 'On, Red-eye reduction',
|
|
77 => 'On, Red-eye reduction, Return not detected',
|
|
79 => 'On, Red-eye reduction, Return detected',
|
|
80 => 'Off, Red-eye reduction',
|
|
88 => 'Auto, Did not fire, Red-eye reduction',
|
|
89 => 'Auto, Fired, Red-eye reduction',
|
|
93 => 'Auto, Fired, Red-eye reduction, Return not detected',
|
|
95 => 'Auto, Fired, Red-eye reduction, Return detected',
|
|
],
|
|
'LightSource' => [
|
|
0 => 'Unknown',
|
|
1 => 'Daylight',
|
|
2 => 'Fluorescent',
|
|
3 => 'Tungsten (Incandescent)',
|
|
4 => 'Flash',
|
|
9 => 'Fine Weather',
|
|
10 => 'Cloudy',
|
|
11 => 'Shade',
|
|
12 => 'Daylight Fluorescent',
|
|
13 => 'Day White Fluorescent',
|
|
14 => 'Cool White Fluorescent',
|
|
15 => 'White Fluorescent',
|
|
16 => 'Warm White Fluorescent',
|
|
17 => 'Standard Light A',
|
|
18 => 'Standard Light B',
|
|
19 => 'Standard Light C',
|
|
20 => 'D55',
|
|
21 => 'D65',
|
|
22 => 'D75',
|
|
23 => 'D50',
|
|
24 => 'ISO Studio Tungsten',
|
|
255 => 'Other',
|
|
],
|
|
];
|
|
$table['Contrast'] = $table['Saturation'];
|
|
|
|
if (is_object($Exif) and is_array($Exif->$key)) {
|
|
$value_arr = [];
|
|
foreach ($Exif->$key as $k) {
|
|
$value_arr[] = $table[$key][$k];
|
|
}
|
|
$value = implode(', ', $value_arr);
|
|
} else {
|
|
// Set $value from table above (translator) if exists
|
|
$value = $table[$key][$Exif->$key] ?: $Exif->$key;
|
|
}
|
|
|
|
switch ($key) {
|
|
case 'DateTime':
|
|
case 'DateTimeOriginal':
|
|
case 'DateTimeDigitized':
|
|
case 'DateTimeModified':
|
|
$value = $value !== null ? preg_replace('/(\d{4})(:)(\d{2})(:)(\d{2})/', '$1-$3-$5', $value) : null;
|
|
break;
|
|
case 'WhiteBalance':
|
|
$value = $value == 0 ? 'Auto' : $value;
|
|
break;
|
|
case 'BrightnessValue':
|
|
case 'MaxApertureValue':
|
|
$value = $value ? G\fraction_to_decimal($value) : null;
|
|
break;
|
|
case 'XResolution':
|
|
case 'YResolution':
|
|
$value = $value ? (floor(G\fraction_to_decimal($value)) . ' dpi') : null;
|
|
break;
|
|
}
|
|
|
|
return $value ?: null;
|
|
}
|
|
|
|
function arr_printer($arr, $tpl = '', $wrap = [])
|
|
{
|
|
ksort($arr);
|
|
$rtn = '';
|
|
$rtn .= $wrap[0];
|
|
foreach ($arr as $k => $v) {
|
|
if (is_array($v)) {
|
|
$rtn .= strtr($tpl, ['%K' => $k, '%V' => arr_printer($v, $tpl, $wrap)]);
|
|
} else {
|
|
$rtn .= strtr($tpl, ['%K' => $k, '%V' => $v]);
|
|
}
|
|
}
|
|
$rtn .= $wrap[1];
|
|
|
|
return $rtn;
|
|
}
|
|
|
|
function versionize_src($src)
|
|
{
|
|
return $src . '?' . md5(CHV\get_chevereto_version());
|
|
}
|
|
|
|
function show_banner($banner, $sfw = true)
|
|
{
|
|
if (!$sfw) {
|
|
$banner .= '_nsfw';
|
|
}
|
|
$banner_code = CHV\get_banner_code($banner, false);
|
|
if ($banner_code) {
|
|
echo '<div id="' . $banner . '" class="ad-banner">' . $banner_code . '</div>';
|
|
}
|
|
}
|
|
|
|
function getPixel($type)
|
|
{
|
|
if (!in_array($type, ['queue', 'ping'])) {
|
|
return;
|
|
}
|
|
|
|
return '<img data-content="' . $type . '-pixel" src="' . G\get_base_url('?' . $type . '&r=' . md5(G\datetimegmt())) . '" width="1" height="1" alt="" style="display: none;">';
|
|
}
|
|
|
|
function showQueuePixel()
|
|
{
|
|
return;
|
|
}
|
|
|
|
function show_queue_img()
|
|
{
|
|
return showQueuePixel();
|
|
}
|
|
|
|
function showPingPixel()
|
|
{
|
|
if (CHV\getSetting('enable_automatic_updates_check') && (is_null(CHV\getSetting('update_check_datetimegmt')) || G\datetime_add(CHV\getSetting('update_check_datetimegmt'), 'P1D') < G\datetimegmt())) {
|
|
echo getPixel('ping');
|
|
}
|
|
}
|
|
|
|
function displayEmptyPixel()
|
|
{
|
|
header('Content-Type: image/gif');
|
|
header('Content-Length: 43');
|
|
echo base64_decode('R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==');
|
|
die();
|
|
}
|
|
|
|
function showComments()
|
|
{
|
|
switch (CHV\getSetting('comments_api')) {
|
|
case 'js':
|
|
$html = CHV\getSetting('comment_code');
|
|
break;
|
|
case 'disqus':
|
|
$disqus_secret = CHV\getSetting('disqus_secret_key');
|
|
$disqus_public = CHV\getSetting('disqus_public_key');
|
|
if (!empty($disqus_secret) && !empty($disqus_public)) {
|
|
$logged_user = CHV\Login::getuser();
|
|
$data = [
|
|
'id' => $logged_user['id_encoded'],
|
|
'username' => $logged_user['name'],
|
|
'email' => $logged_user['email'],
|
|
'avatar' => $logged_user['avatar']['url'],
|
|
'url' => $logged_user['url'],
|
|
];
|
|
function dsq_hmacsha1($data, $key)
|
|
{
|
|
$blocksize = 64;
|
|
$hashfunc = 'sha1';
|
|
if (strlen($key) > $blocksize) {
|
|
$key = pack('H*', $hashfunc($key));
|
|
}
|
|
$key = str_pad($key, $blocksize, chr(0x00));
|
|
$ipad = str_repeat(chr(0x36), $blocksize);
|
|
$opad = str_repeat(chr(0x5c), $blocksize);
|
|
$hmac = pack('H*', $hashfunc(($key ^ $opad) . pack('H*', $hashfunc(($key ^ $ipad) . $data))));
|
|
|
|
return bin2hex($hmac);
|
|
}
|
|
$message = base64_encode(json_encode($data));
|
|
$timestamp = time();
|
|
$hmac = dsq_hmacsha1($message . ' ' . $timestamp, $disqus_secret);
|
|
$auth = $message . ' ' . $hmac . ' ' . $timestamp;
|
|
}
|
|
$html = strtr('<div id="disqus_thread"></div>
|
|
<script>
|
|
var disqus_config = function() {
|
|
this.page.url = "%page_url";
|
|
this.page.identifier = "%page_id";
|
|
};
|
|
(function() {
|
|
var d = document, s = d.createElement("script");
|
|
s.src = "//%shortname.disqus.com/embed.js";
|
|
s.setAttribute("data-timestamp", +new Date());
|
|
(d.head || d.body).appendChild(s);
|
|
})();
|
|
var disqus_config = function () {
|
|
this.language = "%language_code";
|
|
this.page.remote_auth_s3 = "%auth";
|
|
this.page.api_key = "%api_key";
|
|
};
|
|
</script>
|
|
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>', [
|
|
'%page_url' => G\get_current_url(),
|
|
'%page_id' => G\str_replace_first(G\get_route_path(), G\get_route_name(), G\get_route_path(true)), // image.ID
|
|
'%shortname' => CHV\getSetting('disqus_shortname'),
|
|
'%language_code' => CHV\get_language_used()['base'],
|
|
'%auth' => isset($auth) ? $auth : null,
|
|
'%api_key' => $disqus_public,
|
|
]);
|
|
break;
|
|
}
|
|
echo $html;
|
|
}
|