PDF rausgenommen
This commit is contained in:
693
msd2/tracking/piwik/libs/HTML/QuickForm2/Node.php
Normal file
693
msd2/tracking/piwik/libs/HTML/QuickForm2/Node.php
Normal file
@ -0,0 +1,693 @@
|
||||
<?php
|
||||
/**
|
||||
* Base class for all HTML_QuickForm2 elements
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE:
|
||||
*
|
||||
* Copyright (c) 2006-2010, Alexey Borzov <avb@php.net>,
|
||||
* Bertrand Mansion <golgote@mamasam.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * The names of the authors may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @category HTML
|
||||
* @package HTML_QuickForm2
|
||||
* @author Alexey Borzov <avb@php.net>
|
||||
* @author Bertrand Mansion <golgote@mamasam.com>
|
||||
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
||||
* @version SVN: $Id: Node.php 300747 2010-06-25 16:16:50Z mansion $
|
||||
* @link http://pear.php.net/package/HTML_QuickForm2
|
||||
*/
|
||||
|
||||
/**
|
||||
* HTML_Common2 - base class for HTML elements
|
||||
*/
|
||||
// require_once 'HTML/Common2.php';
|
||||
|
||||
// By default, we generate element IDs with numeric indexes appended even for
|
||||
// elements with unique names. If you want IDs to be equal to the element
|
||||
// names by default, set this configuration option to false.
|
||||
if (null === HTML_Common2::getOption('id_force_append_index')) {
|
||||
HTML_Common2::setOption('id_force_append_index', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception classes for HTML_QuickForm2
|
||||
*/
|
||||
// require_once 'HTML/QuickForm2/Exception.php';
|
||||
require_once dirname(__FILE__) . '/Exception.php';
|
||||
|
||||
/**
|
||||
* Static factory class for QuickForm2 elements
|
||||
*/
|
||||
// require_once 'HTML/QuickForm2/Factory.php';
|
||||
|
||||
/**
|
||||
* Base class for HTML_QuickForm2 rules
|
||||
*/
|
||||
// require_once 'HTML/QuickForm2/Rule.php';
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base class for all QuickForm2 Elements and Containers
|
||||
*
|
||||
* This class is mostly here to define the interface that should be implemented
|
||||
* by the subclasses. It also contains static methods handling generation
|
||||
* of unique ids for elements which do not have ids explicitly set.
|
||||
*
|
||||
* @category HTML
|
||||
* @package HTML_QuickForm2
|
||||
* @author Alexey Borzov <avb@php.net>
|
||||
* @author Bertrand Mansion <golgote@mamasam.com>
|
||||
* @version Release: @package_version@
|
||||
*/
|
||||
abstract class HTML_QuickForm2_Node extends HTML_Common2
|
||||
{
|
||||
/**
|
||||
* Array containing the parts of element ids
|
||||
* @var array
|
||||
*/
|
||||
protected static $ids = array();
|
||||
|
||||
/**
|
||||
* Element's "frozen" status
|
||||
* @var boolean
|
||||
*/
|
||||
protected $frozen = false;
|
||||
|
||||
/**
|
||||
* Whether element's value should persist when element is frozen
|
||||
* @var boolean
|
||||
*/
|
||||
protected $persistent = false;
|
||||
|
||||
/**
|
||||
* Element containing current
|
||||
* @var HTML_QuickForm2_Container
|
||||
*/
|
||||
protected $container = null;
|
||||
|
||||
/**
|
||||
* Contains options and data used for the element creation
|
||||
* @var array
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Validation rules for element
|
||||
* @var array
|
||||
*/
|
||||
protected $rules = array();
|
||||
|
||||
/**
|
||||
* An array of callback filters for element
|
||||
* @var array
|
||||
*/
|
||||
protected $filters = array();
|
||||
|
||||
/**
|
||||
* Error message (usually set via Rule if validation fails)
|
||||
* @var string
|
||||
*/
|
||||
protected $error = null;
|
||||
|
||||
/**
|
||||
* Changing 'name' and 'id' attributes requires some special handling
|
||||
* @var array
|
||||
*/
|
||||
protected $watchedAttributes = array('id', 'name');
|
||||
|
||||
/**
|
||||
* Intercepts setting 'name' and 'id' attributes
|
||||
*
|
||||
* These attributes should always be present and thus trying to remove them
|
||||
* will result in an exception. Changing their values is delegated to
|
||||
* setName() and setId() methods, respectively
|
||||
*
|
||||
* @param string Attribute name
|
||||
* @param string Attribute value, null if attribute is being removed
|
||||
* @throws HTML_QuickForm2_InvalidArgumentException if trying to
|
||||
* remove a required attribute
|
||||
*/
|
||||
protected function onAttributeChange($name, $value = null)
|
||||
{
|
||||
if ('name' == $name) {
|
||||
if (null === $value) {
|
||||
throw new HTML_QuickForm2_InvalidArgumentException(
|
||||
"Required attribute 'name' can not be removed"
|
||||
);
|
||||
} else {
|
||||
$this->setName($value);
|
||||
}
|
||||
} elseif ('id' == $name) {
|
||||
if (null === $value) {
|
||||
throw new HTML_QuickForm2_InvalidArgumentException(
|
||||
"Required attribute 'id' can not be removed"
|
||||
);
|
||||
} else {
|
||||
$this->setId($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string Element name
|
||||
* @param mixed Attributes (either a string or an array)
|
||||
* @param array Element data (label, options and data used for element creation)
|
||||
*/
|
||||
public function __construct($name = null, $attributes = null, $data = null)
|
||||
{
|
||||
parent::__construct($attributes);
|
||||
$this->setName($name);
|
||||
// Autogenerating the id if not set on previous steps
|
||||
if ('' == $this->getId()) {
|
||||
$this->setId();
|
||||
}
|
||||
if (!empty($data)) {
|
||||
$this->data = array_merge($this->data, $data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates an id for the element
|
||||
*
|
||||
* Called when an element is created without explicitly given id
|
||||
*
|
||||
* @param string Element name
|
||||
* @return string The generated element id
|
||||
*/
|
||||
protected static function generateId($elementName)
|
||||
{
|
||||
$stop = !self::getOption('id_force_append_index');
|
||||
$tokens = strlen($elementName)
|
||||
? explode('[', str_replace(']', '', $elementName))
|
||||
: ($stop? array('qfauto', ''): array('qfauto'));
|
||||
$container =& self::$ids;
|
||||
$id = '';
|
||||
|
||||
do {
|
||||
$token = array_shift($tokens);
|
||||
// Handle the 'array[]' names
|
||||
if ('' === $token) {
|
||||
if (empty($container)) {
|
||||
$token = 0;
|
||||
} else {
|
||||
$keys = array_keys($container);
|
||||
$token = end($keys);
|
||||
while (isset($container[$token])) {
|
||||
$token++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$id .= '-' . $token;
|
||||
if (!isset($container[$token])) {
|
||||
$container[$token] = array();
|
||||
// Handle duplicate names when not having mandatory indexes
|
||||
} elseif (empty($tokens) && $stop) {
|
||||
$tokens[] = '';
|
||||
}
|
||||
// Handle mandatory indexes
|
||||
if (empty($tokens) && !$stop) {
|
||||
$tokens[] = '';
|
||||
$stop = true;
|
||||
}
|
||||
$container =& $container[$token];
|
||||
} while (!empty($tokens));
|
||||
|
||||
return substr($id, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stores the explicitly given id to prevent duplicate id generation
|
||||
*
|
||||
* @param string Element id
|
||||
*/
|
||||
protected static function storeId($id)
|
||||
{
|
||||
$tokens = explode('-', $id);
|
||||
$container =& self::$ids;
|
||||
|
||||
do {
|
||||
$token = array_shift($tokens);
|
||||
if (!isset($container[$token])) {
|
||||
$container[$token] = array();
|
||||
}
|
||||
$container =& $container[$token];
|
||||
} while (!empty($tokens));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element's type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getType();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element's name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return isset($this->attributes['name'])? $this->attributes['name']: null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the element's name
|
||||
*
|
||||
* @param string
|
||||
* @return HTML_QuickForm2_Node
|
||||
*/
|
||||
abstract public function setName($name);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element's id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return isset($this->attributes['id'])? $this->attributes['id']: null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the elements id
|
||||
*
|
||||
* Please note that elements should always have an id in QuickForm2 and
|
||||
* therefore it will not be possible to remove the element's id or set it to
|
||||
* an empty value. If id is not explicitly given, it will be autogenerated.
|
||||
*
|
||||
* @param string Element's id, will be autogenerated if not given
|
||||
* @return HTML_QuickForm2_Node
|
||||
*/
|
||||
public function setId($id = null)
|
||||
{
|
||||
if (is_null($id)) {
|
||||
$id = self::generateId($this->getName());
|
||||
} else {
|
||||
self::storeId($id);
|
||||
}
|
||||
$this->attributes['id'] = (string)$id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element's value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function getValue();
|
||||
|
||||
|
||||
/**
|
||||
* Sets the element's value
|
||||
*
|
||||
* @param mixed
|
||||
* @return HTML_QuickForm2_Node
|
||||
*/
|
||||
abstract public function setValue($value);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element's label(s)
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
if (isset($this->data['label'])) {
|
||||
return $this->data['label'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the element's label(s)
|
||||
*
|
||||
* @param string|array Label for the element (may be an array of labels)
|
||||
* @return HTML_QuickForm2_Node
|
||||
*/
|
||||
public function setLabel($label)
|
||||
{
|
||||
$this->data['label'] = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes the element's frozen status
|
||||
*
|
||||
* @param bool Whether the element should be frozen or editable. If
|
||||
* omitted, the method will not change the frozen status,
|
||||
* just return its current value
|
||||
* @return bool Old value of element's frozen status
|
||||
*/
|
||||
public function toggleFrozen($freeze = null)
|
||||
{
|
||||
$old = $this->frozen;
|
||||
if (null !== $freeze) {
|
||||
$this->frozen = (bool)$freeze;
|
||||
}
|
||||
return $old;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes the element's persistent freeze behaviour
|
||||
*
|
||||
* If persistent freeze is on, the element's value will be kept (and
|
||||
* submitted) in a hidden field when the element is frozen.
|
||||
*
|
||||
* @param bool New value for "persistent freeze". If omitted, the
|
||||
* method will not set anything, just return the current
|
||||
* value of the flag.
|
||||
* @return bool Old value of "persistent freeze" flag
|
||||
*/
|
||||
public function persistentFreeze($persistent = null)
|
||||
{
|
||||
$old = $this->persistent;
|
||||
if (null !== $persistent) {
|
||||
$this->persistent = (bool)$persistent;
|
||||
}
|
||||
return $old;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the link to the element containing current
|
||||
*
|
||||
* @param HTML_QuickForm2_Container Element containing the current one,
|
||||
* null if the link should really be
|
||||
* removed (if removing from container)
|
||||
* @throws HTML_QuickForm2_InvalidArgumentException If trying to set a
|
||||
* child of an element as its container
|
||||
*/
|
||||
protected function setContainer(HTML_QuickForm2_Container $container = null)
|
||||
{
|
||||
if (null !== $container) {
|
||||
$check = $container;
|
||||
do {
|
||||
if ($this === $check) {
|
||||
throw new HTML_QuickForm2_InvalidArgumentException(
|
||||
'Cannot set an element or its child as its own container'
|
||||
);
|
||||
}
|
||||
} while ($check = $check->getContainer());
|
||||
if (null !== $this->container && $container !== $this->container) {
|
||||
$this->container->removeChild($this);
|
||||
}
|
||||
}
|
||||
$this->container = $container;
|
||||
if (null !== $container) {
|
||||
$this->updateValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element containing current
|
||||
*
|
||||
* @return HTML_QuickForm2_Container|null
|
||||
*/
|
||||
public function getContainer()
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data sources for this element
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDataSources()
|
||||
{
|
||||
if (empty($this->container)) {
|
||||
return array();
|
||||
} else {
|
||||
return $this->container->getDataSources();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the element needs to update its value from form's data sources
|
||||
*/
|
||||
abstract public function updateValue();
|
||||
|
||||
/**
|
||||
* Adds a validation rule
|
||||
*
|
||||
* @param HTML_QuickForm2_Rule|string Validation rule or rule type
|
||||
* @param string|int If first parameter is rule type, then
|
||||
* message to display if validation fails, otherwise constant showing
|
||||
* whether to perfom validation client-side and/or server-side
|
||||
* @param mixed Additional data for the rule
|
||||
* @param int Whether to perfom validation server-side
|
||||
* and/or client side. Combination of HTML_QuickForm2_Rule::RUNAT_* constants
|
||||
* @return HTML_QuickForm2_Rule The added rule
|
||||
* @throws HTML_QuickForm2_InvalidArgumentException if $rule is of a
|
||||
* wrong type or rule name isn't registered with Factory
|
||||
* @throws HTML_QuickForm2_NotFoundException if class for a given rule
|
||||
* name cannot be found
|
||||
* @todo Need some means to mark the Rules for running client-side
|
||||
*/
|
||||
public function addRule($rule, $messageOrRunAt = '', $options = null,
|
||||
$runAt = HTML_QuickForm2_Rule::RUNAT_SERVER)
|
||||
{
|
||||
if ($rule instanceof HTML_QuickForm2_Rule) {
|
||||
$rule->setOwner($this);
|
||||
$runAt = '' == $messageOrRunAt? HTML_QuickForm2_Rule::RUNAT_SERVER: $messageOrRunAt;
|
||||
} elseif (is_string($rule)) {
|
||||
$rule = HTML_QuickForm2_Factory::createRule($rule, $this, $messageOrRunAt, $options);
|
||||
} else {
|
||||
throw new HTML_QuickForm2_InvalidArgumentException(
|
||||
'addRule() expects either a rule type or ' .
|
||||
'a HTML_QuickForm2_Rule instance'
|
||||
);
|
||||
}
|
||||
|
||||
$this->rules[] = array($rule, $runAt);
|
||||
return $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a validation rule
|
||||
*
|
||||
* The method will *not* throw an Exception if the rule wasn't added to the
|
||||
* element.
|
||||
*
|
||||
* @param HTML_QuickForm2_Rule Validation rule to remove
|
||||
* @return HTML_QuickForm2_Rule Removed rule
|
||||
*/
|
||||
public function removeRule(HTML_QuickForm2_Rule $rule)
|
||||
{
|
||||
foreach ($this->rules as $i => $r) {
|
||||
if ($r[0] === $rule) {
|
||||
unset($this->rules[$i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a validation rule
|
||||
*
|
||||
* This method is mostly useful when when chaining several rules together
|
||||
* via {@link HTML_QuickForm2_Rule::and_()} and {@link HTML_QuickForm2_Rule::or_()}
|
||||
* methods:
|
||||
* <code>
|
||||
* $first->addRule('nonempty', 'Fill in either first or second field')
|
||||
* ->or_($second->createRule('nonempty'));
|
||||
* </code>
|
||||
*
|
||||
* @param string Rule type
|
||||
* @param string Message to display if validation fails
|
||||
* @param mixed Additional data for the rule
|
||||
* @return HTML_QuickForm2_Rule The created rule
|
||||
* @throws HTML_QuickForm2_InvalidArgumentException If rule type is unknown
|
||||
* @throws HTML_QuickForm2_NotFoundException If class for the rule
|
||||
* can't be found and/or loaded from file
|
||||
*/
|
||||
public function createRule($type, $message = '', $options = null)
|
||||
{
|
||||
return HTML_QuickForm2_Factory::createRule($type, $this, $message, $options);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether an element is required
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isRequired()
|
||||
{
|
||||
foreach ($this->rules as $rule) {
|
||||
if ($rule[0] instanceof HTML_QuickForm2_Rule_Required) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs the server-side validation
|
||||
*
|
||||
* @return boolean Whether the element is valid
|
||||
*/
|
||||
protected function validate()
|
||||
{
|
||||
foreach ($this->rules as $rule) {
|
||||
if (strlen($this->error)) {
|
||||
break;
|
||||
}
|
||||
if ($rule[1] & HTML_QuickForm2_Rule::RUNAT_SERVER) {
|
||||
$rule[0]->validate();
|
||||
}
|
||||
}
|
||||
return !strlen($this->error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error message to the element
|
||||
*
|
||||
* @param string
|
||||
* @return HTML_QuickForm2_Node
|
||||
*/
|
||||
public function setError($error = null)
|
||||
{
|
||||
$this->error = (string)$error;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error message for the element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Javascript code for getting the element's value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getJavascriptValue();
|
||||
|
||||
/**
|
||||
* Adds a filter
|
||||
*
|
||||
* A filter is simply a PHP callback which will be applied to the element value
|
||||
* when getValue() is called. A filter is by default applied recursively :
|
||||
* if the value is an array, each elements it contains will
|
||||
* also be filtered, unless the recursive flag is set to false.
|
||||
*
|
||||
* @param callback The PHP callback used for filter
|
||||
* @param array Optional arguments for the callback. The first parameter
|
||||
* will always be the element value, then these options will
|
||||
* be used as parameters for the callback.
|
||||
* @param bool Whether to apply the filter recursively to contained elements
|
||||
* @return HTML_QuickForm2_Node The element
|
||||
* @throws HTML_QuickForm2_InvalidArgumentException If callback is incorrect
|
||||
*/
|
||||
public function addFilter($callback, array $options = null, $recursive = true)
|
||||
{
|
||||
if (!is_callable($callback, false, $callbackName)) {
|
||||
throw new HTML_QuickForm2_InvalidArgumentException(
|
||||
'Callback Filter requires a valid callback, \'' . $callbackName .
|
||||
'\' was given'
|
||||
);
|
||||
}
|
||||
$this->filters[] = array($callback, $options, 'recursive' => $recursive);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all element filters
|
||||
*/
|
||||
public function removeFilters()
|
||||
{
|
||||
$this->filters = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies element filters on element value
|
||||
* @param mixed Element value
|
||||
* @return mixed Filtered value
|
||||
*/
|
||||
protected function applyFilters($value)
|
||||
{
|
||||
foreach ($this->filters as $filter) {
|
||||
if (is_array($value) && !empty($filter['recursive'])) {
|
||||
array_walk_recursive($value,
|
||||
array('HTML_QuickForm2_Node', 'applyFilter'), $filter);
|
||||
} else {
|
||||
self::applyFilter($value, null, $filter);
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected static function applyFilter(&$value, $key = null, $filter)
|
||||
{
|
||||
$callback = $filter[0];
|
||||
$options = $filter[1];
|
||||
if (!is_array($options)) {
|
||||
$options = array();
|
||||
}
|
||||
array_unshift($options, $value);
|
||||
$value = call_user_func_array($callback, $options);
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
Reference in New Issue
Block a user