true,
'tel_v2' => true,
'tel_intl' => true,
'tel_intl_v2' => true,
'checkbox' => true,
'radio' => true,
'select' => true,
'kr_zip' => true,
];
/**
* List of types that can have options.
*/
public const OPTION_TYPES = [
'checkbox' => true,
'radio' => true,
'select' => true,
];
/**
* Constructor for compatibility with legacy ExtraItem class.
*
* @param int $module_srl
* @param int $idx
* @param string $name
* @param string $type
* @param mixed $default
* @param string $desc
* @param string $is_required (Y, N)
* @param string $search (Y, N)
* @param string $value
* @param string $eid
* @param string $parent_type
* @param string $is_strict
* @param string $options
* @param string $sort
*/
function __construct(int $module_srl, int $idx, string $name, string $type = 'text', $default = null, $desc = '', $is_required = 'N', $search = 'N', $value = null, $eid = '', $parent_type = 'document', $is_strict = '', $options = null, $sort = 'N')
{
if (!$idx)
{
return;
}
$this->module_srl = $module_srl;
$this->idx = $idx;
$this->eid = $eid;
$this->type = $type;
$this->parent_type = $parent_type;
$this->value = $value;
$this->name = $name;
$this->desc = $desc;
$this->default = $default;
$this->options = $options ? json_decode($options) : null;
$this->is_required = $is_required;
$this->is_strict = $is_strict;
$this->search = $search;
$this->sort = $sort;
}
/**
* Set the raw value.
*
* @param mixed $value
* @return void
*/
public function setValue($value): void
{
$this->value = $value;
}
/**
* Check if this extra variable has a value.
*
* @return bool
*/
public function hasValue(): bool
{
$value = self::_getTypeValue($this->type, $this->value);
if ($value === null || $value === '' || (is_array($value) && !count($value)))
{
return false;
}
else
{
return true;
}
}
/**
* Get the raw value.
*
* @return string|array|null
*/
public function getValue()
{
return self::_getTypeValue($this->type, $this->value);
}
/**
* Get the value formatted as HTML.
*
* @return string
*/
public function getValueHTML(): string
{
return self::_getTypeValueHTML($this->type, $this->value);
}
/**
* Get the HTML form for an extra input field.
*
* @return string
*/
public function getFormHTML(): string
{
$template = new Template(self::_getSkinPath(), 'form.blade.php');
$template->setVars([
'definition' => $this,
'parent_type' => $this->parent_type,
'type' => $this->type,
'value' => self::_getTypeValue($this->type, $this->value),
'default' => self::_getTypeValue($this->type, $this->default),
'input_name' => $this->parent_type === 'document' ? ('extra_vars' . $this->idx) : ($this->input_name ?: $this->eid),
'input_id' => $this->input_id ?: '',
]);
return $template->compile();
}
/**
* Get the default value.
*
* @return mixed
*/
public function getDefaultValue()
{
if (!$this->canHaveOptions())
{
return $this->default;
}
if (is_array($this->options))
{
return $this->default;
}
elseif ($this->default && $this->parent_type !== 'member' && !in_array($this->type, ['checkbox', 'radio']))
{
return is_array($this->default) ? array_first($this->default) : array_first(explode(',', $this->default));
}
else
{
return null;
}
}
/**
* Get options specified by the administrator.
*
* @return array
*/
public function getOptions(): array
{
if (!$this->canHaveOptions())
{
return $this->options ?? [];
}
if (is_array($this->options))
{
return $this->options;
}
elseif ($this->default)
{
return is_array($this->default) ? $this->default : explode(',', $this->default);
}
else
{
return [];
}
}
/**
* Check if the current value can have options.
*
* @return bool
*/
public function canHaveOptions(): bool
{
return isset(self::OPTION_TYPES[$this->type]);
}
/**
* Check if the current value is an array type.
*
* @return bool
*/
public function isArrayType(): bool
{
return isset(self::ARRAY_TYPES[$this->type]);
}
/**
* Validate a value.
*
* @param mixed $value
* @param mixed $old_value
* @return BaseObject
*/
public function validate($value, $old_value = null): BaseObject
{
// Take legacy encoding into consideration.
if (is_array($value))
{
$is_array = true;
$values = $value;
}
elseif (str_contains($value, '|@|'))
{
$is_array = true;
$values = explode('|@|', $value);
}
else
{
$is_array = false;
$values = [$value];
}
// Check if a required value is empty.
if ($this->is_required === 'Y')
{
if ($this->type === 'file' && !$value && $old_value)
{
$value = $old_value;
$values = (array)$old_value;
}
if ($is_array && trim(implode('', $values)) === '')
{
return new BaseObject(-1, sprintf(lang('common.filter.isnull'), Context::replaceUserLang($this->name)));
}
if (!$is_array && trim(strval($value)) === '')
{
return new BaseObject(-1, sprintf(lang('common.filter.isnull'), Context::replaceUserLang($this->name)));
}
}
// Check if a strict value is not one of the specified options.
if ($this->is_strict === 'Y' && $value)
{
if ($this->canHaveOptions())
{
$options = $this->getOptions();
foreach ($values as $v)
{
if (!in_array($v, $options))
{
return new BaseObject(-1, sprintf(lang('common.filter.equalto'), Context::replaceUserLang($this->name)));
}
}
}
elseif ($this->isArrayType())
{
if (!$is_array)
{
return new BaseObject(-1, sprintf(lang('common.filter.equalto'), Context::replaceUserLang($this->name)));
}
}
}
return new BaseObject;
}
/**
* Upload a file.
*
* @param array $file
* @param int $target_srl
* @param string $target_type
* @return BaseObject
*/
public function uploadFile(array $file, int $target_srl, string $target_type): BaseObject
{
$oFileController = FileController::getInstance();
$output = $oFileController->insertFile($file, $this->module_srl, $target_srl);
if ($output->toBool())
{
$oFileController->setFilesValid($target_srl, "ev:$target_type", $output->get('file_srl'));
}
return $output;
}
/**
* Get the next temporary ID.
*
* @return string
*/
public static function getNextTempID(): string
{
return sprintf('rx_tempid_%d', self::$_temp_id++);
}
/**
* Get the normalized value.
*
* This method is public for compatibility with legacy ExtraItem class.
* It should not be called from outside of this class.
*
* @param string $type
* @param string|array $value
* @return string|array|null
*/
public static function _getTypeValue(string $type, $value)
{
// Return if the value is empty.
if (is_array($value))
{
if (!count($value))
{
return;
}
}
else
{
$value = trim(strval($value ?? ''));
if ($value === '')
{
return;
}
}
// Process array types.
if (isset(self::ARRAY_TYPES[$type]))
{
if (is_array($value))
{
$values = $value;
}
elseif (preg_match('/^[\[\{].*[\]\}]$/', $value))
{
$values = json_decode($value, true);
if (!is_array($values))
{
$values = [];
}
}
elseif (str_contains($value, '|@|'))
{
$values = explode('|@|', $value);
}
/*
elseif (str_contains($value, ',') && $type !== 'kr_zip')
{
$values = explode(',', $value);
}
*/
else
{
$values = [$value];
}
return array_map(function($str) {
return trim(escape($str, false));
}, array_values($values));
}
// Process the URL type.
if ($type === 'homepage' || $type === 'url')
{
if ($value && !preg_match('!^[a-z]+://!i', $value))
{
$value = 'http://' . $value;
}
return escape($value, false);
}
// Process the file upload type.
if ($type === 'file')
{
return $value ? intval($value) : null;
}
// Escape and return all other types.
return escape($value, false);
}
/**
* Get the normalized value in HTML format.
*
* @param string $type
* @param string|array $value
* @return string
*/
protected static function _getTypeValueHTML(string $type, $value): string
{
// Return if the value is empty.
$value = self::_getTypeValue($type, $value);
if ($value === null || $value === '' || (is_array($value) && !count($value)))
{
return '';
}
// Apply formatting appropriate for each type.
switch ($type)
{
case 'textarea':
return nl2br($value);
case 'select':
case 'radio':
case 'checkbox':
return is_array($value) ? implode(', ', $value) : $value;
case 'tel':
case 'tel_v2':
return is_array($value) ? implode('-', $value) : $value;
case 'tel_intl':
case 'tel_intl_v2':
if (is_array($value) && count($value))
{
$country_code = $value[0];
$phone_number = array_slice((array)$value, 1);
if (count($phone_number) && ctype_alpha(end($phone_number)))
{
array_pop($phone_number);
}
return sprintf('(+%d) %s', $country_code, implode('-', $phone_number));
}
else
{
return '';
}
case 'homepage':
case 'url':
$display = mb_strlen($value, 'UTF-8') > 60 ? mb_substr($value, 0, 40, 'UTF-8') . '...' . mb_substr($value, -10, 10, 'UTF-8') : $value;
return sprintf('%s', $value, $display);
case 'email_address':
case 'email':
return sprintf('%s', $value, $value);
case 'kr_zip':
return is_array($value) ? trim(implode(' ', $value)) : $value;
case 'country':
$country = i18n::listCountries()[$value] ?? '';
if ($country)
{
$lang_type = \Context::getLangType();
return $lang_type === 'ko' ? $country->name_korean : $country->name_english;
}
else
{
return '';
}
case 'language':
return Lang::getSupportedList()[$value]['name'] ?? '';
case 'date':
return sprintf('%s-%s-%s', substr($value, 0, 4), substr($value, 4, 2), substr($value, 6, 2));
case 'timezone':
return DateTime::getTimezoneList()[$value] ?? '';
case 'file':
if ($value)
{
$file = FileModel::getFile($value);
if ($file)
{
return sprintf('%s (%s)', \RX_BASEURL . ltrim($file->download_url, './'), $file->source_filename, FileHandler::filesize($file->file_size));
}
else
{
return '';
}
}
else
{
return '';
}
default:
return $value;
}
}
/**
* Get the currently configured skin path.
*
* @return string
*/
protected static function _getSkinPath(): string
{
if (self::$_skin_path !== null)
{
return self::$_skin_path;
}
$config = ModuleModel::getModuleConfig('extravar') ?: new \stdClass;
$skin = $config->skin ?? 'default';
if (Storage::isDirectory(\RX_BASEDIR . 'modules/extravar/skins/' . $skin))
{
self::$_skin_path = \RX_BASEDIR . 'modules/extravar/skins/' . $skin;
}
else
{
self::$_skin_path = \RX_BASEDIR . 'modules/extravar/skins/default';
}
return self::$_skin_path;
}
}