.
*/
declare(strict_types=1);
namespace Fisharebest\Webtrees\Elements;
use Fisharebest\Webtrees\Contracts\ElementInterface;
use Fisharebest\Webtrees\Html;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Registry;
use Fisharebest\Webtrees\Tree;
use function array_key_exists;
use function array_map;
use function e;
use function is_numeric;
use function nl2br;
use function preg_replace;
use function str_contains;
use function str_starts_with;
use function strip_tags;
use function trim;
use function view;
/**
* A GEDCOM element is a tag/primitive in a GEDCOM file.
*/
abstract class AbstractElement implements ElementInterface
{
// HTML attributes for an
protected const MAXIMUM_LENGTH = false;
protected const PATTERN = false;
// Which child elements can appear under this element.
protected const SUBTAGS = [];
// A label to describe this element
private string $label;
/** @var array Subtags of this element */
private array $subtags;
/**
* AbstractGedcomElement constructor.
*
* @param string $label
* @param array|null $subtags
*/
public function __construct(string $label, array $subtags = null)
{
$this->label = $label;
$this->subtags = $subtags ?? static::SUBTAGS;
}
/**
* Convert a value to a canonical form.
*
* @param string $value
*
* @return string
*/
public function canonical(string $value): string
{
$value = strtr($value, ["\t" => ' ', "\r" => ' ', "\n" => ' ']);
while (str_contains($value, ' ')) {
$value = strtr($value, [' ' => ' ']);
}
return trim($value);
}
/**
* Convert a multi-line value to a canonical form.
*
* @param string $value
*
* @return string
*/
protected function canonicalText(string $value): string
{
// Browsers use MS-DOS line endings in multi-line data.
$value = strtr($value, ["\t" => ' ', "\r\n" => "\n", "\r" => "\n"]);
// Remove blank lines at start/end
$value = preg_replace('/^( *\n)+/', '', $value);
return preg_replace('/(\n *)+$/', '', $value);
}
/**
* Should we collapse the children of this element when editing?
*
* @return bool
*/
public function collapseChildren(): bool
{
return false;
}
/**
* Create a default value for this element.
*
* @param Tree $tree
*
* @return string
*/
public function default(Tree $tree): string
{
return '';
}
/**
* An edit control for this data.
*
* @param string $id
* @param string $name
* @param string $value
* @param Tree $tree
*
* @return string
*/
public function edit(string $id, string $name, string $value, Tree $tree): string
{
$values = $this->values();
if ($values !== []) {
$value = $this->canonical($value);
// Ensure the current data is in the list.
if (!array_key_exists($value, $values)) {
$values = [$value => $value] + $values;
}
// We may use markup to display values, but not when editing them.
$values = array_map(static fn (string $x): string => strip_tags($x), $values);
return view('components/select', [
'id' => $id,
'name' => $name,
'options' => $values,
'selected' => $value,
]);
}
$attributes = [
'class' => 'form-control',
'dir' => 'auto',
'type' => 'text',
'id' => $id,
'name' => $name,
'value' => $value,
'maxlength' => static::MAXIMUM_LENGTH,
'pattern' => static::PATTERN,
];
return '';
}
/**
* An edit control for this data.
*
* @param string $id
* @param string $name
* @param string $value
*
* @return string
*/
public function editHidden(string $id, string $name, string $value): string
{
return '';
}
/**
* An edit control for this data.
*
* @param string $id
* @param string $name
* @param string $value
*
* @return string
*/
public function editTextArea(string $id, string $name, string $value): string
{
return '';
}
/**
* Escape @ signs in a GEDCOM export.
*
* @param string $value
*
* @return string
*/
public function escape(string $value): string
{
return strtr($value, ['@' => '@@']);
}
/**
* Create a label for this element.
*
* @return string
*/
public function label(): string
{
return $this->label;
}
/**
* Create a label/value pair for this element.
*
* @param string $value
* @param Tree $tree
*
* @return string
*/
public function labelValue(string $value, Tree $tree): string
{
$label = '' . $this->label() . '';
$value = '' . $this->value($value, $tree) . '';
$html = I18N::translate(/* I18N: e.g. "Occupation: farmer" */ '%1$s: %2$s', $label, $value);
return '
' . $html . '
';
}
/**
* Set, remove or replace a subtag.
*
* @param string $subtag
* @param string $repeat
* @param string $before
*
* @return void
*/
public function subtag(string $subtag, string $repeat, string $before = ''): void
{
if ($repeat === '') {
unset($this->subtags[$subtag]);
} elseif ($before === '' || ($this->subtags[$before] ?? null) === null) {
$this->subtags[$subtag] = $repeat;
} else {
$tmp = [];
foreach ($this->subtags as $key => $value) {
if ($key === $before) {
$tmp[$subtag] = $repeat;
}
$tmp[$key] = $value;
}
$this->subtags = $tmp;
}
}
/**
* @return array
*/
public function subtags(): array
{
return $this->subtags;
}
/**
* Display the value of this type of element.
*
* @param string $value
* @param Tree $tree
*
* @return string
*/
public function value(string $value, Tree $tree): string
{
$values = $this->values();
if ($values === []) {
if (str_contains($value, "\n")) {
return '' . nl2br(e($value)) . '';
}
return '' . e($value) . '';
}
$canonical = $this->canonical($value);
return $values[$canonical] ?? '' . e($value) . '';
}
/**
* A list of controlled values for this element
*
* @return array
*/
public function values(): array
{
return [];
}
/**
* Display the value of this type of element - convert URLs to links.
*
* @param string $value
*
* @return string
*/
protected function valueAutoLink(string $value): string
{
$canonical = $this->canonical($value);
if (str_contains($canonical, 'http://') || str_contains($canonical, 'https://')) {
$html = Registry::markdownFactory()->autolink()->convertToHtml($canonical);
return strip_tags($html, ['a']);
}
return e($canonical);
}
/**
* Display the value of this type of element - multi-line text with/without markdown.
*
* @param string $value
* @param Tree $tree
*
* @return string
*/
protected function valueFormatted(string $value, Tree $tree): string
{
$canonical = $this->canonical($value);
$format = $tree->getPreference('FORMAT_TEXT');
switch ($format) {
case 'markdown':
$html = Registry::markdownFactory()->markdown($tree)->convertToHtml($canonical);
return '
' . $html . '
';
case 'markdown-br':
$html = Registry::markdownFactory()->markdown($tree)->convertToHtml($canonical);
return '