homepage-picocms/plugins/PicoContact/src/P01contact_Field.php
2020-05-20 17:34:25 +02:00

431 lines
13 KiB
PHP

<?php
/**
* p01-contact - A simple contact forms manager.
*
* @see https://github.com/nliautaud/p01contact
*
* @author Nicolas Liautaud
*/
namespace P01C;
class P01contactField
{
public $id;
public $type;
public $title;
public $description;
public $value;
public $selected_values;
public $placeholder;
public $required;
public $locked;
public $error;
private $form;
/**
* @param Form $form the container form
* @param int $id the field id
* @param string $type the field type
*/
public function __construct($form, $id, $type)
{
$this->form = $form;
$this->id = $id;
$this->type = $type;
}
/**
* Set the field value or selected value.
*
* @param mixed $new_value the value, or an array of selected values ids
*/
public function setValue($new_value)
{
// simple value
if (!is_array($this->value)) {
$this->value = htmlentities($new_value, ENT_COMPAT, 'UTF-8', false);
return;
}
// multiples-values (checkbox, radio, select)
if (!is_array($new_value)) {
$new_value = [$new_value];
}
foreach ($new_value as $i) {
$this->selected_values[intval($i)] = true;
}
}
/**
* Reset the selected values by finding ones who starts or end with ":".
*/
public function resetSelectedValues()
{
$this->selected_values = [];
foreach ($this->value as $i => $val) {
$value = preg_replace('`(^\s*:|:\s*$)`', '', $val, -1, $count);
if ($count) {
$this->value[$i] = $value;
$this->selected_values[$i] = true;
}
}
}
/**
* Check field value.
*
* @return bool
*/
public function validate()
{
// empty and required
if (empty($this->value) && $this->required) {
$this->error = 'field_required';
return false;
}
// value blacklisted or not in whitelist
if ($reason = $this->isBlacklisted()) {
$this->error = 'field_' . $reason;
return false;
}
// not empty but not valid
if (!empty($this->value) && !$this->isValid()) {
$this->error = 'field_' . $this->type;
return false;
}
return true;
}
/**
* Check if field value is valid
* Mean different things depending on field type.
*
* @return bool
*/
public function isValid()
{
switch ($this->type) {
case 'email':
return filter_var($this->value, FILTER_VALIDATE_EMAIL);
case 'tel':
$pattern = '`^\+?[-0-9(). ]{6,}$$`i';
return preg_match($pattern, $this->value);
case 'url':
return filter_var($this->value, FILTER_VALIDATE_URL);
case 'message':
return strlen($this->value) > $this->form->config('message_len');
case 'captcha':
return $this->reCaptchaValidity($_POST['g-recaptcha-response']);
case 'password':
return $this->value == $this->required;
default:
return true;
}
}
/**
* Check if reCaptcha is valid.
*
* @param mixed $answer
*
* @return bool
*/
public function reCaptchaValidity($answer)
{
if (!$answer) {
return false;
}
$params = [
'secret' => $this->form->config('recaptcha_secret_key'),
'response' => $answer,
];
$url = 'https://www.google.com/recaptcha/api/siteverify?' . http_build_query($params);
if (function_exists('curl_version')) {
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($curl);
} else {
$response = file_get_contents($url);
}
if (empty($response) || is_null($response)) {
return false;
}
return json_decode($response)->success;
}
/**
* Check if field value is blacklisted.
*
* Search for every comma-separated entry of every checklist
* in value, and define if it should or should not be there.
*
* @return bool
*/
public function isBlacklisted()
{
$list = $this->form->config('checklist');
if (!$list) {
return;
}
foreach ($list as $cl) {
if ($cl->name != $this->type) {
continue;
}
$content = array_filter(explode(',', $cl->content));
foreach ($content as $avoid) {
$found = preg_match("`{$avoid}`", $this->value);
$foundBlacklisted = $found && 'blacklist' == $cl->type;
$notFoundWhitelisted = !$found && 'whitelist' == $cl->type;
if ($foundBlacklisted || $notFoundWhitelisted) {
return $cl->type;
}
}
}
return false;
}
/*
* Return the html display of the field
*
* Manage field title, error message, and type-based display
* @return string the <div>
*/
public function html()
{
$id = 'p01-contact' . $this->form->getId() . '_field' . $this->id;
$name = 'p01-contact_fields[' . $this->id . ']';
$type = $this->getGeneralType();
$orig = $type != $this->type ? $this->type : '';
$value = $this->value;
$disabled = $this->locked ? ' disabled="disabled"' : '';
$required = $this->required ? ' required ' : '';
$placeholder = $this->placeholder ? ' placeholder="' . $this->placeholder . '"' : '';
$is_single_option = is_array($this->value) && 1 == count($this->value) ? 'inline' : '';
$html = "<div class=\"col s12 input-field {$is_single_option} {$type} {$orig} {$required}\">";
switch ($type) {
case 'textarea':
$html .= '<textarea id="' . $id . '" rows="10" ';
$html .= 'class="materialize-textarea" ';
$html .= 'name="' . $name . '"' . $disabled . $required . $placeholder;
$html .= '>' . $value . '</textarea>';
break;
case 'captcha':
$key = $this->form->config('recaptcha_public_key');
if (!$key) {
break;
}
if (1 == $this->form->getId()) {
$html .= '<script src="https://www.google.com/recaptcha/api.js"></script>';
}
$html .= '<div class="g-recaptcha" id="' . $id . '" data-sitekey="' . $key . '"></div>';
$html .= "<input type=\"hidden\" id=\"{$id}\" name=\"{$name}\" value=\"trigger\">";
break;
case 'checkbox':
case 'radio':
$html .= '<div class="options row">';
foreach ($this->value as $i => $v) {
$selected = $this->isSelected($i) ? ' checked' : '';
$v = !empty($v) ? $v : 'Default';
$html .= '<p><label class="option col s12">';
$html .= "<input id=\"{$id}_option{$i}\"";
$html .= " type=\"{$type}\" class=\"{$type}\" name=\"{$name}\"";
$html .= " value=\"{$i}\"{$disabled}{$required}{$selected} /><span>{$v}</span>";
$html .= '</label></p>';
}
$html .= '</div>';
break;
case 'select':
$html .= "<select id=\"{$id}\" name=\"{$name}\"{$disabled}{$required}>";
foreach ($this->value as $i => $v) {
$value = !empty($v) ? $v : 'Default';
$selected = $this->isSelected($i) ? ' selected="selected"' : '';
$html .= "<option id=\"{$id}_option{$i}\" value=\"{$i}\"{$selected}>";
$html .= $value . '</option>';
}
$html .= '</select>';
break;
default:
$html .= '<input id="' . $id . '" ';
$html .= 'name="' . $name . '" type="' . $type . '" ';
$html .= 'value="' . $value . '"' . $disabled . $required . $placeholder . ' />';
break;
}
if ('' === $is_single_option) {
$html .= $this->htmlLabel($id);
}
$html .= '</div>';
return $html;
}
// Return a html presentation of the field value.
public function htmlMail()
{
$gen_type = $this->getGeneralType();
$properties = [];
$html = '<table style="width: 100%; margin: 1em 0; border-collapse: collapse;">';
// name
$emphasis = $this->value ? 'font-weight:bold' : 'font-style:italic';
$html .= "\n\n\n";
$html .= '<tr style="background-color: #eeeeee">';
$html .= '<td style="padding: .5em .75em"><span style="' . $emphasis . '">';
$html .= $this->title ? $this->title : ucfirst($this->form->lang($this->type));
$html .= '</span></td>';
$html .= "\t\t";
// properties
$html .= '<td style="padding:.5em 1em; text-transform:lowercase; text-align:right; font-size:.875em; color:#888888; vertical-align: middle"><em>';
if (!$this->value) {
$html .= $this->form->lang('empty') . ' ';
}
if ($this->title) {
$properties[] = $this->type;
}
if ($gen_type != $this->type) {
$properties[] = $gen_type;
}
foreach (['locked', 'required'] as $property) {
if ($this->{$property}) {
$properties[] = $this->form->lang($property);
}
}
if (count($properties)) {
$html .= '(' . implode(', ', $properties) . ') ';
}
$html .= '#' . $this->id;
$html .= '</em></td></tr>';
$html .= "\n\n";
// value
if (!$this->value) {
return $html . '</table>';
}
$html .= '<tr><td colspan=2 style="padding:0">';
$html .= '<div style="padding:.5em 1.5em;border:1px solid #ccc">';
switch ($gen_type) {
case 'checkbox':
case 'radio':
case 'select':
foreach ($this->value as $i => $v) {
if ($this->isSelected($i)) {
$html .= '<div>';
$checkmark = '&#9745;';
} else {
$html .= '<div style="color:#ccc; font-style:italic">';
$checkmark = '&#9744;';
}
$html .= '<span style="font-size:1.5em; vertical-align:middle; margin-right:.5em; font-style:normal">' . $checkmark . '</span>';
$html .= empty($v) ? 'Default' : $v;
$html .= "</div>\n";
}
break;
default:
$address = '~[[:alpha:]]+://[^<>[:space:]]+[[:alnum:]/]~';
$val = nl2br(preg_replace($address, '<a href="\\0">\\0</a>', $this->value));
$html .= "<p style=\"margin:0\">{$val}</p>";
break;
}
$html .= '</div></td></tr></table>';
return $html;
}
private function isSelected($i)
{
return is_int($i) && is_array($this->selected_values) && isset($this->selected_values[$i]);
}
/*
* Return the label of the field
* @param string $for id of the target field
* @return string the <div> (unclosed for captcha)
*/
private function htmlLabel($for)
{
$html .= '<label for="' . $for . '" class="doc validate">';
if ($this->title) {
$html .= $this->title;
} else {
$html .= ucfirst($this->form->lang($this->type));
}
if ($this->description) {
$html .= ' <em class="description">' . $this->description . '</em>';
}
if ($this->error) {
$html .= ' <span class="error-msg">' . $this->form->lang($this->error) . '</span>';
}
$html .= '</label>';
return $html;
}
/**
* Return the general type of a field, even of specials fields.
*/
private function getGeneralType()
{
$types = [
'name' => 'text',
'subject' => 'text',
'message' => 'textarea',
'askcopy' => 'checkbox',
];
if (isset($types[$this->type])) {
return $types[$this->type];
}
return $this->type;
}
}
function preint($arr, $return = false)
{
$out = '<pre class="test" style="white-space:pre-wrap;">' . print_r(@$arr, true) . '</pre>';
if ($return) {
return $out;
}
echo $out;
}
function predump($arr)
{
echo '<pre class="test" style="white-space:pre-wrap;">';
var_dump($arr);
echo '</pre>';
}
function unset_r($a, $i)
{
foreach ($a as $k => $v) {
if (isset($v[$i])) {
unset($a[$k][$i]);
}
}
return $a;
}