1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/dolibarr_ynh.git synced 2024-09-03 18:35:53 +02:00
dolibarr_ynh/sources/dolibarr/htdocs/includes/restler/UI/Forms.php

434 lines
14 KiB
PHP
Raw Normal View History

2015-09-28 22:09:38 +02:00
<?php
namespace Luracast\Restler\UI;
use Luracast\Restler\CommentParser;
use Luracast\Restler\Data\ApiMethodInfo;
use Luracast\Restler\Data\String;
use Luracast\Restler\Data\ValidationInfo;
use Luracast\Restler\Defaults;
use Luracast\Restler\Format\UploadFormat;
use Luracast\Restler\Format\UrlEncodedFormat;
use Luracast\Restler\iFilter;
use Luracast\Restler\RestException;
use Luracast\Restler\Restler;
use Luracast\Restler\Routes;
use Luracast\Restler\Scope;
use Luracast\Restler\UI\Tags as T;
use Luracast\Restler\User;
use Luracast\Restler\Util;
/**
* Utility class for automatically generating forms for the given http method
* and api url
*
* @category Framework
* @package Restler
* @author R.Arul Kumaran <arul@luracast.com>
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
*/
class Forms implements iFilter
{
const FORM_KEY = 'form_key';
public static $filterFormRequestsOnly = false;
public static $excludedPaths = array();
public static $style;
/**
* @var bool should we fill up the form using given data?
*/
public static $preFill = true;
/**
* @var ValidationInfo
*/
public static $validationInfo = null;
protected static $inputTypes = array(
'hidden',
'password',
'button',
'image',
'file',
'reset',
'submit',
'search',
'checkbox',
'radio',
'email',
'text',
'color',
'date',
'datetime',
'datetime-local',
'email',
'month',
'number',
'range',
'search',
'tel',
'time',
'url',
'week',
);
protected static $fileUpload = false;
private static $key = array();
/**
* @var ApiMethodInfo;
*/
private static $info;
/**
* Get the form
*
* @param string $method http method to submit the form
* @param string $action relative path from the web root. When set to null
* it uses the current api method's path
* @param bool $dataOnly if you want to render the form yourself use this
* option
* @param string $prefix used for adjusting the spacing in front of
* form elements
* @param string $indent used for adjusting indentation
*
* @return array|T
*
* @throws \Luracast\Restler\RestException
*/
public static function get($method = 'POST', $action = null, $dataOnly = false, $prefix = '', $indent = ' ')
{
if (!static::$style)
static::$style = FormStyles::$html;
try {
/** @var Restler $restler */
$restler = Scope::get('Restler');
if (is_null($action))
$action = $restler->url;
$info = $restler->url == $action
&& Util::getRequestMethod() == $method
? $restler->apiMethodInfo
: Routes::find(
trim($action, '/'),
$method,
$restler->getRequestedApiVersion(),
static::$preFill ||
($restler->requestMethod == $method &&
$restler->url == $action)
? $restler->getRequestData()
: array()
);
} catch (RestException $e) {
//echo $e->getErrorMessage();
$info = false;
}
if (!$info)
throw new RestException(500, 'invalid action path for form `' . $method . ' ' . $action . '`');
static::$info = $info;
$m = $info->metadata;
$r = static::fields($dataOnly);
if ($method != 'GET' && $method != 'POST') {
if (empty(Defaults::$httpMethodOverrideProperty))
throw new RestException(
500,
'Forms require `Defaults::\$httpMethodOverrideProperty`' .
"for supporting HTTP $method"
);
if ($dataOnly) {
$r[] = array(
'tag' => 'input',
'name' => Defaults::$httpMethodOverrideProperty,
'type' => 'hidden',
'value' => 'method',
);
} else {
$r[] = T::input()
->name(Defaults::$httpMethodOverrideProperty)
->value($method)
->type('hidden');
}
$method = 'POST';
}
if (session_id() != '') {
$form_key = static::key($method, $action);
if ($dataOnly) {
$r[] = array(
'tag' => 'input',
'name' => static::FORM_KEY,
'type' => 'hidden',
'value' => 'hidden',
);
} else {
$key = T::input()
->name(static::FORM_KEY)
->type('hidden')
->value($form_key);
$r[] = $key;
}
}
$s = array(
'tag' => 'button',
'type' => 'submit',
'label' =>
Util::nestedValue($m, 'return', CommentParser::$embeddedDataName, 'label')
? : 'Submit'
);
if (!$dataOnly)
$s = Emmet::make(static::style('submit', $m), $s);
$r[] = $s;
$t = array(
'action' => $restler->getBaseUrl() . '/' . rtrim($action, '/'),
'method' => $method,
);
if (static::$fileUpload) {
static::$fileUpload = false;
$t['enctype'] = 'multipart/form-data';
}
if (!$dataOnly) {
$t = Emmet::make(static::style('form', $m), $t);
$t->prefix = $prefix;
$t->indent = $indent;
$t[] = $r;
} else {
$t['fields'] = $r;
}
return $t;
}
public static function style($name, array $metadata, $type = '')
{
return isset($metadata[CommentParser::$embeddedDataName][$name])
? $metadata[CommentParser::$embeddedDataName][$name]
: (!empty($type) && isset(static::$style["$name-$type"])
? static::$style["$name-$type"]
: (isset(static::$style[$name])
? static::$style[$name]
: null
)
);
}
public static function fields($dataOnly = false)
{
$m = static::$info->metadata;
$params = $m['param'];
$values = static::$info->parameters;
$r = array();
foreach ($params as $k => $p) {
$value = Util::nestedValue($values, $k);
if (
is_scalar($value) ||
($p['type'] == 'array' && is_array($value) && $value == array_values($value)) ||
is_object($value) && $p['type'] == get_class($value)
)
$p['value'] = $value;
static::$validationInfo = $v = new ValidationInfo($p);
if ($v->from == 'path')
continue;
if (!empty($v->children)) {
$t = Emmet::make(static::style('fieldset', $m), array('label' => $v->label));
foreach ($v->children as $n => $c) {
$value = Util::nestedValue($v->value, $n);
if (
is_scalar($value) ||
($c['type'] == 'array' && is_array($value) && $value == array_values($value)) ||
is_object($value) && $c['type'] == get_class($value)
)
$c['value'] = $value;
static::$validationInfo = $vc = new ValidationInfo($c);
if ($vc->from == 'path')
continue;
$vc->name = $v->name . '[' . $vc->name . ']';
$t [] = static::field($vc, $dataOnly);
}
$r[] = $t;
static::$validationInfo = null;
} else {
$f = static::field($v, $dataOnly);
$r [] = $f;
}
static::$validationInfo = null;
}
return $r;
}
/**
* @param ValidationInfo $p
*
* @param bool $dataOnly
*
* @return array|T
*/
public static function field(ValidationInfo $p, $dataOnly = false)
{
if (is_string($p->value)) {
//prevent XSS attacks
$p->value = htmlspecialchars($p->value, ENT_QUOTES | ENT_HTML401, 'UTF-8');
}
$type = $p->field ? : static::guessFieldType($p);
$tag = in_array($type, static::$inputTypes)
? 'input' : $type;
$options = array();
$name = $p->name;
$multiple = null;
if ($p->type == 'array' && $p->contentType != 'associative') {
$name .= '[]';
$multiple = true;
}
if ($p->choice) {
foreach ($p->choice as $i => $choice) {
$option = array('name' => $name, 'value' => $choice);
$option['text'] = isset($p->rules['select'][$i])
? $p->rules['select'][$i]
: $choice;
if ($choice == $p->value)
$option['selected'] = true;
$options[] = $option;
}
} elseif ($p->type == 'boolean' || $p->type == 'bool') {
if (String::beginsWith($type, 'radio')) {
$options[] = array('name' => $p->name, 'text' => ' Yes ',
'value' => 'true');
$options[] = array('name' => $p->name, 'text' => ' No ',
'value' => 'false');
if ($p->value || $p->default)
$options[0]['selected'] = true;
} else {
$r = array(
'tag' => $tag,
'name' => $name,
'type' => $type,
'label' => $p->label,
'value' => 'true',
'default' => $p->default,
);
$r['text'] = 'Yes';
if ($p->default) {
$r['selected'] = true;
}
}
}
if (empty($r)) {
$r = array(
'tag' => $tag,
'name' => $name,
'type' => $type,
'label' => $p->label,
'value' => $p->value,
'default' => $p->default,
'options' => & $options,
'multiple' => $multiple,
);
}
if ($type == 'file') {
static::$fileUpload = true;
$r['accept'] = implode(', ', UploadFormat::$allowedMimeTypes);
}
if (true === $p->required)
$r['required'] = true;
if (isset($p->rules['autofocus']))
$r['autofocus'] = true;
/*
echo "<pre>";
print_r($r);
echo "</pre>";
*/
if ($dataOnly)
return $r;
if (isset($p->rules['form']))
return Emmet::make($p->rules['form'], $r);
$m = static::$info->metadata;
$t = Emmet::make(static::style($type, $m, $p->type) ? : static::style($tag, $m, $p->type), $r);
return $t;
}
protected static function guessFieldType(ValidationInfo $p, $type = 'type')
{
if (in_array($p->$type, static::$inputTypes))
return $p->$type;
if ($p->choice)
return $p->type == 'array' ? 'checkbox' : 'select';
switch ($p->$type) {
case 'boolean':
return 'radio';
case 'int':
case 'number':
case 'float':
return 'number';
case 'array':
return static::guessFieldType($p, 'contentType');
}
if ($p->name == 'password')
return 'password';
return 'text';
}
/**
* Get the form key
*
* @param string $method http method for form key
* @param string $action relative path from the web root. When set to null
* it uses the current api method's path
*
* @return string generated form key
*/
public static function key($method = 'POST', $action = null)
{
if (is_null($action))
$action = Scope::get('Restler')->url;
$target = "$method $action";
if (empty(static::$key[$target]))
static::$key[$target] = md5($target . User::getIpAddress() . uniqid(mt_rand()));
$_SESSION[static::FORM_KEY] = static::$key;
return static::$key[$target];
}
/**
* Access verification method.
*
* API access will be denied when this method returns false
*
* @return boolean true when api access is allowed false otherwise
*
* @throws RestException 403 security violation
*/
public function __isAllowed()
{
if (session_id() == '') {
session_start();
}
/** @var Restler $restler */
$restler = $this->restler;
$url = $restler->url;
foreach (static::$excludedPaths as $exclude) {
if (empty($exclude)) {
if ($url == $exclude)
return true;
} elseif (String::beginsWith($url, $exclude)) {
return true;
}
}
$check = static::$filterFormRequestsOnly
? $restler->requestFormat instanceof UrlEncodedFormat || $restler->requestFormat instanceof UploadFormat
: true;
if (!empty($_POST) && $check) {
if (
isset($_POST[static::FORM_KEY]) &&
($target = Util::getRequestMethod() . ' ' . $restler->url) &&
isset($_SESSION[static::FORM_KEY][$target]) &&
$_POST[static::FORM_KEY] == $_SESSION[static::FORM_KEY][$target]
) {
return true;
}
throw new RestException(403, 'Insecure form submission');
}
return true;
}
}