Implement default extravar skin to replace hardcoded HTML in ExtraItem class

This commit is contained in:
Kijin Sung 2024-04-14 23:16:22 +09:00
parent dd06193a1d
commit 31fa498b19
14 changed files with 604 additions and 590 deletions

View file

@ -0,0 +1,301 @@
<?php
namespace Rhymix\Modules\Extravar\Models;
use ModuleModel;
use Rhymix\Framework\DateTime;
use Rhymix\Framework\i18n;
use Rhymix\Framework\Lang;
use Rhymix\Framework\Template;
use Rhymix\Framework\Storage;
class Value
{
/**
* Public properties for compatibility with legacy ExtraItem class.
*/
public $module_srl = 0;
public $idx = 0;
public $eid = '';
public $input_id = '';
public $input_name = '';
public $parent_type = 'document';
public $type = 'text';
public $value = null;
public $name = '';
public $desc = '';
public $default = null;
public $is_required = 'N';
public $is_disabled = 'N';
public $is_readonly = 'N';
public $search = 'N';
/**
* Skin path cache.
*/
protected static $_skin_path = null;
/**
* Temporary ID cache.
*/
protected static $_temp_id = 1;
/**
* List of types where the value should be interpreted as an array.
*/
public const ARRAY_TYPES = [
'tel' => true,
'tel_v2' => true,
'tel_intl' => true,
'tel_intl_v2' => true,
'checkbox' => true,
'radio' => true,
'select' => true,
'kr_zip' => 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
*/
function __construct(int $module_srl, int $idx, string $name, string $type = 'text', $default = null, $desc = '', $is_required = 'N', $search = 'N', $value = null, string $eid = '')
{
if (!$idx)
{
return;
}
$this->module_srl = $module_srl;
$this->idx = $idx;
$this->eid = $eid;
$this->type = $type;
$this->value = $value;
$this->name = $name;
$this->desc = $desc;
$this->default = $default;
$this->is_required = $is_required;
$this->search = $search;
}
/**
* Set the raw value.
*
* @param mixed $value
* @return void
*/
public function setValue($value): void
{
$this->value = $value;
}
/**
* 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 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 (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);
}
// 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':
$country_code = $value[0] ?? '';
$phone_number = implode('-', array_slice((array)$value, 1));
return $value ? "(+{$country_code}) {$phone_number}": '';
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('<a href="%s" target="_blank">%s</a>', $value, $display);
case 'email_address':
case 'email':
return sprintf('<a href="mailto:%s">%s</a>', $value, $value);
case 'kr_zip':
return is_array($value) ? implode(' ', $value) : $value;
case 'country':
$country = i18n::listCountries()[$value];
$lang_type = \Context::getLangType();
return $lang_type === 'ko' ? $country->name_korean : $country->name_english;
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] ?? '';
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;
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace Rhymix\Modules\Extravar\Models;
class ValueCollection
{
/**
* Properties for compatibility with legacy ExtraVar class.
*/
public $module_srl;
public $keys = [];
/**
* This method only exists for compatibility with legacy ExtraVar class.
* Normally, you should just call new ValueCollection($module_srl).
*
* @deprecated
* @param int $module_srl
* @return self
*/
public static function getInstance(int $module_srl): self
{
return new self($module_srl);
}
/**
* Constructor.
*
* @param int $module_srl
* @param array $keys
*/
public function __construct(int $module_srl, $keys = [])
{
$this->module_srl = $module_srl;
$this->setExtraVarKeys($keys);
}
/**
* Set the list of extra keys for this module.
*
* @param array $keys
* @return void
*/
public function setExtraVarKeys($keys)
{
if (!is_array($keys) || !count($keys))
{
return;
}
foreach ($keys as $val)
{
$this->keys[$val->idx] = new Value($val->module_srl, $val->idx, $val->name, $val->type, $val->default, $val->desc, $val->is_required, $val->search, $val->value ?? null, $val->eid, $val->parent_type ?? 'document');
}
}
/**
* Returns an array of Value.
*
* @return array
*/
public function getExtraVars(): array
{
return $this->keys;
}
}

View file

@ -0,0 +1,50 @@
@switch ($type)
@case ('text')
@include ('form_types/text')
@break
@case ('textarea')
@include ('form_types/textarea')
@break
@case ('password')
@include ('form_types/password')
@break
@case ('select')
@include ('form_types/select')
@break
@case ('radio')
@case ('checkbox')
@include ('form_types/checkbox')
@break
@case ('tel')
@case ('tel_v2')
@case ('tel_intl')
@case ('tel_intl_v2')
@include ('form_types/tel')
@break
@case ('homepage')
@case ('url')
@include ('form_types/text')
@break
@case ('email_address')
@case ('email')
@include ('form_types/text')
@break
@case ('kr_zip')
@include ('form_types/kr_zip')
@break
@case ('country')
@case ('language')
@case ('timezone')
@include ('form_types/locale')
@break
@case ('date')
@case ('time')
@include ('form_types/datetime')
@break
@default
@include ('form_types/text')
@endswitch
@if ($desc)
<p>{{ nl2br(escape(Context::replaceUserLang($definition->desc), false)) }}</p>
@endif

View file

@ -0,0 +1,18 @@
<ul class="rx_ev_{{ $type }}">
@foreach ($default ?? [] as $v)
@php
$column_suffix = $type === 'checkbox' ? '[]' : '';
$tempid = $definition->getNextTempID();
$is_checked = is_array($value) && in_array(trim($v), $value);
@endphp
<li>
<input type="{{ $type }}" name="{{ $input_name . $column_suffix }}"
id="{{ $tempid }}" class="{{ $type }}" value="{{ $v }}"
@checked($is_checked)
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))
/><label for="{{ $tempid }}">{{ $v }}</label>
</li>
@endforeach
</ul>

View file

@ -0,0 +1,32 @@
@if ($type === 'date')
@php
$formatted_value = $value ? sprintf('%s-%s-%s', substr($value, 0, 4), substr($value, 4, 2), substr($value, 6, 2)) : '';
@endphp
<input type="hidden" name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="rx_ev_date"
value="{{ $value }}"
/>
<input type="date" class="date" value="{{ $formatted_value }}"
pattern="\d{4}-\d{2}-\d{2}" placeholder="YYYY-MM-DD"
onchange="jQuery(this).prev('.rx_ev_date').val(this.value.replace(/-/g, ''));"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))
/>
<button type="button" class="btn dateRemover"
onclick="jQuery(this).prev('.date').val('').trigger('change');return false;">
{{ lang('cmd_delete') }}
</button>
@else
<input type="time" name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="rx_ev_time"
value="{{ $value }}" pattern="\d{2}:\d{2}"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))
/>
<button type="button" class="btn timeRemover"
onclick="jQuery(this).prev('.rx_ev_time').val('');return false;">
{{ lang('cmd_delete') }}
</button>
@endif

View file

@ -0,0 +1,5 @@
@php
$oKrzipModel = KrzipModel::getInstance();
@endphp
{!! $oKrzipModel->getKrzipCodeSearchHtml($input_name, $value) !!}

View file

@ -0,0 +1,53 @@
@if ($type === 'country')
@php
$lang_type = Context::getLangType();
$lang_sort = $lang_type === 'ko' ? Rhymix\Framework\i18n::SORT_NAME_KOREAN : Rhymix\Framework\i18n::SORT_NAME_ENGLISH;
$countries = Rhymix\Framework\i18n::listCountries($lang_sort);
@endphp
<select name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="select rx_ev_select rx_ev_select_country"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))>
@foreach ($countries as $country)
@php
$country_name = $lang_type === 'ko' ? $country->name_korean : $country->name_english;
$is_selected = strval($value) !== '' && $value === $country->iso_3166_1_alpha3;
@endphp
<option value="{{ $country->iso_3166_1_alpha3 }}" @selected($is_selected)>{{ $country_name }}</option>
@endforeach
</select>
@elseif ($type === 'language')
@php
$enabled_languages = Rhymix\Framework\Config::get('locale.enabled_lang');
$supported_languages = Rhymix\Framework\Lang::getSupportedList();
@endphp
<select name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="select rx_ev_select rx_ev_select_language"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))>
@foreach ($enabled_languages as $language)
@php
$is_selected = strval($value) !== '' && $value === $language;
@endphp
<option value="{{ $language }}" @selected($is_selected)>{{ $supported_languages[$language]['name'] }}</option>
@endforeach
</select>
@elseif ($type === 'timezone')
@php
$timezones = Rhymix\Framework\DateTime::getTimezoneList();
@endphp
<select name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="select rx_ev_select rx_ev_select_timezone"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))>
@foreach ($timezones as $timezone_code => $timezone_name)
@php
$is_selected = strval($value) !== '' && $value === $timezone_code;
@endphp
<option value="{{ $timezone_code }}" @selected($is_selected)>{{ $timezone_name }}</option>
@endforeach
</select>
@endif

View file

@ -0,0 +1,7 @@
<input type="password" name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="password rx_ev_password"
value="{{ strval($value) !== '' ? $value : $default }}"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))
/>

View file

@ -0,0 +1,9 @@
<select name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="select rx_ev_select"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))>
@foreach ($default ?: [] as $v)
<option value="{{ $v }}" @selected(is_array($value) && in_array(trim($v), $value))>{{ $v }}</option>
@endforeach
</select>

View file

@ -0,0 +1,30 @@
@if ($type === 'tel')
<input type="tel" name="{{ $input_name }}[]" value="{{ $value[0] }}" size="4" maxlength="4" class="tel rx_ev_tel1" />
<input type="tel" name="{{ $input_name }}[]" value="{{ $value[1] }}" size="4" maxlength="4" class="tel rx_ev_tel2" />
<input type="tel" name="{{ $input_name }}[]" value="{{ $value[2] }}" size="4" maxlength="4" class="tel rx_ev_tel3" />
@elseif ($type === 'tel_v2')
<input type="tel" name="{{ $input_name }}[]" value="{{ $value[0] }}" size="16" maxlength="16" class="rx_ev_tel_v2" />
@elseif ($type === 'tel_intl' || $type === 'tel_intl_v2')
@php
$lang_type = Context::getLangType();
$lang_sort = $lang_type === 'ko' ? Rhymix\Framework\i18n::SORT_NAME_KOREAN : Rhymix\Framework\i18n::SORT_NAME_ENGLISH;
$countries = Rhymix\Framework\i18n::listCountries($lang_sort);
@endphp
<select name="{{ $input_name }}[]" class="select rx_ev_select rx_ev_select_country">
<option value=""></option>
@foreach ($countries as $country)
@php
$country_name = $lang_type === 'ko' ? $country->name_korean : $country->name_english;
$is_selected = strval($value[0] ?? '') !== '' && $value[0] === $country->calling_code;
@endphp
<option value="{{ $country->calling_code }}" @selected($is_selected)>{{ $country_name }} (+{{ $country->calling_code }})</option>
@endforeach
</select>
@if ($type === 'tel_intl')
<input type="tel" name="{{ $input_name }}[]" value="{{ $value[1] }}" size="4" maxlength="4" class="tel rx_ev_tel1" />
<input type="tel" name="{{ $input_name }}[]" value="{{ $value[2] }}" size="4" maxlength="4" class="tel rx_ev_tel2" />
<input type="tel" name="{{ $input_name }}[]" value="{{ $value[3] }}" size="4" maxlength="4" class="tel rx_ev_tel3" />
@else
<input type="tel" name="{{ $input_name }}[]" value="{{ $value[1] }}" size="16" maxlength="16" class="rx_ev_tel_v2" />
@endif
@endif

View file

@ -0,0 +1,25 @@
@if ($type === 'homepage' || $type === 'url')
<input type="url" name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="homepage rx_ev_url"
value="{{ $value }}"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))
/>
@elseif ($type === 'email_address' || $type === 'email')
<input type="email" name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="email_address rx_ev_email"
value="{{ $value }}"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))
/>
@else
<input type="text" name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="text rx_ev_text"
value="{{ strval($value) !== '' ? $value : $default }}"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))
/>
@endif

View file

@ -0,0 +1,6 @@
<textarea name="{{ $input_name }}"
id="{{ $input_id }}"|if="$input_id" class="rx_ev_textarea"
@required(toBool($definition->is_required))
@disabled(toBool($definition->is_disabled))
@readonly(toBool($definition->is_readonly))
rows="8" cols="42">{{ $value }}</textarea>