Merge pull request #2771 from aternatik/api_rest
Add new core module : REST API
This commit is contained in:
commit
cb6f23d9b6
@ -13,7 +13,8 @@
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"ext-gd": "*",
|
||||
"ext-curl": "*"
|
||||
"ext-curl": "*",
|
||||
"restler/framework": "3.0.*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mysqli": "*",
|
||||
|
||||
123
dev/skeletons/build_api_class.php
Executable file
123
dev/skeletons/build_api_class.php
Executable file
@ -0,0 +1,123 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file dev/skeletons/build_api_class.php
|
||||
* \ingroup core
|
||||
* \brief Create a complete API class file from existant class file
|
||||
*/
|
||||
|
||||
$sapi_type = php_sapi_name();
|
||||
$script_file = basename(__FILE__);
|
||||
$path=dirname(__FILE__).'/';
|
||||
|
||||
// Test if batch mode
|
||||
if (substr($sapi_type, 0, 3) == 'cgi') {
|
||||
echo "Error: You are using PHP for CGI. To execute ".$script_file." from command line, you must use PHP for CLI mode.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
// Include Dolibarr environment
|
||||
require_once($path."../../htdocs/master.inc.php");
|
||||
// After this $db is a defined handler to database.
|
||||
|
||||
// Main
|
||||
$version='1';
|
||||
@set_time_limit(0);
|
||||
$error=0;
|
||||
|
||||
$langs->load("main");
|
||||
|
||||
|
||||
print "***** $script_file ($version) *****\n";
|
||||
|
||||
|
||||
// -------------------- START OF BUILD_API_FROM_CLASS --------------------
|
||||
|
||||
// Check parameters
|
||||
if (! isset($argv[1]) && ! isset($argv[2]))
|
||||
{
|
||||
print "Usage: $script_file phpClassFile phpClassName\n";
|
||||
exit;
|
||||
}
|
||||
// Show parameters
|
||||
print 'Classfile='.$argv[1]."\n";
|
||||
print 'Classname='.$argv[2]."\n";
|
||||
|
||||
$classfile=$argv[1];
|
||||
$classname=$argv[2];
|
||||
$classmin=strtolower($classname);
|
||||
$classnameApi = $classname.'Api';
|
||||
$property=array();
|
||||
$targetcontent='';
|
||||
|
||||
// Load the class and read properties
|
||||
require_once($classfile);
|
||||
|
||||
$property=array();
|
||||
$class = new $classname($db);
|
||||
$values=get_class_vars($classname);
|
||||
|
||||
unset($values['db']);
|
||||
unset($values['error']);
|
||||
unset($values['errors']);
|
||||
unset($values['element']);
|
||||
unset($values['table_element']);
|
||||
unset($values['table_element_line']);
|
||||
unset($values['fk_element']);
|
||||
unset($values['ismultientitymanaged']);
|
||||
|
||||
// Read skeleton_api_class.class.php file
|
||||
$skeletonfile=$path.'skeleton_api_class.class.php';
|
||||
$sourcecontent=file_get_contents($skeletonfile);
|
||||
if (! $sourcecontent)
|
||||
{
|
||||
print "\n";
|
||||
print "Error: Failed to read skeleton sample '".$skeletonfile."'\n";
|
||||
print "Try to run script from skeletons directory.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
// Define output variables
|
||||
$outfile='out.api_'.$classmin.'.class.php';
|
||||
$targetcontent=$sourcecontent;
|
||||
|
||||
// Substitute class name
|
||||
$targetcontent=preg_replace('/skeleton_api_class\.class\.php/', 'api_'.$classmin.'.class.php', $targetcontent);
|
||||
$targetcontent=preg_replace('/skeleton/', $classmin, $targetcontent);
|
||||
//$targetcontent=preg_replace('/\$table_element=\'skeleton\'/', '\$table_element=\''.$tablenoprefix.'\'', $targetcontent);
|
||||
$targetcontent=preg_replace('/SkeletonApi/', $classnameApi, $targetcontent);
|
||||
$targetcontent=preg_replace('/Skeleton/', $classname, $targetcontent);
|
||||
|
||||
// Build file
|
||||
$fp=fopen($outfile,"w");
|
||||
if ($fp)
|
||||
{
|
||||
fputs($fp, $targetcontent);
|
||||
fclose($fp);
|
||||
print "\n";
|
||||
print "File '".$outfile."' has been built in current directory.\n";
|
||||
}
|
||||
else $error++;
|
||||
|
||||
|
||||
|
||||
// -------------------- END OF BUILD_CLASS_FROM_TABLE SCRIPT --------------------
|
||||
|
||||
print "You can now rename generated files by removing the 'out.' prefix in their name and store them into directory /module/class.\n";
|
||||
return $error;
|
||||
288
dev/skeletons/skeleton_api_class.class.php
Normal file
288
dev/skeletons/skeleton_api_class.class.php
Normal file
@ -0,0 +1,288 @@
|
||||
<?php
|
||||
/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
|
||||
/**
|
||||
* API class for skeleton object
|
||||
*
|
||||
* @smart-auto-routing false
|
||||
* @access protected
|
||||
* @class DolibarrApiAccess {@requires user,external}
|
||||
*
|
||||
*
|
||||
*/
|
||||
class SkeletonApi extends DolibarrApi
|
||||
{
|
||||
/**
|
||||
* @var array $FIELDS Mandatory fields, checked when create and update object
|
||||
*/
|
||||
static $FIELDS = array(
|
||||
'name'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var Skeleton $skeleton {@type Skeleton}
|
||||
*/
|
||||
public $skeleton;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @url GET skeleton/
|
||||
*
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
global $db, $conf;
|
||||
$this->db = $db;
|
||||
$this->skeleton = new Skeleton($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties of a skeleton object
|
||||
*
|
||||
* Return an array with skeleton informations
|
||||
*
|
||||
* @param int $id ID of skeleton
|
||||
* @return array|mixed data without useless information
|
||||
*
|
||||
* @url GET skeleton/{id}
|
||||
* @throws RestException
|
||||
*/
|
||||
function get($id)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->skeleton->read) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$result = $this->skeleton->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Skeleton not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('skeleton',$this->skeleton->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
return $this->_cleanObjectDatas($this->skeleton);
|
||||
}
|
||||
|
||||
/**
|
||||
* List skeletons
|
||||
*
|
||||
* Get a list of skeletons
|
||||
*
|
||||
* @param int $mode Use this param to filter list
|
||||
* @param string $sortfield Sort field
|
||||
* @param string $sortorder Sort order
|
||||
* @param int $limit Limit for list
|
||||
* @param int $page Page number
|
||||
*
|
||||
* @return array Array of skeleton objects
|
||||
*
|
||||
* @url GET /skeletons/
|
||||
*/
|
||||
function getList($mode, $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) {
|
||||
global $db, $conf;
|
||||
|
||||
$obj_ret = array();
|
||||
|
||||
$socid = DolibarrApiAccess::$user->societe_id ? DolibarrApiAccess::$user->societe_id : '';
|
||||
|
||||
// If the internal user must only see his customers, force searching by him
|
||||
if (! DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) $search_sale = DolibarrApiAccess::$user->id;
|
||||
|
||||
$sql = "SELECT s.rowid";
|
||||
if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."skeleton as s";
|
||||
|
||||
if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
|
||||
$sql.= ", ".MAIN_DB_PREFIX."c_stcomm as st";
|
||||
$sql.= " WHERE s.fk_stcomm = st.id";
|
||||
|
||||
// Example of use $mode
|
||||
//if ($mode == 1) $sql.= " AND s.client IN (1, 3)";
|
||||
//if ($mode == 2) $sql.= " AND s.client IN (2, 3)";
|
||||
|
||||
$sql.= ' AND s.entity IN ('.getEntity('skeleton', 1).')';
|
||||
if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= " AND s.fk_soc = sc.fk_soc";
|
||||
if ($socid) $sql.= " AND s.fk_soc = ".$socid;
|
||||
if ($search_sale > 0) $sql.= " AND s.rowid = sc.fk_soc"; // Join for the needed table to filter by sale
|
||||
|
||||
// Insert sale filter
|
||||
if ($search_sale > 0)
|
||||
{
|
||||
$sql .= " AND sc.fk_user = ".$search_sale;
|
||||
}
|
||||
|
||||
$nbtotalofrecords = 0;
|
||||
if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
|
||||
{
|
||||
$result = $db->query($sql);
|
||||
$nbtotalofrecords = $db->num_rows($result);
|
||||
}
|
||||
|
||||
$sql.= $db->order($sortfield, $sortorder);
|
||||
if ($limit) {
|
||||
if ($page < 0)
|
||||
{
|
||||
$page = 0;
|
||||
}
|
||||
$offset = $limit * $page;
|
||||
|
||||
$sql.= $db->plimit($limit + 1, $offset);
|
||||
}
|
||||
|
||||
$result = $db->query($sql);
|
||||
if ($result)
|
||||
{
|
||||
$num = $db->num_rows($result);
|
||||
while ($i < $num)
|
||||
{
|
||||
$obj = $db->fetch_object($result);
|
||||
$skeleton_static = new Skeleton($db);
|
||||
if($skeleton_static->fetch($obj->rowid)) {
|
||||
$obj_ret[] = parent::_cleanObjectDatas($skeleton_static);
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new RestException(503, 'Error when retrieve skeleton list');
|
||||
}
|
||||
if( ! count($obj_ret)) {
|
||||
throw new RestException(404, 'No skeleton found');
|
||||
}
|
||||
return $obj_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create skeleton object
|
||||
*
|
||||
* @param array $request_data Request datas
|
||||
* @return int ID of skeleton
|
||||
*
|
||||
* @url POST skeleton/
|
||||
*/
|
||||
function post($request_data = NULL)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->skeleton->create) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
// Check mandatory fields
|
||||
$result = $this->_validate($request_data);
|
||||
|
||||
foreach($request_data as $field => $value) {
|
||||
$this->skeleton->$field = $value;
|
||||
}
|
||||
if( ! $this->skeleton->create(DolibarrApiAccess::$user)) {
|
||||
throw new RestException(500);
|
||||
}
|
||||
return $this->skeleton->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update skeleton
|
||||
*
|
||||
* @param int $id Id of skeleton to update
|
||||
* @param array $request_data Datas
|
||||
* @return int
|
||||
*
|
||||
* @url PUT skeleton/{id}
|
||||
*/
|
||||
function put($id, $request_data = NULL)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->skeleton->create) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$result = $this->skeleton->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Skeleton not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('skeleton',$this->skeleton->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
foreach($request_data as $field => $value) {
|
||||
$this->skeleton->$field = $value;
|
||||
}
|
||||
|
||||
if($this->skeleton->update($id, DolibarrApiAccess::$user))
|
||||
return $this->get ($id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete skeleton
|
||||
*
|
||||
* @param int $id Skeleton ID
|
||||
* @return array
|
||||
*
|
||||
* @url DELETE skeleton/{id}
|
||||
*/
|
||||
function delete($id)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->skeleton->supprimer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
$result = $this->skeleton->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Skeleton not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('skeleton',$this->skeleton->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
if( !$this->skeleton->delete($id))
|
||||
{
|
||||
throw new RestException(500);
|
||||
}
|
||||
|
||||
return array(
|
||||
'success' => array(
|
||||
'code' => 200,
|
||||
'message' => 'Skeleton deleted'
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate fields before create or update object
|
||||
*
|
||||
* @param array $data Data to validate
|
||||
* @return array
|
||||
*
|
||||
* @throws RestException
|
||||
*/
|
||||
function _validate($data)
|
||||
{
|
||||
$skeleton = array();
|
||||
foreach (SkeletonApi::$FIELDS as $field) {
|
||||
if (!isset($data[$field]))
|
||||
throw new RestException(400, "$field field missing");
|
||||
$skeleton[$field] = $data[$field];
|
||||
}
|
||||
return $skeleton;
|
||||
}
|
||||
}
|
||||
138
htdocs/api/admin/api.php
Normal file
138
htdocs/api/admin/api.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
/* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
|
||||
* Copyright (C) 2005-2010 Laurent Destailleur <eldy@users.sourceforge.org>
|
||||
* Copyright (C) 2011 Juanjo Menent <jmenent@2byte.es>
|
||||
* Copyright (C) 2012 Regis Houssin <regis.houssin@capnetworks.com>
|
||||
* Copyright (C) 2015 Regis Houssin <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file htdocs/api/admin/api.php
|
||||
* \ingroup api
|
||||
* \brief Page to setup api module
|
||||
*/
|
||||
|
||||
require '../../main.inc.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
|
||||
$langs->load("admin");
|
||||
|
||||
if (! $user->admin)
|
||||
accessforbidden();
|
||||
|
||||
$actionsave=GETPOST("save");
|
||||
|
||||
// Sauvegardes parametres
|
||||
if ($actionsave)
|
||||
{
|
||||
$i=0;
|
||||
|
||||
$db->begin();
|
||||
|
||||
$i+=dolibarr_set_const($db,'API_KEY',trim(GETPOST("API_KEY")),'chaine',0,'',$conf->entity);
|
||||
|
||||
if ($i >= 1)
|
||||
{
|
||||
$db->commit();
|
||||
setEventMessage($langs->trans("SetupSaved"));
|
||||
}
|
||||
else
|
||||
{
|
||||
$db->rollback();
|
||||
setEventMessage($langs->trans("Error"), 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
llxHeader();
|
||||
|
||||
$linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php">'.$langs->trans("BackToModuleList").'</a>';
|
||||
print_fiche_titre($langs->trans("ApiSetup"),$linkback,'title_setup');
|
||||
|
||||
print $langs->trans("ApiDesc")."<br>\n";
|
||||
print "<br>\n";
|
||||
|
||||
print '<form name="apisetupform" action="'.$_SERVER["PHP_SELF"].'" method="post">';
|
||||
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
|
||||
print '<table class="noborder" width="100%">';
|
||||
|
||||
print '<tr class="liste_titre">';
|
||||
print "<td>".$langs->trans("Parameter")."</td>";
|
||||
print "<td>".$langs->trans("Value")."</td>";
|
||||
print "<td> </td>";
|
||||
print "</tr>";
|
||||
|
||||
print '<tr class="impair">';
|
||||
print '<td class="fieldrequired">'.$langs->trans("KeyForApiAccess").'</td>';
|
||||
print '<td><input type="text" class="flat" id="API_KEY" name="API_KEY" value="'. (GETPOST('API_KEY')?GETPOST('API_KEY'):(! empty($conf->global->API_KEY)?$conf->global->API_KEY:'')) . '" size="40">';
|
||||
if (! empty($conf->use_javascript_ajax))
|
||||
print ' '.img_picto($langs->trans('Generate'), 'refresh', 'id="generate_token" class="linkobject"');
|
||||
print '</td>';
|
||||
print '<td> </td>';
|
||||
print '</tr>';
|
||||
|
||||
print '</table>';
|
||||
|
||||
print '<br><div class="center">';
|
||||
print '<input type="submit" name="save" class="button" value="'.$langs->trans("Save").'">';
|
||||
print '</div>';
|
||||
|
||||
print '</form>';
|
||||
|
||||
print '<br><br>';
|
||||
|
||||
// API endpoint
|
||||
print '<u>'.$langs->trans("ApiEndPointIs").':</u><br>';
|
||||
$url=DOL_MAIN_URL_ROOT.'/public/api/';
|
||||
print img_picto('','object_globe.png').' <a href="'.$url.'" target="_blank">'.$url."</a><br>\n";
|
||||
$url=DOL_MAIN_URL_ROOT.'/public/api/.json';
|
||||
print img_picto('','object_globe.png').' <a href="'.$url.'" target="_blank">'.$url."</a><br>\n";
|
||||
|
||||
// Explorer
|
||||
print '<u>'.$langs->trans("ApiExporerIs").':</u><br>';
|
||||
$url=DOL_MAIN_URL_ROOT.'/public/api/explorer/index.html';
|
||||
print img_picto('','object_globe.png').' <a href="'.$url.'" target="_blank">'.$url."</a><br>\n";
|
||||
|
||||
print '<br>';
|
||||
|
||||
|
||||
print '<br>';
|
||||
print $langs->trans("OnlyActiveElementsAreExposed", DOL_URL_ROOT.'/admin/modules.php');
|
||||
|
||||
if (! empty($conf->use_javascript_ajax))
|
||||
{
|
||||
print "\n".'<script type="text/javascript">';
|
||||
print '$(document).ready(function () {
|
||||
$("#generate_token").click(function() {
|
||||
$.get( "'.DOL_URL_ROOT.'/core/ajax/security.php", {
|
||||
action: \'getrandompassword\',
|
||||
generic: true
|
||||
},
|
||||
function(token) {
|
||||
$("#API_KEY").val(token);
|
||||
});
|
||||
});
|
||||
});';
|
||||
print '</script>';
|
||||
}
|
||||
|
||||
|
||||
llxFooter();
|
||||
$db->close();
|
||||
215
htdocs/api/class/api.class.php
Normal file
215
htdocs/api/class/api.class.php
Normal file
@ -0,0 +1,215 @@
|
||||
<?php
|
||||
/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use Luracast\Restler\Restler;
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
|
||||
|
||||
/**
|
||||
* Class for API
|
||||
*
|
||||
*/
|
||||
class DolibarrApi
|
||||
{
|
||||
|
||||
/**
|
||||
* @var DoliDb $db Database object
|
||||
*/
|
||||
static protected $db;
|
||||
|
||||
/**
|
||||
* @var Restler $r Restler object
|
||||
*/
|
||||
var $r;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param DoliDb $db Database handler
|
||||
*/
|
||||
function __construct($db) {
|
||||
$this->db = $db;
|
||||
$this->r = new Restler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed method when API is called without parameter
|
||||
*
|
||||
* Display a short message an return a http code 200
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function index()
|
||||
{
|
||||
return array(
|
||||
'success' => array(
|
||||
'code' => 200,
|
||||
'message' => __class__.' is up and running!'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clean sensible object datas
|
||||
*
|
||||
* @param object $object Object to clean
|
||||
* @return array Array of cleaned object properties
|
||||
*
|
||||
* @todo use an array for properties to clean
|
||||
*
|
||||
*/
|
||||
function _cleanObjectDatas($object) {
|
||||
|
||||
// Remove $db object property for object
|
||||
unset($object->db);
|
||||
|
||||
// If object has lines, remove $db property
|
||||
if(isset($object->lines) && count($object->lines) > 0) {
|
||||
for($i=0; $i < count($object->lines); $i++) {
|
||||
$this->_cleanObjectDatas($object->lines[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
// If object has linked objects, remove $db property
|
||||
if(isset($object->linkedObjects) && count($object->linkedObjects) > 0) {
|
||||
foreach($object->linkedObjects as $type_object => $linked_object) {
|
||||
foreach($linked_object as $object2clean) {
|
||||
$this->_cleanObjectDatas($object2clean);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user access to a resource
|
||||
*
|
||||
* Check access by user to a given resource
|
||||
*
|
||||
* @param string $resource element to check
|
||||
* @param int $resource_id Object ID if we want to check a particular record (optional) is linked to a owned thirdparty (optional).
|
||||
* @param type $dbtablename 'TableName&SharedElement' with Tablename is table where object is stored. SharedElement is an optional key to define where to check entity. Not used if objectid is null (optional)
|
||||
* @param string $feature2 Feature to check, second level of permission (optional). Can be or check with 'level1|level2'.
|
||||
* @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional)
|
||||
* @param string $dbt_select Field name for select if not rowid. Not used if objectid is null (optional)
|
||||
* @throws RestException
|
||||
*/
|
||||
static function _checkAccessToResource($resource, $resource_id=0, $dbtablename='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid') {
|
||||
|
||||
// Features/modules to check
|
||||
$featuresarray = array($resource);
|
||||
if (preg_match('/&/', $resource)) {
|
||||
$featuresarray = explode("&", $resource);
|
||||
}
|
||||
else if (preg_match('/\|/', $resource)) {
|
||||
$featuresarray = explode("|", $resource);
|
||||
}
|
||||
|
||||
// More subfeatures to check
|
||||
if (! empty($feature2)) {
|
||||
$feature2 = explode("|", $feature2);
|
||||
}
|
||||
|
||||
return checkUserAccessToObject(DolibarrApiAccess::$user, $featuresarray,$resource_id,$dbtablename,$feature2,$dbt_keyfield,$dbt_select);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API init
|
||||
*
|
||||
*/
|
||||
class DolibarrApiInit extends DolibarrApi
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
global $db;
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login
|
||||
*
|
||||
* Log user with username and password
|
||||
*
|
||||
* @param string $login Username
|
||||
* @param string $password User password
|
||||
* @param int $entity User entity
|
||||
* @return array Response status and user token
|
||||
*
|
||||
* @throws RestException
|
||||
*/
|
||||
public function login($login, $password, $entity = 0) {
|
||||
|
||||
// Authentication mode
|
||||
if (empty($dolibarr_main_authentication))
|
||||
$dolibarr_main_authentication = 'http,dolibarr';
|
||||
// Authentication mode: forceuser
|
||||
if ($dolibarr_main_authentication == 'forceuser' && empty($dolibarr_auto_user))
|
||||
$dolibarr_auto_user = 'auto';
|
||||
// Set authmode
|
||||
$authmode = explode(',', $dolibarr_main_authentication);
|
||||
|
||||
include_once DOL_DOCUMENT_ROOT . '/core/lib/security2.lib.php';
|
||||
$login = checkLoginPassEntity($login, $password, $entity, $authmode);
|
||||
if (empty($login))
|
||||
{
|
||||
throw new RestException(403, 'Access denied');
|
||||
}
|
||||
|
||||
// Generate token for user
|
||||
$token = dol_hash($login.uniqid().$conf->global->MAIN_API_KEY,1);
|
||||
|
||||
// We store API token into database
|
||||
$sql = "UPDATE ".MAIN_DB_PREFIX."user";
|
||||
$sql.= " SET api_key = '".$this->db->escape($token)."'";
|
||||
$sql.= " WHERE login = '".$this->db->escape($login)."'";
|
||||
|
||||
dol_syslog(get_class($this)."::login", LOG_DEBUG); // No log
|
||||
$result = $this->db->query($sql);
|
||||
if (!$result)
|
||||
{
|
||||
throw new RestException(500, 'Error when updating user :'.$this->db->error_msg);
|
||||
}
|
||||
|
||||
//return token
|
||||
return array(
|
||||
'success' => array(
|
||||
'code' => 200,
|
||||
'token' => $token,
|
||||
'message' => 'Welcome ' . $login
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status (Dolibarr version)
|
||||
*
|
||||
* @access protected
|
||||
* @class DolibarrApiAccess {@requires admin}
|
||||
*/
|
||||
function status() {
|
||||
require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php';
|
||||
return array(
|
||||
'success' => array(
|
||||
'code' => 200,
|
||||
'dolibarr_version' => DOL_VERSION
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
144
htdocs/api/class/api_access.class.php
Normal file
144
htdocs/api/class/api_access.class.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use \Luracast\Restler\iAuthenticate;
|
||||
use \Luracast\Restler\Resources;
|
||||
use \Luracast\Restler\Defaults;
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
|
||||
/**
|
||||
* Dolibarr API access class
|
||||
*
|
||||
*/
|
||||
class DolibarrApiAccess implements iAuthenticate
|
||||
{
|
||||
const REALM = 'Restricted Dolibarr API';
|
||||
|
||||
/**
|
||||
* @var array $requires role required by API method user / external / admin
|
||||
*/
|
||||
public static $requires = array('user','external','admin');
|
||||
|
||||
/**
|
||||
* @var string $role user role
|
||||
*/
|
||||
public static $role = 'user';
|
||||
|
||||
/**
|
||||
* @var User $user Loggued user
|
||||
*/
|
||||
public static $user = '';
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
|
||||
/**
|
||||
* @return string string to be used with WWW-Authenticate header
|
||||
* @example Basic
|
||||
* @example Digest
|
||||
* @example OAuth
|
||||
*/
|
||||
public function __getWWWAuthenticateString();
|
||||
|
||||
/**
|
||||
* Check access
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function _isAllowed()
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
global $db;
|
||||
|
||||
$stored_key = '';
|
||||
|
||||
$userClass = Defaults::$userIdentifierClass;
|
||||
|
||||
if (isset($_GET['api_key'])) {
|
||||
$sql = "SELECT u.login, u.datec, u.api_key, ";
|
||||
$sql.= " u.tms as date_modification, u.entity";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."user as u";
|
||||
$sql.= " WHERE u.api_key = '".$db->escape($_GET['api_key'])."'";
|
||||
|
||||
if ($db->query($sql))
|
||||
{
|
||||
if ($db->num_rows($result))
|
||||
{
|
||||
$obj = $db->fetch_object($result);
|
||||
$login = $obj->login;
|
||||
$stored_key = $obj->api_key;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new RestException(503, 'Error when fetching user api_key :'.$db->error_msg);
|
||||
}
|
||||
|
||||
if ( $stored_key != $_GET['api_key']) {
|
||||
$userClass::setCacheIdentifier($_GET['api_key']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$fuser = new User($db);
|
||||
if(! $fuser->fetch('',$login)) {
|
||||
throw new RestException(503, 'Error when fetching user :'.$fuser->error);
|
||||
}
|
||||
$fuser->getrights();
|
||||
static::$user = $fuser;
|
||||
|
||||
if($fuser->societe_id)
|
||||
static::$role = 'external';
|
||||
|
||||
if($fuser->admin)
|
||||
static::$role = 'admin';
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$userClass::setCacheIdentifier(static::$role);
|
||||
Resources::$accessControlFunction = 'DolibarrApiAccess::verifyAccess';
|
||||
return in_array(static::$role, (array) static::$requires) || static::$role == 'admin';
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
public function __getWWWAuthenticateString()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
/**
|
||||
* Verify access
|
||||
*
|
||||
* @param array $m Properties of method
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
public static function verifyAccess(array $m)
|
||||
{
|
||||
$requires = isset($m['class']['DolibarrApiAccess']['properties']['requires'])
|
||||
? $m['class']['DolibarrApiAccess']['properties']['requires']
|
||||
: false;
|
||||
|
||||
|
||||
return $requires
|
||||
? static::$role == 'admin' || in_array(static::$role, (array) $requires)
|
||||
: true;
|
||||
|
||||
}
|
||||
}
|
||||
8
htdocs/api/index.php
Normal file
8
htdocs/api/index.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
50
htdocs/api/readme.md
Normal file
50
htdocs/api/readme.md
Normal file
@ -0,0 +1,50 @@
|
||||
API howto
|
||||
=========
|
||||
|
||||
Explore the api
|
||||
---------------
|
||||
|
||||
You can explore API method by using web interface : https://**yourdolibarr.tld**/htdocs/public/api/explorer/index.html (replace **yourdolibarr.tld** by real hostname of your Dolibarr installation)
|
||||
|
||||
Access to the API
|
||||
---------------
|
||||
|
||||
> **Warning : access to the API should (or better : must!) be secured with SSL connection**
|
||||
|
||||
To access to the API you need a token to identify. When you access the API for the first time, you need to log in with user name and password to get a token. **Only** this token will allow to access API with.
|
||||
|
||||
To log in with the API, use this uri : https://**yourdolibarr.tld**/htdocs/public/api/login?login=**username**&password=**password** (replace bold strings with real values)
|
||||
|
||||
The token will be saved by Dolibarr for next user accesses to the API and it **must** be put into request uri as **api_key** parameter.
|
||||
|
||||
Develop the API
|
||||
--------------
|
||||
|
||||
The API uses Lucarast Restler framework. Please check documentation https://www.luracast.com/products/restler and examples http://help.luracast.com/restler/examples/
|
||||
Github contains also usefull informations : https://github.com/Luracast/Restler
|
||||
|
||||
To implement it into Dolibarr, we need to create a specific class for object we want to use. A skeleton file is available into /dev directory : *skeleton_api_class.class.php*
|
||||
The API class file must be put into object class directory, with specific file name. By example, API class file for '*myobject*' must be put as : /htdocs/*myobject*/class/api_*myobject*.class.php. Class must be named **MyobjectApi**.
|
||||
|
||||
If a module provide several object, use a different name for '*myobject*' and put the file into the same directory.
|
||||
|
||||
**Define url for methods**
|
||||
|
||||
It is possible to specify url for API methods by simply use the PHPDoc tag **@url**. See examples :
|
||||
|
||||
/**
|
||||
* List contacts
|
||||
*
|
||||
* Get a list of contacts
|
||||
*
|
||||
* @url GET /contact/list
|
||||
* @url GET /contact/list/{socid}
|
||||
* @url GET /thirdparty/{socid}/contacts
|
||||
* [...]
|
||||
|
||||
**Other Annotations**
|
||||
Other annotations are used, you are encouraged to read them : https://github.com/Luracast/Restler/blob/master/ANNOTATIONS.md
|
||||
|
||||
PHPDoc tags can also be used to specify variables informations for API. Again, rtfm : https://github.com/Luracast/Restler/blob/master/PARAM.md
|
||||
|
||||
|
||||
355
htdocs/categories/class/api_category.class.php
Normal file
355
htdocs/categories/class/api_category.class.php
Normal file
@ -0,0 +1,355 @@
|
||||
<?php
|
||||
/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
|
||||
|
||||
/**
|
||||
* API class for category object
|
||||
*
|
||||
* @smart-auto-routing false
|
||||
* @access protected
|
||||
* @class DolibarrApiAccess {@requires user,external}
|
||||
*
|
||||
*
|
||||
*/
|
||||
class CategoryApi extends DolibarrApi
|
||||
{
|
||||
/**
|
||||
* @var array $FIELDS Mandatory fields, checked when create and update object
|
||||
*/
|
||||
static $FIELDS = array(
|
||||
'label',
|
||||
'type'
|
||||
);
|
||||
|
||||
static $TYPES = array(
|
||||
0 => 'product',
|
||||
1 => 'supplier',
|
||||
2 => 'customer',
|
||||
3 => 'member',
|
||||
4 => 'contact',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var Categorie $category {@type Categorie}
|
||||
*/
|
||||
public $category;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @url GET category/
|
||||
*
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
global $db, $conf;
|
||||
$this->db = $db;
|
||||
$this->category = new Categorie($this->db);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties of a category object
|
||||
*
|
||||
* Return an array with category informations
|
||||
*
|
||||
* @param int $id ID of category
|
||||
* @return array|mixed data without useless information
|
||||
*
|
||||
* @url GET category/{id}
|
||||
* @throws RestException
|
||||
*/
|
||||
function get($id)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->categorie->lire) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$result = $this->category->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'category not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('category',$this->category->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
return $this->_cleanObjectDatas($this->category);
|
||||
}
|
||||
|
||||
/**
|
||||
* List categories
|
||||
*
|
||||
* Get a list of categories
|
||||
*
|
||||
* @param string $type Type of category ('member', 'customer', 'supplier', 'product', 'contact')
|
||||
* @param string $sortfield Sort field
|
||||
* @param string $sortorder Sort order
|
||||
* @param int $limit Limit for list
|
||||
* @param int $page Page number
|
||||
* @return array Array of category objects
|
||||
*
|
||||
* @url GET /category/list
|
||||
*/
|
||||
function getList($type='product', $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) {
|
||||
global $db, $conf;
|
||||
|
||||
$obj_ret = array();
|
||||
|
||||
if(! DolibarrApiAccess::$user->rights->categorie->lire) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$sql = "SELECT s.rowid";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."categorie as s";
|
||||
$sql.= ' WHERE s.entity IN ('.getEntity('categorie', 1).')';
|
||||
$sql.= ' AND s.type='.array_search($type,CategoryApi::$TYPES);
|
||||
|
||||
$nbtotalofrecords = 0;
|
||||
if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
|
||||
{
|
||||
$result = $db->query($sql);
|
||||
$nbtotalofrecords = $db->num_rows($result);
|
||||
}
|
||||
|
||||
$sql.= $db->order($sortfield, $sortorder);
|
||||
if ($limit) {
|
||||
if ($page < 0)
|
||||
{
|
||||
$page = 0;
|
||||
}
|
||||
$offset = $limit * $page;
|
||||
|
||||
$sql.= $db->plimit($limit + 1, $offset);
|
||||
}
|
||||
|
||||
$result = $db->query($sql);
|
||||
if ($result)
|
||||
{
|
||||
$num = $db->num_rows($result);
|
||||
while ($i < $num)
|
||||
{
|
||||
$obj = $db->fetch_object($result);
|
||||
$category_static = new Categorie($db);
|
||||
if($category_static->fetch($obj->rowid)) {
|
||||
$obj_ret[] = parent::_cleanObjectDatas($category_static);
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new RestException(503, 'Error when retrieve category list : '.$category_static->error);
|
||||
}
|
||||
if( ! count($obj_ret)) {
|
||||
throw new RestException(404, 'No category found');
|
||||
}
|
||||
return $obj_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get member categories list
|
||||
*
|
||||
* @param string $sortfield Sort field
|
||||
* @param string $sortorder Sort order
|
||||
* @param int $limit Limit for list
|
||||
* @param int $page Page number
|
||||
* @return mixed
|
||||
*
|
||||
* @url GET /category/list/member
|
||||
*/
|
||||
function getListCategoryMember($sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) {
|
||||
return $this->getList('member', $sortfield, $sortorder, $limit, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customer categories list
|
||||
*
|
||||
* @param string $sortfield Sort field
|
||||
* @param string $sortorder Sort order
|
||||
* @param int $limit Limit for list
|
||||
* @param int $page Page number
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @url GET /category/list/customer
|
||||
*/
|
||||
function getListCategoryCustomer($sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) {
|
||||
return $this->getList('customer', $sortfield, $sortorder, $limit, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get supplier categories list
|
||||
*
|
||||
* @param string $sortfield Sort field
|
||||
* @param string $sortorder Sort order
|
||||
* @param int $limit Limit for list
|
||||
* @param int $page Page number
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @url GET /category/list/supplier
|
||||
*/
|
||||
function getListCategorySupplier($sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) {
|
||||
return $this->getList('supplier', $sortfield, $sortorder, $limit, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product categories list
|
||||
*
|
||||
* @param string $sortfield Sort field
|
||||
* @param string $sortorder Sort order
|
||||
* @param int $limit Limit for list
|
||||
* @param int $page Page number
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @url GET /category/list/product
|
||||
*/
|
||||
function getListCategoryProduct($sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) {
|
||||
return $this->getList('product', $sortfield, $sortorder, $limit, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get contact categories list
|
||||
*
|
||||
* @param string $sortfield Sort field
|
||||
* @param string $sortorder Sort order
|
||||
* @param int $limit Limit for list
|
||||
* @param int $page Page number
|
||||
* @return mixed
|
||||
*
|
||||
* @url GET /category/list/contact
|
||||
*/
|
||||
function getListCategoryContact($sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) {
|
||||
return $this->getList('contact', $sortfield, $sortorder, $limit, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create category object
|
||||
*
|
||||
* @param array $request_data Request data
|
||||
* @return int ID of category
|
||||
*
|
||||
* @url POST category/
|
||||
*/
|
||||
function post($request_data = NULL)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->categorie->creer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
// Check mandatory fields
|
||||
$result = $this->_validate($request_data);
|
||||
|
||||
foreach($request_data as $field => $value) {
|
||||
$this->category->$field = $value;
|
||||
}
|
||||
if($this->category->create(DolibarrApiAccess::$user) < 0) {
|
||||
throw new RestException(503, 'Error when create category : '.$this->category->error);
|
||||
}
|
||||
return $this->category->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update category
|
||||
*
|
||||
* @param int $id Id of category to update
|
||||
* @param array $request_data Datas
|
||||
* @return int
|
||||
*
|
||||
* @url PUT category/{id}
|
||||
*/
|
||||
function put($id, $request_data = NULL)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->categorie->creer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$result = $this->category->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'category not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('category',$this->category->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
foreach($request_data as $field => $value) {
|
||||
$this->category->$field = $value;
|
||||
}
|
||||
|
||||
if($this->category->update(DolibarrApiAccess::$user))
|
||||
return $this->get ($id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete category
|
||||
*
|
||||
* @param int $id Category ID
|
||||
* @return array
|
||||
*
|
||||
* @url DELETE category/{id}
|
||||
*/
|
||||
function delete($id)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->categorie->supprimer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
$result = $this->category->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'category not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('category',$this->category->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
if (! $this->category->delete(DolibarrApiAccess::$user)) {
|
||||
throw new RestException(401,'error when delete category');
|
||||
}
|
||||
|
||||
return array(
|
||||
'success' => array(
|
||||
'code' => 200,
|
||||
'message' => 'Category deleted'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate fields before create or update object
|
||||
*
|
||||
* @param array $data Data to validate
|
||||
* @return array
|
||||
*
|
||||
* @throws RestException
|
||||
*/
|
||||
function _validate($data)
|
||||
{
|
||||
$category = array();
|
||||
foreach (CategoryApi::$FIELDS as $field) {
|
||||
if (!isset($data[$field]))
|
||||
throw new RestException(400, "$field field missing");
|
||||
$category[$field] = $data[$field];
|
||||
}
|
||||
return $category;
|
||||
}
|
||||
}
|
||||
335
htdocs/commande/class/api_commande.class.php
Normal file
335
htdocs/commande/class/api_commande.class.php
Normal file
@ -0,0 +1,335 @@
|
||||
<?php
|
||||
/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
|
||||
|
||||
/**
|
||||
* API class for commande object
|
||||
*
|
||||
* @smart-auto-routing false
|
||||
* @access protected
|
||||
* @class DolibarrApiAccess {@requires user,external}
|
||||
*
|
||||
* @category Api
|
||||
* @package Api
|
||||
*
|
||||
*
|
||||
*/
|
||||
class CommandeApi extends DolibarrApi
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array $FIELDS Mandatory fields, checked when create and update object
|
||||
*/
|
||||
static $FIELDS = array(
|
||||
'socid'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var Commande $commande {@type Commande}
|
||||
*/
|
||||
public $commande;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @url GET order/
|
||||
*
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
global $db, $conf;
|
||||
$this->db = $db;
|
||||
$this->commande = new Commande($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties of a commande object
|
||||
*
|
||||
* Return an array with commande informations
|
||||
*
|
||||
* @param int $id ID of order
|
||||
* @param string $ref Ref of object
|
||||
* @param string $ref_ext External reference of object
|
||||
* @param string $ref_int Internal reference of other object
|
||||
* @return array|mixed data without useless information
|
||||
*
|
||||
* @url GET order/{id}
|
||||
* @throws RestException
|
||||
*/
|
||||
function get($id='',$ref='', $ref_ext='', $ref_int='')
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->commande->lire) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$result = $this->commande->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Order not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('commande',$this->commande->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
$this->commande->fetchObjectLinked();
|
||||
return $this->_cleanObjectDatas($this->commande);
|
||||
}
|
||||
|
||||
/**
|
||||
* List orders
|
||||
*
|
||||
* Get a list of orders
|
||||
*
|
||||
* @param int $mode Use this param to filter list
|
||||
* @param string $sortfield Sort field
|
||||
* @param string $sortorder Sort order
|
||||
* @param int $limit Limit for list
|
||||
* @param int $page Page number
|
||||
*
|
||||
* @url GET /order/list
|
||||
* @return array Array of order objects
|
||||
*/
|
||||
function getList($mode=0, $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) {
|
||||
global $db, $conf;
|
||||
|
||||
$obj_ret = array();
|
||||
|
||||
$socid = DolibarrApiAccess::$user->societe_id ? DolibarrApiAccess::$user->societe_id : '';
|
||||
|
||||
// If the internal user must only see his customers, force searching by him
|
||||
if (! DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) $search_sale = DolibarrApiAccess::$user->id;
|
||||
|
||||
$sql = "SELECT s.rowid";
|
||||
if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."commande as s";
|
||||
|
||||
if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
|
||||
|
||||
// Example of use $mode
|
||||
//if ($mode == 1) $sql.= " AND s.client IN (1, 3)";
|
||||
//if ($mode == 2) $sql.= " AND s.client IN (2, 3)";
|
||||
|
||||
$sql.= ' WHERE s.entity IN ('.getEntity('commande', 1).')';
|
||||
if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= " AND s.fk_soc = sc.fk_soc";
|
||||
if ($socid) $sql.= " AND s.fk_soc = ".$socid;
|
||||
if ($search_sale > 0) $sql.= " AND s.rowid = sc.fk_soc"; // Join for the needed table to filter by sale
|
||||
|
||||
// Insert sale filter
|
||||
if ($search_sale > 0)
|
||||
{
|
||||
$sql .= " AND sc.fk_user = ".$search_sale;
|
||||
}
|
||||
|
||||
$nbtotalofrecords = 0;
|
||||
if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
|
||||
{
|
||||
$result = $db->query($sql);
|
||||
$nbtotalofrecords = $db->num_rows($result);
|
||||
}
|
||||
|
||||
$sql.= $db->order($sortfield, $sortorder);
|
||||
if ($limit) {
|
||||
if ($page < 0)
|
||||
{
|
||||
$page = 0;
|
||||
}
|
||||
$offset = $limit * $page;
|
||||
|
||||
$sql.= $db->plimit($limit + 1, $offset);
|
||||
}
|
||||
|
||||
$result = $db->query($sql);
|
||||
|
||||
if ($result)
|
||||
{
|
||||
$num = $db->num_rows($result);
|
||||
while ($i < $num)
|
||||
{
|
||||
$obj = $db->fetch_object($result);
|
||||
$commande_static = new Commande($db);
|
||||
if($commande_static->fetch($obj->rowid)) {
|
||||
$obj_ret[] = parent::_cleanObjectDatas($commande_static);
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new RestException(503, 'Error when retrieve commande list');
|
||||
}
|
||||
if( ! count($obj_ret)) {
|
||||
throw new RestException(404, 'No commande found');
|
||||
}
|
||||
return $obj_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create order object
|
||||
*
|
||||
* @param array $request_data Request datas
|
||||
*
|
||||
* @url POST order/
|
||||
*
|
||||
* @return int ID of commande
|
||||
*/
|
||||
function post($request_data = NULL)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->commande->creer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
// Check mandatory fields
|
||||
$result = $this->_validate($request_data);
|
||||
|
||||
foreach($request_data as $field => $value) {
|
||||
$this->commande->$field = $value;
|
||||
}
|
||||
if(! $this->commande->create(DolibarrApiAccess::$user) ) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
return $this->commande->ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update order
|
||||
*
|
||||
* @param int $id Id of commande to update
|
||||
* @param array $request_data Datas
|
||||
*
|
||||
* @url PUT order/{id}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function put($id, $request_data = NULL)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->commande->creer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$result = $this->commande->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Commande not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('commande',$this->commande->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
foreach($request_data as $field => $value) {
|
||||
$this->commande->$field = $value;
|
||||
}
|
||||
|
||||
if($this->commande->update($id, DolibarrApiAccess::$user,1,'','','update'))
|
||||
return $this->get ($id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete order
|
||||
*
|
||||
* @param int $id Order ID
|
||||
*
|
||||
* @url DELETE order/{id}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function delete($id)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->commande->supprimer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
$result = $this->commande->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Order not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('commande',$this->commande->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
if( ! $this->commande->delete(DolibarrApiAccess::$user)) {
|
||||
throw new RestException(500, 'Error when delete order : '.$this->commande->error);
|
||||
}
|
||||
|
||||
return array(
|
||||
'success' => array(
|
||||
'code' => 200,
|
||||
'message' => 'Order deleted'
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an order
|
||||
*
|
||||
* @param int $id Order ID
|
||||
* @param int $idwarehouse Warehouse ID
|
||||
*
|
||||
* @url GET order/{id}/validate
|
||||
* @url POST order/{id}/validate
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
function validOrder($id, $idwarehouse=0)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->commande->creer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
$result = $this->commande->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Order not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('commande',$this->commande->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
if( ! $this->commande->valid(DolibarrApiAccess::$user, $idwarehouse)) {
|
||||
throw new RestException(500, 'Error when validate order');
|
||||
}
|
||||
|
||||
return array(
|
||||
'success' => array(
|
||||
'code' => 200,
|
||||
'message' => 'Order validated'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate fields before create or update object
|
||||
*
|
||||
* @param array $data Array with data to verify
|
||||
* @return array
|
||||
* @throws RestException
|
||||
*/
|
||||
function _validate($data)
|
||||
{
|
||||
$commande = array();
|
||||
foreach (CommandeApi::$FIELDS as $field) {
|
||||
if (!isset($data[$field]))
|
||||
throw new RestException(400, "$field field missing");
|
||||
$commande[$field] = $data[$field];
|
||||
}
|
||||
return $commande;
|
||||
}
|
||||
}
|
||||
297
htdocs/compta/facture/class/api_invoice.class.php
Normal file
297
htdocs/compta/facture/class/api_invoice.class.php
Normal file
@ -0,0 +1,297 @@
|
||||
<?php
|
||||
/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
|
||||
|
||||
/**
|
||||
* API class for invoice object
|
||||
*
|
||||
* @smart-auto-routing false
|
||||
* @access protected
|
||||
* @class DolibarrApiAccess {@requires user,external}
|
||||
*
|
||||
*/
|
||||
class InvoiceApi extends DolibarrApi
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var array $FIELDS Mandatory fields, checked when create and update object
|
||||
*/
|
||||
static $FIELDS = array(
|
||||
'socid'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var Facture $invoice {@type Facture}
|
||||
*/
|
||||
public $invoice;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @url GET invoice/
|
||||
*
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
global $db, $conf;
|
||||
$this->db = $db;
|
||||
$this->invoice = new Facture($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties of a invoice object
|
||||
*
|
||||
* Return an array with invoice informations
|
||||
*
|
||||
* @param int $id ID of invoice
|
||||
* @return array|mixed data without useless information
|
||||
*
|
||||
* @url GET invoice/{id}
|
||||
* @throws RestException
|
||||
*/
|
||||
function get($id)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->facture->lire) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$result = $this->invoice->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Facture not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('facture',$this->invoice->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
return $this->_cleanObjectDatas($this->invoice);
|
||||
}
|
||||
|
||||
/**
|
||||
* List invoices
|
||||
*
|
||||
* Get a list of invoices
|
||||
*
|
||||
* @param int $socid Filter list with thirdparty ID
|
||||
* @param string $mode Filter by invoice status : draft | unpaid | paid | cancelled
|
||||
* @param string $sortfield Sort field
|
||||
* @param string $sortorder Sort order
|
||||
* @param int $limit Limit for list
|
||||
* @param int $page Page number
|
||||
*
|
||||
* @return array Array of invoice objects
|
||||
*
|
||||
* @url GET invoice/list
|
||||
* @url GET invoice/list/{mode}
|
||||
* @url GET thirdparty/{socid}/invoice/list
|
||||
* @url GET thirdparty/{socid}/invoice/list/{mode}
|
||||
*/
|
||||
function getList($socid=0, $mode='', $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0) {
|
||||
global $db, $conf;
|
||||
|
||||
$obj_ret = array();
|
||||
|
||||
$socid = DolibarrApiAccess::$user->societe_id ? DolibarrApiAccess::$user->societe_id : '';
|
||||
|
||||
// If the internal user must only see his customers, force searching by him
|
||||
if (! DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) $search_sale = DolibarrApiAccess::$user->id;
|
||||
|
||||
$sql = "SELECT s.rowid";
|
||||
if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."facture as s";
|
||||
|
||||
if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
|
||||
|
||||
$sql.= ' WHERE s.entity IN ('.getEntity('facture', 1).')';
|
||||
if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= " AND s.fk_soc = sc.fk_soc";
|
||||
if ($socid) $sql.= " AND s.fk_soc = ".$socid;
|
||||
if ($search_sale > 0) $sql.= " AND s.rowid = sc.fk_soc"; // Join for the needed table to filter by sale
|
||||
|
||||
|
||||
// Example of use $mode
|
||||
if ($mode == 'draft') $sql.= " AND s.fk_statut IN (0)";
|
||||
if ($mode == 'unpaid') $sql.= " AND s.fk_statut IN (1)";
|
||||
if ($mode == 'paid') $sql.= " AND s.fk_statut IN (2)";
|
||||
if ($mode == 'cancelled') $sql.= " AND s.fk_statut IN (3)";
|
||||
|
||||
// Insert sale filter
|
||||
if ($search_sale > 0)
|
||||
{
|
||||
$sql .= " AND sc.fk_user = ".$search_sale;
|
||||
}
|
||||
|
||||
$nbtotalofrecords = 0;
|
||||
if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
|
||||
{
|
||||
$result = $db->query($sql);
|
||||
$nbtotalofrecords = $db->num_rows($result);
|
||||
}
|
||||
|
||||
$sql.= $db->order($sortfield, $sortorder);
|
||||
if ($limit) {
|
||||
if ($page < 0)
|
||||
{
|
||||
$page = 0;
|
||||
}
|
||||
$offset = $limit * $page;
|
||||
|
||||
$sql.= $db->plimit($limit + 1, $offset);
|
||||
}
|
||||
|
||||
$result = $db->query($sql);
|
||||
if ($result)
|
||||
{
|
||||
$num = $db->num_rows($result);
|
||||
while ($i < $num)
|
||||
{
|
||||
$obj = $db->fetch_object($result);
|
||||
$invoice_static = new Facture($db);
|
||||
if($invoice_static->fetch($obj->rowid)) {
|
||||
$obj_ret[] = parent::_cleanObjectDatas($invoice_static);
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new RestException(503, 'Error when retrieve invoice list');
|
||||
}
|
||||
if( ! count($obj_ret)) {
|
||||
throw new RestException(404, 'No invoice found');
|
||||
}
|
||||
return $obj_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create invoice object
|
||||
*
|
||||
* @param array $request_data Request datas
|
||||
* @return int ID of invoice
|
||||
*
|
||||
* @url POST invoice/
|
||||
*/
|
||||
function post($request_data = NULL)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->facture->creer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
// Check mandatory fields
|
||||
$result = $this->_validate($request_data);
|
||||
|
||||
foreach($request_data as $field => $value) {
|
||||
$this->invoice->$field = $value;
|
||||
}
|
||||
if(! array_keys($request_data,'date')) {
|
||||
$this->invoice->date = dol_now();
|
||||
}
|
||||
if( ! $this->invoice->create(DolibarrApiAccess::$user)) {
|
||||
throw new RestException(500);
|
||||
}
|
||||
return $this->invoice->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update invoice
|
||||
*
|
||||
* @param int $id Id of invoice to update
|
||||
* @param array $request_data Datas
|
||||
* @return int
|
||||
*
|
||||
* @url PUT invoice/{id}
|
||||
*/
|
||||
function put($id, $request_data = NULL)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->facture->creer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$result = $this->invoice->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Facture not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('facture',$this->invoice->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
foreach($request_data as $field => $value) {
|
||||
$this->invoice->$field = $value;
|
||||
}
|
||||
|
||||
if($this->invoice->update($id, DolibarrApiAccess::$user))
|
||||
return $this->get ($id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete invoice
|
||||
*
|
||||
* @param int $id Invoice ID
|
||||
* @return type
|
||||
*
|
||||
* @url DELETE invoice/{id}
|
||||
*/
|
||||
function delete($id)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->facture->supprimer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
$result = $this->invoice->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Facture not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('facture',$this->facture->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
if( !$this->invoice->delete($id))
|
||||
{
|
||||
throw new RestException(500);
|
||||
}
|
||||
|
||||
return array(
|
||||
'success' => array(
|
||||
'code' => 200,
|
||||
'message' => 'Facture deleted'
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate fields before create or update object
|
||||
*
|
||||
* @param array $data Datas to validate
|
||||
* @return array
|
||||
*
|
||||
* @throws RestException
|
||||
*/
|
||||
function _validate($data)
|
||||
{
|
||||
$invoice = array();
|
||||
foreach (InvoiceApi::$FIELDS as $field) {
|
||||
if (!isset($data[$field]))
|
||||
throw new RestException(400, "$field field missing");
|
||||
$invoice[$field] = $data[$field];
|
||||
}
|
||||
return $invoice;
|
||||
}
|
||||
}
|
||||
@ -331,163 +331,189 @@ function restrictedArea($user, $features, $objectid=0, $dbtablename='', $feature
|
||||
// is linked to a company allowed to $user.
|
||||
if (! empty($objectid) && $objectid > 0)
|
||||
{
|
||||
foreach ($featuresarray as $feature)
|
||||
{
|
||||
$sql='';
|
||||
|
||||
$check = array('adherent','banque','user','usergroup','produit','service','produit|service','categorie'); // Test on entity only (Objects with no link to company)
|
||||
$checksoc = array('societe'); // Test for societe object
|
||||
$checkother = array('contact'); // Test on entity and link to societe. Allowed if link is empty (Ex: contacts...).
|
||||
$checkproject = array('projet'); // Test for project object
|
||||
$nocheck = array('barcode','stock','fournisseur'); // No test
|
||||
$checkdefault = 'all other not already defined'; // Test on entity and link to third party. Not allowed if link is empty (Ex: invoice, orders...).
|
||||
|
||||
// If dbtable not defined, we use same name for table than module name
|
||||
if (empty($dbtablename)) $dbtablename = $feature;
|
||||
|
||||
// Check permission for object with entity
|
||||
if (in_array($feature,$check))
|
||||
{
|
||||
$sql = "SELECT dbt.".$dbt_select;
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.".$dbt_select." = ".$objectid;
|
||||
if (($feature == 'user' || $feature == 'usergroup') && ! empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && ! $user->entity)
|
||||
{
|
||||
$sql.= " AND dbt.entity IS NOT NULL";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql.= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
}
|
||||
else if (in_array($feature,$checksoc)) // We check feature = checksoc
|
||||
{
|
||||
// If external user: Check permission for external users
|
||||
if ($user->societe_id > 0)
|
||||
{
|
||||
if ($user->societe_id <> $objectid) accessforbidden();
|
||||
}
|
||||
// If internal user: Check permission for internal users that are restricted on their objects
|
||||
else if (! empty($conf->societe->enabled) && ($user->rights->societe->lire && ! $user->rights->societe->client->voir))
|
||||
{
|
||||
$sql = "SELECT sc.fk_soc";
|
||||
$sql.= " FROM (".MAIN_DB_PREFIX."societe_commerciaux as sc";
|
||||
$sql.= ", ".MAIN_DB_PREFIX."societe as s)";
|
||||
$sql.= " WHERE sc.fk_soc = ".$objectid;
|
||||
$sql.= " AND sc.fk_user = ".$user->id;
|
||||
$sql.= " AND sc.fk_soc = s.rowid";
|
||||
$sql.= " AND s.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
// If multicompany and internal users with all permissions, check user is in correct entity
|
||||
else if (! empty($conf->multicompany->enabled))
|
||||
{
|
||||
$sql = "SELECT s.rowid";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."societe as s";
|
||||
$sql.= " WHERE s.rowid = ".$objectid;
|
||||
$sql.= " AND s.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
}
|
||||
else if (in_array($feature,$checkother))
|
||||
{
|
||||
// If external user: Check permission for external users
|
||||
if ($user->societe_id > 0)
|
||||
{
|
||||
$sql = "SELECT dbt.rowid";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.rowid = ".$objectid;
|
||||
$sql.= " AND dbt.fk_soc = ".$user->societe_id;
|
||||
}
|
||||
// If internal user: Check permission for internal users that are restricted on their objects
|
||||
else if (! empty($conf->societe->enabled) && ($user->rights->societe->lire && ! $user->rights->societe->client->voir))
|
||||
{
|
||||
$sql = "SELECT dbt.rowid";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON dbt.fk_soc = sc.fk_soc AND sc.fk_user = '".$user->id."'";
|
||||
$sql.= " WHERE dbt.rowid = ".$objectid;
|
||||
$sql.= " AND (dbt.fk_soc IS NULL OR sc.fk_soc IS NOT NULL)"; // Contact not linked to a company or to a company of user
|
||||
$sql.= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
// If multicompany and internal users with all permissions, check user is in correct entity
|
||||
else if (! empty($conf->multicompany->enabled))
|
||||
{
|
||||
$sql = "SELECT dbt.rowid";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.rowid = ".$objectid;
|
||||
$sql.= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
}
|
||||
else if (in_array($feature,$checkproject))
|
||||
{
|
||||
if (! empty($conf->projet->enabled) && ! $user->rights->projet->all->lire)
|
||||
{
|
||||
include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
|
||||
$projectstatic=new Project($db);
|
||||
$tmps=$projectstatic->getProjectsAuthorizedForUser($user,0,1,0);
|
||||
$tmparray=explode(',',$tmps);
|
||||
if (! in_array($objectid,$tmparray)) accessforbidden();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = "SELECT dbt.".$dbt_select;
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.".$dbt_select." = ".$objectid;
|
||||
$sql.= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
}
|
||||
else if (! in_array($feature,$nocheck)) // By default we check with link to third party
|
||||
{
|
||||
// If external user: Check permission for external users
|
||||
if ($user->societe_id > 0)
|
||||
{
|
||||
if (empty($dbt_keyfield)) dol_print_error('','Param dbt_keyfield is required but not defined');
|
||||
$sql = "SELECT dbt.".$dbt_keyfield;
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.rowid = ".$objectid;
|
||||
$sql.= " AND dbt.".$dbt_keyfield." = ".$user->societe_id;
|
||||
}
|
||||
// If internal user: Check permission for internal users that are restricted on their objects
|
||||
else if (! empty($conf->societe->enabled) && ($user->rights->societe->lire && ! $user->rights->societe->client->voir))
|
||||
{
|
||||
if (empty($dbt_keyfield)) dol_print_error('','Param dbt_keyfield is required but not defined');
|
||||
$sql = "SELECT sc.fk_soc";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= ", ".MAIN_DB_PREFIX."societe as s";
|
||||
$sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
|
||||
$sql.= " WHERE dbt.".$dbt_select." = ".$objectid;
|
||||
$sql.= " AND sc.fk_soc = dbt.".$dbt_keyfield;
|
||||
$sql.= " AND dbt.".$dbt_keyfield." = s.rowid";
|
||||
$sql.= " AND s.entity IN (".getEntity($sharedelement, 1).")";
|
||||
$sql.= " AND sc.fk_user = ".$user->id;
|
||||
}
|
||||
// If multicompany and internal users with all permissions, check user is in correct entity
|
||||
else if (! empty($conf->multicompany->enabled))
|
||||
{
|
||||
$sql = "SELECT dbt.".$dbt_select;
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.".$dbt_select." = ".$objectid;
|
||||
$sql.= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
}
|
||||
|
||||
//print "sql=".$sql."<br>";
|
||||
if ($sql)
|
||||
{
|
||||
$resql=$db->query($sql);
|
||||
if ($resql)
|
||||
{
|
||||
if ($db->num_rows($resql) == 0) accessforbidden();
|
||||
}
|
||||
else
|
||||
{
|
||||
accessforbidden();
|
||||
}
|
||||
}
|
||||
}
|
||||
$ok = checkUserAccessToObject($user, $featuresarray,$objectid,$dbtablename,$feature2,$dbt_keyfield,$dbt_select);
|
||||
return $ok ? 1 : accessforbidden();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check access by user to object
|
||||
*
|
||||
* @param User $user User to check
|
||||
* @param array $featuresarray Features/modules to check
|
||||
* @param int $objectid Object ID if we want to check a particular record (optional) is linked to a owned thirdparty (optional).
|
||||
* @param string $dbtablename 'TableName&SharedElement' with Tablename is table where object is stored. SharedElement is an optional key to define where to check entity. Not used if objectid is null (optional)
|
||||
* @param string $feature2 Feature to check, second level of permission (optional). Can be or check with 'level1|level2'.
|
||||
* @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional)
|
||||
* @param string $dbt_select Field name for select if not rowid. Not used if objectid is null (optional)
|
||||
*
|
||||
* @return bool True if user has access, False otherwise
|
||||
*/
|
||||
function checkUserAccessToObject($user, $featuresarray, $objectid=0, $dbtablename='', $feature2='', $dbt_keyfield='', $dbt_select='')
|
||||
{
|
||||
global $db, $conf;
|
||||
|
||||
// More parameters
|
||||
$params = explode('&', $dbtablename);
|
||||
$dbtablename=(! empty($params[0]) ? $params[0] : '');
|
||||
$sharedelement=(! empty($params[1]) ? $params[1] : $dbtablename);
|
||||
|
||||
foreach ($featuresarray as $feature)
|
||||
{
|
||||
$sql='';
|
||||
|
||||
$check = array('adherent','banque','user','usergroup','produit','service','produit|service','categorie'); // Test on entity only (Objects with no link to company)
|
||||
$checksoc = array('societe'); // Test for societe object
|
||||
$checkother = array('contact'); // Test on entity and link to societe. Allowed if link is empty (Ex: contacts...).
|
||||
$checkproject = array('projet'); // Test for project object
|
||||
$nocheck = array('barcode','stock','fournisseur'); // No test
|
||||
$checkdefault = 'all other not already defined'; // Test on entity and link to third party. Not allowed if link is empty (Ex: invoice, orders...).
|
||||
|
||||
// If dbtable not defined, we use same name for table than module name
|
||||
if (empty($dbtablename)) $dbtablename = $feature;
|
||||
|
||||
// Check permission for object with entity
|
||||
if (in_array($feature,$check))
|
||||
{
|
||||
$sql = "SELECT dbt.".$dbt_select;
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.".$dbt_select." = ".$objectid;
|
||||
if (($feature == 'user' || $feature == 'usergroup') && ! empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && ! $user->entity)
|
||||
{
|
||||
$sql.= " AND dbt.entity IS NOT NULL";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql.= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
}
|
||||
else if (in_array($feature,$checksoc)) // We check feature = checksoc
|
||||
{
|
||||
// If external user: Check permission for external users
|
||||
if ($user->societe_id > 0)
|
||||
{
|
||||
if ($user->societe_id <> $objectid) return false;
|
||||
}
|
||||
// If internal user: Check permission for internal users that are restricted on their objects
|
||||
else if (! empty($conf->societe->enabled) && ($user->rights->societe->lire && ! $user->rights->societe->client->voir))
|
||||
{
|
||||
$sql = "SELECT sc.fk_soc";
|
||||
$sql.= " FROM (".MAIN_DB_PREFIX."societe_commerciaux as sc";
|
||||
$sql.= ", ".MAIN_DB_PREFIX."societe as s)";
|
||||
$sql.= " WHERE sc.fk_soc = ".$objectid;
|
||||
$sql.= " AND sc.fk_user = ".$user->id;
|
||||
$sql.= " AND sc.fk_soc = s.rowid";
|
||||
$sql.= " AND s.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
// If multicompany and internal users with all permissions, check user is in correct entity
|
||||
else if (! empty($conf->multicompany->enabled))
|
||||
{
|
||||
$sql = "SELECT s.rowid";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."societe as s";
|
||||
$sql.= " WHERE s.rowid = ".$objectid;
|
||||
$sql.= " AND s.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
}
|
||||
else if (in_array($feature,$checkother))
|
||||
{
|
||||
// If external user: Check permission for external users
|
||||
if ($user->societe_id > 0)
|
||||
{
|
||||
$sql = "SELECT dbt.rowid";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.rowid = ".$objectid;
|
||||
$sql.= " AND dbt.fk_soc = ".$user->societe_id;
|
||||
}
|
||||
// If internal user: Check permission for internal users that are restricted on their objects
|
||||
else if (! empty($conf->societe->enabled) && ($user->rights->societe->lire && ! $user->rights->societe->client->voir))
|
||||
{
|
||||
$sql = "SELECT dbt.rowid";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON dbt.fk_soc = sc.fk_soc AND sc.fk_user = '".$user->id."'";
|
||||
$sql.= " WHERE dbt.rowid = ".$objectid;
|
||||
$sql.= " AND (dbt.fk_soc IS NULL OR sc.fk_soc IS NOT NULL)"; // Contact not linked to a company or to a company of user
|
||||
$sql.= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
// If multicompany and internal users with all permissions, check user is in correct entity
|
||||
else if (! empty($conf->multicompany->enabled))
|
||||
{
|
||||
$sql = "SELECT dbt.rowid";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.rowid = ".$objectid;
|
||||
$sql.= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
}
|
||||
else if (in_array($feature,$checkproject))
|
||||
{
|
||||
if (! empty($conf->projet->enabled) && ! $user->rights->projet->all->lire)
|
||||
{
|
||||
include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
|
||||
$projectstatic=new Project($db);
|
||||
$tmps=$projectstatic->getProjectsAuthorizedForUser($user,0,1,0);
|
||||
$tmparray=explode(',',$tmps);
|
||||
if (! in_array($objectid,$tmparray)) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = "SELECT dbt.".$dbt_select;
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.".$dbt_select." = ".$objectid;
|
||||
$sql.= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
}
|
||||
else if (! in_array($feature,$nocheck)) // By default we check with link to third party
|
||||
{
|
||||
// If external user: Check permission for external users
|
||||
if ($user->societe_id > 0)
|
||||
{
|
||||
if (empty($dbt_keyfield)) dol_print_error('','Param dbt_keyfield is required but not defined');
|
||||
$sql = "SELECT dbt.".$dbt_keyfield;
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.rowid = ".$objectid;
|
||||
$sql.= " AND dbt.".$dbt_keyfield." = ".$user->societe_id;
|
||||
}
|
||||
// If internal user: Check permission for internal users that are restricted on their objects
|
||||
else if (! empty($conf->societe->enabled) && ($user->rights->societe->lire && ! $user->rights->societe->client->voir))
|
||||
{
|
||||
if (empty($dbt_keyfield)) dol_print_error('','Param dbt_keyfield is required but not defined');
|
||||
$sql = "SELECT sc.fk_soc";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= ", ".MAIN_DB_PREFIX."societe as s";
|
||||
$sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
|
||||
$sql.= " WHERE dbt.".$dbt_select." = ".$objectid;
|
||||
$sql.= " AND sc.fk_soc = dbt.".$dbt_keyfield;
|
||||
$sql.= " AND dbt.".$dbt_keyfield." = s.rowid";
|
||||
$sql.= " AND s.entity IN (".getEntity($sharedelement, 1).")";
|
||||
$sql.= " AND sc.fk_user = ".$user->id;
|
||||
}
|
||||
// If multicompany and internal users with all permissions, check user is in correct entity
|
||||
else if (! empty($conf->multicompany->enabled))
|
||||
{
|
||||
$sql = "SELECT dbt.".$dbt_select;
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
|
||||
$sql.= " WHERE dbt.".$dbt_select." = ".$objectid;
|
||||
$sql.= " AND dbt.entity IN (".getEntity($sharedelement, 1).")";
|
||||
}
|
||||
}
|
||||
|
||||
//print "sql=".$sql."<br>";
|
||||
if ($sql)
|
||||
{
|
||||
$resql=$db->query($sql);
|
||||
if ($resql)
|
||||
{
|
||||
if ($db->num_rows($resql) == 0) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a message to say access is forbidden and stop program
|
||||
|
||||
252
htdocs/core/modules/modApi.class.php
Normal file
252
htdocs/core/modules/modApi.class.php
Normal file
@ -0,0 +1,252 @@
|
||||
<?php
|
||||
/* Copyright (C) 2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
|
||||
* Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup api Module Api
|
||||
* \brief Descriptor file for Api modulee
|
||||
* \file htdocs/api/core/modules/modApi.class.php
|
||||
* \ingroup api
|
||||
* \brief Description and activation file for module Api
|
||||
*/
|
||||
include_once DOL_DOCUMENT_ROOT .'/core/modules/DolibarrModules.class.php';
|
||||
|
||||
|
||||
/**
|
||||
* Description and activation class for module Api
|
||||
*/
|
||||
class modApi extends DolibarrModules
|
||||
{
|
||||
/**
|
||||
* Constructor. Define names, constants, directories, boxes, permissions
|
||||
*
|
||||
* @param DoliDB $db Database handler
|
||||
*/
|
||||
function __construct($db)
|
||||
{
|
||||
global $langs,$conf;
|
||||
|
||||
$this->db = $db;
|
||||
|
||||
// Id for module (must be unique).
|
||||
// Use here a free id (See in Home -> System information -> Dolibarr for list of used modules id).
|
||||
$this->numero = 2610;
|
||||
// Key text used to identify module (for permissions, menus, etc...)
|
||||
$this->rights_class = 'api';
|
||||
|
||||
// Family can be 'crm','financial','hr','projects','products','ecm','technic','other'
|
||||
// It is used to group modules in module setup page
|
||||
$this->family = "technic";
|
||||
// Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
|
||||
$this->name = preg_replace('/^mod/i','',get_class($this));
|
||||
// Module description, used if translation string 'ModuleXXXDesc' not found (where XXX is value of numeric property 'numero' of module)
|
||||
$this->description = "REST interface";
|
||||
// Possible values for version are: 'development', 'experimental', 'dolibarr' or 'dolibarr_deprecated' or version
|
||||
$this->version = 'development';
|
||||
// Key used in llx_const table to save module status enabled/disabled (where MYMODULE is value of property name of module in uppercase)
|
||||
$this->const_name = 'MAIN_MODULE_'.strtoupper($this->name);
|
||||
// Where to store the module in setup page (0=common,1=interface,2=others,3=very specific)
|
||||
$this->special = 1;
|
||||
// Name of image file used for this module.
|
||||
// If file is in theme/yourtheme/img directory under name object_pictovalue.png, use this->picto='pictovalue'
|
||||
// If file is in module/img directory under name object_pictovalue.png, use this->picto='pictovalue@module'
|
||||
$this->picto='technic';
|
||||
|
||||
|
||||
$this->module_parts = array();
|
||||
|
||||
// Data directories to create when module is enabled.
|
||||
// Example: this->dirs = array("/api/temp");
|
||||
$this->dirs = array();
|
||||
|
||||
// Config pages. Put here list of php page, stored into api/admin directory, to use to setup module.
|
||||
$this->config_page_url = array("api.php@api");
|
||||
|
||||
// Dependencies
|
||||
$this->hidden = false; // A condition to hide module
|
||||
$this->depends = array(); // List of modules id that must be enabled if this module is enabled
|
||||
$this->requiredby = array(); // List of modules id to disable if this one is disabled
|
||||
$this->conflictwith = array(); // List of modules id this module is in conflict with
|
||||
$this->phpmin = array(5,3); // Minimum version of PHP required by module
|
||||
$this->langfiles = array("other");
|
||||
|
||||
// Constants
|
||||
// List of particular constants to add when module is enabled (key, 'chaine', value, desc, visible, 'current' or 'allentities', deleteonunactive)
|
||||
// Example: $this->const=array(0=>array('MYMODULE_MYNEWCONST1','chaine','myvalue','This is a constant to add',1),
|
||||
// 1=>array('MYMODULE_MYNEWCONST2','chaine','myvalue','This is another constant to add',0, 'current', 1)
|
||||
// );
|
||||
$this->const = array();
|
||||
|
||||
// Array to add new pages in new tabs
|
||||
// Example: $this->tabs = array('objecttype:+tabname1:Title1:mylangfile@api:$user->rights->api->read:/api/mynewtab1.php?id=__ID__', // To add a new tab identified by code tabname1
|
||||
// 'objecttype:+tabname2:SUBSTITUTION_Title2:mylangfile@api:$user->rights->othermodule->read:/api/mynewtab2.php?id=__ID__', // To add another new tab identified by code tabname2. Label will be result of calling all substitution functions on 'Title2' key.
|
||||
// 'objecttype:-tabname:NU:conditiontoremove'); // To remove an existing tab identified by code tabname
|
||||
// where objecttype can be
|
||||
// 'categories_x' to add a tab in category view (replace 'x' by type of category (0=product, 1=supplier, 2=customer, 3=member)
|
||||
// 'contact' to add a tab in contact view
|
||||
// 'contract' to add a tab in contract view
|
||||
// 'group' to add a tab in group view
|
||||
// 'intervention' to add a tab in intervention view
|
||||
// 'invoice' to add a tab in customer invoice view
|
||||
// 'invoice_supplier' to add a tab in supplier invoice view
|
||||
// 'member' to add a tab in fundation member view
|
||||
// 'opensurveypoll' to add a tab in opensurvey poll view
|
||||
// 'order' to add a tab in customer order view
|
||||
// 'order_supplier' to add a tab in supplier order view
|
||||
// 'payment' to add a tab in payment view
|
||||
// 'payment_supplier' to add a tab in supplier payment view
|
||||
// 'product' to add a tab in product view
|
||||
// 'propal' to add a tab in propal view
|
||||
// 'project' to add a tab in project view
|
||||
// 'stock' to add a tab in stock view
|
||||
// 'thirdparty' to add a tab in third party view
|
||||
// 'user' to add a tab in user view
|
||||
$this->tabs = array();
|
||||
|
||||
// Dictionaries
|
||||
if (! isset($conf->api->enabled))
|
||||
{
|
||||
$conf->api=new stdClass();
|
||||
$conf->api->enabled=0;
|
||||
}
|
||||
$this->dictionaries=array();
|
||||
/* Example:
|
||||
if (! isset($conf->api->enabled)) $conf->api->enabled=0; // This is to avoid warnings
|
||||
$this->dictionaries=array(
|
||||
'langs'=>'mylangfile@api',
|
||||
'tabname'=>array(MAIN_DB_PREFIX."table1",MAIN_DB_PREFIX."table2",MAIN_DB_PREFIX."table3"), // List of tables we want to see into dictonnary editor
|
||||
'tablib'=>array("Table1","Table2","Table3"), // Label of tables
|
||||
'tabsql'=>array('SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table1 as f','SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table2 as f','SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table3 as f'), // Request to select fields
|
||||
'tabsqlsort'=>array("label ASC","label ASC","label ASC"), // Sort order
|
||||
'tabfield'=>array("code,label","code,label","code,label"), // List of fields (result of select to show dictionary)
|
||||
'tabfieldvalue'=>array("code,label","code,label","code,label"), // List of fields (list of fields to edit a record)
|
||||
'tabfieldinsert'=>array("code,label","code,label","code,label"), // List of fields (list of fields for insert)
|
||||
'tabrowid'=>array("rowid","rowid","rowid"), // Name of columns with primary key (try to always name it 'rowid')
|
||||
'tabcond'=>array($conf->api->enabled,$conf->api->enabled,$conf->api->enabled) // Condition to show each dictionary
|
||||
);
|
||||
*/
|
||||
|
||||
// Boxes
|
||||
// Add here list of php file(s) stored in core/boxes that contains class to show a box.
|
||||
$this->boxes = array(); // List of boxes
|
||||
// Example:
|
||||
//$this->boxes=array(array(0=>array('file'=>'myboxa.php','note'=>'','enabledbydefaulton'=>'Home'),1=>array('file'=>'myboxb.php','note'=>''),2=>array('file'=>'myboxc.php','note'=>'')););
|
||||
|
||||
// Permissions
|
||||
$this->rights = array(); // Permission array used by this module
|
||||
$r=0;
|
||||
|
||||
// Add here list of permission defined by an id, a label, a boolean and two constant strings.
|
||||
// Example:
|
||||
// $this->rights[$r][0] = $this->numero + $r; // Permission id (must not be already used)
|
||||
// $this->rights[$r][1] = 'Permision label'; // Permission label
|
||||
// $this->rights[$r][3] = 1; // Permission by default for new user (0/1)
|
||||
// $this->rights[$r][4] = 'level1'; // In php code, permission will be checked by test if ($user->rights->permkey->level1->level2)
|
||||
// $this->rights[$r][5] = 'level2'; // In php code, permission will be checked by test if ($user->rights->permkey->level1->level2)
|
||||
// $r++;
|
||||
|
||||
|
||||
// Main menu entries
|
||||
$this->menu = array(); // List of menus to add
|
||||
$r=0;
|
||||
|
||||
// Add here entries to declare new menus
|
||||
//
|
||||
// Example to declare a new Top Menu entry and its Left menu entry:
|
||||
// $this->menu[$r]=array( 'fk_menu'=>0, // Put 0 if this is a top menu
|
||||
// 'type'=>'top', // This is a Top menu entry
|
||||
// 'titre'=>'Api top menu',
|
||||
// 'mainmenu'=>'api',
|
||||
// 'leftmenu'=>'api',
|
||||
// 'url'=>'/api/pagetop.php',
|
||||
// 'langs'=>'mylangfile@api', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
|
||||
// 'position'=>100,
|
||||
// 'enabled'=>'$conf->api->enabled', // Define condition to show or hide menu entry. Use '$conf->api->enabled' if entry must be visible if module is enabled.
|
||||
// 'perms'=>'1', // Use 'perms'=>'$user->rights->api->level1->level2' if you want your menu with a permission rules
|
||||
// 'target'=>'',
|
||||
// 'user'=>2); // 0=Menu for internal users, 1=external users, 2=both
|
||||
// $r++;
|
||||
//
|
||||
// Example to declare a Left Menu entry into an existing Top menu entry:
|
||||
// $this->menu[$r]=array( 'fk_menu'=>'fk_mainmenu=xxx', // Use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode
|
||||
// 'type'=>'left', // This is a Left menu entry
|
||||
// 'titre'=>'Api left menu',
|
||||
// 'mainmenu'=>'xxx',
|
||||
// 'leftmenu'=>'api',
|
||||
// 'url'=>'/api/pagelevel2.php',
|
||||
// 'langs'=>'mylangfile@api', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
|
||||
// 'position'=>100,
|
||||
// 'enabled'=>'$conf->api->enabled', // Define condition to show or hide menu entry. Use '$conf->api->enabled' if entry must be visible if module is enabled. Use '$leftmenu==\'system\'' to show if leftmenu system is selected.
|
||||
// 'perms'=>'1', // Use 'perms'=>'$user->rights->api->level1->level2' if you want your menu with a permission rules
|
||||
// 'target'=>'',
|
||||
// 'user'=>2); // 0=Menu for internal users, 1=external users, 2=both
|
||||
// $r++;
|
||||
|
||||
|
||||
// Exports
|
||||
$r=1;
|
||||
|
||||
// Example:
|
||||
// $this->export_code[$r]=$this->rights_class.'_'.$r;
|
||||
// $this->export_label[$r]='CustomersInvoicesAndInvoiceLines'; // Translation key (used only if key ExportDataset_xxx_z not found)
|
||||
// $this->export_enabled[$r]='1'; // Condition to show export in list (ie: '$user->id==3'). Set to 1 to always show when module is enabled.
|
||||
// $this->export_permission[$r]=array(array("facture","facture","export"));
|
||||
// $this->export_fields_array[$r]=array('s.rowid'=>"IdCompany",'s.nom'=>'CompanyName','s.address'=>'Address','s.zip'=>'Zip','s.town'=>'Town','s.fk_pays'=>'Country','s.phone'=>'Phone','s.siren'=>'ProfId1','s.siret'=>'ProfId2','s.ape'=>'ProfId3','s.idprof4'=>'ProfId4','s.code_compta'=>'CustomerAccountancyCode','s.code_compta_fournisseur'=>'SupplierAccountancyCode','f.rowid'=>"InvoiceId",'f.facnumber'=>"InvoiceRef",'f.datec'=>"InvoiceDateCreation",'f.datef'=>"DateInvoice",'f.total'=>"TotalHT",'f.total_ttc'=>"TotalTTC",'f.tva'=>"TotalVAT",'f.paye'=>"InvoicePaid",'f.fk_statut'=>'InvoiceStatus','f.note'=>"InvoiceNote",'fd.rowid'=>'LineId','fd.description'=>"LineDescription",'fd.price'=>"LineUnitPrice",'fd.tva_tx'=>"LineVATRate",'fd.qty'=>"LineQty",'fd.total_ht'=>"LineTotalHT",'fd.total_tva'=>"LineTotalTVA",'fd.total_ttc'=>"LineTotalTTC",'fd.date_start'=>"DateStart",'fd.date_end'=>"DateEnd",'fd.fk_product'=>'ProductId','p.ref'=>'ProductRef');
|
||||
// $this->export_entities_array[$r]=array('s.rowid'=>"company",'s.nom'=>'company','s.address'=>'company','s.zip'=>'company','s.town'=>'company','s.fk_pays'=>'company','s.phone'=>'company','s.siren'=>'company','s.siret'=>'company','s.ape'=>'company','s.idprof4'=>'company','s.code_compta'=>'company','s.code_compta_fournisseur'=>'company','f.rowid'=>"invoice",'f.facnumber'=>"invoice",'f.datec'=>"invoice",'f.datef'=>"invoice",'f.total'=>"invoice",'f.total_ttc'=>"invoice",'f.tva'=>"invoice",'f.paye'=>"invoice",'f.fk_statut'=>'invoice','f.note'=>"invoice",'fd.rowid'=>'invoice_line','fd.description'=>"invoice_line",'fd.price'=>"invoice_line",'fd.total_ht'=>"invoice_line",'fd.total_tva'=>"invoice_line",'fd.total_ttc'=>"invoice_line",'fd.tva_tx'=>"invoice_line",'fd.qty'=>"invoice_line",'fd.date_start'=>"invoice_line",'fd.date_end'=>"invoice_line",'fd.fk_product'=>'product','p.ref'=>'product');
|
||||
// $this->export_sql_start[$r]='SELECT DISTINCT ';
|
||||
// $this->export_sql_end[$r] =' FROM ('.MAIN_DB_PREFIX.'facture as f, '.MAIN_DB_PREFIX.'facturedet as fd, '.MAIN_DB_PREFIX.'societe as s)';
|
||||
// $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'product as p on (fd.fk_product = p.rowid)';
|
||||
// $this->export_sql_end[$r] .=' WHERE f.fk_soc = s.rowid AND f.rowid = fd.fk_facture';
|
||||
// $this->export_sql_order[$r] .=' ORDER BY s.nom';
|
||||
// $r++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called when module is enabled.
|
||||
* The init function add constants, boxes, permissions and menus (defined in constructor) into Dolibarr database.
|
||||
* It also creates data directories
|
||||
*
|
||||
* @param string $options Options when enabling module ('', 'noboxes')
|
||||
* @return int 1 if OK, 0 if KO
|
||||
*/
|
||||
function init($options='')
|
||||
{
|
||||
$sql = array();
|
||||
|
||||
$result=$this->_load_tables('/api/sql/');
|
||||
|
||||
return $this->_init($sql, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called when module is disabled.
|
||||
* Remove from database constants, boxes and permissions from Dolibarr database.
|
||||
* Data directories are not deleted
|
||||
*
|
||||
* @param string $options Options when enabling module ('', 'noboxes')
|
||||
* @return int 1 if OK, 0 if KO
|
||||
*/
|
||||
function remove($options='')
|
||||
{
|
||||
$sql = array();
|
||||
|
||||
return $this->_remove($sql, $options);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
114
htdocs/includes/restler/ApcCache.php
Normal file
114
htdocs/includes/restler/ApcCache.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Luracast\Restler;
|
||||
|
||||
use Luracast\Restler\iCache;
|
||||
|
||||
/**
|
||||
* Class ApcCache provides an APC based cache for Restler
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @author Joel R. Simpson <joel.simpson@gmail.com>
|
||||
* @copyright 2013 Luracast
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link http://luracast.com/products/restler/
|
||||
* @version 3.0.0rc5
|
||||
*/
|
||||
class ApcCache implements iCache
|
||||
{
|
||||
/**
|
||||
* The namespace that all of the cached entries will be stored under. This allows multiple APIs to run concurrently.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
static public $namespace = 'restler';
|
||||
|
||||
/**
|
||||
* store data in the cache
|
||||
*
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $data
|
||||
*
|
||||
* @return boolean true if successful
|
||||
*/
|
||||
public function set($name, $data)
|
||||
{
|
||||
function_exists('apc_store') || $this->apcNotAvailable();
|
||||
|
||||
try {
|
||||
return apc_store(self::$namespace . "-" . $name, $data);
|
||||
} catch
|
||||
(\Exception $exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function apcNotAvailable()
|
||||
{
|
||||
throw new \Exception('APC is not available for use as Restler Cache. Please make sure the module is installed. http://php.net/manual/en/apc.installation.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve data from the cache
|
||||
*
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $ignoreErrors
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $ignoreErrors = false)
|
||||
{
|
||||
function_exists('apc_fetch') || $this->apcNotAvailable();
|
||||
|
||||
try {
|
||||
return apc_fetch(self::$namespace . "-" . $name);
|
||||
} catch (\Exception $exception) {
|
||||
if (!$ignoreErrors) {
|
||||
throw $exception;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* delete data from the cache
|
||||
*
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $ignoreErrors
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return boolean true if successful
|
||||
*/
|
||||
public function clear($name, $ignoreErrors = false)
|
||||
{
|
||||
function_exists('apc_delete') || $this->apcNotAvailable();
|
||||
|
||||
try {
|
||||
apc_delete(self::$namespace . "-" . $name);
|
||||
} catch (\Exception $exception) {
|
||||
if (!$ignoreErrors) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the given name is cached
|
||||
*
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return boolean true if cached
|
||||
*/
|
||||
public function isCached($name)
|
||||
{
|
||||
function_exists('apc_exists') || $this->apcNotAvailable();
|
||||
return apc_exists(self::$namespace . "-" . $name);
|
||||
}
|
||||
|
||||
}
|
||||
438
htdocs/includes/restler/AutoLoader.php
Normal file
438
htdocs/includes/restler/AutoLoader.php
Normal file
@ -0,0 +1,438 @@
|
||||
<?php
|
||||
namespace Luracast\Restler {
|
||||
|
||||
/**
|
||||
* Class that implements spl_autoload facilities and multiple
|
||||
* conventions support.
|
||||
* Supports composer libraries and 100% PSR-0 compliant.
|
||||
* In addition we enable namespace prefixing and class aliases.
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage helper
|
||||
* @author Nick Lombard <github@jigsoft.co.za>
|
||||
* @copyright 2012 Luracast
|
||||
* @version 3.0.0rc5
|
||||
*/
|
||||
class AutoLoader
|
||||
{
|
||||
protected static $instance, // the singleton instance reference
|
||||
$perfectLoaders, // used to keep the ideal list of loaders
|
||||
$rogueLoaders = array(), // other auto loaders now unregistered
|
||||
$classMap = array(), // the class to include file mapping
|
||||
$aliases = array( // aliases and prefixes instead of null list aliases
|
||||
'Luracast\\Restler' => null,
|
||||
'Luracast\\Restler\\Format' => null,
|
||||
'Luracast\\Restler\\Data' => null,
|
||||
'Luracast\\Restler\\Filter' => null,
|
||||
);
|
||||
|
||||
/**
|
||||
* Singleton instance facility.
|
||||
*
|
||||
* @static
|
||||
* @return AutoLoader the current instance or new instance if none exists.
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
static::$instance = static::$instance ?: new static();
|
||||
return static::thereCanBeOnlyOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to add a path to the include path.
|
||||
* AutoLoader uses the include path to discover classes.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param $path string absolute or relative path.
|
||||
*
|
||||
* @return bool false if the path cannot be resolved
|
||||
* or the resolved absolute path.
|
||||
*/
|
||||
public static function addPath($path) {
|
||||
if (false === $path = stream_resolve_include_path($path))
|
||||
return false;
|
||||
else
|
||||
set_include_path($path.PATH_SEPARATOR.get_include_path());
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Other autoLoaders interfere and cause duplicate class loading.
|
||||
* AutoLoader is capable enough to handle all standards so no need
|
||||
* for others stumbling about.
|
||||
*
|
||||
* @return callable the one true auto loader.
|
||||
*/
|
||||
public static function thereCanBeOnlyOne() {
|
||||
if (static::$perfectLoaders === spl_autoload_functions())
|
||||
return static::$instance;
|
||||
|
||||
if (false !== $loaders = spl_autoload_functions())
|
||||
if (0 < $count = count($loaders))
|
||||
for ($i = 0, static::$rogueLoaders += $loaders;
|
||||
$i < $count && false != ($loader = $loaders[$i]);
|
||||
$i++)
|
||||
if ($loader !== static::$perfectLoaders[0])
|
||||
spl_autoload_unregister($loader);
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seen this before cache handler.
|
||||
* Facilitates both lookup and persist operations as well as convenience,
|
||||
* load complete map functionality. The key can only be given a non falsy
|
||||
* value once, this will be truthy for life.
|
||||
*
|
||||
* @param $key mixed class name considered or a collection of
|
||||
* classMap entries
|
||||
* @param $value mixed optional not required when doing a query on
|
||||
* key. Default is false we haven't seen this
|
||||
* class. Most of the time it will be the filename
|
||||
* for include and is set to true if we are unable
|
||||
* to load this class iow true == it does not exist.
|
||||
* value may also be a callable auto loader function.
|
||||
*
|
||||
* @return mixed The known value for the key or false if key has no value
|
||||
*/
|
||||
public static function seen($key, $value = false)
|
||||
{
|
||||
if (is_array($key)) {
|
||||
static::$classMap = $key + static::$classMap;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty(static::$classMap[$key]))
|
||||
static::$classMap[$key] = $value;
|
||||
|
||||
if (is_string($alias = static::$classMap[$key]))
|
||||
if (isset(static::$classMap[$alias]))
|
||||
return static::$classMap[$alias];
|
||||
|
||||
return static::$classMap[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected constructor to enforce singleton pattern.
|
||||
* Populate a default include path.
|
||||
* All possible includes cant possibly be catered for and if you
|
||||
* require another path then simply add it calling set_include_path.
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
static::$perfectLoaders = array($this);
|
||||
|
||||
if (false === static::seen('__include_path')) {
|
||||
|
||||
$paths = explode(PATH_SEPARATOR, get_include_path());
|
||||
$slash = DIRECTORY_SEPARATOR;
|
||||
$dir = dirname(__DIR__);
|
||||
$source_dir = dirname($dir);
|
||||
$dir = dirname($source_dir);
|
||||
|
||||
foreach (
|
||||
array(
|
||||
array($source_dir),
|
||||
array($dir, '..', '..', 'composer'),
|
||||
array($dir, 'vendor', 'composer'),
|
||||
array($dir, '..', '..', '..', 'php'),
|
||||
array($dir, 'vendor', 'php'))
|
||||
as $includePath)
|
||||
if (false !== $path = stream_resolve_include_path(
|
||||
implode($slash, $includePath)
|
||||
))
|
||||
if ('composer' == end($includePath) &&
|
||||
false !== $classmapPath = stream_resolve_include_path(
|
||||
"$path{$slash}autoload_classmap.php"
|
||||
)
|
||||
) {
|
||||
static::seen(static::loadFile(
|
||||
$classmapPath
|
||||
));
|
||||
$paths = array_merge(
|
||||
$paths,
|
||||
array_values(static::loadFile(
|
||||
"$path{$slash}autoload_namespaces.php"
|
||||
))
|
||||
);
|
||||
} else
|
||||
$paths[] = $path;
|
||||
|
||||
$paths = array_filter(array_map(
|
||||
function ($path) {
|
||||
if (false == $realPath = @realpath($path))
|
||||
return null;
|
||||
return $realPath . DIRECTORY_SEPARATOR;
|
||||
},
|
||||
$paths
|
||||
));
|
||||
natsort($paths);
|
||||
static::seen(
|
||||
'__include_path',
|
||||
implode(PATH_SEPARATOR, array_unique($paths))
|
||||
);
|
||||
}
|
||||
|
||||
set_include_path(static::seen('__include_path'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to include the path location.
|
||||
* Called from a static context which will not expose the AutoLoader
|
||||
* instance itself.
|
||||
*
|
||||
* @param $path string location of php file on the include path
|
||||
*
|
||||
* @return bool|mixed returns reference obtained from the include or false
|
||||
*/
|
||||
private static function loadFile($path)
|
||||
{
|
||||
return \Luracast_Restler_autoloaderInclude($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to load class with namespace prefixes.
|
||||
*
|
||||
* @param $className string class name
|
||||
*
|
||||
* @return bool|mixed reference to discovered include or false
|
||||
*/
|
||||
private function loadPrefixes($className)
|
||||
{
|
||||
$currentClass = $className;
|
||||
if (false !== $pos = strrpos($className, '\\'))
|
||||
$className = substr($className, $pos);
|
||||
else
|
||||
$className = "\\$className";
|
||||
|
||||
for (
|
||||
$i = 0,
|
||||
$file = false,
|
||||
$count = count(static::$aliases),
|
||||
$prefixes = array_keys(static::$aliases);
|
||||
$i < $count
|
||||
&& false === $file
|
||||
&& false === $file = $this->discover(
|
||||
$variant = $prefixes[$i++].$className,
|
||||
$currentClass
|
||||
);
|
||||
$file = $this->loadAliases($variant)
|
||||
);
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to load configured aliases based on namespace part of class name.
|
||||
*
|
||||
* @param $className string fully qualified class name.
|
||||
*
|
||||
* @return bool|mixed reference to discovered include or false
|
||||
*/
|
||||
private function loadAliases($className)
|
||||
{
|
||||
$file = false;
|
||||
if (preg_match('/(.+)(\\\\\w+$)/U', $className, $parts))
|
||||
for (
|
||||
$i = 0,
|
||||
$aliases = isset(static::$aliases[$parts[1]])
|
||||
? static::$aliases[$parts[1]] : array(),
|
||||
$count = count($aliases);
|
||||
$i < $count && false === $file;
|
||||
$file = $this->discover(
|
||||
"{$aliases[$i++]}$parts[2]",
|
||||
$className
|
||||
)
|
||||
) ;
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load from rogueLoaders as last resort.
|
||||
* It may happen that a custom auto loader may load classes in a unique way,
|
||||
* these classes cannot be seen otherwise nor should we attempt to cover every
|
||||
* possible deviation. If we still can't find a class, as a last resort, we will
|
||||
* run through the list of rogue loaders and verify if we succeeded.
|
||||
*
|
||||
* @param $className string className that can't be found
|
||||
* @param null $loader callable loader optional when the loader is known
|
||||
*
|
||||
* @return bool false unless className now exists
|
||||
*/
|
||||
private function loadLastResort($className, $loader = null) {
|
||||
$loaders = array_unique(static::$rogueLoaders);
|
||||
if (isset($loader)) {
|
||||
if (false === array_search($loader, $loaders))
|
||||
static::$rogueLoaders[] = $loader;
|
||||
return $this->loadThisLoader($className, $loader);
|
||||
}
|
||||
foreach ($loaders as $loader)
|
||||
if (false !== $file = $this->loadThisLoader($className, $loader))
|
||||
return $file;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for loadLastResort.
|
||||
* Use loader with $className and see if className exists.
|
||||
*
|
||||
* @param $className string name of a class to load
|
||||
* @param $loader callable autoLoader method
|
||||
*
|
||||
* @return bool false unless className exists
|
||||
*/
|
||||
private function loadThisLoader($className, $loader) {
|
||||
if (is_callable($loader)
|
||||
&& false !== $file = $loader($className)
|
||||
&& $this->exists($className, $loader))
|
||||
return $file;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an alias for class.
|
||||
*
|
||||
* @param $className string the name of the alias class
|
||||
* @param $currentClass string the current class this alias references
|
||||
*/
|
||||
private function alias($className, $currentClass)
|
||||
{
|
||||
if ($className != $currentClass
|
||||
&& false !== strpos($className, $currentClass))
|
||||
if (!class_exists($currentClass, false)
|
||||
&& class_alias($className, $currentClass))
|
||||
static::seen($currentClass, $className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discovery process.
|
||||
*
|
||||
* @param $className string class name to discover
|
||||
* @param $currentClass string optional name of current class when
|
||||
* looking up an alias
|
||||
*
|
||||
* @return bool|mixed resolved include reference or false
|
||||
*/
|
||||
private function discover($className, $currentClass = null)
|
||||
{
|
||||
$currentClass = $currentClass ?: $className;
|
||||
|
||||
/** The short version we've done this before and found it in cache */
|
||||
if (false !== $file = static::seen($className)) {
|
||||
if (!$this->exists($className))
|
||||
if (is_callable($file))
|
||||
$file = $this->loadLastResort($className, $file);
|
||||
elseif($file = stream_resolve_include_path($file))
|
||||
$file = static::loadFile($file);
|
||||
|
||||
$this->alias($className, $currentClass);
|
||||
return $file;
|
||||
}
|
||||
|
||||
/** We did not find it in cache, lets look for it shall we */
|
||||
|
||||
/** replace \ with / and _ in CLASS NAME with / = PSR-0 in 3 lines */
|
||||
$file = preg_replace("/\\\|_(?=\w+$)/", DIRECTORY_SEPARATOR, $className);
|
||||
if (false === $file = stream_resolve_include_path("$file.php"))
|
||||
return false;
|
||||
|
||||
/** have we loaded this file before could this be an alias */
|
||||
if (in_array($file, get_included_files())) {
|
||||
if (false !== $sameFile = array_search($file, static::$classMap))
|
||||
if (!$this->exists($className, $file))
|
||||
if (false !== strpos($sameFile, $className))
|
||||
$this->alias($sameFile, $className);
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
$state = array_merge(get_declared_classes(), get_declared_interfaces());
|
||||
|
||||
if (false !== $result = static::loadFile($file)) {
|
||||
|
||||
if ($this->exists($className, $file))
|
||||
$this->alias($className, $currentClass);
|
||||
elseif (false != $diff = array_diff(
|
||||
array_merge(get_declared_classes(), get_declared_interfaces()), $state))
|
||||
foreach ($diff as $autoLoaded)
|
||||
if ($this->exists($autoLoaded, $file))
|
||||
if (false !== strpos($autoLoaded, $className))
|
||||
$this->alias($autoLoaded, $className);
|
||||
|
||||
if (!$this->exists($currentClass))
|
||||
$result = false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether supplied string exists in a loaded class or interface.
|
||||
* As a convenience the supplied $mapping can be the value for seen.
|
||||
*
|
||||
* @param $className string The class or interface to verify
|
||||
* @param $mapping string (optional) value for map/seen if found to exist
|
||||
*
|
||||
* @return bool whether the class/interface exists without calling auto loader
|
||||
*/
|
||||
private function exists($className, $mapping = null)
|
||||
{
|
||||
if (class_exists($className, false)
|
||||
|| interface_exists($className, false))
|
||||
if (isset($mapping))
|
||||
return static::seen($className, $mapping);
|
||||
else
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto loader callback through __invoke object as function.
|
||||
*
|
||||
* @param $className string class/interface name to auto load
|
||||
*
|
||||
* @return mixed|null the reference from the include or null
|
||||
*/
|
||||
public function __invoke($className)
|
||||
{
|
||||
if (empty($className))
|
||||
return false;
|
||||
|
||||
if (false !== $includeReference = $this->discover($className))
|
||||
return $includeReference;
|
||||
|
||||
static::thereCanBeOnlyOne();
|
||||
|
||||
if (false !== $includeReference = $this->loadAliases($className))
|
||||
return $includeReference;
|
||||
|
||||
if (false !== $includeReference = $this->loadPrefixes($className))
|
||||
return $includeReference;
|
||||
|
||||
if (false !== $includeReference = $this->loadLastResort($className))
|
||||
return $includeReference;
|
||||
|
||||
static::seen($className, true);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* Include function in the root namespace to include files optimized
|
||||
* for the global context.
|
||||
*
|
||||
* @param $path string path of php file to include into the global context.
|
||||
*
|
||||
* @return mixed|bool false if the file could not be included.
|
||||
*/
|
||||
function Luracast_Restler_autoloaderInclude($path) {
|
||||
return include $path;
|
||||
}
|
||||
}
|
||||
|
||||
466
htdocs/includes/restler/CommentParser.php
Normal file
466
htdocs/includes/restler/CommentParser.php
Normal file
@ -0,0 +1,466 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Parses the PHPDoc comments for metadata. Inspired by `Documentor` code base.
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage Helper
|
||||
* @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 CommentParser
|
||||
{
|
||||
/**
|
||||
* name for the embedded data
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $embeddedDataName = 'properties';
|
||||
/**
|
||||
* Regular Expression pattern for finding the embedded data and extract
|
||||
* the inner information. It is used with preg_match.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $embeddedDataPattern
|
||||
= '/```(\w*)[\s]*(([^`]*`{0,2}[^`]+)*)```/ms';
|
||||
/**
|
||||
* Pattern will have groups for the inner details of embedded data
|
||||
* this index is used to locate the data portion.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $embeddedDataIndex = 2;
|
||||
/**
|
||||
* Delimiter used to split the array data.
|
||||
*
|
||||
* When the name portion is of the embedded data is blank auto detection
|
||||
* will be used and if URLEncodedFormat is detected as the data format
|
||||
* the character specified will be used as the delimiter to find split
|
||||
* array data.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $arrayDelimiter = ',';
|
||||
|
||||
/**
|
||||
* character sequence used to escape \@
|
||||
*/
|
||||
const escapedAtChar = '\\@';
|
||||
|
||||
/**
|
||||
* character sequence used to escape end of comment
|
||||
*/
|
||||
const escapedCommendEnd = '{@*}';
|
||||
|
||||
/**
|
||||
* Instance of Restler class injected at runtime.
|
||||
*
|
||||
* @var Restler
|
||||
*/
|
||||
public $restler;
|
||||
/**
|
||||
* Comment information is parsed and stored in to this array.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_data = array();
|
||||
|
||||
/**
|
||||
* Parse the comment and extract the data.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param $comment
|
||||
* @param bool $isPhpDoc
|
||||
*
|
||||
* @return array associative array with the extracted values
|
||||
*/
|
||||
public static function parse($comment, $isPhpDoc = true)
|
||||
{
|
||||
$p = new self();
|
||||
if (empty($comment)) {
|
||||
return $p->_data;
|
||||
}
|
||||
|
||||
if ($isPhpDoc) {
|
||||
$comment = self::removeCommentTags($comment);
|
||||
}
|
||||
|
||||
$p->extractData($comment);
|
||||
return $p->_data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the comment tags from each line of the comment.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $comment PhpDoc style comment
|
||||
*
|
||||
* @return string comments with out the tags
|
||||
*/
|
||||
public static function removeCommentTags($comment)
|
||||
{
|
||||
$pattern = '/(^\/\*\*)|(^\s*\**[ \/]?)|\s(?=@)|\s\*\//m';
|
||||
return preg_replace($pattern, '', $comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts description and long description, uses other methods to get
|
||||
* parameters.
|
||||
*
|
||||
* @param $comment
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function extractData($comment)
|
||||
{
|
||||
//to use @ as part of comment we need to
|
||||
$comment = str_replace(
|
||||
array(self::escapedCommendEnd, self::escapedAtChar),
|
||||
array('*/', '@'),
|
||||
$comment);
|
||||
|
||||
$description = array();
|
||||
$longDescription = array();
|
||||
$params = array();
|
||||
|
||||
$mode = 0; // extract short description;
|
||||
$comments = preg_split("/(\r?\n)/", $comment);
|
||||
// remove first blank line;
|
||||
array_shift($comments);
|
||||
$addNewline = false;
|
||||
foreach ($comments as $line) {
|
||||
$line = trim($line);
|
||||
$newParam = false;
|
||||
if (empty ($line)) {
|
||||
if ($mode == 0) {
|
||||
$mode++;
|
||||
} else {
|
||||
$addNewline = true;
|
||||
}
|
||||
continue;
|
||||
} elseif ($line{0} == '@') {
|
||||
$mode = 2;
|
||||
$newParam = true;
|
||||
}
|
||||
switch ($mode) {
|
||||
case 0 :
|
||||
$description[] = $line;
|
||||
if (count($description) > 3) {
|
||||
// if more than 3 lines take only first line
|
||||
$longDescription = $description;
|
||||
$description[] = array_shift($longDescription);
|
||||
$mode = 1;
|
||||
} elseif (substr($line, -1) == '.') {
|
||||
$mode = 1;
|
||||
}
|
||||
break;
|
||||
case 1 :
|
||||
if ($addNewline) {
|
||||
$line = ' ' . $line;
|
||||
}
|
||||
$longDescription[] = $line;
|
||||
break;
|
||||
case 2 :
|
||||
$newParam
|
||||
? $params[] = $line
|
||||
: $params[count($params) - 1] .= ' ' . $line;
|
||||
}
|
||||
$addNewline = false;
|
||||
}
|
||||
$description = implode(' ', $description);
|
||||
$longDescription = implode(' ', $longDescription);
|
||||
$description = preg_replace('/\s+/msu', ' ', $description);
|
||||
$longDescription = preg_replace('/\s+/msu', ' ', $longDescription);
|
||||
list($description, $d1)
|
||||
= $this->parseEmbeddedData($description);
|
||||
list($longDescription, $d2)
|
||||
= $this->parseEmbeddedData($longDescription);
|
||||
$this->_data = compact('description', 'longDescription');
|
||||
$d2 += $d1;
|
||||
if (!empty($d2)) {
|
||||
$this->_data[self::$embeddedDataName] = $d2;
|
||||
}
|
||||
foreach ($params as $key => $line) {
|
||||
list(, $param, $value) = preg_split('/\@|\s/', $line, 3)
|
||||
+ array('', '', '');
|
||||
list($value, $embedded) = $this->parseEmbeddedData($value);
|
||||
$value = array_filter(preg_split('/\s+/msu', $value));
|
||||
$this->parseParam($param, $value, $embedded);
|
||||
}
|
||||
return $this->_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse parameters that begin with (at)
|
||||
*
|
||||
* @param $param
|
||||
* @param array $value
|
||||
* @param array $embedded
|
||||
*/
|
||||
private function parseParam($param, array $value, array $embedded)
|
||||
{
|
||||
$data = & $this->_data;
|
||||
$allowMultiple = false;
|
||||
switch ($param) {
|
||||
case 'param' :
|
||||
$value = $this->formatParam($value);
|
||||
$allowMultiple = true;
|
||||
break;
|
||||
case 'var' :
|
||||
$value = $this->formatVar($value);
|
||||
break;
|
||||
case 'return' :
|
||||
$value = $this->formatReturn($value);
|
||||
break;
|
||||
case 'class' :
|
||||
$data = & $data[$param];
|
||||
list ($param, $value) = $this->formatClass($value);
|
||||
break;
|
||||
case 'access' :
|
||||
$value = reset($value);
|
||||
break;
|
||||
case 'expires' :
|
||||
case 'status' :
|
||||
$value = intval(reset($value));
|
||||
break;
|
||||
case 'throws' :
|
||||
$value = $this->formatThrows($value);
|
||||
$allowMultiple = true;
|
||||
break;
|
||||
case 'author':
|
||||
$value = $this->formatAuthor($value);
|
||||
$allowMultiple = true;
|
||||
break;
|
||||
case 'header' :
|
||||
case 'link':
|
||||
case 'example':
|
||||
case 'todo':
|
||||
$allowMultiple = true;
|
||||
//don't break, continue with code for default:
|
||||
default :
|
||||
$value = implode(' ', $value);
|
||||
}
|
||||
if (!empty($embedded)) {
|
||||
if (is_string($value)) {
|
||||
$value = array('description' => $value);
|
||||
}
|
||||
$value[self::$embeddedDataName] = $embedded;
|
||||
}
|
||||
if (empty ($data[$param])) {
|
||||
if ($allowMultiple) {
|
||||
$data[$param] = array(
|
||||
$value
|
||||
);
|
||||
} else {
|
||||
$data[$param] = $value;
|
||||
}
|
||||
} elseif ($allowMultiple) {
|
||||
$data[$param][] = $value;
|
||||
} elseif ($param == 'param') {
|
||||
$arr = array(
|
||||
$data[$param],
|
||||
$value
|
||||
);
|
||||
$data[$param] = $arr;
|
||||
} else {
|
||||
if (!is_string($value) && isset($value[self::$embeddedDataName])
|
||||
&& isset($data[$param][self::$embeddedDataName])
|
||||
) {
|
||||
$value[self::$embeddedDataName]
|
||||
+= $data[$param][self::$embeddedDataName];
|
||||
}
|
||||
$data[$param] = $value + $data[$param];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the inline php doc comments and embedded data.
|
||||
*
|
||||
* @param $subject
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
private function parseEmbeddedData($subject)
|
||||
{
|
||||
$data = array();
|
||||
|
||||
//parse {@pattern } tags specially
|
||||
while (preg_match('|(?s-m)({@pattern (/.+/[imsxuADSUXJ]*)})|', $subject, $matches)) {
|
||||
$subject = str_replace($matches[0], '', $subject);
|
||||
$data['pattern'] = $matches[2];
|
||||
}
|
||||
while (preg_match('/{@(\w+)\s?([^}]*)}/ms', $subject, $matches)) {
|
||||
$subject = str_replace($matches[0], '', $subject);
|
||||
if ($matches[2] == 'true' || $matches[2] == 'false') {
|
||||
$matches[2] = $matches[2] == 'true';
|
||||
} elseif ($matches[2] == '') {
|
||||
$matches[2] = true;
|
||||
}
|
||||
if ($matches[1] == 'pattern') {
|
||||
throw new Exception('Inline pattern tag should follow {@pattern /REGEX_PATTERN_HERE/} format and can optionally include PCRE modifiers following the ending `/`');
|
||||
} elseif (false !== strpos($matches[2], static::$arrayDelimiter)) {
|
||||
$matches[2] = explode(static::$arrayDelimiter, $matches[2]);
|
||||
}
|
||||
$data[$matches[1]] = $matches[2];
|
||||
}
|
||||
|
||||
while (preg_match(self::$embeddedDataPattern, $subject, $matches)) {
|
||||
$subject = str_replace($matches[0], '', $subject);
|
||||
$str = $matches[self::$embeddedDataIndex];
|
||||
if (isset ($this->restler)
|
||||
&& self::$embeddedDataIndex > 1
|
||||
&& !empty ($matches[1])
|
||||
) {
|
||||
$extension = $matches[1];
|
||||
$formatMap = $this->restler->getFormatMap();
|
||||
if (isset ($formatMap[$extension])) {
|
||||
/**
|
||||
* @var \Luracast\Restler\Format\iFormat
|
||||
*/
|
||||
$format = $formatMap[$extension];
|
||||
$format = new $format();
|
||||
$data = $format->decode($str);
|
||||
}
|
||||
} else { // auto detect
|
||||
if ($str{0} == '{') {
|
||||
$d = json_decode($str, true);
|
||||
if (json_last_error() != JSON_ERROR_NONE) {
|
||||
throw new Exception('Error parsing embedded JSON data'
|
||||
. " $str");
|
||||
}
|
||||
$data = $d + $data;
|
||||
} else {
|
||||
parse_str($str, $d);
|
||||
//clean up
|
||||
$d = array_filter($d);
|
||||
foreach ($d as $key => $val) {
|
||||
$kt = trim($key);
|
||||
if ($kt != $key) {
|
||||
unset($d[$key]);
|
||||
$key = $kt;
|
||||
$d[$key] = $val;
|
||||
}
|
||||
if (is_string($val)) {
|
||||
if ($val == 'true' || $val == 'false') {
|
||||
$d[$key] = $val == 'true' ? true : false;
|
||||
} else {
|
||||
$val = explode(self::$arrayDelimiter, $val);
|
||||
if (count($val) > 1) {
|
||||
$d[$key] = $val;
|
||||
} else {
|
||||
$d[$key] =
|
||||
preg_replace('/\s+/msu', ' ',
|
||||
$d[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$data = $d + $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return array($subject, $data);
|
||||
}
|
||||
|
||||
private function formatThrows(array $value)
|
||||
{
|
||||
$r = array();
|
||||
$r['code'] = count($value) && is_numeric($value[0])
|
||||
? intval(array_shift($value)) : 500;
|
||||
$reason = implode(' ', $value);
|
||||
$r['reason'] = empty($reason) ? '' : $reason;
|
||||
return $r;
|
||||
}
|
||||
|
||||
private function formatClass(array $value)
|
||||
{
|
||||
$param = array_shift($value);
|
||||
|
||||
if (empty($param)) {
|
||||
$param = 'Unknown';
|
||||
}
|
||||
$value = implode(' ', $value);
|
||||
return array(
|
||||
ltrim($param, '\\'),
|
||||
array('description' => $value)
|
||||
);
|
||||
}
|
||||
|
||||
private function formatAuthor(array $value)
|
||||
{
|
||||
$r = array();
|
||||
$email = end($value);
|
||||
if ($email{0} == '<') {
|
||||
$email = substr($email, 1, -1);
|
||||
array_pop($value);
|
||||
$r['email'] = $email;
|
||||
}
|
||||
$r['name'] = implode(' ', $value);
|
||||
return $r;
|
||||
}
|
||||
|
||||
private function formatReturn(array $value)
|
||||
{
|
||||
$data = explode('|', array_shift($value));
|
||||
$r = array(
|
||||
'type' => count($data) == 1 ? $data[0] : $data
|
||||
);
|
||||
$r['description'] = implode(' ', $value);
|
||||
return $r;
|
||||
}
|
||||
|
||||
private function formatParam(array $value)
|
||||
{
|
||||
$r = array();
|
||||
$data = array_shift($value);
|
||||
if (empty($data)) {
|
||||
$r['type'] = 'mixed';
|
||||
} elseif ($data{0} == '$') {
|
||||
$r['name'] = substr($data, 1);
|
||||
$r['type'] = 'mixed';
|
||||
} else {
|
||||
$data = explode('|', $data);
|
||||
$r['type'] = count($data) == 1 ? $data[0] : $data;
|
||||
|
||||
$data = array_shift($value);
|
||||
if (!empty($data) && $data{0} == '$') {
|
||||
$r['name'] = substr($data, 1);
|
||||
}
|
||||
}
|
||||
if ($value) {
|
||||
$r['description'] = implode(' ', $value);
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
private function formatVar(array $value)
|
||||
{
|
||||
$r = array();
|
||||
$data = array_shift($value);
|
||||
if (empty($data)) {
|
||||
$r['type'] = 'mixed';
|
||||
} elseif ($data{0} == '$') {
|
||||
$r['name'] = substr($data, 1);
|
||||
$r['type'] = 'mixed';
|
||||
} else {
|
||||
$data = explode('|', $data);
|
||||
$r['type'] = count($data) == 1 ? $data[0] : $data;
|
||||
}
|
||||
if ($value) {
|
||||
$r['description'] = implode(' ', $value);
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
}
|
||||
71
htdocs/includes/restler/Compose.php
Normal file
71
htdocs/includes/restler/Compose.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
/**
|
||||
* Default Composer to provide standard structure for all HTTP responses
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage result
|
||||
* @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/
|
||||
*/
|
||||
class Compose implements iCompose
|
||||
{
|
||||
/**
|
||||
* @var bool When restler is not running in production mode, this value will
|
||||
* be checked to include the debug information on error response
|
||||
*/
|
||||
public static $includeDebugInfo = true;
|
||||
/**
|
||||
* Current Restler instance
|
||||
* Injected at runtime
|
||||
*
|
||||
* @var Restler
|
||||
*/
|
||||
public $restler;
|
||||
|
||||
/**
|
||||
* Result of an api call is passed to this method
|
||||
* to create a standard structure for the data
|
||||
*
|
||||
* @param mixed $result can be a primitive or array or object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function response($result)
|
||||
{
|
||||
//TODO: check Defaults::language and change result accordingly
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the api call results in RestException this method
|
||||
* will be called to return the error message
|
||||
*
|
||||
* @param RestException $exception exception that has reasons for failure
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function message(RestException $exception)
|
||||
{
|
||||
//TODO: check Defaults::language and change result accordingly
|
||||
$r = array(
|
||||
'error' => array(
|
||||
'code' => $exception->getCode(),
|
||||
'message' => $exception->getErrorMessage(),
|
||||
) + $exception->getDetails()
|
||||
);
|
||||
if (!Scope::get('Restler')->getProductionMode() && self::$includeDebugInfo) {
|
||||
$r += array(
|
||||
'debug' => array(
|
||||
'source' => $exception->getSource(),
|
||||
'stages' => $exception->getStages(),
|
||||
)
|
||||
);
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
}
|
||||
55
htdocs/includes/restler/Data/ApiMethodInfo.php
Normal file
55
htdocs/includes/restler/Data/ApiMethodInfo.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Data;
|
||||
|
||||
/**
|
||||
* ValueObject for api method info. All needed information about a api method
|
||||
* is stored here
|
||||
*
|
||||
* @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 ApiMethodInfo extends ValueObject
|
||||
{
|
||||
/**
|
||||
* @var string target url
|
||||
*/
|
||||
public $url;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $className;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $methodName;
|
||||
/**
|
||||
* @var array parameters to be passed to the api method
|
||||
*/
|
||||
public $parameters = array();
|
||||
/**
|
||||
* @var array information on parameters in the form of array(name => index)
|
||||
*/
|
||||
public $arguments = array();
|
||||
/**
|
||||
* @var array default values for parameters if any
|
||||
* in the form of array(index => value)
|
||||
*/
|
||||
public $defaults = array();
|
||||
/**
|
||||
* @var array key => value pair of method meta information
|
||||
*/
|
||||
public $metadata = array();
|
||||
/**
|
||||
* @var int access level
|
||||
* 0 - @public - available for all
|
||||
* 1 - @hybrid - both public and protected (enhanced info for authorized)
|
||||
* 2 - @protected comment - only for authenticated users
|
||||
* 3 - protected method - only for authenticated users
|
||||
*/
|
||||
public $accessLevel = 0;
|
||||
}
|
||||
33
htdocs/includes/restler/Data/Arr.php
Normal file
33
htdocs/includes/restler/Data/Arr.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Data;
|
||||
|
||||
/**
|
||||
* Convenience class for Array manipulation
|
||||
*
|
||||
* @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/
|
||||
*/
|
||||
class Arr
|
||||
{
|
||||
/**
|
||||
* Deep copy given array
|
||||
*
|
||||
* @param array $arr
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function copy(array $arr)
|
||||
{
|
||||
$copy = array();
|
||||
foreach ($arr as $key => $value) {
|
||||
if (is_array($value)) $copy[$key] = static::copy($value);
|
||||
else if (is_object($value)) $copy[$key] = clone $value;
|
||||
else $copy[$key] = $value;
|
||||
}
|
||||
return $copy;
|
||||
}
|
||||
}
|
||||
20
htdocs/includes/restler/Data/Invalid.php
Normal file
20
htdocs/includes/restler/Data/Invalid.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Data;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Invalid Exception
|
||||
*
|
||||
* @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 Invalid extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
157
htdocs/includes/restler/Data/Object.php
Normal file
157
htdocs/includes/restler/Data/Object.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Data;
|
||||
|
||||
/**
|
||||
* Convenience class that converts the given object
|
||||
* in to associative array
|
||||
*
|
||||
* @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 Object
|
||||
{
|
||||
/**
|
||||
* @var bool|string|callable
|
||||
*/
|
||||
public static $stringEncoderFunction = false;
|
||||
/**
|
||||
* @var bool|string|callable
|
||||
*/
|
||||
public static $numberEncoderFunction = false;
|
||||
/**
|
||||
* @var array key value pairs for fixing value types using functions.
|
||||
* For example
|
||||
*
|
||||
* 'id'=>'intval' will make sure all values of the id properties
|
||||
* will be converted to integers intval function
|
||||
* 'password'=> null will remove all the password entries
|
||||
*/
|
||||
public static $fix = array();
|
||||
/**
|
||||
* @var string character that is used to identify sub objects
|
||||
*
|
||||
* For example
|
||||
*
|
||||
* when Object::$separatorChar = '.';
|
||||
*
|
||||
* array('my.object'=>true) will result in
|
||||
*
|
||||
* array(
|
||||
* 'my'=>array('object'=>true)
|
||||
* );
|
||||
*/
|
||||
public static $separatorChar = null;
|
||||
/**
|
||||
* @var bool set it to true when empty arrays, blank strings, null values
|
||||
* to be automatically removed from response
|
||||
*/
|
||||
public static $removeEmpty = false;
|
||||
/**
|
||||
* @var bool set it to true to remove all null values from the result
|
||||
*/
|
||||
public static $removeNull = false;
|
||||
|
||||
/**
|
||||
* Convenience function that converts the given object
|
||||
* in to associative array
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param mixed $object that needs to be converted
|
||||
*
|
||||
* @param bool $forceObjectTypeWhenEmpty when set to true outputs
|
||||
* actual type (array or
|
||||
* object) rather than
|
||||
* always an array when the
|
||||
* array/object is empty
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function toArray($object,
|
||||
$forceObjectTypeWhenEmpty = false)
|
||||
{
|
||||
//if ($object instanceof JsonSerializable) { //wont work on PHP < 5.4
|
||||
if (is_object($object)) {
|
||||
if (method_exists($object, 'jsonSerialize')) {
|
||||
$object = $object->jsonSerialize();
|
||||
} elseif (method_exists($object, '__sleep')) {
|
||||
$properties = $object->__sleep();
|
||||
$array = array();
|
||||
foreach ($properties as $key) {
|
||||
$value = self::toArray($object->{$key},
|
||||
$forceObjectTypeWhenEmpty);
|
||||
if (self::$stringEncoderFunction && is_string($value)) {
|
||||
$value = self::$stringEncoderFunction ($value);
|
||||
} elseif (self::$numberEncoderFunction && is_numeric($value)) {
|
||||
$value = self::$numberEncoderFunction ($value);
|
||||
}
|
||||
$array [$key] = $value;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
if (is_array($object) || is_object($object)) {
|
||||
$count = 0;
|
||||
$array = array();
|
||||
foreach ($object as $key => $value) {
|
||||
if (
|
||||
is_string(self::$separatorChar) &&
|
||||
false !== strpos($key, self::$separatorChar)
|
||||
) {
|
||||
list($key, $obj) = explode(self::$separatorChar, $key, 2);
|
||||
$object[$key][$obj] = $value;
|
||||
$value = $object[$key];
|
||||
}
|
||||
if (self::$removeEmpty && empty($value) && !is_numeric($value) && !is_bool($value)) {
|
||||
continue;
|
||||
} elseif (self::$removeNull && is_null($value)) {
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($key, self::$fix)) {
|
||||
if (isset(self::$fix[$key])) {
|
||||
$value = call_user_func(self::$fix[$key], $value);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$value = self::toArray($value, $forceObjectTypeWhenEmpty);
|
||||
if (self::$stringEncoderFunction && is_string($value)) {
|
||||
$value = self::$encoderFunctionName ($value);
|
||||
} elseif (self::$numberEncoderFunction && is_numeric($value)) {
|
||||
$value = self::$numberEncoderFunction ($value);
|
||||
}
|
||||
$array [$key] = $value;
|
||||
$count++;
|
||||
}
|
||||
return $forceObjectTypeWhenEmpty && $count == 0 ? $object : $array;
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
isset(self::$fix[$name]) ? self::$fix[$name] : null;
|
||||
}
|
||||
|
||||
public function __set($name, $function)
|
||||
{
|
||||
self::$fix[$name] = $function;
|
||||
}
|
||||
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset(self::$fix[$name]);
|
||||
}
|
||||
|
||||
public function __unset($name)
|
||||
{
|
||||
unset(self::$fix[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
84
htdocs/includes/restler/Data/String.php
Normal file
84
htdocs/includes/restler/Data/String.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Data;
|
||||
|
||||
/**
|
||||
* Convenience class for String manipulation
|
||||
*
|
||||
* @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/
|
||||
*/
|
||||
class String
|
||||
{
|
||||
/**
|
||||
* Given haystack contains the needle or not?
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
* @param bool $caseSensitive
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function contains($haystack, $needle, $caseSensitive = true)
|
||||
{
|
||||
if (empty($needle))
|
||||
return true;
|
||||
return $caseSensitive
|
||||
? strpos($haystack, $needle) !== false
|
||||
: stripos($haystack, $needle) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given haystack begins with the needle or not?
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function beginsWith($haystack, $needle)
|
||||
{
|
||||
$length = strlen($needle);
|
||||
return (substr($haystack, 0, $length) === $needle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given haystack ends with the needle or not?
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function endsWith($haystack, $needle)
|
||||
{
|
||||
$length = strlen($needle);
|
||||
if ($length == 0) {
|
||||
return true;
|
||||
}
|
||||
return (substr($haystack, -$length) === $needle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert camelCased or underscored string in to a title
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function title($name)
|
||||
{
|
||||
return
|
||||
ucwords(
|
||||
preg_replace(
|
||||
array('/(?<=[^A-Z])([A-Z])/', '/(?<=[^0-9])([0-9])/', '/(_)/'),
|
||||
array(' $0', ' $0', ' '),
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
273
htdocs/includes/restler/Data/ValidationInfo.php
Normal file
273
htdocs/includes/restler/Data/ValidationInfo.php
Normal file
@ -0,0 +1,273 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Data;
|
||||
|
||||
use Luracast\Restler\CommentParser;
|
||||
use Luracast\Restler\Util;
|
||||
|
||||
/**
|
||||
* ValueObject for validation information. An instance is created and
|
||||
* populated by Restler to pass it to iValidate implementing classes for
|
||||
* validation
|
||||
*
|
||||
* @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 ValidationInfo implements iValueObject
|
||||
{
|
||||
/**
|
||||
* @var mixed given value for the parameter
|
||||
*/
|
||||
public $value;
|
||||
/**
|
||||
* @var string proper name for given parameter
|
||||
*/
|
||||
public $label;
|
||||
/**
|
||||
* @var string html element that can be used to represent the parameter for
|
||||
* input
|
||||
*/
|
||||
public $field;
|
||||
/**
|
||||
* @var mixed default value for the parameter
|
||||
*/
|
||||
public $default;
|
||||
/**
|
||||
* Name of the variable being validated
|
||||
*
|
||||
* @var string variable name
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var bool is it required or not
|
||||
*/
|
||||
public $required;
|
||||
|
||||
/**
|
||||
* @var string body or header or query where this parameter is coming from
|
||||
* in the http request
|
||||
*/
|
||||
public $from;
|
||||
|
||||
/**
|
||||
* Data type of the variable being validated.
|
||||
* It will be mostly string
|
||||
*
|
||||
* @var string|array multiple types are specified it will be of
|
||||
* type array otherwise it will be a string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* When the type is array, this field is used to define the type of the
|
||||
* contents of the array
|
||||
*
|
||||
* @var string|null when all the items in an array are of certain type, we
|
||||
* can set this property. It will be null if the items can be of any type
|
||||
*/
|
||||
public $contentType;
|
||||
|
||||
/**
|
||||
* Should we attempt to fix the value?
|
||||
* When set to false validation class should throw
|
||||
* an exception or return false for the validate call.
|
||||
* When set to true it will attempt to fix the value if possible
|
||||
* or throw an exception or return false when it cant be fixed.
|
||||
*
|
||||
* @var boolean true or false
|
||||
*/
|
||||
public $fix = false;
|
||||
|
||||
/**
|
||||
* @var array of children to be validated
|
||||
*/
|
||||
public $children = null;
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// VALUE RANGE
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
/**
|
||||
* Given value should match one of the values in the array
|
||||
*
|
||||
* @var array of choices to match to
|
||||
*/
|
||||
public $choice;
|
||||
/**
|
||||
* If the type is string it will set the lower limit for length
|
||||
* else will specify the lower limit for the value
|
||||
*
|
||||
* @var number minimum value
|
||||
*/
|
||||
public $min;
|
||||
/**
|
||||
* If the type is string it will set the upper limit limit for length
|
||||
* else will specify the upper limit for the value
|
||||
*
|
||||
* @var number maximum value
|
||||
*/
|
||||
public $max;
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// REGEX VALIDATION
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
/**
|
||||
* RegEx pattern to match the value
|
||||
*
|
||||
* @var string regular expression
|
||||
*/
|
||||
public $pattern;
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// CUSTOM VALIDATION
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
/**
|
||||
* Rules specified for the parameter in the php doc comment.
|
||||
* It is passed to the validation method as the second parameter
|
||||
*
|
||||
* @var array custom rule set
|
||||
*/
|
||||
public $rules;
|
||||
|
||||
/**
|
||||
* Specifying a custom error message will override the standard error
|
||||
* message return by the validator class
|
||||
*
|
||||
* @var string custom error response
|
||||
*/
|
||||
public $message;
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// METHODS
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Name of the method to be used for validation.
|
||||
* It will be receiving two parameters $input, $rules (array)
|
||||
*
|
||||
* @var string validation method name
|
||||
*/
|
||||
public $method;
|
||||
|
||||
/**
|
||||
* Instance of the API class currently being called. It will be null most of
|
||||
* the time. Only when method is defined it will contain an instance.
|
||||
* This behavior is for lazy loading of the API class
|
||||
*
|
||||
* @var null|object will be null or api class instance
|
||||
*/
|
||||
public $apiClassInstance = null;
|
||||
|
||||
public static function numericValue($value)
|
||||
{
|
||||
return ( int )$value == $value
|
||||
? ( int )$value
|
||||
: floatval($value);
|
||||
}
|
||||
|
||||
public static function arrayValue($value)
|
||||
{
|
||||
return is_array($value) ? $value : array(
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
public static function stringValue($value, $glue = ',')
|
||||
{
|
||||
return is_array($value)
|
||||
? implode($glue, $value)
|
||||
: ( string )$value;
|
||||
}
|
||||
|
||||
public static function booleanValue($value)
|
||||
{
|
||||
return is_bool($value)
|
||||
? $value
|
||||
: $value !== 'false';
|
||||
}
|
||||
|
||||
public static function filterArray(array $data, $keepNumericKeys)
|
||||
{
|
||||
$r = array();
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_numeric($key)) {
|
||||
if ($keepNumericKeys) {
|
||||
$r[$key] = $value;
|
||||
}
|
||||
} elseif (!$keepNumericKeys) {
|
||||
$r[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return ' new ValidationInfo() ';
|
||||
}
|
||||
|
||||
private function getProperty(array &$from, $property)
|
||||
{
|
||||
$p = Util::nestedValue($from, $property);
|
||||
unset($from[$property]);
|
||||
$p2 = Util::nestedValue(
|
||||
$from, CommentParser::$embeddedDataName, $property
|
||||
);
|
||||
unset($from[CommentParser::$embeddedDataName][$property]);
|
||||
|
||||
if ($property == 'type' && $p == 'array' && $p2) {
|
||||
$this->contentType = $p2;
|
||||
return $p;
|
||||
}
|
||||
$r = is_null($p2) ? (is_null($p) ? null : $p) : $p2;
|
||||
if (!is_null($r)) {
|
||||
if ($property == 'min' || $property == 'max') {
|
||||
return static::numericValue($r);
|
||||
} elseif ($property == 'required' || $property == 'fix') {
|
||||
return static::booleanValue($r);
|
||||
} elseif ($property == 'choice') {
|
||||
return static::arrayValue($r);
|
||||
} elseif ($property == 'pattern') {
|
||||
return static::stringValue($r);
|
||||
}
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function __construct(array $info)
|
||||
{
|
||||
$properties = get_object_vars($this);
|
||||
unset($properties['contentType']);
|
||||
foreach ($properties as $property => $value) {
|
||||
$this->{$property} = $this->getProperty($info, $property);
|
||||
}
|
||||
$inner = Util::nestedValue($info, 'properties');
|
||||
$this->rules = !empty($inner) ? $inner + $info : $info;
|
||||
unset($this->rules['properties']);
|
||||
if (is_string($this->type) && $this->type == 'integer') {
|
||||
$this->type = 'int';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic Method used for creating instance at run time
|
||||
*/
|
||||
public static function __set_state(array $info)
|
||||
{
|
||||
$o = new self ($info);
|
||||
return $o;
|
||||
}
|
||||
}
|
||||
|
||||
626
htdocs/includes/restler/Data/Validator.php
Normal file
626
htdocs/includes/restler/Data/Validator.php
Normal file
@ -0,0 +1,626 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Data;
|
||||
|
||||
use Luracast\Restler\CommentParser;
|
||||
use Luracast\Restler\Format\HtmlFormat;
|
||||
use Luracast\Restler\RestException;
|
||||
use Luracast\Restler\Scope;
|
||||
use Luracast\Restler\Util;
|
||||
|
||||
/**
|
||||
* Default Validator class used by Restler. It can be replaced by any
|
||||
* iValidate implementing class by setting Defaults::$validatorClass
|
||||
*
|
||||
* @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 Validator implements iValidate
|
||||
{
|
||||
public static $holdException = false;
|
||||
public static $exceptions = array();
|
||||
|
||||
/**
|
||||
* Validate alphabetic characters.
|
||||
*
|
||||
* Check that given value contains only alphabetic characters.
|
||||
*
|
||||
* @param $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function alpha($input, ValidationInfo $info = null)
|
||||
{
|
||||
if (ctype_alpha($input)) {
|
||||
return $input;
|
||||
}
|
||||
if ($info && $info->fix) {
|
||||
//remove non alpha characters
|
||||
return preg_replace("/[^a-z]/i", "", $input);
|
||||
}
|
||||
throw new Invalid('Expecting only alphabetic characters.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate alpha numeric characters.
|
||||
*
|
||||
* Check that given value contains only alpha numeric characters.
|
||||
*
|
||||
* @param $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function alphanumeric($input, ValidationInfo $info = null)
|
||||
{
|
||||
if (ctype_alnum($input)) {
|
||||
return $input;
|
||||
}
|
||||
if ($info && $info->fix) {
|
||||
//remove non alpha numeric and space characters
|
||||
return preg_replace("/[^a-z0-9 ]/i", "", $input);
|
||||
}
|
||||
throw new Invalid('Expecting only alpha numeric characters.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate printable characters.
|
||||
*
|
||||
* Check that given value contains only printable characters.
|
||||
*
|
||||
* @param $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function printable($input, ValidationInfo $info = null)
|
||||
{
|
||||
if (ctype_print($input)) {
|
||||
return $input;
|
||||
}
|
||||
if ($info && $info->fix) {
|
||||
//remove non printable characters
|
||||
return preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $input);
|
||||
}
|
||||
throw new Invalid('Expecting only printable characters.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate hexadecimal digits.
|
||||
*
|
||||
* Check that given value contains only hexadecimal digits.
|
||||
*
|
||||
* @param $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function hex($input, ValidationInfo $info = null)
|
||||
{
|
||||
if (ctype_xdigit($input)) {
|
||||
return $input;
|
||||
}
|
||||
throw new Invalid('Expecting only hexadecimal digits.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Telephone number
|
||||
*
|
||||
* Check if the given value is numeric with or without a `+` prefix
|
||||
*
|
||||
* @param $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function tel($input, ValidationInfo $info = null)
|
||||
{
|
||||
if (is_numeric($input) && '-' != substr($input, 0, 1)) {
|
||||
return $input;
|
||||
}
|
||||
throw new Invalid('Expecting phone number, a numeric value ' .
|
||||
'with optional `+` prefix');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Email
|
||||
*
|
||||
* Check if the given string is a valid email
|
||||
*
|
||||
* @param String $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function email($input, ValidationInfo $info = null)
|
||||
{
|
||||
$r = filter_var($input, FILTER_VALIDATE_EMAIL);
|
||||
if ($r) {
|
||||
return $r;
|
||||
} elseif ($info && $info->fix) {
|
||||
$r = filter_var($input, FILTER_SANITIZE_EMAIL);
|
||||
return static::email($r);
|
||||
}
|
||||
throw new Invalid('Expecting email in `name@example.com` format');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate IP Address
|
||||
*
|
||||
* Check if the given string is a valid ip address
|
||||
*
|
||||
* @param String $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function ip($input, ValidationInfo $info = null)
|
||||
{
|
||||
$r = filter_var($input, FILTER_VALIDATE_IP);
|
||||
if ($r)
|
||||
return $r;
|
||||
|
||||
throw new Invalid('Expecting IP address in IPV6 or IPV4 format');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Url
|
||||
*
|
||||
* Check if the given string is a valid url
|
||||
*
|
||||
* @param String $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function url($input, ValidationInfo $info = null)
|
||||
{
|
||||
$r = filter_var($input, FILTER_VALIDATE_URL);
|
||||
if ($r) {
|
||||
return $r;
|
||||
} elseif ($info && $info->fix) {
|
||||
$r = filter_var($input, FILTER_SANITIZE_URL);
|
||||
return static::url($r);
|
||||
}
|
||||
throw new Invalid('Expecting url in `http://example.com` format');
|
||||
}
|
||||
|
||||
/**
|
||||
* MySQL Date
|
||||
*
|
||||
* Check if the given string is a valid date in YYYY-MM-DD format
|
||||
*
|
||||
* @param String $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function date($input, ValidationInfo $info = null)
|
||||
{
|
||||
if (
|
||||
preg_match(
|
||||
'#^(?P<year>\d{2}|\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})$#',
|
||||
$input,
|
||||
$date
|
||||
)
|
||||
&& checkdate($date['month'], $date['day'], $date['year'])
|
||||
) {
|
||||
return $input;
|
||||
}
|
||||
throw new Invalid(
|
||||
'Expecting date in `YYYY-MM-DD` format, such as `'
|
||||
. date("Y-m-d") . '`'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* MySQL DateTime
|
||||
*
|
||||
* Check if the given string is a valid date and time in YYY-MM-DD HH:MM:SS format
|
||||
*
|
||||
* @param String $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function datetime($input, ValidationInfo $info = null)
|
||||
{
|
||||
if (
|
||||
preg_match('/^(?P<year>19\d\d|20\d\d)\-(?P<month>0[1-9]|1[0-2])\-' .
|
||||
'(?P<day>0\d|[1-2]\d|3[0-1]) (?P<h>0\d|1\d|2[0-3]' .
|
||||
')\:(?P<i>[0-5][0-9])\:(?P<s>[0-5][0-9])$/',
|
||||
$input, $date)
|
||||
&& checkdate($date['month'], $date['day'], $date['year'])
|
||||
) {
|
||||
return $input;
|
||||
}
|
||||
throw new Invalid(
|
||||
'Expecting date and time in `YYYY-MM-DD HH:MM:SS` format, such as `'
|
||||
. date("Y-m-d H:i:s") . '`'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for Time
|
||||
*
|
||||
* Check if the given string is a valid time in HH:MM:SS format
|
||||
*
|
||||
* @param String $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function time24($input, ValidationInfo $info = null)
|
||||
{
|
||||
return static::time($input, $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time
|
||||
*
|
||||
* Check if the given string is a valid time in HH:MM:SS format
|
||||
*
|
||||
* @param String $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function time($input, ValidationInfo $info = null)
|
||||
{
|
||||
if (preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/', $input)) {
|
||||
return $input;
|
||||
}
|
||||
throw new Invalid(
|
||||
'Expecting time in `HH:MM:SS` format, such as `'
|
||||
. date("H:i:s") . '`'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time in 12 hour format
|
||||
*
|
||||
* Check if the given string is a valid time 12 hour format
|
||||
*
|
||||
* @param String $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return string
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function time12($input, ValidationInfo $info = null)
|
||||
{
|
||||
if (preg_match(
|
||||
'/^([1-9]|1[0-2]|0[1-9]){1}(:[0-5][0-9])?\s?([aApP][mM]{1})?$/',
|
||||
$input)
|
||||
) {
|
||||
return $input;
|
||||
}
|
||||
throw new Invalid(
|
||||
'Expecting time in 12 hour format, such as `08:00AM` and `10:05:11`'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unix Timestamp
|
||||
*
|
||||
* Check if the given value is a valid timestamp
|
||||
*
|
||||
* @param String $input
|
||||
* @param ValidationInfo $info
|
||||
*
|
||||
* @return int
|
||||
* @throws Invalid
|
||||
*/
|
||||
public static function timestamp($input, ValidationInfo $info = null)
|
||||
{
|
||||
if ((string)(int)$input == $input
|
||||
&& ($input <= PHP_INT_MAX)
|
||||
&& ($input >= ~PHP_INT_MAX)
|
||||
) {
|
||||
return (int)$input;
|
||||
}
|
||||
throw new Invalid('Expecting unix timestamp, such as ' . time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given input
|
||||
*
|
||||
* Validates the input and attempts to fix it when fix is requested
|
||||
*
|
||||
* @param mixed $input
|
||||
* @param ValidationInfo $info
|
||||
* @param null $full
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return array|bool|float|int|mixed|null|number|string
|
||||
*/
|
||||
public static function validate($input, ValidationInfo $info, $full = null)
|
||||
{
|
||||
$html = Scope::get('Restler')->responseFormat instanceof HtmlFormat;
|
||||
$name = $html ? "<strong>$info->label</strong>" : "`$info->name`";
|
||||
try {
|
||||
if (is_null($input)) {
|
||||
if ($info->required) {
|
||||
throw new RestException (400,
|
||||
"$name is required.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
$error = isset ($info->message)
|
||||
? $info->message
|
||||
: "Invalid value specified for $name";
|
||||
|
||||
//if a validation method is specified
|
||||
if (!empty($info->method)) {
|
||||
$method = $info->method;
|
||||
$info->method = '';
|
||||
$r = self::validate($input, $info);
|
||||
return $info->apiClassInstance->{$method} ($r);
|
||||
}
|
||||
|
||||
// when type is an array check if it passes for any type
|
||||
if (is_array($info->type)) {
|
||||
//trace("types are ".print_r($info->type, true));
|
||||
$types = $info->type;
|
||||
foreach ($types as $type) {
|
||||
$info->type = $type;
|
||||
try {
|
||||
$r = self::validate($input, $info);
|
||||
if ($r !== false) {
|
||||
return $r;
|
||||
}
|
||||
} catch (RestException $e) {
|
||||
// just continue
|
||||
}
|
||||
}
|
||||
throw new RestException (400, $error);
|
||||
}
|
||||
|
||||
//patterns are supported only for non numeric types
|
||||
if (isset ($info->pattern)
|
||||
&& $info->type != 'int'
|
||||
&& $info->type != 'float'
|
||||
&& $info->type != 'number'
|
||||
) {
|
||||
if (!preg_match($info->pattern, $input)) {
|
||||
throw new RestException (400, $error);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset ($info->choice)) {
|
||||
if (is_array($input)) {
|
||||
foreach ($input as $i) {
|
||||
if (!in_array($i, $info->choice)) {
|
||||
$error .= ". Expected one of (" . implode(',', $info->choice) . ").";
|
||||
throw new RestException (400, $error);
|
||||
}
|
||||
}
|
||||
} elseif (!in_array($input, $info->choice)) {
|
||||
$error .= ". Expected one of (" . implode(',', $info->choice) . ").";
|
||||
throw new RestException (400, $error);
|
||||
}
|
||||
}
|
||||
|
||||
if (method_exists($class = get_called_class(), $info->type) && $info->type != 'validate') {
|
||||
try {
|
||||
return call_user_func("$class::$info->type", $input, $info);
|
||||
} catch (Invalid $e) {
|
||||
throw new RestException(400, $error . '. ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
switch ($info->type) {
|
||||
case 'int' :
|
||||
case 'float' :
|
||||
case 'number' :
|
||||
if (!is_numeric($input)) {
|
||||
$error .= '. Expecting '
|
||||
. ($info->type == 'int' ? 'integer' : 'numeric')
|
||||
. ' value';
|
||||
break;
|
||||
}
|
||||
if ($info->type == 'int' && (int)$input != $input) {
|
||||
if ($info->fix) {
|
||||
$r = (int)$input;
|
||||
} else {
|
||||
$error .= '. Expecting integer value';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$r = $info->numericValue($input);
|
||||
}
|
||||
if (isset ($info->min) && $r < $info->min) {
|
||||
if ($info->fix) {
|
||||
$r = $info->min;
|
||||
} else {
|
||||
$error .= ". Minimum required value is $info->min.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isset ($info->max) && $r > $info->max) {
|
||||
if ($info->fix) {
|
||||
$r = $info->max;
|
||||
} else {
|
||||
$error .= ". Maximum allowed value is $info->max.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $r;
|
||||
|
||||
case 'string' :
|
||||
if (!is_string($input)) {
|
||||
$error .= '. Expecting alpha numeric value';
|
||||
break;
|
||||
}
|
||||
if ($info->required && $input === '') {
|
||||
$error = "$name is required.";
|
||||
break;
|
||||
}
|
||||
$r = strlen($input);
|
||||
if (isset ($info->min) && $r < $info->min) {
|
||||
if ($info->fix) {
|
||||
$input = str_pad($input, $info->min, $input);
|
||||
} else {
|
||||
$char = $info->min > 1 ? 'characters' : 'character';
|
||||
$error .= ". Minimum $info->min $char required.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isset ($info->max) && $r > $info->max) {
|
||||
if ($info->fix) {
|
||||
$input = substr($input, 0, $info->max);
|
||||
} else {
|
||||
$char = $info->max > 1 ? 'characters' : 'character';
|
||||
$error .= ". Maximum $info->max $char allowed.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $input;
|
||||
|
||||
case 'bool':
|
||||
case 'boolean':
|
||||
if ($input === 'true' || $input === true) return true;
|
||||
if (is_numeric($input)) return $input > 0;
|
||||
return false;
|
||||
|
||||
case 'array':
|
||||
if ($info->fix && is_string($input)) {
|
||||
$input = explode(CommentParser::$arrayDelimiter, $input);
|
||||
}
|
||||
if (is_array($input)) {
|
||||
$contentType =
|
||||
Util::nestedValue($info, 'contentType') ? : null;
|
||||
if ($info->fix) {
|
||||
if ($contentType == 'indexed') {
|
||||
$input = $info->filterArray($input, true);
|
||||
} elseif ($contentType == 'associative') {
|
||||
$input = $info->filterArray($input, true);
|
||||
}
|
||||
} elseif (
|
||||
$contentType == 'indexed' &&
|
||||
array_values($input) != $input
|
||||
) {
|
||||
$error .= '. Expecting a list of items but an item is given';
|
||||
break;
|
||||
} elseif (
|
||||
$contentType == 'associative' &&
|
||||
array_values($input) == $input &&
|
||||
count($input)
|
||||
) {
|
||||
$error .= '. Expecting an item but a list is given';
|
||||
break;
|
||||
}
|
||||
$r = count($input);
|
||||
if (isset ($info->min) && $r < $info->min) {
|
||||
$item = $info->max > 1 ? 'items' : 'item';
|
||||
$error .= ". Minimum $info->min $item required.";
|
||||
break;
|
||||
}
|
||||
if (isset ($info->max) && $r > $info->max) {
|
||||
if ($info->fix) {
|
||||
$input = array_slice($input, 0, $info->max);
|
||||
} else {
|
||||
$item = $info->max > 1 ? 'items' : 'item';
|
||||
$error .= ". Maximum $info->max $item allowed.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (
|
||||
isset($contentType) &&
|
||||
$contentType != 'associative' &&
|
||||
$contentType != 'indexed'
|
||||
) {
|
||||
$name = $info->name;
|
||||
$info->type = $contentType;
|
||||
unset($info->contentType);
|
||||
foreach ($input as $key => $chinput) {
|
||||
$info->name = "{$name}[$key]";
|
||||
$input[$key] = static::validate($chinput, $info);
|
||||
}
|
||||
}
|
||||
return $input;
|
||||
} elseif (isset($contentType)) {
|
||||
$error .= '. Expecting items of type ' .
|
||||
($html ? "<strong>$contentType</strong>" : "`$contentType`");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'mixed':
|
||||
case 'unknown_type':
|
||||
case 'unknown':
|
||||
case null: //treat as unknown
|
||||
return $input;
|
||||
default :
|
||||
if (!is_array($input)) {
|
||||
break;
|
||||
}
|
||||
//do type conversion
|
||||
if (class_exists($info->type)) {
|
||||
$input = $info->filterArray($input, false);
|
||||
$implements = class_implements($info->type);
|
||||
if (
|
||||
is_array($implements) &&
|
||||
in_array('Luracast\\Restler\\Data\\iValueObject', $implements)
|
||||
) {
|
||||
return call_user_func(
|
||||
"{$info->type}::__set_state", $input
|
||||
);
|
||||
}
|
||||
$class = $info->type;
|
||||
$instance = new $class();
|
||||
if (is_array($info->children)) {
|
||||
if (
|
||||
empty($input) ||
|
||||
!is_array($input) ||
|
||||
$input === array_values($input)
|
||||
) {
|
||||
$error .= '. Expecting an item of type ' .
|
||||
($html ? "<strong>$info->type</strong>" : "`$info->type`");
|
||||
break;
|
||||
}
|
||||
foreach ($info->children as $key => $value) {
|
||||
$cv = new ValidationInfo($value);
|
||||
if (array_key_exists($key, $input) || $cv->required) {
|
||||
$instance->{$key} = static::validate(
|
||||
Util::nestedValue($input, $key),
|
||||
$cv
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
throw new RestException (400, $error);
|
||||
} catch (\Exception $e) {
|
||||
static::$exceptions[] = $e;
|
||||
if (static::$holdException) {
|
||||
return null;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
htdocs/includes/restler/Data/ValueObject.php
Normal file
61
htdocs/includes/restler/Data/ValueObject.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Data;
|
||||
|
||||
/**
|
||||
* ValueObject base class, you may use this class to create your
|
||||
* iValueObjects quickly
|
||||
*
|
||||
* @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 ValueObject implements iValueObject
|
||||
{
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return ' new ' . get_called_class() . '() ';
|
||||
}
|
||||
|
||||
public static function __set_state(array $properties)
|
||||
{
|
||||
$class = get_called_class();
|
||||
$instance = new $class ();
|
||||
$vars = get_object_vars($instance);
|
||||
foreach ($properties as $property => $value) {
|
||||
if (property_exists($instance, $property)) {
|
||||
// see if the property is accessible
|
||||
if (array_key_exists($property, $vars)) {
|
||||
$instance->{$property} = $value;
|
||||
} else {
|
||||
$method = 'set' . ucfirst($property);
|
||||
if (method_exists($instance, $method)) {
|
||||
call_user_func(array(
|
||||
$instance,
|
||||
$method
|
||||
), $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public function __toArray()
|
||||
{
|
||||
$r = get_object_vars($this);
|
||||
$methods = get_class_methods($this);
|
||||
foreach ($methods as $m) {
|
||||
if (substr($m, 0, 3) == 'get') {
|
||||
$r [lcfirst(substr($m, 3))] = @$this->{$m} ();
|
||||
}
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
32
htdocs/includes/restler/Data/iValidate.php
Normal file
32
htdocs/includes/restler/Data/iValidate.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Data;
|
||||
|
||||
/**
|
||||
* Validation classes should implement this interface
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
interface iValidate {
|
||||
|
||||
/**
|
||||
* method used for validation.
|
||||
*
|
||||
* @param mixed $input
|
||||
* data that needs to be validated
|
||||
* @param ValidationInfo $info
|
||||
* information to be used for validation
|
||||
* @return boolean false in case of failure or fixed value in the expected
|
||||
* type
|
||||
* @throws \Luracast\Restler\RestException 400 with information about the
|
||||
* failed
|
||||
* validation
|
||||
*/
|
||||
public static function validate($input, ValidationInfo $info);
|
||||
}
|
||||
|
||||
39
htdocs/includes/restler/Data/iValueObject.php
Normal file
39
htdocs/includes/restler/Data/iValueObject.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Data;
|
||||
|
||||
/**
|
||||
* Restler is using many ValueObjects across to make it easy for the developers
|
||||
* to use them with the help of code hinting etc.,
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
interface iValueObject
|
||||
{
|
||||
|
||||
/**
|
||||
* This static method is called for creating an instance of the class by
|
||||
* passing the initiation values as an array.
|
||||
*
|
||||
* @static
|
||||
* @abstract
|
||||
*
|
||||
* @param array $properties
|
||||
*
|
||||
* @return iValueObject
|
||||
*/
|
||||
public static function __set_state(array $properties);
|
||||
|
||||
/**
|
||||
* This method provides a string representation for the instance
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString();
|
||||
}
|
||||
|
||||
360
htdocs/includes/restler/Defaults.php
Normal file
360
htdocs/includes/restler/Defaults.php
Normal file
@ -0,0 +1,360 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
use Luracast\Restler\Data\ValidationInfo;
|
||||
use Luracast\Restler\Data\Validator;
|
||||
|
||||
/**
|
||||
* Static class to hold all restler defaults, change the values to suit your
|
||||
* needs in the gateway file (index.php), you may also allow the api users to
|
||||
* change them per request by adding the properties to Defaults::$overridables
|
||||
*
|
||||
* @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 Defaults
|
||||
{
|
||||
// ==================================================================
|
||||
//
|
||||
// Class Mappings
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var string of name of the class that implements
|
||||
* \Luracast\Restler\iCache the cache class to be used
|
||||
*/
|
||||
public static $cacheClass = 'Luracast\\Restler\\HumanReadableCache';
|
||||
|
||||
/**
|
||||
* @var string full path of the directory where all the generated files will
|
||||
* be kept. When set to null (default) it will use the cache folder that is
|
||||
* in the same folder as index.php (gateway)
|
||||
*/
|
||||
public static $cacheDirectory;
|
||||
|
||||
/**
|
||||
* @var string of name of the class that implements
|
||||
* \Luracast\Restler\Data\iValidate the validator class to be used
|
||||
*/
|
||||
public static $validatorClass = 'Luracast\\Restler\\Data\\Validator';
|
||||
|
||||
/**
|
||||
* @var string name of the class that implements \Luracast\Restler\iCompose
|
||||
* the class to be used to compose the response
|
||||
*/
|
||||
public static $composeClass = 'Luracast\\Restler\\Compose';
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Routing
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var bool should auto routing for public and protected api methods
|
||||
* should be enabled by default or not. Set this to false to get
|
||||
* Restler 1.0 style behavior
|
||||
*/
|
||||
public static $autoRoutingEnabled = true;
|
||||
|
||||
/**
|
||||
* @var boolean avoids creating multiple routes that can increase the
|
||||
* ambiguity when set to true. when a method parameter is optional it is
|
||||
* not mapped to the url and should only be used in request body or as
|
||||
* query string `/resource?id=value`. When a parameter is required and is
|
||||
* scalar, it will be mapped as part of the url `/resource/{id}`
|
||||
*/
|
||||
public static $smartAutoRouting = true;
|
||||
|
||||
/**
|
||||
* @var boolean enables more ways of finding the parameter data in the request.
|
||||
* If you need backward compatibility with Restler 2 or below turn this off
|
||||
*/
|
||||
public static $smartParameterParsing = true;
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// API Version Management
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var null|string name that is used for vendor specific media type and
|
||||
* api version using the Accept Header for example
|
||||
* application/vnd.{vendor}-v1+json
|
||||
*
|
||||
* Keep this null if you do not want to use vendor MIME for specifying api version
|
||||
*/
|
||||
public static $apiVendor = null;
|
||||
|
||||
/**
|
||||
* @var bool set it to true to force vendor specific MIME for versioning.
|
||||
* It will be automatically set to true when Defaults::$vendor is not
|
||||
* null and client is requesting for the custom MIME type
|
||||
*/
|
||||
public static $useVendorMIMEVersioning = false;
|
||||
|
||||
/**
|
||||
* @var bool set it to true to use enableUrl based versioning
|
||||
*/
|
||||
public static $useUrlBasedVersioning = false;
|
||||
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Request
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var string name to be used for the method parameter to capture the
|
||||
* entire request data
|
||||
*/
|
||||
public static $fullRequestDataName = 'request_data';
|
||||
|
||||
/**
|
||||
* @var string name of the property that can sent through $_GET or $_POST to
|
||||
* override the http method of the request. Set it to null or
|
||||
* blank string to disable http method override through request
|
||||
* parameters.
|
||||
*/
|
||||
public static $httpMethodOverrideProperty = 'http_method';
|
||||
|
||||
/**
|
||||
* @var bool should auto validating api parameters should be enabled by
|
||||
* default or not. Set this to false to avoid validation.
|
||||
*/
|
||||
public static $autoValidationEnabled = true;
|
||||
|
||||
/**
|
||||
* @var string name of the class that implements iUser interface to identify
|
||||
* the user for caching purposes
|
||||
*/
|
||||
public static $userIdentifierClass = 'Luracast\\Restler\\User';
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Response
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var bool HTTP status codes are set on all responses by default.
|
||||
* Some clients (like flash, mobile) have trouble dealing with non-200
|
||||
* status codes on error responses.
|
||||
*
|
||||
* You can set it to true to force a HTTP 200 status code on all responses,
|
||||
* even when errors occur. If you suppress status codes, look for an error
|
||||
* response to determine if an error occurred.
|
||||
*/
|
||||
public static $suppressResponseCode = false;
|
||||
|
||||
public static $supportedCharsets = array('utf-8', 'iso-8859-1');
|
||||
public static $supportedLanguages = array('en', 'en-US');
|
||||
|
||||
public static $charset = 'utf-8';
|
||||
public static $language = 'en';
|
||||
|
||||
/**
|
||||
* @var bool when set to true, it will exclude the response body
|
||||
*/
|
||||
public static $emptyBodyForNullResponse = true;
|
||||
|
||||
/**
|
||||
* @var bool enables CORS support
|
||||
*/
|
||||
public static $crossOriginResourceSharing = false;
|
||||
public static $accessControlAllowOrigin = '*';
|
||||
public static $accessControlAllowMethods =
|
||||
'GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD';
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Header
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var array default Cache-Control template that used to set the
|
||||
* Cache-Control header and has two values, first one is used when
|
||||
* Defaults::$headerExpires is 0 and second one when it has some time
|
||||
* value specified. When only one value is specified it will be used for
|
||||
* both cases
|
||||
*/
|
||||
public static $headerCacheControl = array(
|
||||
'no-cache, must-revalidate',
|
||||
|
||||
/* "public, " or "private, " will be prepended based on api method
|
||||
* called (public or protected)
|
||||
*/
|
||||
'max-age={expires}, must-revalidate',
|
||||
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* @var int sets the content to expire immediately when set to zero
|
||||
* alternatively you can specify the number of seconds the content will
|
||||
* expire. This setting can be altered at api level using php doc comment
|
||||
* with @expires numOfSeconds
|
||||
*/
|
||||
public static $headerExpires = 0;
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Access Control
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var null|callable if the api methods are under access control mechanism
|
||||
* you can attach a function here that returns true or false to determine
|
||||
* visibility of a protected api method. this function will receive method
|
||||
* info as the only parameter.
|
||||
*/
|
||||
public static $accessControlFunction = null;
|
||||
|
||||
/**
|
||||
* @var int set the default api access mode
|
||||
* value of 0 = public api
|
||||
* value of 1 = hybrid api using `@access hybrid` comment
|
||||
* value of 2 = protected api using `@access protected` comment
|
||||
* value of 3 = protected api using `protected function` method
|
||||
*/
|
||||
public static $apiAccessLevel = 0;
|
||||
|
||||
/**
|
||||
* @var string authentication method to be called in iAuthenticate
|
||||
* Interface
|
||||
*/
|
||||
public static $authenticationMethod = '__isAllowed';
|
||||
|
||||
/**
|
||||
* @var int time in milliseconds for bandwidth throttling,
|
||||
* which is the minimum response time for each api request. You can
|
||||
* change it per api method by setting `@throttle 3000` in php doc
|
||||
* comment either at the method level or class level
|
||||
*/
|
||||
public static $throttle = 0;
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Overrides for API User
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var array use 'alternativeName'=> 'actualName' to set alternative
|
||||
* names that can be used to represent the api method parameters and/or
|
||||
* static properties of Defaults
|
||||
*/
|
||||
public static $aliases = array(
|
||||
/**
|
||||
* suppress_response_codes=true as an URL parameter to force
|
||||
* a HTTP 200 status code on all responses
|
||||
*/
|
||||
'suppress_response_codes' => 'suppressResponseCode',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array determines the defaults that can be overridden by the api
|
||||
* user by passing them as URL parameters
|
||||
*/
|
||||
public static $overridables = array(
|
||||
'suppressResponseCode',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array contains validation details for defaults to be used when
|
||||
* set through URL parameters
|
||||
*/
|
||||
public static $validation = array(
|
||||
'suppressResponseCode' => array('type' => 'bool'),
|
||||
'headerExpires' => array('type' => 'int', 'min' => 0),
|
||||
);
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Overrides API Developer
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @var array determines what are the phpdoc comment tags that will
|
||||
* override the Defaults here with their values
|
||||
*/
|
||||
public static $fromComments = array(
|
||||
|
||||
/**
|
||||
* use PHPDoc comments such as the following
|
||||
* `
|
||||
*
|
||||
* @cache no-cache, must-revalidate` to set the Cache-Control header
|
||||
* for a specific api method
|
||||
*/
|
||||
'cache' => 'headerCacheControl',
|
||||
|
||||
/**
|
||||
* use PHPDoc comments such as the following
|
||||
* `
|
||||
*
|
||||
* @expires 50` to set the Expires header
|
||||
* for a specific api method
|
||||
*/
|
||||
'expires' => 'headerExpires',
|
||||
|
||||
/**
|
||||
* use PHPDoc comments such as the following
|
||||
* `
|
||||
*
|
||||
* @throttle 300`
|
||||
* to set the bandwidth throttling for 300 milliseconds
|
||||
* for a specific api method
|
||||
*/
|
||||
'throttle' => 'throttle',
|
||||
|
||||
/**
|
||||
* enable or disable smart auto routing from method comments
|
||||
* this one is hardwired so cant be turned off
|
||||
* it is placed here just for documentation purpose
|
||||
*/
|
||||
'smart-auto-routing' => 'smartAutoRouting',
|
||||
);
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Util
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Use this method to set value to a static properly of Defaults when
|
||||
* you want to make sure only proper values are taken in with the help of
|
||||
* validation
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $name name of the static property
|
||||
* @param mixed $value value to set the property to
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function setProperty($name, $value)
|
||||
{
|
||||
if (!property_exists(__CLASS__, $name)) return false;
|
||||
if (@is_array(Defaults::$validation[$name])) {
|
||||
$info = new ValidationInfo(Defaults::$validation[$name]);
|
||||
$value = Validator::validate($value, $info);
|
||||
}
|
||||
Defaults::$$name = $value;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
98
htdocs/includes/restler/EventDispatcher.php
Normal file
98
htdocs/includes/restler/EventDispatcher.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
/**
|
||||
* Static event broadcasting system for Restler
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
use Closure;
|
||||
|
||||
class EventDispatcher
|
||||
{
|
||||
private $listeners = array();
|
||||
protected static $_waitList = array();
|
||||
|
||||
public static $self;
|
||||
protected $events = array();
|
||||
|
||||
public function __construct() {
|
||||
static::$self = $this;
|
||||
if (!empty(static::$_waitList)) {
|
||||
foreach (static::$_waitList as $param) {
|
||||
call_user_func_array(array($this,$param[0]), $param[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function __callStatic($eventName, $params)
|
||||
{
|
||||
if (0 === strpos($eventName, 'on')) {
|
||||
if(static::$self){
|
||||
return call_user_func_array(array(static::$self, $eventName), $params);
|
||||
}
|
||||
static::$_waitList[] = func_get_args();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function __call($eventName, $params)
|
||||
{
|
||||
if (0 === strpos($eventName, 'on')) {
|
||||
if (!@is_array($this->listeners[$eventName]))
|
||||
$this->listeners[$eventName] = array();
|
||||
$this->listeners[$eventName][] = $params[0];
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function addListener($eventName, Closure $callback)
|
||||
{
|
||||
return static::$eventName($callback);
|
||||
}
|
||||
|
||||
public function on(array $eventHandlers)
|
||||
{
|
||||
for (
|
||||
$count = count($eventHandlers),
|
||||
$events = array_map(
|
||||
'ucfirst',
|
||||
$keys = array_keys(
|
||||
$eventHandlers = array_change_key_case(
|
||||
$eventHandlers,
|
||||
CASE_LOWER
|
||||
)
|
||||
)
|
||||
),
|
||||
$i = 0;
|
||||
$i < $count;
|
||||
call_user_func(
|
||||
array($this, "on{$events[$i]}"),
|
||||
$eventHandlers[$keys[$i++]]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire an event to notify all listeners
|
||||
*
|
||||
* @param string $eventName name of the event
|
||||
* @param array $params event related data
|
||||
*/
|
||||
protected function dispatch($eventName, array $params = array())
|
||||
{
|
||||
$this->events[] = $eventName;
|
||||
$params = func_get_args();
|
||||
$eventName = 'on'.ucfirst(array_shift($params));
|
||||
if (isset($this->listeners[$eventName]))
|
||||
foreach ($this->listeners[$eventName] as $callback)
|
||||
call_user_func_array($callback, $params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
178
htdocs/includes/restler/Filter/RateLimit.php
Normal file
178
htdocs/includes/restler/Filter/RateLimit.php
Normal file
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Filter;
|
||||
|
||||
use Luracast\Restler\Defaults;
|
||||
use Luracast\Restler\iFilter;
|
||||
use Luracast\Restler\iUseAuthentication;
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
/**
|
||||
* Describe the purpose of this class/interface/trait
|
||||
*
|
||||
* @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 RateLimit implements iFilter, iUseAuthentication
|
||||
{
|
||||
/**
|
||||
* @var \Luracast\Restler\Restler;
|
||||
*/
|
||||
public $restler;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public static $usagePerUnit = 1200;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public static $authenticatedUsagePerUnit = 5000;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $unit = 'hour';
|
||||
/**
|
||||
* @var string group the current api belongs to
|
||||
*/
|
||||
public static $group = 'common';
|
||||
|
||||
protected static $units = array(
|
||||
'second' => 1,
|
||||
'minute' => 60,
|
||||
'hour' => 3600, // 60*60 seconds
|
||||
'day' => 86400, // 60*60*24 seconds
|
||||
'week' => 604800, // 60*60*24*7 seconds
|
||||
'month' => 2592000, // 60*60*24*30 seconds
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array all paths beginning with any of the following will be excluded
|
||||
* from documentation
|
||||
*/
|
||||
public static $excludedPaths = array('resources');
|
||||
|
||||
|
||||
/**
|
||||
* @param string $unit
|
||||
* @param int $usagePerUnit
|
||||
* @param int $authenticatedUsagePerUnit set it to false to give unlimited access
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @return void
|
||||
*/
|
||||
public static function setLimit(
|
||||
$unit, $usagePerUnit, $authenticatedUsagePerUnit = null
|
||||
)
|
||||
{
|
||||
static::$unit = $unit;
|
||||
static::$usagePerUnit = $usagePerUnit;
|
||||
static::$authenticatedUsagePerUnit =
|
||||
is_null($authenticatedUsagePerUnit) ? $usagePerUnit : $authenticatedUsagePerUnit;
|
||||
}
|
||||
|
||||
public function __isAllowed()
|
||||
{
|
||||
if (static::$authenticatedUsagePerUnit
|
||||
== static::$usagePerUnit
|
||||
) return $this->check();
|
||||
return null;
|
||||
}
|
||||
|
||||
public function __setAuthenticationStatus($isAuthenticated = false)
|
||||
{
|
||||
header('X-Auth-Status: ' . ($isAuthenticated ? 'true' : 'false'));
|
||||
$this->check($isAuthenticated);
|
||||
}
|
||||
|
||||
private static function validate($unit)
|
||||
{
|
||||
if (!isset(static::$units[$unit]))
|
||||
throw new \InvalidArgumentException(
|
||||
'Rate Limit time unit should be '
|
||||
. implode('|', array_keys(static::$units)) . '.'
|
||||
);
|
||||
}
|
||||
|
||||
private function check($isAuthenticated = false)
|
||||
{
|
||||
$path = $this->restler->url;
|
||||
foreach (static::$excludedPaths as $exclude) {
|
||||
if (empty($exclude) && empty($path)) {
|
||||
return true;
|
||||
} elseif (0 === strpos($path, $exclude)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
static::validate(static::$unit);
|
||||
$timeUnit = static::$units[static::$unit];
|
||||
$maxPerUnit = $isAuthenticated
|
||||
? static::$authenticatedUsagePerUnit
|
||||
: static::$usagePerUnit;
|
||||
if ($maxPerUnit) {
|
||||
$user = Defaults::$userIdentifierClass;
|
||||
if (!method_exists($user, 'getUniqueIdentifier')) {
|
||||
throw new \UnexpectedValueException('`Defaults::$userIdentifierClass` must implement `iIdentifyUser` interface');
|
||||
}
|
||||
$id = "RateLimit_" . $maxPerUnit . '_per_' . static::$unit
|
||||
. '_for_' . static::$group
|
||||
. '_' . $user::getUniqueIdentifier();
|
||||
$lastRequest = $this->restler->cache->get($id, true)
|
||||
? : array('time' => 0, 'used' => 0);
|
||||
$time = $lastRequest['time'];
|
||||
$diff = time() - $time; # in seconds
|
||||
$used = $lastRequest['used'];
|
||||
|
||||
header("X-RateLimit-Limit: $maxPerUnit per " . static::$unit);
|
||||
if ($diff >= $timeUnit) {
|
||||
$used = 1;
|
||||
$time = time();
|
||||
} elseif ($used >= $maxPerUnit) {
|
||||
header("X-RateLimit-Remaining: 0");
|
||||
$wait = $timeUnit - $diff;
|
||||
sleep(1);
|
||||
throw new RestException(429,
|
||||
'Rate limit of ' . $maxPerUnit . ' request' .
|
||||
($maxPerUnit > 1 ? 's' : '') . ' per '
|
||||
. static::$unit . ' exceeded. Please wait for '
|
||||
. static::duration($wait) . '.'
|
||||
);
|
||||
} else {
|
||||
$used++;
|
||||
}
|
||||
$remainingPerUnit = $maxPerUnit - $used;
|
||||
header("X-RateLimit-Remaining: $remainingPerUnit");
|
||||
$this->restler->cache->set($id,
|
||||
array('time' => $time, 'used' => $used));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function duration($secs)
|
||||
{
|
||||
$units = array(
|
||||
'week' => (int)($secs / 86400 / 7),
|
||||
'day' => $secs / 86400 % 7,
|
||||
'hour' => $secs / 3600 % 24,
|
||||
'minute' => $secs / 60 % 60,
|
||||
'second' => $secs % 60);
|
||||
|
||||
$ret = array();
|
||||
|
||||
//$unit = 'days';
|
||||
foreach ($units as $k => $v) {
|
||||
if ($v > 0) {
|
||||
$ret[] = $v > 1 ? "$v {$k}s" : "$v $k";
|
||||
//$unit = $k;
|
||||
}
|
||||
}
|
||||
$i = count($ret) - 1;
|
||||
if ($i) {
|
||||
$ret[$i] = 'and ' . $ret[$i];
|
||||
}
|
||||
return implode(' ', $ret); //." $unit.";
|
||||
}
|
||||
}
|
||||
146
htdocs/includes/restler/Flash.php
Normal file
146
htdocs/includes/restler/Flash.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
use Luracast\Restler\Format\HtmlFormat;
|
||||
|
||||
/**
|
||||
* Storing and retrieving a message or array of key value pairs for one time use using $_SESSION
|
||||
*
|
||||
* They are typically used in view templates when using HtmlFormat
|
||||
*
|
||||
* @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 Flash //implements \JsonSerializable
|
||||
{
|
||||
const SUCCESS = 'success';
|
||||
const INFO = 'info';
|
||||
const WARNING = 'warning';
|
||||
const DANGER = 'danger';
|
||||
|
||||
/**
|
||||
* @var Flash
|
||||
*/
|
||||
private static $instance;
|
||||
private $usedOnce = false;
|
||||
|
||||
/**
|
||||
* Flash a success message to user
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $header
|
||||
*
|
||||
* @return Flash
|
||||
*/
|
||||
public static function success($message, $header = '')
|
||||
{
|
||||
return static::message($message, $header, Flash::SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash a info message to user
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $header
|
||||
*
|
||||
* @return Flash
|
||||
*/
|
||||
public static function info($message, $header = '')
|
||||
{
|
||||
return static::message($message, $header, Flash::INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash a warning message to user
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $header
|
||||
*
|
||||
* @return Flash
|
||||
*/
|
||||
public static function warning($message, $header = '')
|
||||
{
|
||||
return static::message($message, $header, Flash::WARNING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash a error message to user
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $header
|
||||
*
|
||||
* @return Flash
|
||||
*/
|
||||
public static function danger($message, $header = '')
|
||||
{
|
||||
return static::message($message, $header, Flash::DANGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash a message to user
|
||||
*
|
||||
* @param string $text message text
|
||||
* @param string $header
|
||||
* @param string $type
|
||||
*
|
||||
* @return Flash
|
||||
*/
|
||||
public static function message($text, $header = '', $type = Flash::WARNING)
|
||||
{
|
||||
return static::set(array('message' => $text, 'header' => $header, 'type' => $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set some data for one time use
|
||||
*
|
||||
* @param array $data array of key value pairs {@type associative}
|
||||
*
|
||||
* @return Flash
|
||||
*/
|
||||
public static function set(array $data)
|
||||
{
|
||||
if (!static::$instance)
|
||||
static::$instance = new Flash();
|
||||
if (!isset($_SESSION['flash']))
|
||||
$_SESSION['flash'] = array();
|
||||
$_SESSION['flash'] += $data;
|
||||
HtmlFormat::$data['flash'] = static::$instance;
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
$this->usedOnce = true;
|
||||
return Util::nestedValue($_SESSION, 'flash', $name);
|
||||
}
|
||||
|
||||
public function __isset($name)
|
||||
{
|
||||
return !is_null(Util::nestedValue($_SESSION, 'flash', $name));
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->usedOnce)
|
||||
unset($_SESSION['flash']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$this->usedOnce = true;
|
||||
return isset($_SESSION['flash'])
|
||||
? $_SESSION['flash']
|
||||
: array();
|
||||
}
|
||||
}
|
||||
45
htdocs/includes/restler/Format/AmfFormat.php
Normal file
45
htdocs/includes/restler/Format/AmfFormat.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
use ZendAmf\Parser\Amf3\Deserializer;
|
||||
use ZendAmf\Parser\Amf3\Serializer;
|
||||
use ZendAmf\Parser\InputStream;
|
||||
use ZendAmf\Parser\OutputStream;
|
||||
|
||||
/**
|
||||
* AMF Binary Format for Restler Framework.
|
||||
* Native format supported by Adobe Flash and Adobe AIR
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 AmfFormat extends Format
|
||||
{
|
||||
const MIME = 'application/x-amf';
|
||||
const EXTENSION = 'amf';
|
||||
|
||||
public function encode($data, $humanReadable = false)
|
||||
{
|
||||
|
||||
$stream = new OutputStream();
|
||||
$serializer = new Serializer($stream);
|
||||
$serializer->writeTypeMarker($data);
|
||||
|
||||
return $stream->getStream();
|
||||
}
|
||||
|
||||
public function decode($data)
|
||||
{
|
||||
$stream = new InputStream(substr($data, 1));
|
||||
$deserializer = new Deserializer($stream);
|
||||
|
||||
return $deserializer->readTypeMarker();
|
||||
}
|
||||
}
|
||||
|
||||
181
htdocs/includes/restler/Format/CsvFormat.php
Normal file
181
htdocs/includes/restler/Format/CsvFormat.php
Normal file
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
|
||||
use Luracast\Restler\Data\Object;
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
/**
|
||||
* Comma Separated Value Format
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 CsvFormat extends Format implements iDecodeStream
|
||||
{
|
||||
|
||||
const MIME = 'text/csv';
|
||||
const EXTENSION = 'csv';
|
||||
public static $delimiter = ',';
|
||||
public static $enclosure = '"';
|
||||
public static $escape = '\\';
|
||||
public static $haveHeaders = null;
|
||||
|
||||
/**
|
||||
* Encode the given data in the csv format
|
||||
*
|
||||
* @param array $data
|
||||
* resulting data that needs to
|
||||
* be encoded in the given format
|
||||
* @param boolean $humanReadable
|
||||
* set to TRUE when restler
|
||||
* is not running in production mode. Formatter has to
|
||||
* make the encoded output more human readable
|
||||
*
|
||||
* @return string encoded string
|
||||
*
|
||||
* @throws RestException 500 on unsupported data
|
||||
*/
|
||||
public function encode($data, $humanReadable = false)
|
||||
{
|
||||
$char = Object::$separatorChar;
|
||||
Object::$separatorChar = false;
|
||||
$data = Object::toArray($data);
|
||||
Object::$separatorChar = $char;
|
||||
if (is_array($data) && array_values($data) == $data) {
|
||||
//if indexed array
|
||||
$lines = array();
|
||||
$row = array_shift($data);
|
||||
if (array_values($row) != $row) {
|
||||
$lines[] = static::putRow(array_keys($row));
|
||||
}
|
||||
$lines[] = static::putRow(array_values($row));
|
||||
foreach ($data as $row) {
|
||||
$lines[] = static::putRow(array_values($row));
|
||||
}
|
||||
return implode(PHP_EOL, $lines) . PHP_EOL;
|
||||
}
|
||||
throw new RestException(
|
||||
500,
|
||||
'Unsupported data for ' . strtoupper(static::EXTENSION) . ' format'
|
||||
);
|
||||
}
|
||||
|
||||
protected static function putRow($data)
|
||||
{
|
||||
$fp = fopen('php://temp', 'r+');
|
||||
fputcsv($fp, $data, static::$delimiter, static::$enclosure);
|
||||
rewind($fp);
|
||||
$data = fread($fp, 1048576);
|
||||
fclose($fp);
|
||||
return rtrim($data, PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given data from the csv format
|
||||
*
|
||||
* @param string $data
|
||||
* data sent from client to
|
||||
* the api in the given format.
|
||||
*
|
||||
* @return array associative array of the parsed data
|
||||
*/
|
||||
public function decode($data)
|
||||
{
|
||||
$decoded = array();
|
||||
|
||||
if (empty($data)) {
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
$lines = array_filter(explode(PHP_EOL, $data));
|
||||
|
||||
$keys = false;
|
||||
$row = static::getRow(array_shift($lines));
|
||||
|
||||
if (is_null(static::$haveHeaders)) {
|
||||
//try to guess with the given data
|
||||
static::$haveHeaders = !count(array_filter($row, 'is_numeric'));
|
||||
}
|
||||
|
||||
static::$haveHeaders ? $keys = $row : $decoded[] = $row;
|
||||
|
||||
while (($row = static::getRow(array_shift($lines), $keys)) !== FALSE)
|
||||
$decoded [] = $row;
|
||||
|
||||
$char = Object::$separatorChar;
|
||||
Object::$separatorChar = false;
|
||||
$decoded = Object::toArray($decoded);
|
||||
Object::$separatorChar = $char;
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
protected static function getRow($data, $keys = false)
|
||||
{
|
||||
if (empty($data)) {
|
||||
return false;
|
||||
}
|
||||
$line = str_getcsv(
|
||||
$data,
|
||||
static::$delimiter,
|
||||
static::$enclosure,
|
||||
static::$escape
|
||||
);
|
||||
|
||||
$row = array();
|
||||
foreach ($line as $key => $value) {
|
||||
if (is_numeric($value))
|
||||
$value = floatval($value);
|
||||
if ($keys) {
|
||||
if (isset($keys [$key]))
|
||||
$row [$keys [$key]] = $value;
|
||||
} else {
|
||||
$row [$key] = $value;
|
||||
}
|
||||
}
|
||||
if ($keys) {
|
||||
for ($i = count($row); $i < count($keys); $i++) {
|
||||
$row[$keys[$i]] = null;
|
||||
}
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given data stream
|
||||
*
|
||||
* @param string $stream A stream resource with data
|
||||
* sent from client to the api
|
||||
* in the given format.
|
||||
*
|
||||
* @return array associative array of the parsed data
|
||||
*/
|
||||
public function decodeStream($stream)
|
||||
{
|
||||
$decoded = array();
|
||||
|
||||
$keys = false;
|
||||
$row = static::getRow(stream_get_line($stream, 0, PHP_EOL));
|
||||
if (is_null(static::$haveHeaders)) {
|
||||
//try to guess with the given data
|
||||
static::$haveHeaders = !count(array_filter($row, 'is_numeric'));
|
||||
}
|
||||
|
||||
static::$haveHeaders ? $keys = $row : $decoded[] = $row;
|
||||
|
||||
while (($row = static::getRow(stream_get_line($stream, 0, PHP_EOL), $keys)) !== FALSE)
|
||||
$decoded [] = $row;
|
||||
|
||||
$char = Object::$separatorChar;
|
||||
Object::$separatorChar = false;
|
||||
$decoded = Object::toArray($decoded);
|
||||
Object::$separatorChar = $char;
|
||||
return $decoded;
|
||||
}
|
||||
}
|
||||
140
htdocs/includes/restler/Format/Format.php
Normal file
140
htdocs/includes/restler/Format/Format.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
/**
|
||||
* Abstract class to implement common methods of iFormat
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
abstract class Format implements iFormat
|
||||
{
|
||||
/**
|
||||
* override in the extending class
|
||||
*/
|
||||
const MIME = 'text/plain';
|
||||
/**
|
||||
* override in the extending class
|
||||
*/
|
||||
const EXTENSION = 'txt';
|
||||
|
||||
/**
|
||||
* @var string charset encoding defaults to UTF8
|
||||
*/
|
||||
protected $charset='utf-8';
|
||||
|
||||
/**
|
||||
* Injected at runtime
|
||||
*
|
||||
* @var \Luracast\Restler\Restler
|
||||
*/
|
||||
public $restler;
|
||||
|
||||
/**
|
||||
* Get MIME type => Extension mappings as an associative array
|
||||
*
|
||||
* @return array list of mime strings for the format
|
||||
* @example array('application/json'=>'json');
|
||||
*/
|
||||
public function getMIMEMap()
|
||||
{
|
||||
return array(
|
||||
static::MIME => static::EXTENSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected MIME type
|
||||
*
|
||||
* @param string $mime
|
||||
* MIME type
|
||||
*/
|
||||
public function setMIME($mime)
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Content-Type field of the HTTP header can send a charset
|
||||
* parameter in the HTTP header to specify the character
|
||||
* encoding of the document.
|
||||
* This information is passed
|
||||
* here so that Format class can encode data accordingly
|
||||
* Format class may choose to ignore this and use its
|
||||
* default character set.
|
||||
*
|
||||
* @param string $charset
|
||||
* Example utf-8
|
||||
*/
|
||||
public function setCharset($charset)
|
||||
{
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content-Type accepted by the Format class
|
||||
*
|
||||
* @return string $charset Example utf-8
|
||||
*/
|
||||
public function getCharset()
|
||||
{
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get selected MIME type
|
||||
*/
|
||||
public function getMIME()
|
||||
{
|
||||
return static::MIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected file extension
|
||||
*
|
||||
* @param string $extension
|
||||
* file extension
|
||||
*/
|
||||
public function setExtension($extension)
|
||||
{
|
||||
//do nothing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected file extension
|
||||
*
|
||||
* @return string file extension
|
||||
*/
|
||||
public function getExtension()
|
||||
{
|
||||
return static::EXTENSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean is parsing the request supported?
|
||||
*/
|
||||
public function isReadable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean is composing response supported?
|
||||
*/
|
||||
public function isWritable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
485
htdocs/includes/restler/Format/HtmlFormat.php
Normal file
485
htdocs/includes/restler/Format/HtmlFormat.php
Normal file
@ -0,0 +1,485 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Events\Dispatcher;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\View\Compilers\BladeCompiler;
|
||||
use Illuminate\View\Engines\CompilerEngine;
|
||||
use Illuminate\View\Engines\EngineResolver;
|
||||
use Illuminate\View\Factory;
|
||||
use Illuminate\View\FileViewFinder;
|
||||
use Illuminate\View\View;
|
||||
use Luracast\Restler\Data\ApiMethodInfo;
|
||||
use Luracast\Restler\Data\Object;
|
||||
use Luracast\Restler\Defaults;
|
||||
use Luracast\Restler\RestException;
|
||||
use Luracast\Restler\Restler;
|
||||
use Luracast\Restler\Scope;
|
||||
use Luracast\Restler\UI\Nav;
|
||||
use Luracast\Restler\Util;
|
||||
|
||||
/**
|
||||
* Html template format
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 HtmlFormat extends Format
|
||||
{
|
||||
public static $mime = 'text/html';
|
||||
public static $extension = 'html';
|
||||
public static $view;
|
||||
public static $errorView = 'debug.php';
|
||||
public static $template = 'php';
|
||||
public static $handleSession = true;
|
||||
|
||||
public static $useSmartViews = true;
|
||||
/**
|
||||
* @var null|string defaults to template named folder in Defaults::$cacheDirectory
|
||||
*/
|
||||
public static $cacheDirectory = null;
|
||||
/**
|
||||
* @var array global key value pair to be supplied to the templates. All
|
||||
* keys added here will be available as a variable inside the template
|
||||
*/
|
||||
public static $data = array();
|
||||
/**
|
||||
* @var string set it to the location of your the view files. Defaults to
|
||||
* views folder which is same level as vendor directory.
|
||||
*/
|
||||
public static $viewPath;
|
||||
/**
|
||||
* @var array template and its custom extension key value pair
|
||||
*/
|
||||
public static $customTemplateExtensions = array('blade' => 'blade.php');
|
||||
/**
|
||||
* @var bool used internally for error handling
|
||||
*/
|
||||
protected static $parseViewMetadata = true;
|
||||
/**
|
||||
* @var Restler;
|
||||
*/
|
||||
public $restler;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
//============ SESSION MANAGEMENT =============//
|
||||
if (static::$handleSession) {
|
||||
if (session_start() && isset($_SESSION['flash'])) {
|
||||
static::$data['flash'] = $_SESSION['flash'];
|
||||
unset($_SESSION['flash']);
|
||||
}
|
||||
}
|
||||
if (!static::$viewPath) {
|
||||
$array = explode('vendor', __DIR__, 2);
|
||||
static::$viewPath = $array[0] . 'views';
|
||||
}
|
||||
}
|
||||
|
||||
public static function blade(array $data, $debug = true)
|
||||
{
|
||||
if (!class_exists('\Illuminate\View\View', true))
|
||||
throw new RestException(500,
|
||||
'Blade templates require laravel view classes to be installed using `composer install`');
|
||||
$resolver = new EngineResolver();
|
||||
$files = new Filesystem();
|
||||
$compiler = new BladeCompiler($files, static::$cacheDirectory);
|
||||
$engine = new CompilerEngine($compiler);
|
||||
$resolver->register('blade', function () use ($engine) {
|
||||
return $engine;
|
||||
});
|
||||
|
||||
/** @var Restler $restler */
|
||||
$restler = Scope::get('Restler');
|
||||
|
||||
//Lets expose shortcuts for our classes
|
||||
spl_autoload_register(function ($className) use ($restler) {
|
||||
if (isset($restler->apiMethodInfo->metadata['scope'][$className])) {
|
||||
return class_alias($restler->apiMethodInfo->metadata['scope'][$className], $className);
|
||||
}
|
||||
if (isset(Scope::$classAliases[$className])) {
|
||||
return class_alias(Scope::$classAliases[$className], $className);
|
||||
}
|
||||
return false;
|
||||
}, true, true);
|
||||
|
||||
$viewFinder = new FileViewFinder($files, array(static::$viewPath));
|
||||
$factory = new Factory($resolver, $viewFinder, new Dispatcher());
|
||||
$path = $viewFinder->find(self::$view);
|
||||
$view = new View($factory, $engine, self::$view, $path, $data);
|
||||
$factory->callCreator($view);
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
public static function twig(array $data, $debug = true)
|
||||
{
|
||||
if (!class_exists('\Twig_Environment', true))
|
||||
throw new RestException(500,
|
||||
'Twig templates require twig classes to be installed using `composer install`');
|
||||
$loader = new \Twig_Loader_Filesystem(static::$viewPath);
|
||||
$twig = new \Twig_Environment($loader, array(
|
||||
'cache' => static::$cacheDirectory,
|
||||
'debug' => $debug,
|
||||
'use_strict_variables' => $debug,
|
||||
));
|
||||
if ($debug)
|
||||
$twig->addExtension(new \Twig_Extension_Debug());
|
||||
|
||||
$twig->addFunction(
|
||||
new \Twig_SimpleFunction(
|
||||
'form',
|
||||
'Luracast\Restler\UI\Forms::get',
|
||||
array('is_safe' => array('html'))
|
||||
)
|
||||
);
|
||||
$twig->addFunction(
|
||||
new \Twig_SimpleFunction(
|
||||
'form_key',
|
||||
'Luracast\Restler\UI\Forms::key'
|
||||
)
|
||||
);
|
||||
$twig->addFunction(
|
||||
new \Twig_SimpleFunction(
|
||||
'nav',
|
||||
'Luracast\Restler\UI\Nav::get'
|
||||
)
|
||||
);
|
||||
|
||||
$twig->registerUndefinedFunctionCallback(function ($name) {
|
||||
if (
|
||||
isset(HtmlFormat::$data[$name]) &&
|
||||
is_callable(HtmlFormat::$data[$name])
|
||||
) {
|
||||
return new \Twig_SimpleFunction(
|
||||
$name,
|
||||
HtmlFormat::$data[$name]
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
$template = $twig->loadTemplate(static::getViewFile());
|
||||
return $template->render($data);
|
||||
}
|
||||
|
||||
public static function handlebar(array $data, $debug = true)
|
||||
{
|
||||
return static::mustache($data, $debug);
|
||||
}
|
||||
|
||||
public static function mustache(array $data, $debug = true)
|
||||
{
|
||||
if (!class_exists('\Mustache_Engine', true))
|
||||
throw new RestException(
|
||||
500,
|
||||
'Mustache/Handlebar templates require mustache classes ' .
|
||||
'to be installed using `composer install`'
|
||||
);
|
||||
if (!isset($data['nav']))
|
||||
$data['nav'] = array_values(Nav::get());
|
||||
$options = array(
|
||||
'loader' => new \Mustache_Loader_FilesystemLoader(
|
||||
static::$viewPath,
|
||||
array('extension' => static::getViewExtension())
|
||||
),
|
||||
'helpers' => array(
|
||||
'form' => function ($text, \Mustache_LambdaHelper $m) {
|
||||
$params = explode(',', $m->render($text));
|
||||
return call_user_func_array(
|
||||
'Luracast\Restler\UI\Forms::get',
|
||||
$params
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
if (!$debug)
|
||||
$options['cache'] = static::$cacheDirectory;
|
||||
$m = new \Mustache_Engine($options);
|
||||
return $m->render(static::getViewFile(), $data);
|
||||
}
|
||||
|
||||
public static function php(array $data, $debug = true)
|
||||
{
|
||||
if (static::$view == 'debug')
|
||||
static::$viewPath = dirname(__DIR__) . '/views';
|
||||
$view = static::getViewFile(true);
|
||||
|
||||
if (!is_readable($view)) {
|
||||
throw new RestException(
|
||||
500,
|
||||
"view file `$view` is not readable. " .
|
||||
'Check for file presence and file permissions'
|
||||
);
|
||||
}
|
||||
|
||||
$path = static::$viewPath . DIRECTORY_SEPARATOR;
|
||||
$template = function ($view) use ($data, $path) {
|
||||
$form = function () {
|
||||
return call_user_func_array(
|
||||
'Luracast\Restler\UI\Forms::get',
|
||||
func_get_args()
|
||||
);
|
||||
};
|
||||
if (!isset($data['form']))
|
||||
$data['form'] = $form;
|
||||
$nav = function () {
|
||||
return call_user_func_array(
|
||||
'Luracast\Restler\UI\Nav::get',
|
||||
func_get_args()
|
||||
);
|
||||
};
|
||||
if (!isset($data['nav']))
|
||||
$data['nav'] = $nav;
|
||||
|
||||
$_ = function () use ($data, $path) {
|
||||
extract($data);
|
||||
$args = func_get_args();
|
||||
$task = array_shift($args);
|
||||
switch ($task) {
|
||||
case 'require':
|
||||
case 'include':
|
||||
$file = $path . $args[0];
|
||||
if (is_readable($file)) {
|
||||
if (
|
||||
isset($args[1]) &&
|
||||
($arrays = Util::nestedValue($data, $args[1]))
|
||||
) {
|
||||
$str = '';
|
||||
foreach ($arrays as $arr) {
|
||||
extract($arr);
|
||||
$str .= include $file;
|
||||
}
|
||||
return $str;
|
||||
} else {
|
||||
return include $file;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'if':
|
||||
if (count($args) < 2)
|
||||
$args[1] = '';
|
||||
if (count($args) < 3)
|
||||
$args[2] = '';
|
||||
return $args[0] ? $args[1] : $args[2];
|
||||
break;
|
||||
default:
|
||||
if (isset($data[$task]) && is_callable($data[$task]))
|
||||
return call_user_func_array($data[$task], $args);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
extract($data);
|
||||
return @include $view;
|
||||
};
|
||||
$value = $template($view);
|
||||
if (is_string($value))
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the given data in the format
|
||||
*
|
||||
* @param array $data resulting data that needs to
|
||||
* be encoded in the given format
|
||||
* @param boolean $humanReadable set to TRUE when restler
|
||||
* is not running in production mode.
|
||||
* Formatter has to make the encoded
|
||||
* output more human readable
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return string encoded string
|
||||
*/
|
||||
public function encode($data, $humanReadable = false)
|
||||
{
|
||||
if (!is_readable(static::$viewPath)) {
|
||||
throw new \Exception(
|
||||
'The views directory `'
|
||||
. self::$viewPath . '` should exist with read permission.'
|
||||
);
|
||||
}
|
||||
static::$data['basePath'] = dirname($_SERVER['SCRIPT_NAME']);
|
||||
static::$data['baseUrl'] = $this->restler->getBaseUrl();
|
||||
static::$data['currentPath'] = $this->restler->url;
|
||||
|
||||
try {
|
||||
$exception = $this->restler->exception;
|
||||
$success = is_null($exception);
|
||||
$error = $success ? null : $exception->getMessage();
|
||||
$data = array(
|
||||
'response' => Object::toArray($data),
|
||||
'stages' => $this->restler->getEvents(),
|
||||
'success' => $success,
|
||||
'error' => $error
|
||||
);
|
||||
$info = $data['api'] = $this->restler->apiMethodInfo;
|
||||
$metadata = Util::nestedValue(
|
||||
$this->restler, 'apiMethodInfo', 'metadata'
|
||||
);
|
||||
$view = $success ? 'view' : 'errorView';
|
||||
$value = false;
|
||||
if (static::$parseViewMetadata && isset($metadata[$view])) {
|
||||
if (is_array($metadata[$view])) {
|
||||
self::$view = $metadata[$view]['description'];
|
||||
$value = Util::nestedValue(
|
||||
$metadata[$view], 'properties', 'value'
|
||||
);
|
||||
} else {
|
||||
self::$view = $metadata[$view];
|
||||
}
|
||||
} elseif (!self::$view) {
|
||||
$file = static::$viewPath . '/' . $this->restler->url . '.' . static::getViewExtension();
|
||||
self::$view = static::$useSmartViews && is_readable($file)
|
||||
? $this->restler->url
|
||||
: static::$errorView;
|
||||
}
|
||||
if (
|
||||
isset($metadata['param'])
|
||||
&& (!$value || 0 === strpos($value, 'request'))
|
||||
) {
|
||||
$params = $metadata['param'];
|
||||
foreach ($params as $index => &$param) {
|
||||
$index = intval($index);
|
||||
if (is_numeric($index)) {
|
||||
$param['value'] = $this
|
||||
->restler
|
||||
->apiMethodInfo
|
||||
->parameters[$index];
|
||||
}
|
||||
}
|
||||
$data['request']['parameters'] = $params;
|
||||
}
|
||||
if ($value) {
|
||||
$data = Util::nestedValue($data, explode('.', $value));
|
||||
}
|
||||
$data += static::$data;
|
||||
if (false === ($i = strrpos(self::$view, '.'))) {
|
||||
$template = self::$template;
|
||||
} else {
|
||||
self::$template = $template = substr(self::$view, $i + 1);
|
||||
self::$view = substr(self::$view, 0, $i);
|
||||
}
|
||||
if (!static::$cacheDirectory) {
|
||||
static::$cacheDirectory = Defaults::$cacheDirectory . DIRECTORY_SEPARATOR . $template;
|
||||
if (!file_exists(static::$cacheDirectory)) {
|
||||
if (!mkdir(static::$cacheDirectory)) {
|
||||
throw new RestException(500, 'Unable to create cache directory `' . static::$cacheDirectory . '`');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (method_exists($class = get_called_class(), $template)) {
|
||||
return call_user_func("$class::$template", $data, $humanReadable);
|
||||
}
|
||||
throw new RestException(500, "Unsupported template system `$template`");
|
||||
} catch (Exception $e) {
|
||||
static::$parseViewMetadata = false;
|
||||
$this->reset();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getViewExtension()
|
||||
{
|
||||
return isset(static::$customTemplateExtensions[static::$template])
|
||||
? static::$customTemplateExtensions[static::$template]
|
||||
: static::$template;
|
||||
}
|
||||
|
||||
public static function getViewFile($fullPath = false, $includeExtension = true)
|
||||
{
|
||||
$v = $fullPath ? static::$viewPath . '/' : '';
|
||||
$v .= static::$view;
|
||||
if ($includeExtension)
|
||||
$v .= '.' . static::getViewExtension();
|
||||
return $v;
|
||||
}
|
||||
|
||||
private function reset()
|
||||
{
|
||||
static::$mime = 'text/html';
|
||||
static::$extension = 'html';
|
||||
static::$view = 'debug';
|
||||
static::$template = 'php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given data from the format
|
||||
*
|
||||
* @param string $data
|
||||
* data sent from client to
|
||||
* the api in the given format.
|
||||
*
|
||||
* @return array associative array of the parsed data
|
||||
*
|
||||
* @throws RestException
|
||||
*/
|
||||
public function decode($data)
|
||||
{
|
||||
throw new RestException(500, 'HtmlFormat is write only');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool false as HTML format is write only
|
||||
*/
|
||||
public function isReadable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MIME type => Extension mappings as an associative array
|
||||
*
|
||||
* @return array list of mime strings for the format
|
||||
* @example array('application/json'=>'json');
|
||||
*/
|
||||
public function getMIMEMap()
|
||||
{
|
||||
return array(
|
||||
static::$mime => static::$extension
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected MIME type
|
||||
*
|
||||
* @param string $mime MIME type
|
||||
*/
|
||||
public function setMIME($mime)
|
||||
{
|
||||
static::$mime = $mime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get selected MIME type
|
||||
*/
|
||||
public function getMIME()
|
||||
{
|
||||
return static::$mime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected file extension
|
||||
*
|
||||
* @return string file extension
|
||||
*/
|
||||
public function getExtension()
|
||||
{
|
||||
return static::$extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected file extension
|
||||
*
|
||||
* @param string $extension file extension
|
||||
*/
|
||||
public function setExtension($extension)
|
||||
{
|
||||
static::$extension = $extension;
|
||||
}
|
||||
}
|
||||
48
htdocs/includes/restler/Format/JsFormat.php
Normal file
48
htdocs/includes/restler/Format/JsFormat.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
/**
|
||||
* Javascript Object Notation Packaged in a method (JSONP)
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 JsFormat extends JsonFormat
|
||||
{
|
||||
const MIME = 'text/javascript';
|
||||
const EXTENSION = 'js';
|
||||
|
||||
public static $callbackMethodName = 'parseResponse';
|
||||
public static $callbackOverrideQueryString = 'callback';
|
||||
public static $includeHeaders = true;
|
||||
|
||||
public function encode($data, $human_readable = false)
|
||||
{
|
||||
$r = array();
|
||||
if (static::$includeHeaders) {
|
||||
$r['meta'] = array();
|
||||
foreach (headers_list() as $header) {
|
||||
list($h, $v) = explode(': ', $header, 2);
|
||||
$r['meta'][$h] = $v;
|
||||
}
|
||||
}
|
||||
$r['data'] = $data;
|
||||
if (isset($_GET[static::$callbackOverrideQueryString])) {
|
||||
static::$callbackMethodName
|
||||
= (string) $_GET[static::$callbackOverrideQueryString];
|
||||
}
|
||||
return static::$callbackMethodName . '('
|
||||
. parent::encode($r, $human_readable) . ');';
|
||||
}
|
||||
|
||||
public function isReadable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
210
htdocs/includes/restler/Format/JsonFormat.php
Normal file
210
htdocs/includes/restler/Format/JsonFormat.php
Normal file
@ -0,0 +1,210 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
use Luracast\Restler\Data\Object;
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
/**
|
||||
* Javascript Object Notation Format
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 JsonFormat extends Format
|
||||
{
|
||||
/**
|
||||
* @var boolean|null shim for json_encode option JSON_PRETTY_PRINT set
|
||||
* it to null to use smart defaults
|
||||
*/
|
||||
public static $prettyPrint = null;
|
||||
|
||||
/**
|
||||
* @var boolean|null shim for json_encode option JSON_UNESCAPED_SLASHES
|
||||
* set it to null to use smart defaults
|
||||
*/
|
||||
public static $unEscapedSlashes = null;
|
||||
|
||||
/**
|
||||
* @var boolean|null shim for json_encode JSON_UNESCAPED_UNICODE set it
|
||||
* to null to use smart defaults
|
||||
*/
|
||||
public static $unEscapedUnicode = null;
|
||||
|
||||
/**
|
||||
* @var boolean|null shim for json_decode JSON_BIGINT_AS_STRING set it to
|
||||
* null to
|
||||
* use smart defaults
|
||||
*/
|
||||
public static $bigIntAsString = null;
|
||||
|
||||
const MIME = 'application/json';
|
||||
const EXTENSION = 'json';
|
||||
|
||||
public function encode($data, $humanReadable = false)
|
||||
{
|
||||
if (!is_null(self::$prettyPrint)) {
|
||||
$humanReadable = self::$prettyPrint;
|
||||
}
|
||||
if (is_null(self::$unEscapedSlashes)) {
|
||||
self::$unEscapedSlashes = $humanReadable;
|
||||
}
|
||||
if (is_null(self::$unEscapedUnicode)) {
|
||||
self::$unEscapedUnicode = $this->charset == 'utf-8';
|
||||
}
|
||||
$options = 0;
|
||||
if ((PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) // PHP >= 5.4
|
||||
|| PHP_MAJOR_VERSION > 5 // PHP >= 6.0
|
||||
) {
|
||||
if ($humanReadable) $options |= JSON_PRETTY_PRINT;
|
||||
if (self::$unEscapedSlashes) $options |= JSON_UNESCAPED_SLASHES;
|
||||
if (self::$bigIntAsString) $options |= JSON_BIGINT_AS_STRING;
|
||||
if (self::$unEscapedUnicode) $options |= JSON_UNESCAPED_UNICODE;
|
||||
return json_encode(
|
||||
Object::toArray($data, true), $options
|
||||
);
|
||||
}
|
||||
|
||||
$result = json_encode(Object::toArray($data, true));
|
||||
if ($humanReadable) $result = $this->formatJson($result);
|
||||
if (self::$unEscapedUnicode) {
|
||||
$result = preg_replace_callback('/\\\u(\w\w\w\w)/',
|
||||
function($matches)
|
||||
{
|
||||
if (function_exists('mb_convert_encoding'))
|
||||
{
|
||||
return mb_convert_encoding(pack('H*', $matches[1]), 'UTF-8', 'UTF-16BE');
|
||||
}
|
||||
else
|
||||
{
|
||||
return iconv('UTF-16BE','UTF-8',pack('H*', $matches[1]));
|
||||
}
|
||||
}
|
||||
, $result);
|
||||
}
|
||||
if (self::$unEscapedSlashes) $result = str_replace('\/', '/', $result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function decode($data)
|
||||
{
|
||||
$options = 0;
|
||||
if (self::$bigIntAsString) {
|
||||
if ((PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) // PHP >= 5.4
|
||||
|| PHP_MAJOR_VERSION > 5 // PHP >= 6.0
|
||||
) {
|
||||
$options |= JSON_BIGINT_AS_STRING;
|
||||
} else {
|
||||
$data = preg_replace(
|
||||
'/:\s*(\-?\d+(\.\d+)?([e|E][\-|\+]\d+)?)/',
|
||||
': "$1"', $data
|
||||
);
|
||||
}
|
||||
}
|
||||
$decoded = json_decode($data, $options);
|
||||
if (function_exists('json_last_error')) {
|
||||
switch (json_last_error()) {
|
||||
case JSON_ERROR_NONE :
|
||||
return Object::toArray($decoded);
|
||||
break;
|
||||
case JSON_ERROR_DEPTH :
|
||||
$message = 'maximum stack depth exceeded';
|
||||
break;
|
||||
case JSON_ERROR_STATE_MISMATCH :
|
||||
$message = 'underflow or the modes mismatch';
|
||||
break;
|
||||
case JSON_ERROR_CTRL_CHAR :
|
||||
$message = 'unexpected control character found';
|
||||
break;
|
||||
case JSON_ERROR_SYNTAX :
|
||||
$message = 'malformed JSON';
|
||||
break;
|
||||
case JSON_ERROR_UTF8 :
|
||||
$message = 'malformed UTF-8 characters, possibly ' .
|
||||
'incorrectly encoded';
|
||||
break;
|
||||
default :
|
||||
$message = 'unknown error';
|
||||
break;
|
||||
}
|
||||
throw new RestException (400, 'Error parsing JSON, ' . $message);
|
||||
} elseif (strlen($data) && $decoded === null || $decoded === $data) {
|
||||
throw new RestException (400, 'Error parsing JSON');
|
||||
}
|
||||
|
||||
return Object::toArray($decoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretty print JSON string
|
||||
*
|
||||
* @param string $json
|
||||
*
|
||||
* @return string formatted json
|
||||
*/
|
||||
private function formatJson($json)
|
||||
{
|
||||
$tab = ' ';
|
||||
$newJson = '';
|
||||
$indentLevel = 0;
|
||||
$inString = false;
|
||||
$len = strlen($json);
|
||||
for ($c = 0; $c < $len; $c++) {
|
||||
$char = $json [$c];
|
||||
switch ($char) {
|
||||
case '{' :
|
||||
case '[' :
|
||||
if (!$inString) {
|
||||
$newJson .= $char . "\n" .
|
||||
str_repeat($tab, $indentLevel + 1);
|
||||
$indentLevel++;
|
||||
} else {
|
||||
$newJson .= $char;
|
||||
}
|
||||
break;
|
||||
case '}' :
|
||||
case ']' :
|
||||
if (!$inString) {
|
||||
$indentLevel--;
|
||||
$newJson .= "\n" .
|
||||
str_repeat($tab, $indentLevel) . $char;
|
||||
} else {
|
||||
$newJson .= $char;
|
||||
}
|
||||
break;
|
||||
case ',' :
|
||||
if (!$inString) {
|
||||
$newJson .= ",\n" .
|
||||
str_repeat($tab, $indentLevel);
|
||||
} else {
|
||||
$newJson .= $char;
|
||||
}
|
||||
break;
|
||||
case ':' :
|
||||
if (!$inString) {
|
||||
$newJson .= ': ';
|
||||
} else {
|
||||
$newJson .= $char;
|
||||
}
|
||||
break;
|
||||
case '"' :
|
||||
if ($c == 0) {
|
||||
$inString = true;
|
||||
} elseif ($c > 0 && $json [$c - 1] != '\\') {
|
||||
$inString = !$inString;
|
||||
}
|
||||
default :
|
||||
$newJson .= $char;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $newJson;
|
||||
}
|
||||
}
|
||||
|
||||
144
htdocs/includes/restler/Format/MultiFormat.php
Normal file
144
htdocs/includes/restler/Format/MultiFormat.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
/**
|
||||
* Describe the purpose of this class/interface/trait
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
abstract class MultiFormat implements iFormat
|
||||
{
|
||||
/**
|
||||
* override in the extending class
|
||||
*/
|
||||
const MIME = 'text/plain,text/html';
|
||||
/**
|
||||
* override in the extending class
|
||||
*/
|
||||
const EXTENSION = 'txt,html';
|
||||
|
||||
/**
|
||||
* @var string charset encoding defaults to UTF8
|
||||
*/
|
||||
protected $charset='utf-8';
|
||||
|
||||
public static $mime;
|
||||
public static $extension;
|
||||
|
||||
/**
|
||||
* Injected at runtime
|
||||
*
|
||||
* @var \Luracast\Restler\Restler
|
||||
*/
|
||||
public $restler;
|
||||
|
||||
/**
|
||||
* Get MIME type => Extension mappings as an associative array
|
||||
*
|
||||
* @return array list of mime strings for the format
|
||||
* @example array('application/json'=>'json');
|
||||
*/
|
||||
public function getMIMEMap()
|
||||
{
|
||||
$extensions = explode(',',static::EXTENSION);
|
||||
$mimes = explode(',',static::MIME);
|
||||
$count = max(count($extensions), count($mimes));
|
||||
$extensions += array_fill(0, $count, end($extensions));
|
||||
$mimes += array_fill(0, $count, end($mimes));
|
||||
return array_combine($mimes,$extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected MIME type
|
||||
*
|
||||
* @param string $mime
|
||||
* MIME type
|
||||
*/
|
||||
public function setMIME($mime)
|
||||
{
|
||||
static::$mime = $mime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content-Type field of the HTTP header can send a charset
|
||||
* parameter in the HTTP header to specify the character
|
||||
* encoding of the document.
|
||||
* This information is passed
|
||||
* here so that Format class can encode data accordingly
|
||||
* Format class may choose to ignore this and use its
|
||||
* default character set.
|
||||
*
|
||||
* @param string $charset
|
||||
* Example utf-8
|
||||
*/
|
||||
public function setCharset($charset)
|
||||
{
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content-Type accepted by the Format class
|
||||
*
|
||||
* @return string $charset Example utf-8
|
||||
*/
|
||||
public function getCharset()
|
||||
{
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get selected MIME type
|
||||
*/
|
||||
public function getMIME()
|
||||
{
|
||||
return static::$mime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected file extension
|
||||
*
|
||||
* @param string $extension
|
||||
* file extension
|
||||
*/
|
||||
public function setExtension($extension)
|
||||
{
|
||||
static::$extension = $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected file extension
|
||||
*
|
||||
* @return string file extension
|
||||
*/
|
||||
public function getExtension()
|
||||
{
|
||||
return static::$extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean is parsing the request supported?
|
||||
*/
|
||||
public function isReadable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean is composing response supported?
|
||||
*/
|
||||
public function isWritable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getExtension();
|
||||
}
|
||||
}
|
||||
|
||||
91
htdocs/includes/restler/Format/PlistFormat.php
Normal file
91
htdocs/includes/restler/Format/PlistFormat.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
use Luracast\Restler\Data\Object;
|
||||
use CFPropertyList\CFTypeDetector;
|
||||
use CFPropertyList\CFPropertyList;
|
||||
|
||||
/**
|
||||
* Plist Format for Restler Framework.
|
||||
* Plist is the native data exchange format for Apple iOS and Mac platform.
|
||||
* Use this format to talk to mac applications and iOS devices.
|
||||
* This class is capable of serving both xml plist and binary plist.
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 PlistFormat extends MultiFormat
|
||||
{
|
||||
/**
|
||||
* @var boolean set it to true binary plist is preferred
|
||||
*/
|
||||
public static $compact = null;
|
||||
const MIME = 'application/xml,application/x-plist';
|
||||
const EXTENSION = 'plist';
|
||||
|
||||
public function setMIME($mime)
|
||||
{
|
||||
static::$mime = $mime;
|
||||
static::$compact = $mime == 'application/x-plist';
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the given data in plist format
|
||||
*
|
||||
* @param array $data
|
||||
* resulting data that needs to
|
||||
* be encoded in plist format
|
||||
* @param boolean $humanReadable
|
||||
* set to true when restler
|
||||
* is not running in production mode. Formatter has to
|
||||
* make the encoded output more human readable
|
||||
*
|
||||
* @return string encoded string
|
||||
*/
|
||||
public function encode($data, $humanReadable = false)
|
||||
{
|
||||
//require_once 'CFPropertyList.php';
|
||||
if (!isset(self::$compact)) {
|
||||
self::$compact = !$humanReadable;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @var CFPropertyList
|
||||
*/
|
||||
$plist = new CFPropertyList ();
|
||||
$td = new CFTypeDetector ();
|
||||
$guessedStructure = $td->toCFType(
|
||||
Object::toArray($data)
|
||||
);
|
||||
$plist->add($guessedStructure);
|
||||
|
||||
return self::$compact
|
||||
? $plist->toBinary()
|
||||
: $plist->toXML(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given data from plist format
|
||||
*
|
||||
* @param string $data
|
||||
* data sent from client to
|
||||
* the api in the given format.
|
||||
*
|
||||
* @return array associative array of the parsed data
|
||||
*/
|
||||
public function decode($data)
|
||||
{
|
||||
//require_once 'CFPropertyList.php';
|
||||
$plist = new CFPropertyList ();
|
||||
$plist->parse($data);
|
||||
|
||||
return $plist->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
24
htdocs/includes/restler/Format/TsvFormat.php
Normal file
24
htdocs/includes/restler/Format/TsvFormat.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
/**
|
||||
* Tab Separated Value Format
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 TsvFormat extends CsvFormat
|
||||
{
|
||||
const MIME = 'text/csv';
|
||||
const EXTENSION = 'csv';
|
||||
public static $delimiter = "\t";
|
||||
public static $enclosure = '"';
|
||||
public static $escape = '\\';
|
||||
public static $haveHeaders = null;
|
||||
}
|
||||
145
htdocs/includes/restler/Format/UploadFormat.php
Normal file
145
htdocs/includes/restler/Format/UploadFormat.php
Normal file
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
/**
|
||||
* Support for Multi Part Form Data and File Uploads
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 UploadFormat extends Format
|
||||
{
|
||||
const MIME = 'multipart/form-data';
|
||||
const EXTENSION = 'post';
|
||||
public static $errors = array(
|
||||
0 => false,
|
||||
1 => "The uploaded file exceeds the maximum allowed size",
|
||||
2 => "The uploaded file exceeds the maximum allowed size",
|
||||
3 => "The uploaded file was only partially uploaded",
|
||||
4 => "No file was uploaded",
|
||||
6 => "Missing a temporary folder"
|
||||
);
|
||||
/**
|
||||
* use it if you need to restrict uploads based on file type
|
||||
* setting it as an empty array allows all file types
|
||||
* default is to allow only png and jpeg images
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $allowedMimeTypes = array('image/jpeg', 'image/png');
|
||||
/**
|
||||
* use it to restrict uploads based on file size
|
||||
* set it to 0 to allow all sizes
|
||||
* please note that it upload restrictions in the server
|
||||
* takes precedence so it has to be lower than or equal to that
|
||||
* default value is 1MB (1024x1024)bytes
|
||||
* usual value for the server is 8388608
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $maximumFileSize = 1048576;
|
||||
/**
|
||||
* Your own validation function for validating each uploaded file
|
||||
* it can return false or throw an exception for invalid file
|
||||
* use anonymous function / closure in PHP 5.3 and above
|
||||
* use function name in other cases
|
||||
*
|
||||
* @var Callable
|
||||
*/
|
||||
public static $customValidationFunction;
|
||||
/**
|
||||
* Since exceptions are triggered way before at the `get` stage
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $suppressExceptionsAsError = false;
|
||||
|
||||
protected static function checkFile(& $file, $doMimeCheck = false, $doSizeCheck = false)
|
||||
{
|
||||
try {
|
||||
if ($file['error']) {
|
||||
//server is throwing an error
|
||||
//assume that the error is due to maximum size limit
|
||||
throw new RestException($file['error'] > 5 ? 500 : 413, static::$errors[$file['error']]);
|
||||
}
|
||||
$typeElements = explode('/', $file['type']);
|
||||
$genericType = $typeElements[0].'/*';
|
||||
if (
|
||||
$doMimeCheck
|
||||
&& !(
|
||||
in_array($file['type'], self::$allowedMimeTypes)
|
||||
|| in_array($genericType, self::$allowedMimeTypes)
|
||||
)
|
||||
) {
|
||||
throw new RestException(403, "File type ({$file['type']}) is not supported.");
|
||||
}
|
||||
if ($doSizeCheck && $file['size'] > self::$maximumFileSize) {
|
||||
throw new RestException(413, "Uploaded file ({$file['name']}) is too big.");
|
||||
}
|
||||
if (self::$customValidationFunction) {
|
||||
if (!call_user_func(self::$customValidationFunction, $file)) {
|
||||
throw new RestException(403, "File ({$file['name']}) is not supported.");
|
||||
}
|
||||
}
|
||||
} catch (RestException $e) {
|
||||
if (static::$suppressExceptionsAsError) {
|
||||
$file['error'] = $e->getCode() == 413 ? 1 : 6;
|
||||
$file['exception'] = $e;
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function encode($data, $humanReadable = false)
|
||||
{
|
||||
throw new RestException(500, 'UploadFormat is read only');
|
||||
}
|
||||
|
||||
public function decode($data)
|
||||
{
|
||||
$doMimeCheck = !empty(self::$allowedMimeTypes);
|
||||
$doSizeCheck = self::$maximumFileSize ? TRUE : FALSE;
|
||||
//validate
|
||||
foreach ($_FILES as & $file) {
|
||||
if (is_array($file['error'])) {
|
||||
foreach ($file['error'] as $i => $error) {
|
||||
$innerFile = array();
|
||||
foreach ($file as $property => $value) {
|
||||
$innerFile[$property] = $value[$i];
|
||||
}
|
||||
if ($innerFile['name'])
|
||||
static::checkFile($innerFile, $doMimeCheck, $doSizeCheck);
|
||||
|
||||
if (isset($innerFile['exception'])) {
|
||||
$file['error'][$i] = $innerFile['error'];
|
||||
$file['exception'] = $innerFile['exception'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($file['name'])
|
||||
static::checkFile($file, $doMimeCheck, $doSizeCheck);
|
||||
if (isset($innerFile['exception'])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//sort file order if needed;
|
||||
return UrlEncodedFormat::decoderTypeFix($_FILES + $_POST);
|
||||
}
|
||||
|
||||
function isWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
58
htdocs/includes/restler/Format/UrlEncodedFormat.php
Normal file
58
htdocs/includes/restler/Format/UrlEncodedFormat.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
/**
|
||||
* URL Encoded String Format
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 UrlEncodedFormat extends Format
|
||||
{
|
||||
const MIME = 'application/x-www-form-urlencoded';
|
||||
const EXTENSION = 'post';
|
||||
|
||||
public function encode($data, $humanReadable = false)
|
||||
{
|
||||
return http_build_query(static::encoderTypeFix($data));
|
||||
}
|
||||
|
||||
public function decode($data)
|
||||
{
|
||||
parse_str($data, $r);
|
||||
return self::decoderTypeFix($r);
|
||||
}
|
||||
|
||||
public static function encoderTypeFix(array $data)
|
||||
{
|
||||
foreach ($data as $k => $v) {
|
||||
if (is_bool($v)) {
|
||||
$data[$k] = $v = $v ? 'true' : 'false';
|
||||
} elseif (is_array($v)) {
|
||||
$data[$k] = $v = static::decoderTypeFix($v);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
public static function decoderTypeFix(array $data)
|
||||
{
|
||||
foreach ($data as $k => $v) {
|
||||
if ($v === 'true' || $v === 'false') {
|
||||
$data[$k] = $v = $v === 'true';
|
||||
} elseif (is_array($v)) {
|
||||
$data[$k] = $v = static::decoderTypeFix($v);
|
||||
} elseif (empty($v) && $v != 0) {
|
||||
unset($data[$k]);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
348
htdocs/includes/restler/Format/XmlFormat.php
Normal file
348
htdocs/includes/restler/Format/XmlFormat.php
Normal file
@ -0,0 +1,348 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
use Luracast\Restler\Data\Object;
|
||||
use Luracast\Restler\RestException;
|
||||
use SimpleXMLElement;
|
||||
use XMLWriter;
|
||||
|
||||
/**
|
||||
* XML Markup Format for Restler Framework
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 XmlFormat extends Format
|
||||
{
|
||||
const MIME = 'application/xml';
|
||||
const EXTENSION = 'xml';
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Properties related to reading/parsing/decoding xml
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
public static $importSettingsFromXml = false;
|
||||
public static $parseAttributes = true;
|
||||
public static $parseNamespaces = true;
|
||||
public static $parseTextNodeAsProperty = true;
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Properties related to writing/encoding xml
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
public static $useTextNodeProperty = true;
|
||||
public static $useNamespaces = true;
|
||||
public static $cdataNames = array();
|
||||
|
||||
// ==================================================================
|
||||
//
|
||||
// Common Properties
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
public static $attributeNames = array();
|
||||
public static $textNodeName = 'text';
|
||||
public static $namespaces = array();
|
||||
public static $namespacedProperties = array();
|
||||
/**
|
||||
* Default name for the root node.
|
||||
*
|
||||
* @var string $rootNodeName
|
||||
*/
|
||||
public static $rootName = 'response';
|
||||
public static $defaultTagName = 'item';
|
||||
|
||||
/**
|
||||
* When you decode an XML its structure is copied to the static vars
|
||||
* we can use this function to echo them out and then copy paste inside
|
||||
* our service methods
|
||||
*
|
||||
* @return string PHP source code to reproduce the configuration
|
||||
*/
|
||||
public static function exportCurrentSettings()
|
||||
{
|
||||
$s = 'XmlFormat::$rootName = "' . (self::$rootName) . "\";\n";
|
||||
$s .= 'XmlFormat::$attributeNames = ' .
|
||||
(var_export(self::$attributeNames, true)) . ";\n";
|
||||
$s .= 'XmlFormat::$defaultTagName = "' .
|
||||
self::$defaultTagName . "\";\n";
|
||||
$s .= 'XmlFormat::$parseAttributes = ' .
|
||||
(self::$parseAttributes ? 'true' : 'false') . ";\n";
|
||||
$s .= 'XmlFormat::$parseNamespaces = ' .
|
||||
(self::$parseNamespaces ? 'true' : 'false') . ";\n";
|
||||
if (self::$parseNamespaces) {
|
||||
$s .= 'XmlFormat::$namespaces = ' .
|
||||
(var_export(self::$namespaces, true)) . ";\n";
|
||||
$s .= 'XmlFormat::$namespacedProperties = ' .
|
||||
(var_export(self::$namespacedProperties, true)) . ";\n";
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
public function encode($data, $humanReadable = false)
|
||||
{
|
||||
$data = Object::toArray($data);
|
||||
$xml = new XMLWriter();
|
||||
$xml->openMemory();
|
||||
$xml->startDocument('1.0', $this->charset);
|
||||
if ($humanReadable) {
|
||||
$xml->setIndent(true);
|
||||
$xml->setIndentString(' ');
|
||||
}
|
||||
static::$useNamespaces && isset(static::$namespacedProperties[static::$rootName])
|
||||
?
|
||||
$xml->startElementNs(
|
||||
static::$namespacedProperties[static::$rootName],
|
||||
static::$rootName,
|
||||
static::$namespaces[static::$namespacedProperties[static::$rootName]]
|
||||
)
|
||||
:
|
||||
$xml->startElement(static::$rootName);
|
||||
if (static::$useNamespaces) {
|
||||
foreach (static::$namespaces as $prefix => $ns) {
|
||||
if (isset(static::$namespacedProperties[static::$rootName])
|
||||
&& static::$namespacedProperties[static::$rootName] == $prefix
|
||||
)
|
||||
continue;
|
||||
$prefix = 'xmlns' . (empty($prefix) ? '' : ':' . $prefix);
|
||||
$xml->writeAttribute($prefix, $ns);
|
||||
}
|
||||
}
|
||||
$this->write($xml, $data, static::$rootName);
|
||||
$xml->endElement();
|
||||
return $xml->outputMemory();
|
||||
}
|
||||
|
||||
public function write(XMLWriter $xml, $data, $parent)
|
||||
{
|
||||
$text = array();
|
||||
if (is_array($data)) {
|
||||
if (static::$useTextNodeProperty && isset($data[static::$textNodeName])) {
|
||||
$text [] = $data[static::$textNodeName];
|
||||
unset($data[static::$textNodeName]);
|
||||
}
|
||||
$attributes = array_flip(static::$attributeNames);
|
||||
//make sure we deal with attributes first
|
||||
$temp = array();
|
||||
foreach ($data as $key => $value) {
|
||||
if (isset($attributes[$key])) {
|
||||
$temp[$key] = $data[$key];
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
$data = array_merge($temp, $data);
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_numeric($key)) {
|
||||
if (!is_array($value)) {
|
||||
$text [] = $value;
|
||||
continue;
|
||||
}
|
||||
$key = static::$defaultTagName;
|
||||
}
|
||||
$useNS = static::$useNamespaces
|
||||
&& !empty(static::$namespacedProperties[$key])
|
||||
&& false === strpos($key, ':');
|
||||
if (is_array($value)) {
|
||||
if ($value == array_values($value)) {
|
||||
//numeric array, create siblings
|
||||
foreach ($value as $v) {
|
||||
$useNS
|
||||
? $xml->startElementNs(
|
||||
static::$namespacedProperties[$key],
|
||||
$key,
|
||||
null
|
||||
)
|
||||
: $xml->startElement($key);
|
||||
$this->write($xml, $v, $key);
|
||||
$xml->endElement();
|
||||
}
|
||||
} else {
|
||||
$useNS
|
||||
? $xml->startElementNs(
|
||||
static::$namespacedProperties[$key],
|
||||
$key,
|
||||
null
|
||||
)
|
||||
: $xml->startElement($key);
|
||||
$this->write($xml, $value, $key);
|
||||
$xml->endElement();
|
||||
}
|
||||
continue;
|
||||
} elseif (is_bool($value)) {
|
||||
$value = $value ? 'true' : 'false';
|
||||
}
|
||||
if (isset($attributes[$key])) {
|
||||
$xml->writeAttribute($useNS ? static::$namespacedProperties[$key] . ':' . $key : $key, $value);
|
||||
} else {
|
||||
$useNS
|
||||
?
|
||||
$xml->startElementNs(
|
||||
static::$namespacedProperties[$key],
|
||||
$key,
|
||||
null
|
||||
)
|
||||
: $xml->startElement($key);
|
||||
$this->write($xml, $value, $key);
|
||||
$xml->endElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$text [] = (string)$data;
|
||||
}
|
||||
if (!empty($text)) {
|
||||
if (count($text) == 1) {
|
||||
in_array($parent, static::$cdataNames)
|
||||
? $xml->writeCdata(implode('', $text))
|
||||
: $xml->text(implode('', $text));
|
||||
} else {
|
||||
foreach ($text as $t) {
|
||||
$xml->writeElement(static::$textNodeName, $t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function decode($data)
|
||||
{
|
||||
try {
|
||||
if ($data == '') {
|
||||
return array();
|
||||
}
|
||||
libxml_use_internal_errors(true);
|
||||
$xml = simplexml_load_string($data,
|
||||
"SimpleXMLElement", LIBXML_NOBLANKS | LIBXML_NOCDATA | LIBXML_COMPACT);
|
||||
if (false === $xml) {
|
||||
$error = libxml_get_last_error();
|
||||
throw new RestException(400, 'Malformed XML. '
|
||||
. trim($error->message, "\r\n") . ' at line ' . $error->line);
|
||||
}
|
||||
libxml_clear_errors();
|
||||
if (static::$importSettingsFromXml) {
|
||||
static::$attributeNames = array();
|
||||
static::$namespacedProperties = array();
|
||||
static::$namespaces = array();
|
||||
static::$rootName = $xml->getName();
|
||||
$namespaces = $xml->getNamespaces();
|
||||
if (count($namespaces)) {
|
||||
$p = strpos($data, $xml->getName());
|
||||
if ($p && $data{$p - 1} == ':') {
|
||||
$s = strpos($data, '<') + 1;
|
||||
$prefix = substr($data, $s, $p - $s - 1);
|
||||
static::$namespacedProperties[static::$rootName] = $prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
$data = $this->read($xml);
|
||||
if (count($data) == 1 && isset($data[static::$textNodeName]))
|
||||
$data = $data[static::$textNodeName];
|
||||
return $data;
|
||||
} catch (\RuntimeException $e) {
|
||||
throw new RestException(400,
|
||||
"Error decoding request. " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function read(SimpleXMLElement $xml, $namespaces = null)
|
||||
{
|
||||
$r = array();
|
||||
$text = (string)$xml;
|
||||
|
||||
if (static::$parseAttributes) {
|
||||
$attributes = $xml->attributes();
|
||||
foreach ($attributes as $key => $value) {
|
||||
if (static::$importSettingsFromXml
|
||||
&& !in_array($key, static::$attributeNames)
|
||||
) {
|
||||
static::$attributeNames[] = $key;
|
||||
}
|
||||
$r[$key] = static::setType((string)$value);
|
||||
}
|
||||
}
|
||||
$children = $xml->children();
|
||||
foreach ($children as $key => $value) {
|
||||
if (isset($r[$key])) {
|
||||
if (is_array($r[$key])) {
|
||||
if ($r[$key] != array_values($r[$key]))
|
||||
$r[$key] = array($r[$key]);
|
||||
} else {
|
||||
$r[$key] = array($r[$key]);
|
||||
}
|
||||
$r[$key][] = $this->read($value, $namespaces);
|
||||
} else {
|
||||
$r[$key] = $this->read($value);
|
||||
}
|
||||
}
|
||||
|
||||
if (static::$parseNamespaces) {
|
||||
if (is_null($namespaces))
|
||||
$namespaces = $xml->getDocNamespaces(true);
|
||||
foreach ($namespaces as $prefix => $ns) {
|
||||
static::$namespaces[$prefix] = $ns;
|
||||
if (static::$parseAttributes) {
|
||||
$attributes = $xml->attributes($ns);
|
||||
foreach ($attributes as $key => $value) {
|
||||
if (isset($r[$key])) {
|
||||
$key = "{$prefix}:$key";
|
||||
}
|
||||
if (static::$importSettingsFromXml
|
||||
&& !in_array($key, static::$attributeNames)
|
||||
) {
|
||||
static::$namespacedProperties[$key] = $prefix;
|
||||
static::$attributeNames[] = $key;
|
||||
}
|
||||
$r[$key] = static::setType((string)$value);
|
||||
}
|
||||
}
|
||||
$children = $xml->children($ns);
|
||||
foreach ($children as $key => $value) {
|
||||
if (static::$importSettingsFromXml)
|
||||
static::$namespacedProperties[$key] = $prefix;
|
||||
if (isset($r[$key])) {
|
||||
if (is_array($r[$key])) {
|
||||
if ($r[$key] != array_values($r[$key]))
|
||||
$r[$key] = array($r[$key]);
|
||||
} else {
|
||||
$r[$key] = array($r[$key]);
|
||||
}
|
||||
$r[$key][] = $this->read($value, $namespaces);
|
||||
} else {
|
||||
$r[$key] = $this->read($value, $namespaces);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($text) && $text !== '0') {
|
||||
if (empty($r)) return null;
|
||||
} else {
|
||||
empty($r)
|
||||
? $r = static::setType($text)
|
||||
: (
|
||||
static::$parseTextNodeAsProperty
|
||||
? $r[static::$textNodeName] = static::setType($text)
|
||||
: $r[] = static::setType($text)
|
||||
);
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
public static function setType($value)
|
||||
{
|
||||
if (empty($value) && $value !== '0')
|
||||
return null;
|
||||
if ($value == 'true')
|
||||
return true;
|
||||
if ($value == 'false')
|
||||
return true;
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
36
htdocs/includes/restler/Format/YamlFormat.php
Normal file
36
htdocs/includes/restler/Format/YamlFormat.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Luracast\Restler\Data\Object;
|
||||
|
||||
/**
|
||||
* YAML Format for Restler Framework
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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 YamlFormat extends Format
|
||||
{
|
||||
const MIME = 'text/plain';
|
||||
const EXTENSION = 'yaml';
|
||||
|
||||
public function encode($data, $humanReadable = false)
|
||||
{
|
||||
// require_once 'sfyaml.php';
|
||||
return @Yaml::dump(Object::toArray($data));
|
||||
}
|
||||
|
||||
public function decode($data)
|
||||
{
|
||||
// require_once 'sfyaml.php';
|
||||
return Yaml::parse($data);
|
||||
}
|
||||
}
|
||||
|
||||
30
htdocs/includes/restler/Format/iDecodeStream.php
Normal file
30
htdocs/includes/restler/Format/iDecodeStream.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
/**
|
||||
* Interface for creating formats that accept steams for decoding
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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
|
||||
*/
|
||||
interface iDecodeStream
|
||||
{
|
||||
|
||||
/**
|
||||
* Decode the given data stream
|
||||
*
|
||||
* @param string $stream A stream resource with data
|
||||
* sent from client to the api
|
||||
* in the given format.
|
||||
*
|
||||
* @return array associative array of the parsed data
|
||||
*/
|
||||
public function decodeStream($stream);
|
||||
|
||||
}
|
||||
109
htdocs/includes/restler/Format/iFormat.php
Normal file
109
htdocs/includes/restler/Format/iFormat.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\Format;
|
||||
|
||||
/**
|
||||
* Interface for creating custom data formats
|
||||
* like xml, json, yaml, amf etc
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage format
|
||||
* @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
|
||||
*/
|
||||
interface iFormat
|
||||
{
|
||||
/**
|
||||
* Get MIME type => Extension mappings as an associative array
|
||||
*
|
||||
* @return array list of mime strings for the format
|
||||
* @example array('application/json'=>'json');
|
||||
*/
|
||||
public function getMIMEMap();
|
||||
|
||||
/**
|
||||
* Set the selected MIME type
|
||||
*
|
||||
* @param string $mime
|
||||
* MIME type
|
||||
*/
|
||||
public function setMIME($mime);
|
||||
|
||||
/**
|
||||
* Content-Type field of the HTTP header can send a charset
|
||||
* parameter in the HTTP header to specify the character
|
||||
* encoding of the document.
|
||||
* This information is passed
|
||||
* here so that Format class can encode data accordingly
|
||||
* Format class may choose to ignore this and use its
|
||||
* default character set.
|
||||
*
|
||||
* @param string $charset
|
||||
* Example utf-8
|
||||
*/
|
||||
public function setCharset($charset);
|
||||
|
||||
/**
|
||||
* Content-Type accepted by the Format class
|
||||
*
|
||||
* @return string $charset Example utf-8
|
||||
*/
|
||||
public function getCharset();
|
||||
|
||||
/**
|
||||
* Get selected MIME type
|
||||
*/
|
||||
public function getMIME();
|
||||
|
||||
/**
|
||||
* Set the selected file extension
|
||||
*
|
||||
* @param string $extension
|
||||
* file extension
|
||||
*/
|
||||
public function setExtension($extension);
|
||||
|
||||
/**
|
||||
* Get the selected file extension
|
||||
*
|
||||
* @return string file extension
|
||||
*/
|
||||
public function getExtension();
|
||||
|
||||
/**
|
||||
* Encode the given data in the format
|
||||
*
|
||||
* @param array $data
|
||||
* resulting data that needs to
|
||||
* be encoded in the given format
|
||||
* @param boolean $humanReadable
|
||||
* set to TRUE when restler
|
||||
* is not running in production mode. Formatter has to
|
||||
* make the encoded output more human readable
|
||||
* @return string encoded string
|
||||
*/
|
||||
public function encode($data, $humanReadable = false);
|
||||
|
||||
/**
|
||||
* Decode the given data from the format
|
||||
*
|
||||
* @param string $data
|
||||
* data sent from client to
|
||||
* the api in the given format.
|
||||
* @return array associative array of the parsed data
|
||||
*/
|
||||
public function decode($data);
|
||||
|
||||
/**
|
||||
* @return boolean is parsing the request supported?
|
||||
*/
|
||||
public function isReadable();
|
||||
|
||||
/**
|
||||
* @return boolean is composing response supported?
|
||||
*/
|
||||
public function isWritable();
|
||||
}
|
||||
|
||||
129
htdocs/includes/restler/HumanReadableCache.php
Normal file
129
htdocs/includes/restler/HumanReadableCache.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
/**
|
||||
* Default Cache that writes/reads human readable files for caching purpose
|
||||
*
|
||||
* @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 HumanReadableCache implements iCache
|
||||
{
|
||||
/**
|
||||
* @var string path of the folder to hold cache files
|
||||
*/
|
||||
public static $cacheDir;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (is_null(self::$cacheDir)) {
|
||||
self::$cacheDir = Defaults::$cacheDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* store data in the cache
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $data
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return boolean true if successful
|
||||
*/
|
||||
public function set($name, $data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$s = '$o = array();' . PHP_EOL . PHP_EOL;
|
||||
$s .= '// ** THIS IS AN AUTO GENERATED FILE.'
|
||||
. ' DO NOT EDIT MANUALLY ** ';
|
||||
foreach ($data as $key => $value) {
|
||||
$s .= PHP_EOL . PHP_EOL .
|
||||
"//==================== $key ===================="
|
||||
. PHP_EOL . PHP_EOL;
|
||||
if (is_array($value)) {
|
||||
$s .= '$o[\'' . $key . '\'] = array();';
|
||||
foreach ($value as $ke => $va) {
|
||||
$s .= PHP_EOL . PHP_EOL . "//==== $key $ke ===="
|
||||
. PHP_EOL . PHP_EOL;
|
||||
$s .= '$o[\'' . $key . '\'][\'' . $ke . '\'] = ' .
|
||||
str_replace(' ', ' ',
|
||||
var_export($va, true)) . ';';
|
||||
}
|
||||
} else {
|
||||
$s .= '$o[\'' . $key . '\'] = '
|
||||
. var_export($value, true) . ';';
|
||||
}
|
||||
}
|
||||
$s .= PHP_EOL . 'return $o;';
|
||||
} else {
|
||||
$s = 'return ' . var_export($data, true) . ';';
|
||||
}
|
||||
$file = $this->_file($name);
|
||||
$r = @file_put_contents($file, "<?php $s");
|
||||
@chmod($file, 0777);
|
||||
if ($r === false) {
|
||||
$this->throwException();
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve data from the cache
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $ignoreErrors
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $ignoreErrors = false)
|
||||
{
|
||||
$file = $this->_file($name);
|
||||
if (file_exists($file)) {
|
||||
return include($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* delete data from the cache
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $ignoreErrors
|
||||
*
|
||||
* @return boolean true if successful
|
||||
*/
|
||||
public function clear($name, $ignoreErrors = false)
|
||||
{
|
||||
return @unlink($this->_file($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the given name is cached
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return boolean true if cached
|
||||
*/
|
||||
public function isCached($name)
|
||||
{
|
||||
return file_exists($this->_file($name));
|
||||
}
|
||||
|
||||
private function _file($name)
|
||||
{
|
||||
return self::$cacheDir . '/' . $name . '.php';
|
||||
}
|
||||
|
||||
private function throwException()
|
||||
{
|
||||
throw new \Exception(
|
||||
'The cache directory `'
|
||||
. self::$cacheDir . '` should exist with write permission.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
8
htdocs/includes/restler/README.md
Normal file
8
htdocs/includes/restler/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
Luracast Restler Framework
|
||||
==========================
|
||||
|
||||
Restler is a simple and effective multi-format Web API Server written in PHP.
|
||||
|
||||
This repository contains just the framework files for installing the framework core using composer require statements.
|
||||
|
||||
For more information, usage examples, pull requests, and issues go to the [Main Repository](https://github.com/Luracast/Restler)
|
||||
53
htdocs/includes/restler/Redirect.php
Normal file
53
htdocs/includes/restler/Redirect.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
use Luracast\Restler\Format\JsonFormat;
|
||||
|
||||
/**
|
||||
* Static class for handling redirection
|
||||
*
|
||||
* @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 Redirect
|
||||
{
|
||||
/**
|
||||
* Redirect to given url
|
||||
*
|
||||
* @param string $url relative path or full url
|
||||
* @param array $params associative array of query parameters
|
||||
* @param array $flashData associative array of properties to be set in $_SESSION for one time use
|
||||
* @param int $status http status code to send the response with ideally 301 or 302
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function to($url, array $params = array(), array $flashData = array(), $status = 302)
|
||||
{
|
||||
$url = ltrim($url, '/');
|
||||
/** @var $r Restler */
|
||||
$r = Scope::get('Restler');
|
||||
$base = $r->getBaseUrl() . '/';
|
||||
if (0 !== strpos($url, 'http'))
|
||||
$url = $base . $url;
|
||||
if (!empty($flashData) || $base . $r->url !== $url || Util::getRequestMethod() != 'GET') {
|
||||
if ($r->responseFormat instanceof JsonFormat)
|
||||
return array('redirect' => $url);
|
||||
if (!empty($params)) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
Flash::set($flashData);
|
||||
header(
|
||||
"{$_SERVER['SERVER_PROTOCOL']} $status " .
|
||||
(isset(RestException::$codes[$status]) ? RestException::$codes[$status] : '')
|
||||
);
|
||||
header("Location: $url");
|
||||
die('');
|
||||
}
|
||||
return array();
|
||||
}
|
||||
}
|
||||
1006
htdocs/includes/restler/Resources.php
Normal file
1006
htdocs/includes/restler/Resources.php
Normal file
File diff suppressed because it is too large
Load Diff
138
htdocs/includes/restler/RestException.php
Normal file
138
htdocs/includes/restler/RestException.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Special Exception for raising API errors
|
||||
* that can be used in API methods
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage exception
|
||||
* @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/
|
||||
*/
|
||||
|
||||
class RestException extends Exception
|
||||
{
|
||||
/**
|
||||
* HTTP status codes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $codes = array(
|
||||
100 => 'Continue',
|
||||
101 => 'Switching Protocols',
|
||||
200 => 'OK',
|
||||
201 => 'Created',
|
||||
202 => 'Accepted',
|
||||
203 => 'Non-Authoritative Information',
|
||||
204 => 'No Content',
|
||||
205 => 'Reset Content',
|
||||
206 => 'Partial Content',
|
||||
300 => 'Multiple Choices',
|
||||
301 => 'Moved Permanently',
|
||||
302 => 'Found',
|
||||
303 => 'See Other',
|
||||
304 => 'Not Modified',
|
||||
305 => 'Use Proxy',
|
||||
306 => '(Unused)',
|
||||
307 => 'Temporary Redirect',
|
||||
400 => 'Bad Request',
|
||||
401 => 'Unauthorized',
|
||||
402 => 'Payment Required',
|
||||
403 => 'Forbidden',
|
||||
404 => 'Not Found',
|
||||
405 => 'Method Not Allowed',
|
||||
406 => 'Not Acceptable',
|
||||
407 => 'Proxy Authentication Required',
|
||||
408 => 'Request Timeout',
|
||||
409 => 'Conflict',
|
||||
410 => 'Gone',
|
||||
411 => 'Length Required',
|
||||
412 => 'Precondition Failed',
|
||||
413 => 'Request Entity Too Large',
|
||||
414 => 'Request-URI Too Long',
|
||||
415 => 'Unsupported Media Type',
|
||||
416 => 'Requested Range Not Satisfiable',
|
||||
417 => 'Expectation Failed',
|
||||
429 => 'Too Many Requests', //still in draft but used for rate limiting
|
||||
500 => 'Internal Server Error',
|
||||
501 => 'Not Implemented',
|
||||
502 => 'Bad Gateway',
|
||||
503 => 'Service Unavailable',
|
||||
504 => 'Gateway Timeout',
|
||||
505 => 'HTTP Version Not Supported'
|
||||
);
|
||||
private $details;
|
||||
private $stage;
|
||||
|
||||
/**
|
||||
* @param string $httpStatusCode http status code
|
||||
* @param string|null $errorMessage error message
|
||||
* @param array $details any extra detail about the exception
|
||||
* @param Exception $previous previous exception if any
|
||||
*/
|
||||
public function __construct($httpStatusCode, $errorMessage = null, array $details = array(), Exception $previous = null)
|
||||
{
|
||||
$events = Scope::get('Restler')->getEvents();
|
||||
if(count($events)<= 1){
|
||||
$this->stage = 'setup';
|
||||
} else {
|
||||
$this->stage = $previous ? $events[count($events)-2] : end($events);
|
||||
}
|
||||
$this->details = $details;
|
||||
parent::__construct($errorMessage, $httpStatusCode, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra details about the exception
|
||||
*
|
||||
* @return array details array
|
||||
*/
|
||||
public function getDetails()
|
||||
{
|
||||
return $this->details;
|
||||
}
|
||||
|
||||
public function getStage()
|
||||
{
|
||||
return $this->stage;
|
||||
}
|
||||
|
||||
public function getStages()
|
||||
{
|
||||
$e = Scope::get('Restler')->getEvents();
|
||||
$i = array_search($this->stage, $e);
|
||||
return array(
|
||||
'success' => array_slice($e, 0, $i),
|
||||
'failure' => array_slice($e, $i),
|
||||
);
|
||||
}
|
||||
|
||||
public function getErrorMessage()
|
||||
{
|
||||
$statusCode = $this->getCode();
|
||||
$message = $this->getMessage();
|
||||
if (isset(RestException::$codes[$statusCode])) {
|
||||
$message = RestException::$codes[$statusCode] .
|
||||
(empty($message) ? '' : ': ' . $message);
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
public function getSource()
|
||||
{
|
||||
$e = $this;
|
||||
while ($e->getPrevious()) {
|
||||
$e = $e->getPrevious();
|
||||
}
|
||||
return basename($e->getFile()) . ':'
|
||||
. $e->getLine() . ' at '
|
||||
. $this->getStage() . ' stage';
|
||||
}
|
||||
}
|
||||
|
||||
1451
htdocs/includes/restler/Restler.php
Normal file
1451
htdocs/includes/restler/Restler.php
Normal file
File diff suppressed because it is too large
Load Diff
696
htdocs/includes/restler/Routes.php
Normal file
696
htdocs/includes/restler/Routes.php
Normal file
@ -0,0 +1,696 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
use Luracast\Restler\Data\ApiMethodInfo;
|
||||
use Luracast\Restler\Data\String;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Router class that routes the urls to api methods along with parameters
|
||||
*
|
||||
* @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 Routes
|
||||
{
|
||||
public static $prefixingParameterNames = array(
|
||||
'id'
|
||||
);
|
||||
protected static $routes = array();
|
||||
|
||||
protected static $models = array();
|
||||
|
||||
/**
|
||||
* Route the public and protected methods of an Api class
|
||||
*
|
||||
* @param string $className
|
||||
* @param string $resourcePath
|
||||
* @param int $version
|
||||
*
|
||||
* @throws RestException
|
||||
*/
|
||||
public static function addAPIClass($className, $resourcePath = '', $version = 1)
|
||||
{
|
||||
|
||||
/*
|
||||
* Mapping Rules
|
||||
* =============
|
||||
*
|
||||
* - Optional parameters should not be mapped to URL
|
||||
* - If a required parameter is of primitive type
|
||||
* - If one of the self::$prefixingParameterNames
|
||||
* - Map it to URL
|
||||
* - Else If request method is POST/PUT/PATCH
|
||||
* - Map it to body
|
||||
* - Else If request method is GET/DELETE
|
||||
* - Map it to body
|
||||
* - If a required parameter is not primitive type
|
||||
* - Do not include it in URL
|
||||
*/
|
||||
$class = new ReflectionClass($className);
|
||||
try {
|
||||
$classMetadata = CommentParser::parse($class->getDocComment());
|
||||
} catch (Exception $e) {
|
||||
throw new RestException(500, "Error while parsing comments of `$className` class. " . $e->getMessage());
|
||||
}
|
||||
$classMetadata['scope'] = $scope = static::scope($class);
|
||||
$methods = $class->getMethods(ReflectionMethod::IS_PUBLIC +
|
||||
ReflectionMethod::IS_PROTECTED);
|
||||
foreach ($methods as $method) {
|
||||
$methodUrl = strtolower($method->getName());
|
||||
//method name should not begin with _
|
||||
if ($methodUrl{0} == '_') {
|
||||
continue;
|
||||
}
|
||||
$doc = $method->getDocComment();
|
||||
|
||||
try {
|
||||
$metadata = CommentParser::parse($doc) + $classMetadata;
|
||||
} catch (Exception $e) {
|
||||
throw new RestException(500, "Error while parsing comments of `{$className}::{$method->getName()}` method. " . $e->getMessage());
|
||||
}
|
||||
//@access should not be private
|
||||
if (isset($metadata['access'])
|
||||
&& $metadata['access'] == 'private'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$arguments = array();
|
||||
$defaults = array();
|
||||
$params = $method->getParameters();
|
||||
$position = 0;
|
||||
$pathParams = array();
|
||||
$allowAmbiguity
|
||||
= (isset($metadata['smart-auto-routing'])
|
||||
&& $metadata['smart-auto-routing'] != 'true')
|
||||
|| !Defaults::$smartAutoRouting;
|
||||
$metadata['resourcePath'] = $resourcePath;
|
||||
if (isset($classMetadata['description'])) {
|
||||
$metadata['classDescription'] = $classMetadata['description'];
|
||||
}
|
||||
if (isset($classMetadata['classLongDescription'])) {
|
||||
$metadata['classLongDescription']
|
||||
= $classMetadata['longDescription'];
|
||||
}
|
||||
if (!isset($metadata['param'])) {
|
||||
$metadata['param'] = array();
|
||||
}
|
||||
if (isset($metadata['return']['type'])) {
|
||||
if ($qualified = Scope::resolve($metadata['return']['type'], $scope))
|
||||
list($metadata['return']['type'], $metadata['return']['children']) =
|
||||
static::getTypeAndModel(new ReflectionClass($qualified), $scope);
|
||||
} else {
|
||||
//assume return type is array
|
||||
$metadata['return']['type'] = 'array';
|
||||
}
|
||||
foreach ($params as $param) {
|
||||
$children = array();
|
||||
$type =
|
||||
$param->isArray() ? 'array' : $param->getClass();
|
||||
$arguments[$param->getName()] = $position;
|
||||
$defaults[$position] = $param->isDefaultValueAvailable() ?
|
||||
$param->getDefaultValue() : null;
|
||||
if (!isset($metadata['param'][$position])) {
|
||||
$metadata['param'][$position] = array();
|
||||
}
|
||||
$m = & $metadata ['param'] [$position];
|
||||
$m ['name'] = $param->getName();
|
||||
if (empty($m['label']))
|
||||
$m['label'] = static::label($m['name']);
|
||||
if (is_null($type) && isset($m['type'])) {
|
||||
$type = $m['type'];
|
||||
}
|
||||
if ($m['name'] == 'email' && empty($m[CommentParser::$embeddedDataName]['type']) && $type == 'string')
|
||||
$m[CommentParser::$embeddedDataName]['type'] = 'email';
|
||||
$m ['default'] = $defaults [$position];
|
||||
$m ['required'] = !$param->isOptional();
|
||||
$contentType = Util::nestedValue(
|
||||
$m,
|
||||
CommentParser::$embeddedDataName,
|
||||
'type'
|
||||
);
|
||||
if ($contentType && $qualified = Scope::resolve($contentType, $scope)) {
|
||||
list($m[CommentParser::$embeddedDataName]['type'], $children) = static::getTypeAndModel(
|
||||
new ReflectionClass($qualified), $scope
|
||||
);
|
||||
}
|
||||
if ($type instanceof ReflectionClass) {
|
||||
list($type, $children) = static::getTypeAndModel($type, $scope);
|
||||
} elseif ($type && is_string($type) && $qualified = Scope::resolve($type, $scope)) {
|
||||
list($type, $children)
|
||||
= static::getTypeAndModel(new ReflectionClass($qualified), $scope);
|
||||
}
|
||||
if (isset($type)) {
|
||||
$m['type'] = $type;
|
||||
}
|
||||
$m['children'] = $children;
|
||||
|
||||
if ($m['name'] == Defaults::$fullRequestDataName) {
|
||||
$from = 'body';
|
||||
if (!isset($m['type'])) {
|
||||
$type = $m['type'] = 'array';
|
||||
}
|
||||
|
||||
} elseif (isset($m[CommentParser::$embeddedDataName]['from'])) {
|
||||
$from = $m[CommentParser::$embeddedDataName]['from'];
|
||||
} else {
|
||||
if ((isset($type) && Util::isObjectOrArray($type))
|
||||
) {
|
||||
$from = 'body';
|
||||
if (!isset($type)) {
|
||||
$type = $m['type'] = 'array';
|
||||
}
|
||||
} elseif ($m['required'] && in_array($m['name'], static::$prefixingParameterNames)) {
|
||||
$from = 'path';
|
||||
} else {
|
||||
$from = 'body';
|
||||
}
|
||||
}
|
||||
$m[CommentParser::$embeddedDataName]['from'] = $from;
|
||||
if (!isset($m['type'])) {
|
||||
$type = $m['type'] = static::type($defaults[$position]);
|
||||
}
|
||||
|
||||
if ($allowAmbiguity || $from == 'path') {
|
||||
$pathParams [] = $position;
|
||||
}
|
||||
$position++;
|
||||
}
|
||||
$accessLevel = 0;
|
||||
if ($method->isProtected()) {
|
||||
$accessLevel = 3;
|
||||
} elseif (isset($metadata['access'])) {
|
||||
if ($metadata['access'] == 'protected') {
|
||||
$accessLevel = 2;
|
||||
} elseif ($metadata['access'] == 'hybrid') {
|
||||
$accessLevel = 1;
|
||||
}
|
||||
} elseif (isset($metadata['protected'])) {
|
||||
$accessLevel = 2;
|
||||
}
|
||||
/*
|
||||
echo " access level $accessLevel for $className::"
|
||||
.$method->getName().$method->isProtected().PHP_EOL;
|
||||
*/
|
||||
|
||||
// take note of the order
|
||||
$call = array(
|
||||
'url' => null,
|
||||
'className' => $className,
|
||||
'path' => rtrim($resourcePath, '/'),
|
||||
'methodName' => $method->getName(),
|
||||
'arguments' => $arguments,
|
||||
'defaults' => $defaults,
|
||||
'metadata' => $metadata,
|
||||
'accessLevel' => $accessLevel,
|
||||
);
|
||||
// if manual route
|
||||
if (preg_match_all(
|
||||
'/@url\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)'
|
||||
. '[ \t]*\/?(\S*)/s',
|
||||
$doc, $matches, PREG_SET_ORDER
|
||||
)
|
||||
) {
|
||||
foreach ($matches as $match) {
|
||||
$httpMethod = $match[1];
|
||||
$url = rtrim($resourcePath . $match[2], '/');
|
||||
//deep copy the call, as it may change for each @url
|
||||
$copy = unserialize(serialize($call));
|
||||
foreach ($copy['metadata']['param'] as $i => $p) {
|
||||
$inPath =
|
||||
strpos($url, '{' . $p['name'] . '}') ||
|
||||
strpos($url, ':' . $p['name']);
|
||||
if ($inPath) {
|
||||
$copy['metadata']['param'][$i][CommentParser::$embeddedDataName]['from'] = 'path';
|
||||
} elseif ($httpMethod == 'GET' || $httpMethod == 'DELETE') {
|
||||
$copy['metadata']['param'][$i][CommentParser::$embeddedDataName]['from'] = 'query';
|
||||
} elseif ($p[CommentParser::$embeddedDataName]['from'] == 'path') {
|
||||
$copy['metadata']['param'][$i][CommentParser::$embeddedDataName]['from'] = 'body';
|
||||
}
|
||||
}
|
||||
$url = preg_replace_callback('/{[^}]+}|:[^\/]+/',
|
||||
function ($matches) use ($call) {
|
||||
$match = trim($matches[0], '{}:');
|
||||
$index = $call['arguments'][$match];
|
||||
return '{' .
|
||||
Routes::typeChar(isset(
|
||||
$call['metadata']['param'][$index]['type'])
|
||||
? $call['metadata']['param'][$index]['type']
|
||||
: null)
|
||||
. $index . '}';
|
||||
}, $url);
|
||||
static::addPath($url, $copy, $httpMethod, $version);
|
||||
}
|
||||
//if auto route enabled, do so
|
||||
} elseif (Defaults::$autoRoutingEnabled) {
|
||||
// no configuration found so use convention
|
||||
if (preg_match_all(
|
||||
'/^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/i',
|
||||
$methodUrl, $matches)
|
||||
) {
|
||||
$httpMethod = strtoupper($matches[0][0]);
|
||||
$methodUrl = substr($methodUrl, strlen($httpMethod));
|
||||
} else {
|
||||
$httpMethod = 'GET';
|
||||
}
|
||||
if ($methodUrl == 'index') {
|
||||
$methodUrl = '';
|
||||
}
|
||||
$url = empty($methodUrl) ? rtrim($resourcePath, '/')
|
||||
: $resourcePath . $methodUrl;
|
||||
$lastPathParam = array_keys($pathParams);
|
||||
$lastPathParam = end($lastPathParam);
|
||||
for ($position = 0; $position < count($params); $position++) {
|
||||
$from = $metadata['param'][$position][CommentParser::$embeddedDataName]['from'];
|
||||
if ($from == 'body' && ($httpMethod == 'GET' ||
|
||||
$httpMethod == 'DELETE')
|
||||
) {
|
||||
$call['metadata']['param'][$position][CommentParser::$embeddedDataName]['from']
|
||||
= 'query';
|
||||
}
|
||||
}
|
||||
if (empty($pathParams) || $allowAmbiguity) {
|
||||
static::addPath($url, $call, $httpMethod, $version);
|
||||
}
|
||||
foreach ($pathParams as $position) {
|
||||
if (!empty($url))
|
||||
$url .= '/';
|
||||
$url .= '{' .
|
||||
static::typeChar(isset($call['metadata']['param'][$position]['type'])
|
||||
? $call['metadata']['param'][$position]['type']
|
||||
: null)
|
||||
. $position . '}';
|
||||
if ($allowAmbiguity || $position == $lastPathParam) {
|
||||
static::addPath($url, $call, $httpMethod, $version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
public static function typeChar($type = null)
|
||||
{
|
||||
if (!$type) {
|
||||
return 's';
|
||||
}
|
||||
switch ($type{0}) {
|
||||
case 'i':
|
||||
case 'f':
|
||||
return 'n';
|
||||
}
|
||||
return 's';
|
||||
}
|
||||
|
||||
protected static function addPath($path, array $call,
|
||||
$httpMethod = 'GET', $version = 1)
|
||||
{
|
||||
$call['url'] = preg_replace_callback(
|
||||
"/\{\S(\d+)\}/",
|
||||
function ($matches) use ($call) {
|
||||
return '{' .
|
||||
$call['metadata']['param'][$matches[1]]['name'] . '}';
|
||||
},
|
||||
$path
|
||||
);
|
||||
//check for wildcard routes
|
||||
if (substr($path, -1, 1) == '*') {
|
||||
$path = rtrim($path, '/*');
|
||||
static::$routes["v$version"]['*'][$path][$httpMethod] = $call;
|
||||
} else {
|
||||
static::$routes["v$version"][$path][$httpMethod] = $call;
|
||||
//create an alias with index if the method name is index
|
||||
if ($call['methodName'] == 'index')
|
||||
static::$routes["v$version"][ltrim("$path/index", '/')][$httpMethod] = $call;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the api method for the given url and http method
|
||||
*
|
||||
* @param string $path Requested url path
|
||||
* @param string $httpMethod GET|POST|PUT|PATCH|DELETE etc
|
||||
* @param int $version Api Version number
|
||||
* @param array $data Data collected from the request
|
||||
*
|
||||
* @throws RestException
|
||||
* @return ApiMethodInfo
|
||||
*/
|
||||
public static function find($path, $httpMethod,
|
||||
$version = 1, array $data = array())
|
||||
{
|
||||
$p = Util::nestedValue(static::$routes, "v$version");
|
||||
if (!$p) {
|
||||
throw new RestException(
|
||||
404,
|
||||
$version == 1 ? '' : "Version $version is not supported"
|
||||
);
|
||||
}
|
||||
$status = 404;
|
||||
$message = null;
|
||||
$methods = array();
|
||||
if (isset($p[$path][$httpMethod])) {
|
||||
//================== static routes ==========================
|
||||
return static::populate($p[$path][$httpMethod], $data);
|
||||
} elseif (isset($p['*'])) {
|
||||
//================== wildcard routes ========================
|
||||
uksort($p['*'], function ($a, $b) {
|
||||
return strlen($b) - strlen($a);
|
||||
});
|
||||
foreach ($p['*'] as $key => $value) {
|
||||
if (strpos($path, $key) === 0 && isset($value[$httpMethod])) {
|
||||
//path found, convert rest of the path to parameters
|
||||
$path = substr($path, strlen($key) + 1);
|
||||
$call = ApiMethodInfo::__set_state($value[$httpMethod]);
|
||||
$call->parameters = empty($path)
|
||||
? array()
|
||||
: explode('/', $path);
|
||||
return $call;
|
||||
}
|
||||
}
|
||||
}
|
||||
//================== dynamic routes =============================
|
||||
//add newline char if trailing slash is found
|
||||
if (substr($path, -1) == '/')
|
||||
$path .= PHP_EOL;
|
||||
//if double slash is found fill in newline char;
|
||||
$path = str_replace('//', '/' . PHP_EOL . '/', $path);
|
||||
ksort($p);
|
||||
foreach ($p as $key => $value) {
|
||||
if (!isset($value[$httpMethod])) {
|
||||
continue;
|
||||
}
|
||||
$regex = str_replace(array('{', '}'),
|
||||
array('(?P<', '>[^/]+)'), $key);
|
||||
if (preg_match_all(":^$regex$:i", $path, $matches, PREG_SET_ORDER)) {
|
||||
$matches = $matches[0];
|
||||
$found = true;
|
||||
foreach ($matches as $k => $v) {
|
||||
if (is_numeric($k)) {
|
||||
unset($matches[$k]);
|
||||
continue;
|
||||
}
|
||||
$index = intval(substr($k, 1));
|
||||
$details = $value[$httpMethod]['metadata']['param'][$index];
|
||||
if ($k{0} == 's' || strpos($k, static::pathVarTypeOf($v)) === 0) {
|
||||
//remove the newlines
|
||||
$data[$details['name']] = trim($v, PHP_EOL);
|
||||
} else {
|
||||
$status = 400;
|
||||
$message = 'invalid value specified for `'
|
||||
. $details['name'] . '`';
|
||||
$found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($found) {
|
||||
return static::populate($value[$httpMethod], $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($status == 404) {
|
||||
//check if other methods are allowed
|
||||
if (isset($p[$path])) {
|
||||
$status = 405;
|
||||
$methods = array_keys($p[$path]);
|
||||
}
|
||||
}
|
||||
if ($status == 405) {
|
||||
header('Allow: ' . implode(', ', $methods));
|
||||
}
|
||||
throw new RestException($status, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the parameter values
|
||||
*
|
||||
* @param array $call
|
||||
* @param $data
|
||||
*
|
||||
* @return ApiMethodInfo
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
protected static function populate(array $call, $data)
|
||||
{
|
||||
$call['parameters'] = $call['defaults'];
|
||||
$p = & $call['parameters'];
|
||||
foreach ($data as $key => $value) {
|
||||
if (isset($call['arguments'][$key])) {
|
||||
$p[$call['arguments'][$key]] = $value;
|
||||
}
|
||||
}
|
||||
if (Defaults::$smartParameterParsing && 'post' != (string)Util::$restler->requestFormat) {
|
||||
if (
|
||||
count($p) == 1 &&
|
||||
($m = Util::nestedValue($call, 'metadata', 'param', 0)) &&
|
||||
!array_key_exists($m['name'], $data) &&
|
||||
array_key_exists(Defaults::$fullRequestDataName, $data) &&
|
||||
!is_null($d = $data[Defaults::$fullRequestDataName]) &&
|
||||
isset($m['type']) &&
|
||||
static::typeMatch($m['type'], $d)
|
||||
) {
|
||||
$p[0] = $d;
|
||||
} else {
|
||||
$bodyParamCount = 0;
|
||||
$lastBodyParamIndex = -1;
|
||||
$lastM = null;
|
||||
foreach ($call['metadata']['param'] as $k => $m) {
|
||||
if ($m[CommentParser::$embeddedDataName]['from'] == 'body') {
|
||||
$bodyParamCount++;
|
||||
$lastBodyParamIndex = $k;
|
||||
$lastM = $m;
|
||||
}
|
||||
}
|
||||
if (
|
||||
$bodyParamCount == 1 &&
|
||||
!array_key_exists($lastM['name'], $data) &&
|
||||
array_key_exists(Defaults::$fullRequestDataName, $data) &&
|
||||
!is_null($d = $data[Defaults::$fullRequestDataName])
|
||||
) {
|
||||
$p[$lastBodyParamIndex] = $d;
|
||||
}
|
||||
}
|
||||
}
|
||||
$r = ApiMethodInfo::__set_state($call);
|
||||
$modifier = "_modify_{$r->methodName}_api";
|
||||
if (method_exists($r->className, $modifier)) {
|
||||
$stage = end(Scope::get('Restler')->getEvents());
|
||||
if (empty($stage))
|
||||
$stage = 'setup';
|
||||
$r = Scope::get($r->className)->$modifier($r, $stage) ? : $r;
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
protected static function pathVarTypeOf($var)
|
||||
{
|
||||
if (is_numeric($var)) {
|
||||
return 'n';
|
||||
}
|
||||
if ($var === 'true' || $var === 'false') {
|
||||
return 'b';
|
||||
}
|
||||
return 's';
|
||||
}
|
||||
|
||||
protected static function typeMatch($type, $var)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'boolean':
|
||||
case 'bool':
|
||||
return is_bool($var);
|
||||
case 'array':
|
||||
case 'object':
|
||||
return is_array($var);
|
||||
case 'string':
|
||||
case 'int':
|
||||
case 'integer':
|
||||
case 'float':
|
||||
case 'number':
|
||||
return is_scalar($var);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type and associated model
|
||||
*
|
||||
* @param ReflectionClass $class
|
||||
* @param array $scope
|
||||
*
|
||||
* @throws RestException
|
||||
* @throws \Exception
|
||||
* @return array
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected static function getTypeAndModel(ReflectionClass $class, array $scope)
|
||||
{
|
||||
$className = $class->getName();
|
||||
if (isset(static::$models[$className])) {
|
||||
return static::$models[$className];
|
||||
}
|
||||
$children = array();
|
||||
try {
|
||||
$props = $class->getProperties(ReflectionProperty::IS_PUBLIC);
|
||||
foreach ($props as $prop) {
|
||||
$name = $prop->getName();
|
||||
$child = array('name' => $name);
|
||||
if ($c = $prop->getDocComment()) {
|
||||
$child += Util::nestedValue(CommentParser::parse($c), 'var');
|
||||
} else {
|
||||
$o = $class->newInstance();
|
||||
$p = $prop->getValue($o);
|
||||
if (is_object($p)) {
|
||||
$child['type'] = get_class($p);
|
||||
} elseif (is_array($p)) {
|
||||
$child['type'] = 'array';
|
||||
if (count($p)) {
|
||||
$pc = reset($p);
|
||||
if (is_object($pc)) {
|
||||
$child['contentType'] = get_class($pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$child += array(
|
||||
'type' => $child['name'] == 'email' ? 'email' : 'string',
|
||||
'label' => static::label($child['name'])
|
||||
);
|
||||
isset($child[CommentParser::$embeddedDataName])
|
||||
? $child[CommentParser::$embeddedDataName] += array('required' => true)
|
||||
: $child[CommentParser::$embeddedDataName]['required'] = true;
|
||||
if ($qualified = Scope::resolve($child['type'], $scope)) {
|
||||
list($child['type'], $child['children'])
|
||||
= static::getTypeAndModel(new ReflectionClass($qualified), $scope);
|
||||
} elseif (
|
||||
($contentType = Util::nestedValue($child, CommentParser::$embeddedDataName, 'type')) &&
|
||||
($qualified = Scope::resolve($contentType, $scope))
|
||||
) {
|
||||
list($child['contentType'], $child['children'])
|
||||
= static::getTypeAndModel(new ReflectionClass($qualified), $scope);
|
||||
}
|
||||
$children[$name] = $child;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if (String::endsWith($e->getFile(), 'CommentParser.php')) {
|
||||
throw new RestException(500, "Error while parsing comments of `$className` class. " . $e->getMessage());
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
static::$models[$className] = array($className, $children);
|
||||
return static::$models[$className];
|
||||
}
|
||||
|
||||
/**
|
||||
* Import previously created routes from cache
|
||||
*
|
||||
* @param array $routes
|
||||
*/
|
||||
public static function fromArray(array $routes)
|
||||
{
|
||||
static::$routes = $routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export current routes for cache
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function toArray()
|
||||
{
|
||||
return static::$routes;
|
||||
}
|
||||
|
||||
public static function type($var)
|
||||
{
|
||||
if (is_object($var)) return get_class($var);
|
||||
if (is_array($var)) return 'array';
|
||||
if (is_bool($var)) return 'boolean';
|
||||
if (is_numeric($var)) return is_float($var) ? 'float' : 'int';
|
||||
return 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a label from name of the parameter or property
|
||||
*
|
||||
* Convert `camelCase` style names into proper `Title Case` names
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function label($name)
|
||||
{
|
||||
return ucfirst(preg_replace(array('/(?<=[^A-Z])([A-Z])/', '/(?<=[^0-9])([0-9])/'), ' $0', $name));
|
||||
}
|
||||
|
||||
public static function scope(ReflectionClass $class)
|
||||
{
|
||||
$namespace = $class->getNamespaceName();
|
||||
$imports = array(
|
||||
'*' => empty($namespace) ? '' : $namespace . '\\'
|
||||
);
|
||||
$file = file_get_contents($class->getFileName());
|
||||
$tokens = token_get_all($file);
|
||||
$namespace = '';
|
||||
$alias = '';
|
||||
$reading = false;
|
||||
$last = 0;
|
||||
foreach ($tokens as $token) {
|
||||
if (is_string($token)) {
|
||||
if ($reading && ',' == $token) {
|
||||
//===== STOP =====//
|
||||
$reading = false;
|
||||
if (!empty($namespace))
|
||||
$imports[$alias] = trim($namespace, '\\');
|
||||
//===== START =====//
|
||||
$reading = true;
|
||||
$namespace = '';
|
||||
$alias = '';
|
||||
} else {
|
||||
//===== STOP =====//
|
||||
$reading = false;
|
||||
if (!empty($namespace))
|
||||
$imports[$alias] = trim($namespace, '\\');
|
||||
}
|
||||
} elseif (T_USE == $token[0]) {
|
||||
//===== START =====//
|
||||
$reading = true;
|
||||
$namespace = '';
|
||||
$alias = '';
|
||||
} elseif ($reading) {
|
||||
//echo token_name($token[0]) . ' ' . $token[1] . PHP_EOL;
|
||||
switch ($token[0]) {
|
||||
case T_WHITESPACE:
|
||||
continue 2;
|
||||
case T_STRING:
|
||||
$alias = $token[1];
|
||||
if (T_AS == $last) {
|
||||
break;
|
||||
}
|
||||
//don't break;
|
||||
case T_NS_SEPARATOR:
|
||||
$namespace .= $token[1];
|
||||
break;
|
||||
}
|
||||
$last = $token[0];
|
||||
}
|
||||
}
|
||||
return $imports;
|
||||
}
|
||||
}
|
||||
190
htdocs/includes/restler/Scope.php
Normal file
190
htdocs/includes/restler/Scope.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
/**
|
||||
* Scope resolution class, manages instantiation and acts as a dependency
|
||||
* injection container
|
||||
*
|
||||
* @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 Scope
|
||||
{
|
||||
public static $classAliases = array(
|
||||
|
||||
//Core
|
||||
'Restler' => 'Luracast\Restler\Restler',
|
||||
|
||||
//Format classes
|
||||
'AmfFormat' => 'Luracast\Restler\Format\AmfFormat',
|
||||
'JsFormat' => 'Luracast\Restler\Format\JsFormat',
|
||||
'JsonFormat' => 'Luracast\Restler\Format\JsonFormat',
|
||||
'HtmlFormat' => 'Luracast\Restler\Format\HtmlFormat',
|
||||
'PlistFormat' => 'Luracast\Restler\Format\PlistFormat',
|
||||
'UploadFormat' => 'Luracast\Restler\Format\UploadFormat',
|
||||
'UrlEncodedFormat' => 'Luracast\Restler\Format\UrlEncodedFormat',
|
||||
'XmlFormat' => 'Luracast\Restler\Format\XmlFormat',
|
||||
'YamlFormat' => 'Luracast\Restler\Format\YamlFormat',
|
||||
'CsvFormat' => 'Luracast\Restler\Format\CsvFormat',
|
||||
'TsvFormat' => 'Luracast\Restler\Format\TsvFormat',
|
||||
|
||||
//Filter classes
|
||||
'RateLimit' => 'Luracast\Restler\Filter\RateLimit',
|
||||
|
||||
//UI classes
|
||||
'Forms' => 'Luracast\Restler\UI\Forms',
|
||||
'Nav' => 'Luracast\Restler\UI\Nav',
|
||||
'Emmet' => 'Luracast\Restler\UI\Emmet',
|
||||
'T' => 'Luracast\Restler\UI\Tags',
|
||||
|
||||
//API classes
|
||||
'Resources' => 'Luracast\Restler\Resources',
|
||||
|
||||
//Cache classes
|
||||
'HumanReadableCache' => 'Luracast\Restler\HumanReadableCache',
|
||||
'ApcCache' => 'Luracast\Restler\ApcCache',
|
||||
|
||||
//Utility classes
|
||||
'Object' => 'Luracast\Restler\Data\Object',
|
||||
'String' => 'Luracast\Restler\Data\String',
|
||||
'Arr' => 'Luracast\Restler\Data\Arr',
|
||||
|
||||
//Exception
|
||||
'RestException' => 'Luracast\Restler\RestException'
|
||||
);
|
||||
public static $properties = array();
|
||||
protected static $instances = array();
|
||||
protected static $registry = array();
|
||||
|
||||
public static function register($name, Callable $function, $singleton = true)
|
||||
{
|
||||
static::$registry[$name] = (object)compact('function', 'singleton');
|
||||
}
|
||||
|
||||
public static function set($name, $instance)
|
||||
{
|
||||
static::$instances[$name] = (object)array('instance' => $instance);
|
||||
}
|
||||
|
||||
public static function get($name)
|
||||
{
|
||||
$r = null;
|
||||
$initialized = false;
|
||||
$properties = array();
|
||||
if (array_key_exists($name, static::$instances)) {
|
||||
$initialized = true;
|
||||
$r = static::$instances[$name]->instance;
|
||||
} elseif (!empty(static::$registry[$name])) {
|
||||
$function = static::$registry[$name]->function;
|
||||
$r = $function();
|
||||
if (static::$registry[$name]->singleton)
|
||||
static::$instances[$name] = (object)array('instance' => $r);
|
||||
} else {
|
||||
$fullName = $name;
|
||||
if (isset(static::$classAliases[$name])) {
|
||||
$fullName = static::$classAliases[$name];
|
||||
}
|
||||
if (class_exists($fullName)) {
|
||||
$shortName = Util::getShortName($name);
|
||||
$r = new $fullName();
|
||||
static::$instances[$name] = (object)array('instance' => $r);
|
||||
if ($name != 'Restler') {
|
||||
$r->restler = static::get('Restler');
|
||||
$m = Util::nestedValue($r->restler, 'apiMethodInfo', 'metadata');
|
||||
if ($m) {
|
||||
$properties = Util::nestedValue(
|
||||
$m, 'class', $fullName,
|
||||
CommentParser::$embeddedDataName
|
||||
) ? : (Util::nestedValue(
|
||||
$m, 'class', $shortName,
|
||||
CommentParser::$embeddedDataName
|
||||
) ? : array());
|
||||
} else {
|
||||
static::$instances[$name]->initPending = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
$r instanceof iUseAuthentication &&
|
||||
static::get('Restler')->_authVerified &&
|
||||
!isset(static::$instances[$name]->authVerified)
|
||||
) {
|
||||
static::$instances[$name]->authVerified = true;
|
||||
$r->__setAuthenticationStatus
|
||||
(static::get('Restler')->_authenticated);
|
||||
}
|
||||
if (isset(static::$instances[$name]->initPending)) {
|
||||
$m = Util::nestedValue(static::get('Restler'), 'apiMethodInfo', 'metadata');
|
||||
$fullName = $name;
|
||||
if (class_exists($name)) {
|
||||
$shortName = Util::getShortName($name);
|
||||
} else {
|
||||
$shortName = $name;
|
||||
if (isset(static::$classAliases[$name]))
|
||||
$fullName = static::$classAliases[$name];
|
||||
}
|
||||
if ($m) {
|
||||
$properties = Util::nestedValue(
|
||||
$m, 'class', $fullName,
|
||||
CommentParser::$embeddedDataName
|
||||
) ? : (Util::nestedValue(
|
||||
$m, 'class', $shortName,
|
||||
CommentParser::$embeddedDataName
|
||||
) ? : array());
|
||||
unset(static::$instances[$name]->initPending);
|
||||
$initialized = false;
|
||||
}
|
||||
}
|
||||
if (!$initialized && is_object($r)) {
|
||||
$properties += static::$properties;
|
||||
$objectVars = get_object_vars($r);
|
||||
$className = get_class($r);
|
||||
foreach ($properties as $property => $value) {
|
||||
if (property_exists($className, $property)) {
|
||||
//if not a static property
|
||||
array_key_exists($property, $objectVars)
|
||||
? $r->{$property} = $value
|
||||
: $r::$$property = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fully qualified class name for the given scope
|
||||
*
|
||||
* @param string $className
|
||||
* @param array $scope local scope
|
||||
*
|
||||
* @return string|boolean returns the class name or false
|
||||
*/
|
||||
public static function resolve($className, array $scope)
|
||||
{
|
||||
if (empty($className) || !is_string($className))
|
||||
return false;
|
||||
$divider = '\\';
|
||||
$qualified = false;
|
||||
if ($className{0} == $divider) {
|
||||
$qualified = trim($className, $divider);
|
||||
} elseif (array_key_exists($className, $scope)) {
|
||||
$qualified = $scope[$className];
|
||||
} else {
|
||||
$qualified = $scope['*'] . $className;
|
||||
}
|
||||
if (class_exists($qualified))
|
||||
return $qualified;
|
||||
if (isset(static::$classAliases[$className])) {
|
||||
$qualified = static::$classAliases[$className];
|
||||
if (class_exists($qualified))
|
||||
return $qualified;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
383
htdocs/includes/restler/UI/Emmet.php
Normal file
383
htdocs/includes/restler/UI/Emmet.php
Normal file
@ -0,0 +1,383 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\UI;
|
||||
|
||||
use Luracast\Restler\UI\Tags as T;
|
||||
use Luracast\Restler\Util;
|
||||
|
||||
class Emmet
|
||||
{
|
||||
const DELIMITERS = '.#*>+^[=" ]{$@-#}';
|
||||
|
||||
/**
|
||||
* Create the needed tag hierarchy from emmet string
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @param array|string $data
|
||||
*
|
||||
* @return array|T
|
||||
*/
|
||||
public static function make($string, $data = null)
|
||||
{
|
||||
if (!strlen($string))
|
||||
return array();
|
||||
|
||||
$implicitTag =
|
||||
function () use (& $tag) {
|
||||
if (empty($tag->tag)) {
|
||||
switch ($tag->parent->tag) {
|
||||
case 'ul':
|
||||
case 'ol':
|
||||
$tag->tag = 'li';
|
||||
break;
|
||||
case 'em':
|
||||
$tag->tag = 'span';
|
||||
break;
|
||||
case 'table':
|
||||
case 'tbody':
|
||||
case 'thead':
|
||||
case 'tfoot':
|
||||
$tag->tag = 'tr';
|
||||
break;
|
||||
case 'tr':
|
||||
$tag->tag = 'td';
|
||||
break;
|
||||
case 'select':
|
||||
case 'optgroup':
|
||||
$tag->tag = 'option';
|
||||
break;
|
||||
default:
|
||||
$tag->tag = 'div';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$parseText =
|
||||
function (
|
||||
$text, $round, $total, $data, $delimiter = null
|
||||
)
|
||||
use (
|
||||
& $tokens, & $tag
|
||||
) {
|
||||
$digits = 0;
|
||||
if ($delimiter == null)
|
||||
$delimiter = array(
|
||||
'.' => true,
|
||||
'#' => true,
|
||||
'*' => true,
|
||||
'>' => true,
|
||||
'+' => true,
|
||||
'^' => true,
|
||||
'[' => true,
|
||||
']' => true,
|
||||
'=' => true,
|
||||
);
|
||||
while (!empty($tokens) &&
|
||||
!isset($delimiter[$t = array_shift($tokens)])) {
|
||||
while ('$' === $t) {
|
||||
$digits++;
|
||||
$t = array_shift($tokens);
|
||||
}
|
||||
if ($digits) {
|
||||
$negative = false;
|
||||
$offset = 0;
|
||||
if ('@' == $t) {
|
||||
if ('-' == ($t = array_shift($tokens))) {
|
||||
$negative = true;
|
||||
if (is_numeric(reset($tokens))) {
|
||||
$offset = array_shift($tokens);
|
||||
}
|
||||
} elseif (is_numeric($t)) {
|
||||
$offset = $t;
|
||||
} else {
|
||||
array_unshift($tokens, $t);
|
||||
}
|
||||
} elseif ('#' == ($h = array_shift($tokens))) {
|
||||
if (!empty($t)) {
|
||||
$data = Util::nestedValue($data, $t);
|
||||
if (is_null($data)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (is_numeric($data)) {
|
||||
$text .= sprintf("%0{$digits}d", (int)$data);
|
||||
} elseif (is_string($data)) {
|
||||
$text .= $data;
|
||||
}
|
||||
$digits = 0;
|
||||
continue;
|
||||
} else {
|
||||
array_unshift($tokens, $t, $h);
|
||||
}
|
||||
if ($negative) {
|
||||
$n = $total + 1 - $round + $offset;
|
||||
} else {
|
||||
$n = $round + $offset;
|
||||
}
|
||||
$text .= sprintf("%0{$digits}d", $n);
|
||||
$digits = 0;
|
||||
} else {
|
||||
$text .= $t;
|
||||
}
|
||||
}
|
||||
if (isset($t))
|
||||
array_unshift($tokens, $t);
|
||||
return $text;
|
||||
};
|
||||
|
||||
$parseAttributes =
|
||||
function (Callable $self, $round, $total, $data)
|
||||
use (& $tokens, & $tag, $parseText) {
|
||||
$a = $parseText(
|
||||
'', $round, $total, $data
|
||||
);
|
||||
if (is_null($a))
|
||||
return;
|
||||
if ('=' == ($v = array_shift($tokens))) {
|
||||
//value
|
||||
if ('"' == ($v = array_shift($tokens))) {
|
||||
$text = '';
|
||||
$tag->$a($parseText(
|
||||
$text, $round, $total, $data,
|
||||
array('"' => true)
|
||||
));
|
||||
} else {
|
||||
array_unshift($tokens, $v);
|
||||
$text = '';
|
||||
$tag->$a($parseText(
|
||||
$text, $round, $total, $data,
|
||||
array(' ' => true, ']' => true)
|
||||
));
|
||||
}
|
||||
if (' ' == ($v = array_shift($tokens))) {
|
||||
$self($self, $round, $total, $data);
|
||||
}
|
||||
} elseif (']' == $v) {
|
||||
//end
|
||||
$tag->$a('');
|
||||
return;
|
||||
} elseif (' ' == $v) {
|
||||
$tag->$a('');
|
||||
$self($self, $round, $total, $data);
|
||||
}
|
||||
};
|
||||
|
||||
$tokens = static::tokenize($string);
|
||||
$tag = new T(array_shift($tokens));
|
||||
$parent = $root = new T;
|
||||
|
||||
$parse =
|
||||
function (
|
||||
Callable $self, $round = 1, $total = 1
|
||||
)
|
||||
use (
|
||||
& $tokens, & $parent, & $tag, & $data,
|
||||
$parseAttributes, $implicitTag, $parseText
|
||||
) {
|
||||
$offsetTokens = null;
|
||||
$parent[] = $tag;
|
||||
$isInChild = false;
|
||||
while ($tokens) {
|
||||
switch (array_shift($tokens)) {
|
||||
//class
|
||||
case '.':
|
||||
$offsetTokens = array_values($tokens);
|
||||
array_unshift($offsetTokens, '.');
|
||||
$implicitTag();
|
||||
$e = array_filter(explode(' ', $tag->class));
|
||||
$e[] = $parseText('', $round, $total, $data);
|
||||
$tag->class(implode(' ', array_unique($e)));
|
||||
break;
|
||||
//id
|
||||
case '#':
|
||||
$offsetTokens = array_values($tokens);
|
||||
array_unshift($offsetTokens, '#');
|
||||
$implicitTag();
|
||||
$tag->id(
|
||||
$parseText(
|
||||
array_shift($tokens), $round, $total, $data
|
||||
)
|
||||
);
|
||||
break;
|
||||
//attributes
|
||||
case '[':
|
||||
$offsetTokens = array_values($tokens);
|
||||
array_unshift($offsetTokens, '[');
|
||||
$implicitTag();
|
||||
$parseAttributes(
|
||||
$parseAttributes, $round, $total, $data
|
||||
);
|
||||
break;
|
||||
//child
|
||||
case '{':
|
||||
$text = '';
|
||||
$tag[] = $parseText(
|
||||
$text, $round, $total, $data, array('}' => true)
|
||||
);
|
||||
break;
|
||||
case '>':
|
||||
$isInChild = true;
|
||||
$offsetTokens = null;
|
||||
if ('{' == ($t = array_shift($tokens))) {
|
||||
array_unshift($tokens, $t);
|
||||
$child = new T();
|
||||
$tag[] = $child;
|
||||
$parent = $tag;
|
||||
$tag = $child;
|
||||
} elseif ('[' == $t) {
|
||||
array_unshift($tokens, $t);
|
||||
} else {
|
||||
$child = new T($t);
|
||||
$tag[] = $child;
|
||||
$parent = $tag;
|
||||
$tag = $child;
|
||||
}
|
||||
break;
|
||||
//sibling
|
||||
case '+':
|
||||
$offsetTokens = null;
|
||||
if (!$isInChild && $round != $total) {
|
||||
$tokens = array();
|
||||
break;
|
||||
}
|
||||
if ('{' == ($t = array_shift($tokens))) {
|
||||
$tag = $tag->parent;
|
||||
array_unshift($tokens, $t);
|
||||
break;
|
||||
} elseif ('[' == $t) {
|
||||
array_unshift($tokens, $t);
|
||||
} else {
|
||||
$child = new T($t);
|
||||
$tag = $tag->parent;
|
||||
$tag[] = $child;
|
||||
$tag = $child;
|
||||
}
|
||||
break;
|
||||
//sibling of parent
|
||||
case '^':
|
||||
if ($round != $total) {
|
||||
$tokens = array();
|
||||
break;
|
||||
}
|
||||
$tag = $tag->parent;
|
||||
if ($tag->parent)
|
||||
$tag = $tag->parent;
|
||||
while ('^' == ($t = array_shift($tokens))) {
|
||||
if ($tag->parent)
|
||||
$tag = $tag->parent;
|
||||
}
|
||||
$child = new T($t);
|
||||
$tag[] = $child;
|
||||
$tag = $child;
|
||||
break;
|
||||
//clone
|
||||
case '*':
|
||||
$times = array_shift($tokens);
|
||||
$removeCount = 2;
|
||||
$delimiter = array(
|
||||
'.' => true,
|
||||
'#' => true,
|
||||
'*' => true,
|
||||
'>' => true,
|
||||
'+' => true,
|
||||
'^' => true,
|
||||
'[' => true,
|
||||
']' => true,
|
||||
'=' => true,
|
||||
);
|
||||
if (!is_numeric($times)) {
|
||||
if (is_string($times)) {
|
||||
if (!isset($delimiter[$times])) {
|
||||
$data = Util::nestedValue($data, $times)
|
||||
? : $data;
|
||||
} else {
|
||||
array_unshift($tokens, $times);
|
||||
$removeCount = 1;
|
||||
}
|
||||
}
|
||||
$indexed = array_values($data);
|
||||
$times = is_array($data) && $indexed == $data
|
||||
? count($data) : 0;
|
||||
}
|
||||
$source = $tag;
|
||||
if (!empty($offsetTokens)) {
|
||||
if (false !== strpos($source->class, ' ')) {
|
||||
$class = explode(' ', $source->class);
|
||||
array_pop($class);
|
||||
$class = implode(' ', $class);
|
||||
} else {
|
||||
$class = null;
|
||||
}
|
||||
$tag->class($class);
|
||||
$star = array_search('*', $offsetTokens);
|
||||
array_splice($offsetTokens, $star, $removeCount);
|
||||
$remainingTokens = $offsetTokens;
|
||||
} else {
|
||||
$remainingTokens = $tokens;
|
||||
}
|
||||
$source->parent = null;
|
||||
$sourceData = $data;
|
||||
$currentParent = $parent;
|
||||
for ($i = 1; $i <= $times; $i++) {
|
||||
$tag = clone $source;
|
||||
$parent = $currentParent;
|
||||
$data = is_array($sourceData)
|
||||
&& isset($sourceData[$i - 1])
|
||||
? $sourceData[$i - 1]
|
||||
: @(string)$sourceData;
|
||||
$tokens = array_values($remainingTokens);
|
||||
$self($self, $i, $times);
|
||||
}
|
||||
$round = 1;
|
||||
$offsetTokens = null;
|
||||
$tag = $source;
|
||||
$tokens = array(); //$remainingTokens;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
$parse($parse);
|
||||
return count($root) == 1 ? $root[0] : $root;
|
||||
}
|
||||
|
||||
public static function tokenize($string)
|
||||
{
|
||||
$r = array();
|
||||
$f = strtok($string, static::DELIMITERS);
|
||||
$pos = 0;
|
||||
do {
|
||||
$start = $pos;
|
||||
$pos = strpos($string, $f, $start);
|
||||
$tokens = array();
|
||||
for ($i = $start; $i < $pos; $i++) {
|
||||
$token = $string{$i};
|
||||
if (('#' == $token || '.' == $token) &&
|
||||
(!empty($tokens) || $i == 0)
|
||||
) {
|
||||
$r[] = '';
|
||||
}
|
||||
$r[] = $tokens[] = $token;
|
||||
}
|
||||
$pos += strlen($f);
|
||||
$r[] = $f;
|
||||
} while (false != ($f = strtok(static::DELIMITERS)));
|
||||
for ($i = $pos; $i < strlen($string); $i++) {
|
||||
$token = $string{$i};
|
||||
$r[] = $tokens[] = $token;
|
||||
}
|
||||
return $r;
|
||||
/* sample output produced by ".row*3>.col*3"
|
||||
[0] => div
|
||||
[1] => .
|
||||
[2] => row
|
||||
[3] => *
|
||||
[4] => 3
|
||||
[5] => >
|
||||
[6] => div
|
||||
[7] => .
|
||||
[8] => col
|
||||
[9] => *
|
||||
[10] => 4
|
||||
*/
|
||||
}
|
||||
}
|
||||
59
htdocs/includes/restler/UI/FormStyles.php
Normal file
59
htdocs/includes/restler/UI/FormStyles.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\UI;
|
||||
|
||||
/**
|
||||
* Utility class for providing preset styles for html forms
|
||||
*
|
||||
* @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 FormStyles
|
||||
{
|
||||
public static $html = array(
|
||||
'form' => 'form[role=form id=$id# name=$name# method=$method# action=$action# enctype=$enctype#]',
|
||||
'input' => '.row>section>label{$label#}^input[name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept#]',
|
||||
'textarea' => '.row>label{$label#}^textarea[name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3]{$value#}',
|
||||
'radio' => '.row>section>label{$label#}^span>label*options>input[name=$name# value=$value# type=radio checked=$selected# required=$required#]+{ $text#}',
|
||||
'select' => '.row>label{$label#}^select[name=$name# required=$required#]>option[value]+option[value=$value# selected=$selected#]{$text#}*options',
|
||||
'submit' => '.row>label{ }^button[type=submit]{$label#}',
|
||||
'fieldset' => 'fieldset>legend{$label#}',
|
||||
'checkbox' => '.row>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{$label#}',
|
||||
//------------- TYPE BASED STYLES ---------------------//
|
||||
'checkbox-array' => 'fieldset>legend{$label#}+section*options>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{ $text#}',
|
||||
'select-array' => 'label{$label#}+select[name=$name# required=$required# multiple style="height: auto;background-image: none; outline: inherit;"]>option[value=$value# selected=$selected#]{$text#}*options',
|
||||
);
|
||||
public static $bootstrap3 = array(
|
||||
'form' => 'form[role=form id=$id# name=$name# method=$method# action=$action# enctype=$enctype#]',
|
||||
'input' => '.form-group>label{$label#}+input.form-control[name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept#]',
|
||||
'textarea' => '.form-group>label{$label#}+textarea.form-control[name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3]{$value#}',
|
||||
'radio' => 'fieldset>legend{$label#}>.radio*options>label>input.radio[name=$name# value=$value# type=radio checked=$selected# required=$required#]{$text#}',
|
||||
'select' => '.form-group>label{$label#}+select.form-control[name=$name# multiple=$multiple# required=$required#]>option[value]+option[value=$value# selected=$selected#]{$text#}*options',
|
||||
'submit' => 'button.btn.btn-primary[type=submit]{$label#}',
|
||||
'fieldset' => 'fieldset>legend{$label#}',
|
||||
'checkbox' => '.checkbox>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{$label#}',
|
||||
//------------- TYPE BASED STYLES ---------------------//
|
||||
'checkbox-array' => 'fieldset>legend{$label#}>.checkbox*options>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required#]{$text#}',
|
||||
'select-array' => '.form-group>label{$label#}+select.form-control[name=$name# multiple=$multiple# required=$required#] size=$options#>option[value=$value# selected=$selected#]{$text#}*options',
|
||||
//------------- CUSTOM STYLES ---------------------//
|
||||
'radio-inline' => '.form-group>label{$label# : }+label.radio-inline*options>input.radio[name=$name# value=$value# type=radio checked=$selected# required=$required#]+{$text#}',
|
||||
);
|
||||
public static $foundation5 = array(
|
||||
'form' => 'form[id=$id# name=$name# method=$method# action=$action# enctype=$enctype#]',
|
||||
'input' => 'label{$label#}+input[name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept#]',
|
||||
'textarea' => 'label{$label#}+textarea[name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3]{$value#}',
|
||||
'radio' => 'label{$label# : }+label.radio-inline*options>input.radio[name=$name# value=$value# type=radio checked=$selected# required=$required#]+{$text#}',
|
||||
'select' => 'label{$label#}+select[name=$name# required=$required#]>option[value]+option[value=$value# selected=$selected#]{$text#}*options',
|
||||
'submit' => 'button.button[type=submit]{$label#}',
|
||||
'fieldset' => 'fieldset>legend{$label#}',
|
||||
'checkbox' => 'label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{ $label#}',
|
||||
//------------- TYPE BASED STYLES ---------------------//
|
||||
'checkbox-array' => 'fieldset>legend{$label#}+label*options>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{ $text#}',
|
||||
'select-array' => 'label{$label#}+select[name=$name# required=$required# multiple style="height: auto;background-image: none; outline: inherit;"]>option[value=$value# selected=$selected#]{$text#}*options',
|
||||
//------------- CUSTOM STYLES ---------------------//
|
||||
);
|
||||
}
|
||||
434
htdocs/includes/restler/UI/Forms.php
Normal file
434
htdocs/includes/restler/UI/Forms.php
Normal file
@ -0,0 +1,434 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
208
htdocs/includes/restler/UI/Nav.php
Normal file
208
htdocs/includes/restler/UI/Nav.php
Normal file
@ -0,0 +1,208 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\UI;
|
||||
|
||||
use Luracast\Restler\CommentParser;
|
||||
use Luracast\Restler\Defaults;
|
||||
use Luracast\Restler\Restler;
|
||||
use Luracast\Restler\Routes;
|
||||
use Luracast\Restler\Scope;
|
||||
use Luracast\Restler\Util;
|
||||
|
||||
|
||||
/**
|
||||
* Utility class for automatically creating data to build an navigation interface
|
||||
* based on available routes that are accessible by the current user
|
||||
*
|
||||
* @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 Nav
|
||||
{
|
||||
public static $root = 'home';
|
||||
/**
|
||||
* @var null|callable if the api methods are under access control mechanism
|
||||
* you can attach a function here that returns true or false to determine
|
||||
* visibility of a protected api method. this function will receive method
|
||||
* info as the only parameter.
|
||||
*/
|
||||
public static $accessControlFunction = null;
|
||||
/**
|
||||
* @var array all paths beginning with any of the following will be excluded
|
||||
* from documentation. if an empty string is given it will exclude the root
|
||||
*/
|
||||
public static $excludedPaths = array('');
|
||||
/**
|
||||
* @var array prefix additional menu items with one of the following syntax
|
||||
* [$path => $text]
|
||||
* [$path]
|
||||
* [$path => ['text' => $text, 'url' => $url]]
|
||||
*/
|
||||
public static $prepends = array();
|
||||
/**
|
||||
* @var array suffix additional menu items with one of the following syntax
|
||||
* [$path => $text]
|
||||
* [$path]
|
||||
* [$path => ['text' => $text, 'url' => $url]]
|
||||
*/
|
||||
public static $appends = array();
|
||||
|
||||
public static $addExtension = true;
|
||||
|
||||
protected static $extension = '';
|
||||
|
||||
public static function get($for = '', $activeUrl = null)
|
||||
{
|
||||
if (!static::$accessControlFunction && Defaults::$accessControlFunction)
|
||||
static::$accessControlFunction = Defaults::$accessControlFunction;
|
||||
/** @var Restler $restler */
|
||||
$restler = Scope::get('Restler');
|
||||
if (static::$addExtension)
|
||||
static::$extension = '.' . $restler->responseFormat->getExtension();
|
||||
if (is_null($activeUrl))
|
||||
$activeUrl = $restler->url;
|
||||
|
||||
$tree = array();
|
||||
foreach (static::$prepends as $path => $text) {
|
||||
$url = null;
|
||||
if (is_array($text)) {
|
||||
if (isset($text['url'])) {
|
||||
$url = $text['url'];
|
||||
$text = $text['text'];
|
||||
} else {
|
||||
$url = current(array_keys($text));
|
||||
$text = current($text);
|
||||
}
|
||||
}
|
||||
if (is_numeric($path)) {
|
||||
$path = $text;
|
||||
$text = null;
|
||||
}
|
||||
if (empty($for) || 0 === strpos($path, "$for/"))
|
||||
static::build($tree, $path, $url, $text, $activeUrl);
|
||||
}
|
||||
$routes = Routes::toArray();
|
||||
$routes = $routes['v' . $restler->getRequestedApiVersion()];
|
||||
foreach ($routes as $value) {
|
||||
foreach ($value as $httpMethod => $route) {
|
||||
if ($httpMethod != 'GET') {
|
||||
continue;
|
||||
}
|
||||
$path = $route['url'];
|
||||
if (false !== strpos($path, '{'))
|
||||
continue;
|
||||
if ($route['accessLevel'] > 1 && !Util::$restler->_authenticated)
|
||||
continue;
|
||||
foreach (static::$excludedPaths as $exclude) {
|
||||
if (empty($exclude)) {
|
||||
if (empty($path))
|
||||
continue 2;
|
||||
} elseif (0 === strpos($path, $exclude)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
if ($restler->_authenticated
|
||||
&& static::$accessControlFunction
|
||||
&& (!call_user_func(
|
||||
static::$accessControlFunction, $route['metadata']))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$text = Util::nestedValue(
|
||||
$route,
|
||||
'metadata',
|
||||
CommentParser::$embeddedDataName,
|
||||
'label'
|
||||
);
|
||||
if (empty($for) || 0 === strpos($path, "$for/"))
|
||||
static::build($tree, $path, null, $text, $activeUrl);
|
||||
}
|
||||
}
|
||||
foreach (static::$appends as $path => $text) {
|
||||
$url = null;
|
||||
if (is_array($text)) {
|
||||
if (isset($text['url'])) {
|
||||
$url = $text['url'];
|
||||
$text = $text['text'];
|
||||
} else {
|
||||
$url = current(array_keys($text));
|
||||
$text = current($text);
|
||||
}
|
||||
}
|
||||
if (is_numeric($path)) {
|
||||
$path = $text;
|
||||
$text = null;
|
||||
}
|
||||
if (empty($for) || 0 === strpos($path, "$for/"))
|
||||
static::build($tree, $path, $url, $text, $activeUrl);
|
||||
}
|
||||
if (!empty($for)) {
|
||||
$for = explode('/', $for);
|
||||
$p = & $tree;
|
||||
foreach ($for as $f) {
|
||||
if (isset($p[$f]['children'])) {
|
||||
$p = & $p[$f]['children'];
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
return $p;
|
||||
}
|
||||
return $tree;
|
||||
}
|
||||
|
||||
protected static function build(&$tree, $path,
|
||||
$url = null, $text = null, $activeUrl = null)
|
||||
{
|
||||
$parts = explode('/', $path);
|
||||
if (count($parts) == 1 && empty($parts[0]))
|
||||
$parts = array(static::$root);
|
||||
$p = & $tree;
|
||||
$end = end($parts);
|
||||
foreach ($parts as $part) {
|
||||
if (!isset($p[$part])) {
|
||||
$p[$part] = array(
|
||||
'href' => '#',
|
||||
'text' => static::title($part)
|
||||
);
|
||||
if ($part == $end) {
|
||||
$p[$part]['class'] = $part;
|
||||
if ($text)
|
||||
$p[$part]['text'] = $text;
|
||||
if (is_null($url)) {
|
||||
if (empty($path) && !empty(static::$extension))
|
||||
$path = 'index';
|
||||
$p[$part]['href'] = Util::$restler->getBaseUrl()
|
||||
. '/' . $path . static::$extension;
|
||||
} else {
|
||||
if (empty($url) && !empty(static::$extension))
|
||||
$url = 'index';
|
||||
$p[$part]['href'] = $url . static::$extension;
|
||||
}
|
||||
if ($path == $activeUrl) {
|
||||
$p[$part]['active'] = true;
|
||||
}
|
||||
}
|
||||
$p[$part]['children'] = array();
|
||||
|
||||
}
|
||||
$p = & $p[$part]['children'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static function title($name)
|
||||
{
|
||||
if (empty($name)) {
|
||||
$name = static::$root;
|
||||
} else {
|
||||
$name = ltrim($name, '#');
|
||||
}
|
||||
return ucfirst(preg_replace(array('/(?<=[^A-Z])([A-Z])/', '/(?<=[^0-9])([0-9])/'), ' $0', $name));
|
||||
}
|
||||
|
||||
}
|
||||
282
htdocs/includes/restler/UI/Tags.php
Normal file
282
htdocs/includes/restler/UI/Tags.php
Normal file
@ -0,0 +1,282 @@
|
||||
<?php
|
||||
namespace Luracast\Restler\UI;
|
||||
|
||||
use ArrayAccess;
|
||||
use Countable;
|
||||
use Luracast\Restler\Util;
|
||||
|
||||
/**
|
||||
* Utility class for generating html tags in an object oriented way
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* ============================ magic properties ==============================
|
||||
* @property Tags parent parent tag
|
||||
* ============================== magic methods ===============================
|
||||
* @method Tags name(string $value) name attribute
|
||||
* @method Tags action(string $value) action attribute
|
||||
* @method Tags placeholder(string $value) placeholder attribute
|
||||
* @method Tags value(string $value) value attribute
|
||||
* @method Tags required(boolean $value) required attribute
|
||||
* @method Tags class(string $value) required attribute
|
||||
*
|
||||
* =========================== static magic methods ============================
|
||||
* @method static Tags form() creates a html form
|
||||
* @method static Tags input() creates a html input element
|
||||
* @method static Tags button() creates a html button element
|
||||
*
|
||||
*/
|
||||
class Tags implements ArrayAccess, Countable
|
||||
{
|
||||
public static $humanReadable = true;
|
||||
public static $initializer = null;
|
||||
protected static $instances = array();
|
||||
public $prefix = '';
|
||||
public $indent = ' ';
|
||||
public $tag;
|
||||
protected $attributes = array();
|
||||
protected $children = array();
|
||||
protected $_parent;
|
||||
|
||||
public function __construct($name = null, array $children = array())
|
||||
{
|
||||
$this->tag = $name;
|
||||
$c = array();
|
||||
foreach ($children as $child) {
|
||||
is_array($child)
|
||||
? $c = array_merge($c, $child)
|
||||
: $c [] = $child;
|
||||
}
|
||||
$this->markAsChildren($c);
|
||||
$this->children = $c;
|
||||
if (static::$initializer)
|
||||
call_user_func_array(static::$initializer, array(& $this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Tag by id
|
||||
*
|
||||
* Retrieve a tag by its id attribute
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return Tags|null
|
||||
*/
|
||||
public static function byId($id)
|
||||
{
|
||||
return Util::nestedValue(static::$instances, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param array $children
|
||||
*
|
||||
* @return Tags
|
||||
*/
|
||||
public static function __callStatic($name, array $children)
|
||||
{
|
||||
return new static($name, $children);
|
||||
}
|
||||
|
||||
public function toString($prefix = '', $indent = ' ')
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
$this->indent = $indent;
|
||||
return $this->__toString();
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$children = '';
|
||||
if (static::$humanReadable) {
|
||||
$lineBreak = false;
|
||||
foreach ($this->children as $key => $child) {
|
||||
$prefix = $this->prefix;
|
||||
if (!is_null($this->tag))
|
||||
$prefix .= $this->indent;
|
||||
if ($child instanceof $this) {
|
||||
$child->prefix = $prefix;
|
||||
$child->indent = $this->indent;
|
||||
$children .= PHP_EOL . $child;
|
||||
$lineBreak = true;
|
||||
} else {
|
||||
$children .= $child;
|
||||
}
|
||||
}
|
||||
if ($lineBreak)
|
||||
$children .= PHP_EOL . $this->prefix;
|
||||
} else {
|
||||
$children = implode('', $this->children);
|
||||
}
|
||||
if (is_null($this->tag))
|
||||
return $children;
|
||||
$attributes = '';
|
||||
foreach ($this->attributes as $attribute => &$value)
|
||||
$attributes .= " $attribute=\"$value\"";
|
||||
|
||||
if (count($this->children))
|
||||
return static::$humanReadable
|
||||
? "$this->prefix<{$this->tag}{$attributes}>"
|
||||
. "$children"
|
||||
. "</{$this->tag}>"
|
||||
: "<{$this->tag}{$attributes}>$children</{$this->tag}>";
|
||||
|
||||
return "$this->prefix<{$this->tag}{$attributes}/>";
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
$r = array();
|
||||
$r['attributes'] = $this->attributes;
|
||||
$r['tag'] = $this->tag;
|
||||
$children = array();
|
||||
foreach ($this->children as $key => $child) {
|
||||
$children[$key] = $child instanceof $this
|
||||
? $child->toArray()
|
||||
: $child;
|
||||
}
|
||||
$r['children'] = $children;
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the id attribute of the current tag
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id($value)
|
||||
{
|
||||
$this->attributes['id'] = isset($value)
|
||||
? (string)$value
|
||||
: Util::nestedValue($this->attributes, 'name');
|
||||
static::$instances[$value] = $this;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if ('parent' == $name)
|
||||
return $this->_parent;
|
||||
if (isset($this->attributes[$name]))
|
||||
return $this->attributes[$name];
|
||||
return;
|
||||
}
|
||||
|
||||
public function __set($name, $value)
|
||||
{
|
||||
if ('parent' == $name) {
|
||||
if ($this->_parent) {
|
||||
unset($this->_parent[array_search($this, $this->_parent->children)]);
|
||||
}
|
||||
if (!empty($value)) {
|
||||
$value[] = $this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($this->attributes[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $attribute
|
||||
* @param $value
|
||||
*
|
||||
* @return Tags
|
||||
*/
|
||||
public function __call($attribute, $value)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
return isset($this->attributes[$attribute])
|
||||
? $this->attributes[$attribute]
|
||||
: null;
|
||||
}
|
||||
$value = $value[0];
|
||||
if (is_null($value)) {
|
||||
unset($this->attributes[$attribute]);
|
||||
return $this;
|
||||
}
|
||||
$this->attributes[$attribute] = is_bool($value)
|
||||
? ($value ? 'true' : 'false')
|
||||
: @(string)$value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function offsetGet($index)
|
||||
{
|
||||
if ($this->offsetExists($index)) {
|
||||
return $this->children[$index];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function offsetExists($index)
|
||||
{
|
||||
return isset($this->children[$index]);
|
||||
}
|
||||
|
||||
public function offsetSet($index, $value)
|
||||
{
|
||||
if ($index) {
|
||||
$this->children[$index] = $value;
|
||||
} elseif (is_array($value)) {
|
||||
$c = array();
|
||||
foreach ($value as $child) {
|
||||
is_array($child)
|
||||
? $c = array_merge($c, $child)
|
||||
: $c [] = $child;
|
||||
}
|
||||
$this->markAsChildren($c);
|
||||
$this->children += $c;
|
||||
} else {
|
||||
$c = array($value);
|
||||
$this->markAsChildren($c);
|
||||
$this->children[] = $value;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function offsetUnset($index)
|
||||
{
|
||||
$this->children[$index]->_parent = null;
|
||||
unset($this->children[$index]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getContents()
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->children);
|
||||
}
|
||||
|
||||
private function markAsChildren(& $children)
|
||||
{
|
||||
foreach ($children as $i => $child) {
|
||||
if (is_string($child))
|
||||
continue;
|
||||
if (!is_object($child)) {
|
||||
unset($children[$i]);
|
||||
continue;
|
||||
}
|
||||
//echo $child;
|
||||
if (isset($child->_parent) && $child->_parent != $this) {
|
||||
//remove from current parent
|
||||
unset($child->_parent[array_search($child, $child->_parent->children)]);
|
||||
}
|
||||
$child->_parent = $this;
|
||||
}
|
||||
}
|
||||
}
|
||||
100
htdocs/includes/restler/User.php
Normal file
100
htdocs/includes/restler/User.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
/**
|
||||
* Information gathered about the api user is kept here using static methods
|
||||
* and properties for other classes to make use of them.
|
||||
* Typically Authentication classes populate them
|
||||
*
|
||||
* @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 User implements iIdentifyUser
|
||||
{
|
||||
private static $initialized = false;
|
||||
public static $id = null;
|
||||
public static $cacheId = null;
|
||||
public static $ip;
|
||||
public static $browser = '';
|
||||
public static $platform = '';
|
||||
|
||||
public static function init()
|
||||
{
|
||||
static::$initialized = true;
|
||||
static::$ip = static::getIpAddress();
|
||||
}
|
||||
|
||||
public static function getUniqueIdentifier($includePlatform = false)
|
||||
{
|
||||
if (!static::$initialized) static::init();
|
||||
return static::$id ? : base64_encode('ip:' . ($includePlatform
|
||||
? static::$ip . '-' . static::$platform
|
||||
: static::$ip
|
||||
));
|
||||
}
|
||||
|
||||
public static function getIpAddress($ignoreProxies = false)
|
||||
{
|
||||
foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR',
|
||||
'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP',
|
||||
'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED',
|
||||
'REMOTE_ADDR') as $key) {
|
||||
if (array_key_exists($key, $_SERVER) === true) {
|
||||
foreach (explode(',', $_SERVER[$key]) as $ip) {
|
||||
$ip = trim($ip); // just to be safe
|
||||
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4
|
||||
| FILTER_FLAG_NO_PRIV_RANGE
|
||||
| FILTER_FLAG_NO_RES_RANGE) !== false
|
||||
) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication classes should call this method
|
||||
*
|
||||
* @param string $id user id as identified by the authentication classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setUniqueIdentifier($id)
|
||||
{
|
||||
static::$id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* User identity to be used for caching purpose
|
||||
*
|
||||
* When the dynamic cache service places an object in the cache, it needs to
|
||||
* label it with a unique identifying string known as a cache ID. This
|
||||
* method gives that identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCacheIdentifier()
|
||||
{
|
||||
return static::$cacheId ?: static::$id;
|
||||
}
|
||||
|
||||
/**
|
||||
* User identity for caching purpose
|
||||
*
|
||||
* In a role based access control system this will be based on role
|
||||
*
|
||||
* @param $id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setCacheIdentifier($id)
|
||||
{
|
||||
static::$cacheId = $id;
|
||||
}
|
||||
}
|
||||
201
htdocs/includes/restler/Util.php
Normal file
201
htdocs/includes/restler/Util.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
/**
|
||||
* Describe the purpose of this class/interface/trait
|
||||
*
|
||||
* @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 Util
|
||||
{
|
||||
/**
|
||||
* @var Restler instance injected at runtime
|
||||
*/
|
||||
public static $restler;
|
||||
|
||||
/**
|
||||
* verify if the given data type string is scalar or not
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $type data type as string
|
||||
*
|
||||
* @return bool true or false
|
||||
*/
|
||||
public static function isObjectOrArray($type)
|
||||
{
|
||||
if (is_array($type)) {
|
||||
foreach ($type as $t) {
|
||||
if (static::isObjectOrArray($t)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return !(boolean)strpos('|bool|boolean|int|float|string|', $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value deeply nested inside an array / object
|
||||
*
|
||||
* Using isset() to test the presence of nested value can give a false positive
|
||||
*
|
||||
* This method serves that need
|
||||
*
|
||||
* When the deeply nested property is found its value is returned, otherwise
|
||||
* false is returned.
|
||||
*
|
||||
* @param array $from array to extract the value from
|
||||
* @param string|array $key ... pass more to go deeply inside the array
|
||||
* alternatively you can pass a single array
|
||||
*
|
||||
* @return null|mixed null when not found, value otherwise
|
||||
*/
|
||||
public static function nestedValue($from, $key/**, $key2 ... $key`n` */)
|
||||
{
|
||||
if (is_array($key)) {
|
||||
$keys = $key;
|
||||
} else {
|
||||
$keys = func_get_args();
|
||||
array_shift($keys);
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (is_array($from) && isset($from[$key])) {
|
||||
$from = $from[$key];
|
||||
continue;
|
||||
} elseif (is_object($from) && isset($from->{$key})) {
|
||||
$from = $from->{$key};
|
||||
continue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return $from;
|
||||
}
|
||||
|
||||
public static function getResourcePath($className,
|
||||
$resourcePath = null,
|
||||
$prefix = '')
|
||||
{
|
||||
if (is_null($resourcePath)) {
|
||||
if (Defaults::$autoRoutingEnabled) {
|
||||
$resourcePath = strtolower($className);
|
||||
if (false !== ($index = strrpos($className, '\\')))
|
||||
$resourcePath = substr($resourcePath, $index + 1);
|
||||
if (false !== ($index = strrpos($resourcePath, '_')))
|
||||
$resourcePath = substr($resourcePath, $index + 1);
|
||||
} else {
|
||||
$resourcePath = '';
|
||||
}
|
||||
} else
|
||||
$resourcePath = trim($resourcePath, '/');
|
||||
if (strlen($resourcePath) > 0)
|
||||
$resourcePath .= '/';
|
||||
return $prefix . $resourcePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two strings and remove the common
|
||||
* sub string from the first string and return it
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $fromPath
|
||||
* @param string $usingPath
|
||||
* @param string $char
|
||||
* optional, set it as
|
||||
* blank string for char by char comparison
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function removeCommonPath($fromPath, $usingPath, $char = '/')
|
||||
{
|
||||
if (empty($fromPath))
|
||||
return '';
|
||||
$fromPath = explode($char, $fromPath);
|
||||
$usingPath = explode($char, $usingPath);
|
||||
while (count($usingPath)) {
|
||||
if (count($fromPath) && $fromPath[0] == $usingPath[0]) {
|
||||
array_shift($fromPath);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
array_shift($usingPath);
|
||||
}
|
||||
return implode($char, $fromPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the request to figure out the http request type
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @return string which will be one of the following
|
||||
* [GET, POST, PUT, PATCH, DELETE]
|
||||
* @example GET
|
||||
*/
|
||||
public static function getRequestMethod()
|
||||
{
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
|
||||
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
|
||||
} elseif (
|
||||
!empty(Defaults::$httpMethodOverrideProperty)
|
||||
&& isset($_REQUEST[Defaults::$httpMethodOverrideProperty])
|
||||
) {
|
||||
// support for exceptional clients who can't set the header
|
||||
$m = strtoupper($_REQUEST[Defaults::$httpMethodOverrideProperty]);
|
||||
if ($m == 'PUT' || $m == 'DELETE' ||
|
||||
$m == 'POST' || $m == 'PATCH'
|
||||
) {
|
||||
$method = $m;
|
||||
}
|
||||
}
|
||||
// support for HEAD request
|
||||
if ($method == 'HEAD') {
|
||||
$method = 'GET';
|
||||
}
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass any content negotiation header such as Accept,
|
||||
* Accept-Language to break it up and sort the resulting array by
|
||||
* the order of negotiation.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $accept header value
|
||||
*
|
||||
* @return array sorted by the priority
|
||||
*/
|
||||
public static function sortByPriority($accept)
|
||||
{
|
||||
$acceptList = array();
|
||||
$accepts = explode(',', strtolower($accept));
|
||||
if (!is_array($accepts)) {
|
||||
$accepts = array($accepts);
|
||||
}
|
||||
foreach ($accepts as $pos => $accept) {
|
||||
$parts = explode(';q=', trim($accept));
|
||||
$type = array_shift($parts);
|
||||
$quality = count($parts) ?
|
||||
floatval(array_shift($parts)) :
|
||||
(1000 - $pos) / 1000;
|
||||
$acceptList[$type] = $quality;
|
||||
}
|
||||
arsort($acceptList);
|
||||
return $acceptList;
|
||||
}
|
||||
|
||||
public static function getShortName($className)
|
||||
{
|
||||
$className = explode('\\', $className);
|
||||
return end($className);
|
||||
}
|
||||
}
|
||||
|
||||
10
htdocs/includes/restler/compatibility/iAuthenticate.php
Normal file
10
htdocs/includes/restler/compatibility/iAuthenticate.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Interface iAuthenticate only exists for compatibility mode for Restler 2 and below, it should
|
||||
* not be used otherwise.
|
||||
*/
|
||||
interface iAuthenticate
|
||||
{
|
||||
public function __isAuthenticated();
|
||||
}
|
||||
14
htdocs/includes/restler/compatibility/restler1.php
Normal file
14
htdocs/includes/restler/compatibility/restler1.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* Restler 1 compatibility mode enabler
|
||||
*/
|
||||
use Luracast\Restler\Defaults;
|
||||
|
||||
//changes in iAuthenticate
|
||||
Defaults::$authenticationMethod = 'isAuthenticated';
|
||||
include __DIR__ . '/iAuthenticate.php';
|
||||
|
||||
//changes in routing
|
||||
Defaults::$autoRoutingEnabled = false;
|
||||
Defaults::$smartParameterParsing = false;
|
||||
Defaults::$autoValidationEnabled = false;
|
||||
40
htdocs/includes/restler/compatibility/restler2.php
Normal file
40
htdocs/includes/restler/compatibility/restler2.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* Restler 2 compatibility mode enabler
|
||||
*/
|
||||
use Luracast\Restler\Defaults;
|
||||
use Luracast\Restler\AutoLoader;
|
||||
use Luracast\Restler\CommentParser;
|
||||
|
||||
//changes in auto loading
|
||||
$classMap = array();
|
||||
//find lowercase php files representing a class/interface
|
||||
foreach (explode(PATH_SEPARATOR, get_include_path()) as $path)
|
||||
foreach (new DirectoryIterator($path) as $fileInfo)
|
||||
if ($fileInfo->isFile()
|
||||
&& 'php' === $fileInfo->getExtension()
|
||||
&& ctype_lower($fileInfo->getBasename('.php'))
|
||||
&& preg_match(
|
||||
'/^ *(class|interface|abstract +class)'
|
||||
. ' +([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/m',
|
||||
file_get_contents($fileInfo->getPathname()),
|
||||
$matches
|
||||
)
|
||||
)
|
||||
$classMap[$matches[2]] = $fileInfo->getPathname();
|
||||
|
||||
AutoLoader::seen($classMap);
|
||||
|
||||
//changes in iAuthenticate
|
||||
Defaults::$authenticationMethod = '__isAuthenticated';
|
||||
|
||||
include __DIR__ . '/iAuthenticate.php';
|
||||
|
||||
//changes in auto routing
|
||||
Defaults::$smartAutoRouting = false;
|
||||
Defaults::$smartParameterParsing = false;
|
||||
Defaults::$autoValidationEnabled = false;
|
||||
|
||||
//changes in parsing embedded data in comments
|
||||
CommentParser::$embeddedDataPattern = '/\((\S+)\)/ms';
|
||||
CommentParser::$embeddedDataIndex = 1;
|
||||
70
htdocs/includes/restler/composer.json
Normal file
70
htdocs/includes/restler/composer.json
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"name":"restler/framework",
|
||||
"description":"Just the Restler Framework without the tests and examples",
|
||||
"type":"library",
|
||||
"keywords":["server","api","framework","REST"],
|
||||
"homepage":"http://luracast.com/products/restler/",
|
||||
"license":"LGPL-2.1",
|
||||
"authors":[
|
||||
{
|
||||
"name":"Luracast",
|
||||
"email":"arul@luracast.com"
|
||||
},
|
||||
{
|
||||
"name":"Nick nickl- Lombard",
|
||||
"email":"github@jigsoft.co.za"
|
||||
}
|
||||
],
|
||||
"extra":{
|
||||
"branch-alias":{
|
||||
"master":"v3.0.x-dev"
|
||||
}
|
||||
},
|
||||
"suggest":{
|
||||
"luracast/explorer":"Restler's very own api explorer (see require-dev for details)",
|
||||
"rodneyrehm/plist":"Restler supports tho Apple plist xml format (see require-dev for details)",
|
||||
"zendframework/zendamf":"Support for the amf document format (see require-dev for details)",
|
||||
"symfony/yaml":"Restler can produce content in yaml format as well (see require-dev for details)",
|
||||
"twig/twig":"Restler can render HtmlView using twig templates (see require-dev for details)",
|
||||
"mustache/mustache":"Restler can render HtmlView using mustache/handlebar templates (see require-dev for details)",
|
||||
"bshaffer/oauth2-server-php":"Restler can provide OAuth2 authentication using this library (see require-dev for details)"
|
||||
},
|
||||
"require":{
|
||||
"php":">=5.3.0"
|
||||
},
|
||||
"require-dev":{
|
||||
"luracast/explorer":"*",
|
||||
"rodneyrehm/plist":"dev-master",
|
||||
"zendframework/zendamf":"dev-master",
|
||||
"symfony/yaml":"*",
|
||||
"mustache/mustache": "dev-master",
|
||||
"twig/twig": "v1.13.0",
|
||||
"bshaffer/oauth2-server-php":"v1.0"
|
||||
},
|
||||
"repositories":[
|
||||
{
|
||||
"type":"vcs",
|
||||
"url":"https://github.com/zendframework/ZendAmf.git"
|
||||
},
|
||||
{
|
||||
"type":"package",
|
||||
"package":{
|
||||
"name":"luracast/explorer",
|
||||
"version":"v3.0.0",
|
||||
"dist":{
|
||||
"type":"zip",
|
||||
"url":"https://github.com/Luracast/Restler-API-Explorer/zipball/v3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"autoload":{
|
||||
"psr-0":{
|
||||
"Luracast\\Restler":""
|
||||
}
|
||||
},
|
||||
"target-dir": "Luracast/Restler",
|
||||
"replace": {
|
||||
"luracast/restler":"3.*"
|
||||
}
|
||||
}
|
||||
23
htdocs/includes/restler/composer.lock
generated
Normal file
23
htdocs/includes/restler/composer.lock
generated
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "ee84444dcf34101555d20a813d528c44",
|
||||
"packages": [],
|
||||
"packages-dev": null,
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"rodneyrehm/plist": 20,
|
||||
"zendframework/zendamf": 20,
|
||||
"mustache/mustache": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"platform-dev": []
|
||||
}
|
||||
25
htdocs/includes/restler/iAuthenticate.php
Normal file
25
htdocs/includes/restler/iAuthenticate.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
/**
|
||||
* Interface for creating authentication classes
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage auth
|
||||
* @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
|
||||
*/
|
||||
interface iAuthenticate extends iFilter
|
||||
{
|
||||
/**
|
||||
* @return string string to be used with WWW-Authenticate header
|
||||
* @example Basic
|
||||
* @example Digest
|
||||
* @example OAuth
|
||||
*/
|
||||
public function __getWWWAuthenticateString();
|
||||
}
|
||||
63
htdocs/includes/restler/iCache.php
Executable file
63
htdocs/includes/restler/iCache.php
Executable file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
/**
|
||||
* Interface for the cache system that manages caching of given data
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
interface iCache
|
||||
{
|
||||
/**
|
||||
* store data in the cache
|
||||
*
|
||||
* @abstract
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $data
|
||||
*
|
||||
* @return boolean true if successful
|
||||
*/
|
||||
public function set($name, $data);
|
||||
|
||||
/**
|
||||
* retrieve data from the cache
|
||||
*
|
||||
* @abstract
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $ignoreErrors
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $ignoreErrors = false);
|
||||
|
||||
/**
|
||||
* delete data from the cache
|
||||
*
|
||||
* @abstract
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $ignoreErrors
|
||||
*
|
||||
* @return boolean true if successful
|
||||
*/
|
||||
public function clear($name, $ignoreErrors = false);
|
||||
|
||||
/**
|
||||
* check if the given name is cached
|
||||
*
|
||||
* @abstract
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return boolean true if cached
|
||||
*/
|
||||
public function isCached($name);
|
||||
}
|
||||
|
||||
36
htdocs/includes/restler/iCompose.php
Normal file
36
htdocs/includes/restler/iCompose.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Interface for composing response
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage result
|
||||
* @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
|
||||
*/
|
||||
interface iCompose {
|
||||
/**
|
||||
* Result of an api call is passed to this method
|
||||
* to create a standard structure for the data
|
||||
*
|
||||
* @param mixed $result can be a primitive or array or object
|
||||
*/
|
||||
public function response($result);
|
||||
|
||||
/**
|
||||
* When the api call results in RestException this method
|
||||
* will be called to return the error message
|
||||
*
|
||||
* @param RestException $exception exception that has reasons for failure
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function message(RestException $exception);
|
||||
}
|
||||
30
htdocs/includes/restler/iFilter.php
Normal file
30
htdocs/includes/restler/iFilter.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
/**
|
||||
* Interface for creating classes that perform authentication/access
|
||||
* verification
|
||||
*
|
||||
* @category Framework
|
||||
* @package Restler
|
||||
* @subpackage auth
|
||||
* @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
|
||||
*/
|
||||
interface iFilter
|
||||
{
|
||||
/**
|
||||
* Access verification method.
|
||||
*
|
||||
* API access will be denied when this method returns false
|
||||
*
|
||||
* @abstract
|
||||
* @return boolean true when api access is allowed false otherwise
|
||||
*/
|
||||
public function __isAllowed();
|
||||
|
||||
}
|
||||
|
||||
63
htdocs/includes/restler/iIdentifyUser.php
Normal file
63
htdocs/includes/restler/iIdentifyUser.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
/**
|
||||
* Interface to identify the user
|
||||
*
|
||||
* When the user is known we will be able to monitor, rate limit and do more
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
interface iIdentifyUser
|
||||
{
|
||||
/**
|
||||
* A way to uniquely identify the current api consumer
|
||||
*
|
||||
* When his user id is known it should be used otherwise ip address
|
||||
* can be used
|
||||
*
|
||||
* @param bool $includePlatform Should we consider user alone or should
|
||||
* consider the application/platform/device
|
||||
* as well for generating unique id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getUniqueIdentifier($includePlatform = false);
|
||||
|
||||
/**
|
||||
* User identity to be used for caching purpose
|
||||
*
|
||||
* When the dynamic cache service places an object in the cache, it needs to
|
||||
* label it with a unique identifying string known as a cache ID. This
|
||||
* method gives that identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCacheIdentifier();
|
||||
|
||||
/**
|
||||
* Authentication classes should call this method
|
||||
*
|
||||
* @param string $id user id as identified by the authentication classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setUniqueIdentifier($id);
|
||||
|
||||
/**
|
||||
* User identity for caching purpose
|
||||
*
|
||||
* In a role based access control system this will be based on role
|
||||
*
|
||||
* @param $id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setCacheIdentifier($id);
|
||||
}
|
||||
11
htdocs/includes/restler/iProvideMultiVersionApi.php
Normal file
11
htdocs/includes/restler/iProvideMultiVersionApi.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
|
||||
interface iProvideMultiVersionApi {
|
||||
/**
|
||||
* Maximum api version supported by the api class
|
||||
* @return int
|
||||
*/
|
||||
public static function __getMaximumSupportedVersion();
|
||||
}
|
||||
32
htdocs/includes/restler/iUseAuthentication.php
Normal file
32
htdocs/includes/restler/iUseAuthentication.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Luracast\Restler;
|
||||
|
||||
/**
|
||||
* Api classes or filter classes can implement this interface to know about
|
||||
* authentication status
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
interface iUseAuthentication
|
||||
{
|
||||
/**
|
||||
* This method will be called first for filter classes and api classes so
|
||||
* that they can respond accordingly for filer method call and api method
|
||||
* calls
|
||||
*
|
||||
* @abstract
|
||||
*
|
||||
* @param bool $isAuthenticated passes true when the authentication is
|
||||
* done false otherwise
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __setAuthenticationStatus($isAuthenticated=false);
|
||||
}
|
||||
|
||||
7
htdocs/includes/restler/vendor/autoload.php
vendored
Normal file
7
htdocs/includes/restler/vendor/autoload.php
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInite65e15efc7e9ea1f2b7ba7fa697ba485::getLoader();
|
||||
413
htdocs/includes/restler/vendor/composer/ClassLoader.php
vendored
Normal file
413
htdocs/includes/restler/vendor/composer/ClassLoader.php
vendored
Normal file
@ -0,0 +1,413 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0 class loader
|
||||
*
|
||||
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
||||
if ('\\' == $class[0]) {
|
||||
$class = substr($class, 1);
|
||||
}
|
||||
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if ($file === null && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if ($file === null) {
|
||||
// Remember that this class does not exist.
|
||||
return $this->classMap[$class] = false;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
||||
9
htdocs/includes/restler/vendor/composer/autoload_classmap.php
vendored
Normal file
9
htdocs/includes/restler/vendor/composer/autoload_classmap.php
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
10
htdocs/includes/restler/vendor/composer/autoload_namespaces.php
vendored
Normal file
10
htdocs/includes/restler/vendor/composer/autoload_namespaces.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Luracast\\Restler' => array($baseDir . '/'),
|
||||
);
|
||||
9
htdocs/includes/restler/vendor/composer/autoload_psr4.php
vendored
Normal file
9
htdocs/includes/restler/vendor/composer/autoload_psr4.php
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
70
htdocs/includes/restler/vendor/composer/autoload_real.php
vendored
Normal file
70
htdocs/includes/restler/vendor/composer/autoload_real.php
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInite65e15efc7e9ea1f2b7ba7fa697ba485
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInite65e15efc7e9ea1f2b7ba7fa697ba485', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInite65e15efc7e9ea1f2b7ba7fa697ba485', 'loadClassLoader'));
|
||||
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInite65e15efc7e9ea1f2b7ba7fa697ba485', 'autoload'), true, true);
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
return $loader;
|
||||
}
|
||||
|
||||
public static function autoload($class)
|
||||
{
|
||||
$dir = dirname(dirname(__DIR__)) . '/';
|
||||
$prefixes = array('Luracast\\Restler');
|
||||
foreach ($prefixes as $prefix) {
|
||||
if (0 !== strpos($class, $prefix)) {
|
||||
continue;
|
||||
}
|
||||
$path = $dir . implode('/', array_slice(explode('\\', $class), 2)).'.php';
|
||||
if (!$path = stream_resolve_include_path($path)) {
|
||||
return false;
|
||||
}
|
||||
require $path;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequiree65e15efc7e9ea1f2b7ba7fa697ba485($file)
|
||||
{
|
||||
require $file;
|
||||
}
|
||||
1
htdocs/includes/restler/vendor/composer/installed.json
vendored
Normal file
1
htdocs/includes/restler/vendor/composer/installed.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
[]
|
||||
441
htdocs/includes/restler/views/debug.css
Normal file
441
htdocs/includes/restler/views/debug.css
Normal file
File diff suppressed because one or more lines are too long
169
htdocs/includes/restler/views/debug.php
Normal file
169
htdocs/includes/restler/views/debug.php
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
use Luracast\Restler\Restler;
|
||||
use Luracast\Restler\Util;
|
||||
|
||||
$template_vars = $data;//get_defined_vars();
|
||||
|
||||
unset($template_vars['response']);
|
||||
unset($template_vars['api']);
|
||||
unset($template_vars['request']);
|
||||
unset($template_vars['stages']);
|
||||
$template_vars['request'] = $data['request'];
|
||||
$template_vars['stages'] = $data['stages'];
|
||||
|
||||
$call_trace = '';
|
||||
|
||||
function exceptions()
|
||||
{
|
||||
global $call_trace;
|
||||
$r = Util::$restler;
|
||||
$source = $r->_exceptions;
|
||||
if (count($source)) {
|
||||
$source = end($source);
|
||||
$traces = array();
|
||||
do {
|
||||
$traces += $source->getTrace();
|
||||
} while ($source = $source->getPrevious());
|
||||
$traces += debug_backtrace();
|
||||
$call_trace
|
||||
= parse_backtrace($traces, 0);
|
||||
} else {
|
||||
$call_trace
|
||||
= parse_backtrace(debug_backtrace());
|
||||
}
|
||||
|
||||
}
|
||||
exceptions();
|
||||
|
||||
function parse_backtrace($raw, $skip = 1)
|
||||
{
|
||||
$output = "";
|
||||
foreach ($raw as $entry) {
|
||||
if ($skip-- > 0) {
|
||||
continue;
|
||||
}
|
||||
//$output .= print_r($entry, true) . "\n";
|
||||
$output .= "\nFile: " . $entry['file'] . " (Line: " . $entry['line'] . ")\n";
|
||||
if (isset($entry['class']))
|
||||
$output .= $entry['class'] . "::";
|
||||
$output .= $entry['function']
|
||||
. "( " . json_encode($entry['args']) . " )\n";
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
//print_r(get_defined_vars());
|
||||
//print_r($response);
|
||||
$icon;
|
||||
if ($success && isset($api)) {
|
||||
$arguments = implode(', ', $api->parameters);
|
||||
$icon = "<icon class=\"success\"></icon>";
|
||||
$title = "{$api->className}::"
|
||||
. "{$api->methodName}({$arguments})";
|
||||
} else {
|
||||
if (isset($response['error']['message'])) {
|
||||
$icon = '<icon class="denied"></icon>';
|
||||
$title = end(explode(':',$response['error']['message']));
|
||||
} else {
|
||||
$icon = '<icon class="warning"></icon>';
|
||||
$title = 'No Matching Resource';
|
||||
}
|
||||
}
|
||||
function render($data, $shadow=true)
|
||||
{
|
||||
$r = '';
|
||||
if (empty($data))
|
||||
return $r;
|
||||
$r .= $shadow ? "<ul class=\"shadow\">\n": "<ul>\n";
|
||||
if (is_array($data)) {
|
||||
// field name
|
||||
foreach ($data as $key => $value) {
|
||||
$r .= '<li>';
|
||||
$r .= is_numeric($key)
|
||||
? "<strong>[$key]</strong> "
|
||||
: "<strong>$key: </strong>";
|
||||
$r .= '<span>';
|
||||
if (is_array($value)) {
|
||||
// recursive
|
||||
$r .= render($value,false);
|
||||
} else {
|
||||
// value, with hyperlinked hyperlinks
|
||||
if (is_bool($value)) {
|
||||
$value = $value ? 'true' : 'false';
|
||||
}
|
||||
$value = htmlentities($value, ENT_COMPAT, 'UTF-8');
|
||||
if (strpos($value, 'http://') === 0) {
|
||||
$r .= '<a href="' . $value . '">' . $value . '</a>';
|
||||
} else {
|
||||
$r .= $value;
|
||||
}
|
||||
}
|
||||
$r .= "</span></li>\n";
|
||||
}
|
||||
} elseif (is_bool($data)) {
|
||||
$r .= '<li>' . ($data ? 'true' : 'false') . '</li>';
|
||||
} else {
|
||||
$r .= "<li><strong>$data</strong></li>";
|
||||
}
|
||||
$r .= "</ul>\n";
|
||||
return $r;
|
||||
}
|
||||
$reqHeadersArr = array();
|
||||
$requestHeaders = $_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['REQUEST_URI'] . ' ' . $_SERVER['SERVER_PROTOCOL'] . PHP_EOL;
|
||||
foreach ($reqHeadersArr as $key => $value) {
|
||||
if ($key == 'Host')
|
||||
continue;
|
||||
$requestHeaders .= "$key: $value" . PHP_EOL;
|
||||
}
|
||||
// $requestHeaders = $this->encode(apache_request_headers(), FALSE,
|
||||
// FALSE);
|
||||
$responseHeaders = implode(PHP_EOL, headers_list()).PHP_EOL.'Status: HTTP/1.1 ';
|
||||
$responseHeaders .= Util::$restler->responseCode.' '.\Luracast\Restler\RestException::$codes[Util::$restler->responseCode];
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title><?php echo $title?></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<style>
|
||||
<?php include __DIR__.'/debug.css'; ?>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="breadcrumbs-one">
|
||||
<?php
|
||||
if(Util::$restler->exception){
|
||||
$stages = Util::$restler->exception->getStages();
|
||||
$curStage = Util::$restler->exception->getStage();
|
||||
foreach($stages['success'] as $stage){
|
||||
echo "<a href=\"#\">$stage</a>";
|
||||
}
|
||||
foreach($stages['failure'] as $stage){
|
||||
echo '<a href="#" class="failure">'
|
||||
. $stage
|
||||
. ($stage==$curStage ? ' <span class="state"/> ' : '')
|
||||
. '</a>';
|
||||
}
|
||||
} else {
|
||||
foreach(Util::$restler->_events as $stage){
|
||||
echo "<a href=\"#\">$stage</a>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<header>
|
||||
<h1><?php echo $title ?></h1>
|
||||
</header>
|
||||
<article>
|
||||
|
||||
<h2>Response:<right><?php echo $icon;?></right></h2>
|
||||
<pre class="header"><?php echo $responseHeaders ?></pre>
|
||||
<?php echo render($response); ?>
|
||||
<h2>Additional Template Data:</h2>
|
||||
<?php echo render($template_vars); ?>
|
||||
<p>Restler v<?php echo Restler::VERSION?></p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
@ -85,7 +85,7 @@ ALTER TABLE llx_projet_task MODIFY COLUMN duration_effective real DEFAULT 0 NULL
|
||||
ALTER TABLE llx_projet_task MODIFY COLUMN planned_workload real DEFAULT 0 NULL;
|
||||
|
||||
|
||||
ALTER TABLE llx_commande_fournisseur MODIFY COLUMN date_livraison datetime;
|
||||
ALTER TABLE llx_commande_fournisseur MODIFY COLUMN date_livraison datetime;
|
||||
|
||||
-- Add id commandefourndet in llx_commande_fournisseur_dispatch to correct /fourn/commande/dispatch.php display when several times same product in supplier order
|
||||
ALTER TABLE llx_commande_fournisseur_dispatch ADD COLUMN fk_commandefourndet INTEGER NOT NULL DEFAULT 0 AFTER fk_product;
|
||||
@ -112,12 +112,12 @@ ALTER TABLE llx_product_price ADD COLUMN fk_price_expression integer DEFAULT NUL
|
||||
|
||||
|
||||
--create table for user conf of printing driver
|
||||
CREATE TABLE llx_printing
|
||||
CREATE TABLE llx_printing
|
||||
(
|
||||
rowid integer AUTO_INCREMENT PRIMARY KEY,
|
||||
tms timestamp,
|
||||
datec datetime,
|
||||
printer_name text NOT NULL,
|
||||
printer_name text NOT NULL,
|
||||
printer_location text NOT NULL,
|
||||
printer_id varchar(255) NOT NULL,
|
||||
copy integer NOT NULL DEFAULT '1',
|
||||
@ -199,7 +199,7 @@ CREATE TABLE llx_expensereport (
|
||||
total_ht double(24,8) DEFAULT 0,
|
||||
total_tva double(24,8) DEFAULT 0,
|
||||
localtax1 double(24,8) DEFAULT 0, -- amount total localtax1
|
||||
localtax2 double(24,8) DEFAULT 0, -- amount total localtax2
|
||||
localtax2 double(24,8) DEFAULT 0, -- amount total localtax2
|
||||
total_ttc double(24,8) DEFAULT 0,
|
||||
date_debut date NOT NULL,
|
||||
date_fin date NOT NULL,
|
||||
@ -292,11 +292,11 @@ ALTER TABLE llx_commande_fournisseurdet ADD COLUMN special_code integer DEFAULT
|
||||
ALTER TABLE llx_commande_fournisseurdet ADD COLUMN rang integer DEFAULT 0;
|
||||
ALTER TABLE llx_commande_fournisseurdet ADD COLUMN fk_parent_line integer NULL after fk_commande;
|
||||
|
||||
ALTER TABLE llx_projet ADD COLUMN date_close datetime DEFAULT NULL;
|
||||
ALTER TABLE llx_projet ADD COLUMN date_close datetime DEFAULT NULL;
|
||||
ALTER TABLE llx_projet ADD COLUMN fk_user_close integer DEFAULT NULL;
|
||||
|
||||
|
||||
|
||||
|
||||
-- Module AskPriceSupplier --
|
||||
CREATE TABLE llx_askpricesupplier (
|
||||
rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
@ -602,6 +602,12 @@ ALTER TABLE llx_user DROP INDEX idx_user_fk_societe;
|
||||
ALTER TABLE llx_user CHANGE COLUMN fk_societe fk_soc INTEGER;
|
||||
ALTER TABLE llx_user ADD INDEX idx_user_fk_societe (fk_soc);
|
||||
|
||||
-- API module
|
||||
ALTER TABLE llx_user ADD api_key VARCHAR(128) DEFAULT NULL AFTER pass_temp;
|
||||
ALTER TABLE llx_user ADD INDEX idx_user_api_key (api_key);
|
||||
|
||||
|
||||
|
||||
ALTER TABLE llx_actioncomm ADD COLUMN email_msgid varchar(256);
|
||||
ALTER TABLE llx_actioncomm ADD COLUMN email_from varchar(256);
|
||||
ALTER TABLE llx_actioncomm ADD COLUMN email_sender varchar(256);
|
||||
|
||||
@ -25,3 +25,4 @@ ALTER TABLE llx_user ADD INDEX idx_user_fk_societe (fk_soc);
|
||||
|
||||
ALTER TABLE llx_user ADD UNIQUE INDEX uk_user_fk_socpeople (fk_socpeople);
|
||||
ALTER TABLE llx_user ADD UNIQUE INDEX uk_user_fk_member (fk_member);
|
||||
ALTER TABLE llx_user ADD UNIQUE INDEX uk_user_api_key (api_key);
|
||||
|
||||
@ -34,6 +34,7 @@ create table llx_user
|
||||
pass varchar(32),
|
||||
pass_crypted varchar(128),
|
||||
pass_temp varchar(32), -- temporary password when asked for forget password
|
||||
api_key varchar(128),
|
||||
civility varchar(6),
|
||||
lastname varchar(50),
|
||||
firstname varchar(50),
|
||||
|
||||
@ -526,6 +526,8 @@ Module2500Name=Electronic Content Management
|
||||
Module2500Desc=Save and share documents
|
||||
Module2600Name=WebServices
|
||||
Module2600Desc=Enable the Dolibarr web services server
|
||||
Module2610Name=Api
|
||||
Module2610Desc=Enable the Dolibarr REST API
|
||||
Module2650Name=WebServices (client)
|
||||
Module2650Desc=Enable the Dolibarr web services client (Can be used to push data/requests to external servers. Supplier orders supported only for the moment)
|
||||
Module2700Name=Gravatar
|
||||
@ -1558,6 +1560,13 @@ WebServicesSetup=Webservices module setup
|
||||
WebServicesDesc=By enabling this module, Dolibarr become a web service server to provide miscellaneous web services.
|
||||
WSDLCanBeDownloadedHere=WSDL descriptor files of provided services can be download here
|
||||
EndPointIs=SOAP clients must send their requests to the Dolibarr endpoint available at Url
|
||||
##### API ####
|
||||
ApiSetup=API module setup
|
||||
ApiDesc=By enabling this module, Dolibarr become a REST server to provide miscellaneous web services.
|
||||
KeyForApiAccess=Key to use API (parameter "api_key")
|
||||
ApiEndPointIs=You can access to the API at url
|
||||
ApiExporerIs=You can explore the API at url
|
||||
OnlyActiveElementsAreExposed=Only elements from enabled modules are exposed
|
||||
##### Bank #####
|
||||
BankSetupModule=Bank module setup
|
||||
FreeLegalTextOnChequeReceipts=Free text on cheque receipts
|
||||
|
||||
273
htdocs/product/class/api_product.class.php
Normal file
273
htdocs/product/class/api_product.class.php
Normal file
@ -0,0 +1,273 @@
|
||||
<?php
|
||||
/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use Luracast\Restler\RestException;
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||
|
||||
/**
|
||||
* API class for product object
|
||||
*
|
||||
* @smart-auto-routing false
|
||||
* @access protected
|
||||
* @class DolibarrApiAccess {@requires user,external}
|
||||
*
|
||||
*/
|
||||
class ProductApi extends DolibarrApi
|
||||
{
|
||||
/**
|
||||
* @var array $FIELDS Mandatory fields, checked when create and update object
|
||||
*/
|
||||
static $FIELDS = array(
|
||||
'ref',
|
||||
'label'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var Product $product {@type Product}
|
||||
*/
|
||||
public $product;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @url product/
|
||||
*
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
global $db, $conf;
|
||||
$this->db = $db;
|
||||
$this->product = new Product($this->db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties of a product object
|
||||
*
|
||||
* Return an array with product informations
|
||||
*
|
||||
* @param int $id ID of product
|
||||
* @param string $ref Product ref
|
||||
* @param string $ref_ext Product ref ext
|
||||
* @return array|mixed data without useless information
|
||||
*
|
||||
* @url GET product/{id}
|
||||
* @throws RestException
|
||||
*/
|
||||
function get($id='', $ref='', $ref_ext='')
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->produit->lire) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$result = $this->product->fetch($id,$ref,$ref_ext);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Product not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('product',$this->product->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
$this->product->load_stock();
|
||||
|
||||
return $this->_cleanObjectDatas($this->product);
|
||||
}
|
||||
|
||||
/**
|
||||
* List products
|
||||
*
|
||||
* Get a list of products
|
||||
*
|
||||
* @param int $mode Use this param to filter list (0 for all, 1 for only product, 2 for only service)
|
||||
* @param mixed $to_sell Filter products to sell (1) or not to sell (0)
|
||||
* @param mixed $to_buy Filter products to nuy (1) or not to buy (0)
|
||||
* @param string $sortfield Sort field
|
||||
* @param string $sortorder Sort order
|
||||
* @param int $limit Limit for list
|
||||
* @param int $page Page number
|
||||
*
|
||||
* @return array Array of product objects
|
||||
*
|
||||
* @url GET /product/list
|
||||
*/
|
||||
function getList($mode=0, $to_sell='', $to_buy='', $sortfield = "p.ref", $sortorder = 'ASC', $limit = 0, $page = 0) {
|
||||
global $db, $conf;
|
||||
|
||||
$obj_ret = array();
|
||||
|
||||
$socid = DolibarrApiAccess::$user->societe_id ? DolibarrApiAccess::$user->societe_id : '';
|
||||
|
||||
$sql ="SELECT rowid, ref, ref_ext";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."product as p";
|
||||
$sql.= ' WHERE p.entity IN ('.getEntity('product', 1).')';
|
||||
|
||||
// Show products
|
||||
if ($mode == 1) $sql.= " AND p.fk_product_type = 0";
|
||||
// Show services
|
||||
if ($mode == 2) $sql.= " AND p.fk_product_type = 1";
|
||||
// Show product on sell
|
||||
if ($to_sell) $sql.= " AND p.to_sell = ".$db->escape($to_sell);
|
||||
// Show product on buy
|
||||
if ($to_buy) $sql.= " AND p.to_nuy = ".$db->escape($to_nuy);
|
||||
|
||||
$nbtotalofrecords = 0;
|
||||
if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
|
||||
{
|
||||
$result = $db->query($sql);
|
||||
$nbtotalofrecords = $db->num_rows($result);
|
||||
}
|
||||
|
||||
$sql.= $db->order($sortfield, $sortorder);
|
||||
if ($limit) {
|
||||
if ($page < 0)
|
||||
{
|
||||
$page = 0;
|
||||
}
|
||||
$offset = $limit * $page;
|
||||
|
||||
$sql.= $db->plimit($limit + 1, $offset);
|
||||
}
|
||||
|
||||
$result = $db->query($sql);
|
||||
if ($result)
|
||||
{
|
||||
$num = $db->num_rows($result);
|
||||
while ($i < $num)
|
||||
{
|
||||
$obj = $db->fetch_object($result);
|
||||
$product_static = new Product($db);
|
||||
if($product_static->fetch($obj->rowid)) {
|
||||
$obj_ret[] = parent::_cleanObjectDatas($product_static);
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new RestException(503, 'Error when retrieve product list');
|
||||
}
|
||||
if( ! count($obj_ret)) {
|
||||
throw new RestException(404, 'No product found');
|
||||
}
|
||||
return $obj_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create product object
|
||||
*
|
||||
* @param array $request_data Request data
|
||||
* @return int ID of product
|
||||
*
|
||||
* @url POST product/
|
||||
*/
|
||||
function post($request_data = NULL)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->produit->creer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
// Check mandatory fields
|
||||
$result = $this->_validate($request_data);
|
||||
|
||||
foreach($request_data as $field => $value) {
|
||||
$this->product->$field = $value;
|
||||
}
|
||||
$result = $this->product->create(DolibarrApiAccess::$user);
|
||||
if($result < 0) {
|
||||
throw new RestException(503,'Error when creating product : '.$this->product->error);
|
||||
}
|
||||
|
||||
return $this->product->id;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update product
|
||||
*
|
||||
* @param int $id Id of product to update
|
||||
* @param array $request_data Datas
|
||||
* @return int
|
||||
*
|
||||
* @url PUT product/{id}
|
||||
*/
|
||||
function put($id, $request_data = NULL)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->produit->creer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
|
||||
$result = $this->product->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Product not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('product',$this->product->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
foreach($request_data as $field => $value) {
|
||||
$this->product->$field = $value;
|
||||
}
|
||||
|
||||
if($this->product->update($id, DolibarrApiAccess::$user,1,'','','update'))
|
||||
return $this->get ($id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete product
|
||||
*
|
||||
* @param int $id Product ID
|
||||
* @return array
|
||||
*
|
||||
* @url DELETE product/{id}
|
||||
*/
|
||||
function delete($id)
|
||||
{
|
||||
if(! DolibarrApiAccess::$user->rights->product->supprimer) {
|
||||
throw new RestException(401);
|
||||
}
|
||||
$result = $this->product->fetch($id);
|
||||
if( ! $result ) {
|
||||
throw new RestException(404, 'Product not found');
|
||||
}
|
||||
|
||||
if( ! DolibarrApi::_checkAccessToResource('product',$this->product->id)) {
|
||||
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
|
||||
}
|
||||
|
||||
return $this->product->delete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate fields before create or update object
|
||||
*
|
||||
* @param array $data Datas to validate
|
||||
* @return array
|
||||
* @throws RestException
|
||||
*/
|
||||
function _validate($data)
|
||||
{
|
||||
$product = array();
|
||||
foreach (ProductApi::$FIELDS as $field) {
|
||||
if (!isset($data[$field]))
|
||||
throw new RestException(400, "$field field missing");
|
||||
$product[$field] = $data[$field];
|
||||
}
|
||||
return $product;
|
||||
}
|
||||
}
|
||||
15
htdocs/public/api/.htaccess
Normal file
15
htdocs/public/api/.htaccess
Normal file
@ -0,0 +1,15 @@
|
||||
#
|
||||
# Apache configuration file to use API
|
||||
#
|
||||
|
||||
DirectoryIndex index.php
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule ^$ index.php [QSA,L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [QSA,L]
|
||||
</IfModule>
|
||||
<IfModule mod_php5.c>
|
||||
php_flag display_errors On
|
||||
</IfModule>
|
||||
1538
htdocs/public/api/explorer/css/screen.css
Normal file
1538
htdocs/public/api/explorer/css/screen.css
Normal file
File diff suppressed because it is too large
Load Diff
BIN
htdocs/public/api/explorer/images/pet_store_api.png
Normal file
BIN
htdocs/public/api/explorer/images/pet_store_api.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 824 B |
BIN
htdocs/public/api/explorer/images/throbber.gif
Normal file
BIN
htdocs/public/api/explorer/images/throbber.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
95
htdocs/public/api/explorer/index.html
Normal file
95
htdocs/public/api/explorer/index.html
Normal file
@ -0,0 +1,95 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Restler API Explorer</title>
|
||||
<link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>
|
||||
<link href="http://netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet">
|
||||
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
|
||||
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
|
||||
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
|
||||
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
|
||||
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
|
||||
<script src='lib/handlebars-1.0.rc.1.js' type='text/javascript'></script>
|
||||
<script src='lib/underscore-min.js' type='text/javascript'></script>
|
||||
<script src='lib/backbone-min.js' type='text/javascript'></script>
|
||||
<script src='lib/swagger.js' type='text/javascript'></script>
|
||||
<script src='swagger-ui.js' type='text/javascript'></script>
|
||||
|
||||
<style type="text/css">
|
||||
.swagger-ui-wrap {
|
||||
max-width: 960px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#message-bar {
|
||||
min-height: 30px;
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.message-success {
|
||||
color: #89BF04;
|
||||
}
|
||||
|
||||
.message-fail {
|
||||
color: #cc0000;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
window.swaggerUi = new SwaggerUi({
|
||||
discoveryUrl:"../resources.json",
|
||||
apiKey:"",
|
||||
dom_id:"swagger-ui-container",
|
||||
//supportHeaderParams: true,
|
||||
supportedSubmitMethods: ['get', 'post', 'put', 'patch', 'delete'],
|
||||
onComplete: function(swaggerApi, swaggerUi){
|
||||
if(console) {
|
||||
console.log("Loaded SwaggerUI")
|
||||
console.log(swaggerApi);
|
||||
console.log(swaggerUi);
|
||||
}
|
||||
},
|
||||
onFailure: function(data) {
|
||||
if(console) {
|
||||
console.log("Unable to Load SwaggerUI");
|
||||
console.log(data);
|
||||
}
|
||||
},
|
||||
docExpansion: "none"
|
||||
});
|
||||
|
||||
window.swaggerUi.load();
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id='header'>
|
||||
<div class="swagger-ui-wrap">
|
||||
<a id="logo" href="https://github.com/Luracast/Restler-API-Explorer" target="_blank">API Explorer</a>
|
||||
<form id='api_selector'>
|
||||
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="hidden"/></div>
|
||||
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
|
||||
<div class='input'><a id="explore" href="#">Explore</a></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="message-bar" class="swagger-ui-wrap">
|
||||
|
||||
</div>
|
||||
|
||||
<div id="swagger-ui-container" class="swagger-ui-wrap">
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
38
htdocs/public/api/explorer/lib/backbone-min.js
vendored
Normal file
38
htdocs/public/api/explorer/lib/backbone-min.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// Backbone.js 0.9.2
|
||||
|
||||
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Backbone may be freely distributed under the MIT license.
|
||||
// For all details and documentation:
|
||||
// http://backbonejs.org
|
||||
(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks=
|
||||
{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g=
|
||||
z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent=
|
||||
{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null==
|
||||
b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent:
|
||||
b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};
|
||||
a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error,
|
||||
h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t();
|
||||
return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending=
|
||||
{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||
|
||||
!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator);
|
||||
this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c<d;c++){if(!(e=a[c]=this._prepareModel(a[c],b)))throw Error("Can't add an invalid model to a collection");g=e.cid;i=e.id;j[g]||this._byCid[g]||null!=i&&(k[i]||this._byId[i])?
|
||||
l.push(c):j[g]=k[i]=e}for(c=l.length;c--;)a.splice(l[c],1);c=0;for(d=a.length;c<d;c++)(e=a[c]).on("all",this._onModelEvent,this),this._byCid[e.cid]=e,null!=e.id&&(this._byId[e.id]=e);this.length+=d;A.apply(this.models,[null!=b.at?b.at:this.models.length,0].concat(a));this.comparator&&this.sort({silent:!0});if(b.silent)return this;c=0;for(d=this.models.length;c<d;c++)if(j[(e=this.models[c]).cid])b.index=c,e.trigger("add",e,this,b);return this},remove:function(a,b){var c,d,e,g;b||(b={});a=f.isArray(a)?
|
||||
a.slice():[a];c=0;for(d=a.length;c<d;c++)if(g=this.getByCid(a[c])||this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],e=this.indexOf(g),this.models.splice(e,1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,b);return a},pop:function(a){var b=this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:0},b));return a},
|
||||
shift:function(a){var b=this.at(0);this.remove(b,a);return b},get:function(a){return null==a?void 0:this._byId[null!=a.id?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},where:function(a){return f.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");var b=f.bind(this.comparator,this);1==this.comparator.length?
|
||||
this.models=this.sortBy(b):this.models.sort(b);a.silent||this.trigger("reset",this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},reset:function(a,b){a||(a=[]);b||(b={});for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);this._reset();this.add(a,f.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d,
|
||||
e,f){b[a.add?"add":"reset"](b.parse(d,f),a);c&&c(b,d)};a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},create:function(a,b){var c=this,b=b?f.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(e,f){b.wait&&c.add(e,b);d?d(e,f):e.trigger("sync",a,f,b)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId=
|
||||
{};this._byCid={}},_prepareModel:function(a,b){b||(b={});a instanceof o?a.collection||(a.collection=this):(b.collection=this,a=new this.model(a,b),a._validate(a.attributes,b)||(a=!1));return a},_removeReference:function(a){this==a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"==a||"remove"==a)&&c!=this||("destroy"==a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,
|
||||
arguments))}});f.each("forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split(","),function(a){r.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});var u=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},B=/:\w+/g,
|
||||
C=/\*\w+/g,D=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(u.prototype,k,{initialize:function(){},route:function(a,b,c){g.history||(g.history=new m);f.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,
|
||||
this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(D,"\\$&").replace(B,"([^/]+)").replace(C,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var m=g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")},s=/^[#\/]/,E=/msie [\w.]+/;m.started=!1;f.extend(m.prototype,k,{interval:50,getHash:function(a){return(a=(a?a.location:window.location).href.match(/#(.*)$/))?a[1]:
|
||||
""},getFragment:function(a,b){if(null==a)if(this._hasPushState||b){var a=window.location.pathname,c=window.location.search;c&&(a+=c)}else a=this.getHash();a.indexOf(this.options.root)||(a=a.substr(this.options.root.length));return a.replace(s,"")},start:function(a){if(m.started)throw Error("Backbone.history has already been started");m.started=!0;this.options=f.extend({},{root:"/"},this.options,a);this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=
|
||||
!(!this.options.pushState||!window.history||!window.history.pushState);var a=this.getFragment(),b=document.documentMode;if(b=E.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b))this.iframe=i('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?i(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?i(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,
|
||||
this.interval));this.fragment=a;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&(this.fragment=this.getHash().replace(s,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},
|
||||
stop:function(){i(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a==this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,
|
||||
function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(s,"");this.fragment!=c&&(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.getHash(this.iframe))&&(b.replace||
|
||||
this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,"")+"#"+b):a.hash=b}});var v=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},F=/^(\S+)\s*(.*)$/,w="model,collection,el,id,attributes,className,tagName".split(",");
|
||||
f.extend(v.prototype,k,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);b&&i(a).attr(b);c&&i(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof i?a:i(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=n(this,"events"))){this.undelegateEvents();
|
||||
for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(F),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=w.length;b<c;b++){var d=w[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el)this.setElement(this.el,
|
||||
!1);else{var a=n(this,"attributes")||{};this.id&&(a.id=this.id);this.className&&(a["class"]=this.className);this.setElement(this.make(this.tagName,a),!1)}}});o.extend=r.extend=u.extend=v.extend=function(a,b){var c=G(this,a,b);c.extend=this.extend;return c};var H={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=H[a];c||(c={});var e={type:d,dataType:"json"};c.url||(e.url=n(b,"url")||t());if(!c.data&&b&&("create"==a||"update"==a))e.contentType="application/json",
|
||||
e.data=JSON.stringify(b.toJSON());g.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(g.emulateHTTP&&("PUT"===d||"DELETE"===d))g.emulateJSON&&(e.data._method=d),e.type="POST",e.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d)};"GET"!==e.type&&!g.emulateJSON&&(e.processData=!1);return i.ajax(f.extend(e,c))};g.wrapError=function(a,b,c){return function(d,e){e=d===b?e:d;a?a(b,e,c):b.trigger("error",b,e,c)}};var x=function(){},G=function(a,
|
||||
b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};f.extend(d,a);x.prototype=a.prototype;d.prototype=new x;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},n=function(a,b){return!a||!a[b]?null:f.isFunction(a[b])?a[b]():a[b]},t=function(){throw Error('A "url" property or function must be specified');}}).call(this);
|
||||
1920
htdocs/public/api/explorer/lib/handlebars-1.0.rc.1.js
Normal file
1920
htdocs/public/api/explorer/lib/handlebars-1.0.rc.1.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,223 @@
|
||||
// lib/handlebars/base.js
|
||||
var Handlebars = {};
|
||||
|
||||
Handlebars.VERSION = "1.0.beta.6";
|
||||
|
||||
Handlebars.helpers = {};
|
||||
Handlebars.partials = {};
|
||||
|
||||
Handlebars.registerHelper = function(name, fn, inverse) {
|
||||
if(inverse) { fn.not = inverse; }
|
||||
this.helpers[name] = fn;
|
||||
};
|
||||
|
||||
Handlebars.registerPartial = function(name, str) {
|
||||
this.partials[name] = str;
|
||||
};
|
||||
|
||||
Handlebars.registerHelper('helperMissing', function(arg) {
|
||||
if(arguments.length === 2) {
|
||||
return undefined;
|
||||
} else {
|
||||
throw new Error("Could not find property '" + arg + "'");
|
||||
}
|
||||
});
|
||||
|
||||
var toString = Object.prototype.toString, functionType = "[object Function]";
|
||||
|
||||
Handlebars.registerHelper('blockHelperMissing', function(context, options) {
|
||||
var inverse = options.inverse || function() {}, fn = options.fn;
|
||||
|
||||
|
||||
var ret = "";
|
||||
var type = toString.call(context);
|
||||
|
||||
if(type === functionType) { context = context.call(this); }
|
||||
|
||||
if(context === true) {
|
||||
return fn(this);
|
||||
} else if(context === false || context == null) {
|
||||
return inverse(this);
|
||||
} else if(type === "[object Array]") {
|
||||
if(context.length > 0) {
|
||||
for(var i=0, j=context.length; i<j; i++) {
|
||||
ret = ret + fn(context[i]);
|
||||
}
|
||||
} else {
|
||||
ret = inverse(this);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return fn(context);
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('each', function(context, options) {
|
||||
var fn = options.fn, inverse = options.inverse;
|
||||
var ret = "";
|
||||
|
||||
if(context && context.length > 0) {
|
||||
for(var i=0, j=context.length; i<j; i++) {
|
||||
ret = ret + fn(context[i]);
|
||||
}
|
||||
} else {
|
||||
ret = inverse(this);
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('if', function(context, options) {
|
||||
var type = toString.call(context);
|
||||
if(type === functionType) { context = context.call(this); }
|
||||
|
||||
if(!context || Handlebars.Utils.isEmpty(context)) {
|
||||
return options.inverse(this);
|
||||
} else {
|
||||
return options.fn(this);
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('unless', function(context, options) {
|
||||
var fn = options.fn, inverse = options.inverse;
|
||||
options.fn = inverse;
|
||||
options.inverse = fn;
|
||||
|
||||
return Handlebars.helpers['if'].call(this, context, options);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('with', function(context, options) {
|
||||
return options.fn(context);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('log', function(context) {
|
||||
Handlebars.log(context);
|
||||
});
|
||||
;
|
||||
// lib/handlebars/utils.js
|
||||
Handlebars.Exception = function(message) {
|
||||
var tmp = Error.prototype.constructor.apply(this, arguments);
|
||||
|
||||
for (var p in tmp) {
|
||||
if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; }
|
||||
}
|
||||
|
||||
this.message = tmp.message;
|
||||
};
|
||||
Handlebars.Exception.prototype = new Error;
|
||||
|
||||
// Build out our basic SafeString type
|
||||
Handlebars.SafeString = function(string) {
|
||||
this.string = string;
|
||||
};
|
||||
Handlebars.SafeString.prototype.toString = function() {
|
||||
return this.string.toString();
|
||||
};
|
||||
|
||||
(function() {
|
||||
var escape = {
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'",
|
||||
"`": "`"
|
||||
};
|
||||
|
||||
var badChars = /&(?!\w+;)|[<>"'`]/g;
|
||||
var possible = /[&<>"'`]/;
|
||||
|
||||
var escapeChar = function(chr) {
|
||||
return escape[chr] || "&";
|
||||
};
|
||||
|
||||
Handlebars.Utils = {
|
||||
escapeExpression: function(string) {
|
||||
// don't escape SafeStrings, since they're already safe
|
||||
if (string instanceof Handlebars.SafeString) {
|
||||
return string.toString();
|
||||
} else if (string == null || string === false) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if(!possible.test(string)) { return string; }
|
||||
return string.replace(badChars, escapeChar);
|
||||
},
|
||||
|
||||
isEmpty: function(value) {
|
||||
if (typeof value === "undefined") {
|
||||
return true;
|
||||
} else if (value === null) {
|
||||
return true;
|
||||
} else if (value === false) {
|
||||
return true;
|
||||
} else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
})();;
|
||||
// lib/handlebars/runtime.js
|
||||
Handlebars.VM = {
|
||||
template: function(templateSpec) {
|
||||
// Just add water
|
||||
var container = {
|
||||
escapeExpression: Handlebars.Utils.escapeExpression,
|
||||
invokePartial: Handlebars.VM.invokePartial,
|
||||
programs: [],
|
||||
program: function(i, fn, data) {
|
||||
var programWrapper = this.programs[i];
|
||||
if(data) {
|
||||
return Handlebars.VM.program(fn, data);
|
||||
} else if(programWrapper) {
|
||||
return programWrapper;
|
||||
} else {
|
||||
programWrapper = this.programs[i] = Handlebars.VM.program(fn);
|
||||
return programWrapper;
|
||||
}
|
||||
},
|
||||
programWithDepth: Handlebars.VM.programWithDepth,
|
||||
noop: Handlebars.VM.noop
|
||||
};
|
||||
|
||||
return function(context, options) {
|
||||
options = options || {};
|
||||
return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
|
||||
};
|
||||
},
|
||||
|
||||
programWithDepth: function(fn, data, $depth) {
|
||||
var args = Array.prototype.slice.call(arguments, 2);
|
||||
|
||||
return function(context, options) {
|
||||
options = options || {};
|
||||
|
||||
return fn.apply(this, [context, options.data || data].concat(args));
|
||||
};
|
||||
},
|
||||
program: function(fn, data) {
|
||||
return function(context, options) {
|
||||
options = options || {};
|
||||
|
||||
return fn(context, options.data || data);
|
||||
};
|
||||
},
|
||||
noop: function() { return ""; },
|
||||
invokePartial: function(partial, name, context, helpers, partials, data) {
|
||||
options = { helpers: helpers, partials: partials, data: data };
|
||||
|
||||
if(partial === undefined) {
|
||||
throw new Handlebars.Exception("The partial " + name + " could not be found");
|
||||
} else if(partial instanceof Function) {
|
||||
return partial(context, options);
|
||||
} else if (!Handlebars.compile) {
|
||||
throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
|
||||
} else {
|
||||
partials[name] = Handlebars.compile(partial);
|
||||
return partials[name](context, options);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Handlebars.template = Handlebars.VM.template;
|
||||
;
|
||||
2
htdocs/public/api/explorer/lib/jquery-1.8.0.min.js
vendored
Normal file
2
htdocs/public/api/explorer/lib/jquery-1.8.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
htdocs/public/api/explorer/lib/jquery.ba-bbq.min.js
vendored
Normal file
18
htdocs/public/api/explorer/lib/jquery.ba-bbq.min.js
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
|
||||
* http://benalman.com/projects/jquery-bbq-plugin/
|
||||
*
|
||||
* Copyright (c) 2010 "Cowboy" Ben Alman
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* http://benalman.com/about/license/
|
||||
*/
|
||||
(function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M<N?O[P]||(R[M+1]&&isNaN(R[M+1])?{}:[]):J}}else{if($.isArray(H[P])){H[P].push(J)}else{if(H[P]!==i){H[P]=[H[P],J]}else{H[P]=J}}}}else{if(P){H[P]=F?i:""}}});return H};function z(H,F,G){if(F===i||typeof F==="boolean"){G=F;F=a[H?D:A]()}else{F=E(F)?F.replace(H?w:x,""):F}return l(F,G)}l[A]=B(z,0);l[D]=v=B(z,1);$[y]||($[y]=function(F){return $.extend(C,F)})({a:k,base:k,iframe:t,img:t,input:t,form:"action",link:k,script:t});j=$[y];function s(I,G,H,F){if(!E(H)&&typeof H!=="object"){F=H;H=G;G=i}return this.each(function(){var L=$(this),J=G||j()[(this.nodeName||"").toLowerCase()]||"",K=J&&L.attr(J)||"";L.attr(J,a[I](K,H,F))})}$.fn[A]=B(s,A);$.fn[D]=B(s,D);b.pushState=q=function(I,F){if(E(I)&&/^#/.test(I)&&F===i){F=2}var H=I!==i,G=c(p[g][k],H?I:{},H?F:2);p[g][k]=G+(/#/.test(G)?"":"#")};b.getState=u=function(F,G){return F===i||typeof F==="boolean"?v(F):v(G)[F]};b.removeState=function(F){var G={};if(F!==i){G=u();$.each($.isArray(F)?F:arguments,function(I,H){delete G[H]})}q(G,2)};e[d]=$.extend(e[d],{add:function(F){var H;function G(J){var I=J[D]=c();J.getState=function(K,L){return K===i||typeof K==="boolean"?l(I,K):l(I,L)[K]};H.apply(this,arguments)}if($.isFunction(F)){H=F;return G}else{H=F.handler;F.handler=G}}})})(jQuery,this);
|
||||
/*
|
||||
* jQuery hashchange event - v1.2 - 2/11/2010
|
||||
* http://benalman.com/projects/jquery-hashchange-plugin/
|
||||
*
|
||||
* Copyright (c) 2010 "Cowboy" Ben Alman
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* http://benalman.com/about/license/
|
||||
*/
|
||||
(function($,i,b){var j,k=$.event.special,c="location",d="hashchange",l="href",f=$.browser,g=document.documentMode,h=f.msie&&(g===b||g<8),e="on"+d in i&&!h;function a(m){m=m||i[c][l];return m.replace(/^[^#]*#?(.*)$/,"$1")}$[d+"Delay"]=100;k[d]=$.extend(k[d],{setup:function(){if(e){return false}$(j.start)},teardown:function(){if(e){return false}$(j.stop)}});j=(function(){var m={},r,n,o,q;function p(){o=q=function(s){return s};if(h){n=$('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);
|
||||
1
htdocs/public/api/explorer/lib/jquery.slideto.min.js
vendored
Normal file
1
htdocs/public/api/explorer/lib/jquery.slideto.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
(function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery);
|
||||
8
htdocs/public/api/explorer/lib/jquery.wiggle.min.js
vendored
Normal file
8
htdocs/public/api/explorer/lib/jquery.wiggle.min.js
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/*
|
||||
jQuery Wiggle
|
||||
Author: WonderGroup, Jordan Thomas
|
||||
URL: http://labs.wondergroup.com/demos/mini-ui/index.html
|
||||
License: MIT (http://en.wikipedia.org/wiki/MIT_License)
|
||||
*/
|
||||
jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('<div class="wiggle-wrap"></div>').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
|
||||
if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};
|
||||
694
htdocs/public/api/explorer/lib/swagger.js
Normal file
694
htdocs/public/api/explorer/lib/swagger.js
Normal file
@ -0,0 +1,694 @@
|
||||
// Generated by CoffeeScript 1.3.3
|
||||
(function() {
|
||||
var SwaggerApi, SwaggerModel, SwaggerModelProperty, SwaggerOperation, SwaggerRequest, SwaggerResource,
|
||||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
|
||||
SwaggerApi = (function() {
|
||||
|
||||
SwaggerApi.prototype.discoveryUrl = "http://api.wordnik.com/v4/resources.json";
|
||||
|
||||
SwaggerApi.prototype.debug = false;
|
||||
|
||||
SwaggerApi.prototype.api_key = null;
|
||||
|
||||
SwaggerApi.prototype.basePath = null;
|
||||
|
||||
function SwaggerApi(options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
if (options.discoveryUrl != null) {
|
||||
this.discoveryUrl = options.discoveryUrl;
|
||||
}
|
||||
if (options.debug != null) {
|
||||
this.debug = options.debug;
|
||||
}
|
||||
this.apiKeyName = options.apiKeyName != null ? options.apiKeyName : 'api_key';
|
||||
if (options.apiKey != null) {
|
||||
this.api_key = options.apiKey;
|
||||
}
|
||||
if (options.api_key != null) {
|
||||
this.api_key = options.api_key;
|
||||
}
|
||||
if (options.verbose != null) {
|
||||
this.verbose = options.verbose;
|
||||
}
|
||||
this.supportHeaderParams = options.supportHeaderParams != null ? options.supportHeaderParams : false;
|
||||
this.supportedSubmitMethods = options.supportedSubmitMethods != null ? options.supportedSubmitMethods : ['get'];
|
||||
if (options.success != null) {
|
||||
this.success = options.success;
|
||||
}
|
||||
this.failure = options.failure != null ? options.failure : function() {};
|
||||
this.progress = options.progress != null ? options.progress : function() {};
|
||||
this.headers = options.headers != null ? options.headers : {};
|
||||
this.booleanValues = options.booleanValues != null ? options.booleanValues : new Array('true', 'false');
|
||||
this.discoveryUrl = this.suffixApiKey(this.discoveryUrl);
|
||||
if (options.success != null) {
|
||||
this.build();
|
||||
}
|
||||
}
|
||||
|
||||
SwaggerApi.prototype.build = function() {
|
||||
var _this = this;
|
||||
this.progress('fetching resource list: ' + this.discoveryUrl);
|
||||
return jQuery.getJSON(this.discoveryUrl, function(response) {
|
||||
var res, resource, _i, _j, _len, _len1, _ref, _ref1;
|
||||
if ((response.basePath != null) && jQuery.trim(response.basePath).length > 0) {
|
||||
_this.basePath = response.basePath;
|
||||
if (_this.basePath.match(/^HTTP/i) == null) {
|
||||
_this.fail("discoveryUrl basePath must be a URL.");
|
||||
}
|
||||
_this.basePath = _this.basePath.replace(/\/$/, '');
|
||||
} else {
|
||||
_this.basePath = _this.discoveryUrl.substring(0, _this.discoveryUrl.lastIndexOf('/'));
|
||||
log('derived basepath from discoveryUrl as ' + _this.basePath);
|
||||
}
|
||||
_this.resources = {};
|
||||
_this.resourcesArray = [];
|
||||
if (response.resourcePath != null) {
|
||||
_this.resourcePath = response.resourcePath;
|
||||
res = null;
|
||||
_ref = response.apis;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
resource = _ref[_i];
|
||||
if (res === null) {
|
||||
res = new SwaggerResource(resource, _this);
|
||||
} else {
|
||||
res.addOperations(resource.path, resource.operations);
|
||||
}
|
||||
}
|
||||
if (res != null) {
|
||||
_this.resources[res.name] = res;
|
||||
_this.resourcesArray.push(res);
|
||||
res.ready = true;
|
||||
_this.selfReflect();
|
||||
}
|
||||
} else {
|
||||
_ref1 = response.apis;
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
resource = _ref1[_j];
|
||||
res = new SwaggerResource(resource, _this);
|
||||
_this.resources[res.name] = res;
|
||||
_this.resourcesArray.push(res);
|
||||
}
|
||||
}
|
||||
return _this;
|
||||
}).error(function(error) {
|
||||
return _this.fail(error.status + ' : ' + error.statusText + ' ' + _this.discoveryUrl);
|
||||
});
|
||||
};
|
||||
|
||||
SwaggerApi.prototype.selfReflect = function() {
|
||||
var resource, resource_name, _ref;
|
||||
if (this.resources == null) {
|
||||
return false;
|
||||
}
|
||||
_ref = this.resources;
|
||||
for (resource_name in _ref) {
|
||||
resource = _ref[resource_name];
|
||||
if (resource.ready == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this.setConsolidatedModels();
|
||||
this.ready = true;
|
||||
if (this.success != null) {
|
||||
return this.success();
|
||||
}
|
||||
};
|
||||
|
||||
SwaggerApi.prototype.fail = function(message) {
|
||||
this.failure(message);
|
||||
throw message;
|
||||
};
|
||||
|
||||
SwaggerApi.prototype.setConsolidatedModels = function() {
|
||||
var model, modelName, resource, resource_name, _i, _len, _ref, _ref1, _results;
|
||||
this.modelsArray = [];
|
||||
this.models = {};
|
||||
_ref = this.resources;
|
||||
for (resource_name in _ref) {
|
||||
resource = _ref[resource_name];
|
||||
for (modelName in resource.models) {
|
||||
if (!(this.models[modelName] != null)) {
|
||||
this.models[modelName] = resource.models[modelName];
|
||||
this.modelsArray.push(resource.models[modelName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ref1 = this.modelsArray;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
model = _ref1[_i];
|
||||
_results.push(model.setReferencedModels(this.models));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
SwaggerApi.prototype.suffixApiKey = function(url) {
|
||||
var sep;
|
||||
if ((this.api_key != null) && jQuery.trim(this.api_key).length > 0 && (url != null)) {
|
||||
sep = url.indexOf('?') > 0 ? '&' : '?';
|
||||
return url + sep + this.apiKeyName + '=' + this.api_key;
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
};
|
||||
|
||||
SwaggerApi.prototype.help = function() {
|
||||
var operation, operation_name, parameter, resource, resource_name, _i, _len, _ref, _ref1, _ref2;
|
||||
_ref = this.resources;
|
||||
for (resource_name in _ref) {
|
||||
resource = _ref[resource_name];
|
||||
console.log(resource_name);
|
||||
_ref1 = resource.operations;
|
||||
for (operation_name in _ref1) {
|
||||
operation = _ref1[operation_name];
|
||||
console.log(" " + operation.nickname);
|
||||
_ref2 = operation.parameters;
|
||||
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
|
||||
parameter = _ref2[_i];
|
||||
console.log(" " + parameter.name + (parameter.required ? ' (required)' : '') + " - " + parameter.description);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
return SwaggerApi;
|
||||
|
||||
})();
|
||||
|
||||
SwaggerResource = (function() {
|
||||
|
||||
function SwaggerResource(resourceObj, api) {
|
||||
var parts,
|
||||
_this = this;
|
||||
this.api = api;
|
||||
this.path = this.api.resourcePath != null ? this.api.resourcePath : resourceObj.path;
|
||||
this.description = resourceObj.description;
|
||||
parts = this.path.split("/");
|
||||
this.name = parts[parts.length - 1].replace('.{format}', '');
|
||||
this.basePath = this.api.basePath;
|
||||
this.operations = {};
|
||||
this.operationsArray = [];
|
||||
this.modelsArray = [];
|
||||
this.models = {};
|
||||
if ((resourceObj.operations != null) && (this.api.resourcePath != null)) {
|
||||
this.api.progress('reading resource ' + this.name + ' models and operations');
|
||||
this.addModels(resourceObj.models);
|
||||
this.addOperations(resourceObj.path, resourceObj.operations);
|
||||
this.api[this.name] = this;
|
||||
} else {
|
||||
if (this.path == null) {
|
||||
this.api.fail("SwaggerResources must have a path.");
|
||||
}
|
||||
this.url = this.api.suffixApiKey(this.api.basePath + this.path.replace('{format}', 'json'));
|
||||
this.api.progress('fetching resource ' + this.name + ': ' + this.url);
|
||||
jQuery.getJSON(this.url, function(response) {
|
||||
var endpoint, _i, _len, _ref;
|
||||
if ((response.basePath != null) && jQuery.trim(response.basePath).length > 0) {
|
||||
_this.basePath = response.basePath;
|
||||
_this.basePath = _this.basePath.replace(/\/$/, '');
|
||||
}
|
||||
_this.addModels(response.models);
|
||||
if (response.apis) {
|
||||
_ref = response.apis;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
endpoint = _ref[_i];
|
||||
_this.addOperations(endpoint.path, endpoint.operations);
|
||||
}
|
||||
}
|
||||
_this.api[_this.name] = _this;
|
||||
_this.ready = true;
|
||||
return _this.api.selfReflect();
|
||||
}).error(function(error) {
|
||||
return _this.api.fail(error.status + ' : ' + error.statusText + ' ' + _this.url);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SwaggerResource.prototype.addModels = function(models) {
|
||||
var model, modelName, swaggerModel, _i, _len, _ref, _results;
|
||||
if (models != null) {
|
||||
for (modelName in models) {
|
||||
swaggerModel = new SwaggerModel(modelName, models[modelName]);
|
||||
this.modelsArray.push(swaggerModel);
|
||||
this.models[modelName] = swaggerModel;
|
||||
}
|
||||
_ref = this.modelsArray;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
model = _ref[_i];
|
||||
_results.push(model.setReferencedModels(this.models));
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
};
|
||||
|
||||
SwaggerResource.prototype.addOperations = function(resource_path, ops) {
|
||||
var o, op, _i, _len, _results;
|
||||
if (ops) {
|
||||
_results = [];
|
||||
for (_i = 0, _len = ops.length; _i < _len; _i++) {
|
||||
o = ops[_i];
|
||||
op = new SwaggerOperation(o.nickname, resource_path, o.httpMethod, o.parameters, o.summary, o.notes, o.responseClass, this);
|
||||
this.operations[op.nickname] = op;
|
||||
_results.push(this.operationsArray.push(op));
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
};
|
||||
|
||||
SwaggerResource.prototype.help = function() {
|
||||
var operation, operation_name, parameter, _i, _len, _ref, _ref1;
|
||||
_ref = this.operations;
|
||||
for (operation_name in _ref) {
|
||||
operation = _ref[operation_name];
|
||||
console.log(" " + operation.nickname);
|
||||
_ref1 = operation.parameters;
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
parameter = _ref1[_i];
|
||||
console.log(" " + parameter.name + (parameter.required ? ' (required)' : '') + " - " + parameter.description);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
return SwaggerResource;
|
||||
|
||||
})();
|
||||
|
||||
SwaggerModel = (function() {
|
||||
|
||||
function SwaggerModel(modelName, obj) {
|
||||
var propertyName;
|
||||
this.name = obj.id != null ? obj.id : modelName;
|
||||
this.properties = [];
|
||||
for (propertyName in obj.properties) {
|
||||
this.properties.push(new SwaggerModelProperty(propertyName, obj.properties[propertyName]));
|
||||
}
|
||||
}
|
||||
|
||||
SwaggerModel.prototype.setReferencedModels = function(allModels) {
|
||||
var prop, _i, _len, _ref, _results;
|
||||
_ref = this.properties;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
prop = _ref[_i];
|
||||
if (allModels[prop.dataType] != null) {
|
||||
_results.push(prop.refModel = allModels[prop.dataType]);
|
||||
} else if ((prop.refDataType != null) && (allModels[prop.refDataType] != null)) {
|
||||
_results.push(prop.refModel = allModels[prop.refDataType]);
|
||||
} else {
|
||||
_results.push(void 0);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
SwaggerModel.prototype.getMockSignature = function(prefix, modelToIgnore) {
|
||||
var classClose, classOpen, prop, propertiesStr, returnVal, strong, strongClose, stronger, _i, _j, _len, _len1, _ref, _ref1;
|
||||
propertiesStr = [];
|
||||
_ref = this.properties;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
prop = _ref[_i];
|
||||
propertiesStr.push(prop.name+': <tag>'+prop.dataType+'</tag>');
|
||||
}
|
||||
strong = '<span style="font-weight: bold; color: #000; font-size: 1.0em">';
|
||||
stronger = '<span style="font-weight: bold; color: #000; font-size: 1.1em">';
|
||||
strongClose = '</span>';
|
||||
classOpen = strong + ' ' + (prefix != null? prefix : this.name) + ' ( ' + strongClose;
|
||||
classClose = strong + ' )' + strongClose;
|
||||
returnVal = classOpen + '<span>' + propertiesStr.join('</span>, <span>') + '</span>' + classClose;
|
||||
_ref1 = this.properties;
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
prop = _ref1[_j];
|
||||
if ((prop.refModel != null) && (!(prop.refModel === modelToIgnore))) {
|
||||
returnVal = returnVal + ('<br>' + prop.refModel.getMockSignature(void 0, this));
|
||||
}
|
||||
}
|
||||
return returnVal;
|
||||
};
|
||||
|
||||
return SwaggerModel;
|
||||
|
||||
})();
|
||||
|
||||
SwaggerModelProperty = (function() {
|
||||
|
||||
function SwaggerModelProperty(name, obj) {
|
||||
this.name = name;
|
||||
this.dataType = obj.type;
|
||||
this.isArray = this.dataType.toLowerCase() === 'array';
|
||||
this.descr = obj.description;
|
||||
if (obj.items != null) {
|
||||
if (obj.items.type != null) {
|
||||
this.refDataType = obj.items.type;
|
||||
}
|
||||
if (obj.items.$ref != null) {
|
||||
this.refDataType = obj.items.$ref;
|
||||
}
|
||||
}
|
||||
this.dataTypeWithRef = this.refDataType != null ? this.dataType + '[' + this.refDataType + ']' : this.dataType;
|
||||
if (obj.allowableValues != null) {
|
||||
this.valueType = obj.allowableValues.valueType;
|
||||
this.values = obj.allowableValues.values;
|
||||
if (this.values != null) {
|
||||
this.valuesString = "'" + this.values.join("' or '") + "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwaggerModelProperty.prototype.toString = function() {
|
||||
var str;
|
||||
str = this.name + ': ' + this.dataTypeWithRef;
|
||||
if (this.values != null) {
|
||||
str += " = ['" + this.values.join("' or '") + "']";
|
||||
}
|
||||
if (this.descr != null) {
|
||||
str += ' {' + this.descr + '}';
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
return SwaggerModelProperty;
|
||||
|
||||
})();
|
||||
|
||||
SwaggerOperation = (function() {
|
||||
|
||||
function SwaggerOperation(nickname, path, httpMethod, parameters, summary, notes, responseClass, resource) {
|
||||
var parameter, v, _i, _j, _len, _len1, _ref, _ref1,
|
||||
_this = this;
|
||||
this.nickname = nickname;
|
||||
this.path = path;
|
||||
this.httpMethod = httpMethod;
|
||||
this.parameters = parameters != null ? parameters : [];
|
||||
this.summary = summary;
|
||||
this.notes = notes;
|
||||
this.responseClass = responseClass;
|
||||
this.resource = resource;
|
||||
this["do"] = __bind(this["do"], this);
|
||||
|
||||
if (this.nickname == null) {
|
||||
this.resource.api.fail("SwaggerOperations must have a nickname.");
|
||||
}
|
||||
if (this.path == null) {
|
||||
this.resource.api.fail("SwaggerOperation " + nickname + " is missing path.");
|
||||
}
|
||||
if (this.httpMethod == null) {
|
||||
this.resource.api.fail("SwaggerOperation " + nickname + " is missing httpMethod.");
|
||||
}
|
||||
this.path = this.path.replace('{format}', 'json');
|
||||
this.httpMethod = this.httpMethod.toLowerCase();
|
||||
this.isGetMethod = this.httpMethod === "get";
|
||||
this.resourceName = this.resource.name;
|
||||
if (this.responseClass.toLowerCase() === 'void') {
|
||||
this.responseClass = void 0;
|
||||
}
|
||||
if (this.responseClass != null) {
|
||||
this.responseClassSignature = this.getSignature(this.responseClass, this.resource.models);
|
||||
}
|
||||
_ref = this.parameters;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
parameter = _ref[_i];
|
||||
parameter.name = parameter.name || parameter.dataType;
|
||||
if (parameter.dataType.toLowerCase() === 'boolean') {
|
||||
parameter.allowableValues = {};
|
||||
parameter.allowableValues.values = this.resource.api.booleanValues;
|
||||
}
|
||||
parameter.signature = this.getSignature(parameter.dataType, this.resource.models);
|
||||
if (parameter.allowableValues != null) {
|
||||
if (parameter.allowableValues.valueType === "RANGE") {
|
||||
parameter.isRange = true;
|
||||
} else {
|
||||
parameter.isList = true;
|
||||
}
|
||||
if (parameter.allowableValues.values != null) {
|
||||
parameter.allowableValues.descriptiveValues = [];
|
||||
_ref1 = parameter.allowableValues.values;
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
v = _ref1[_j];
|
||||
if ((parameter.defaultValue != null) && parameter.defaultValue === v) {
|
||||
parameter.allowableValues.descriptiveValues.push({
|
||||
value: v,
|
||||
isDefault: true
|
||||
});
|
||||
} else {
|
||||
parameter.allowableValues.descriptiveValues.push({
|
||||
value: v,
|
||||
isDefault: false
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.resource[this.nickname] = function(args, callback, error) {
|
||||
return _this["do"](args, callback, error);
|
||||
};
|
||||
}
|
||||
|
||||
SwaggerOperation.prototype.isListType = function(dataType) {
|
||||
if (dataType.indexOf('[') >= 0) {
|
||||
return dataType.substring(dataType.indexOf('[') + 1, dataType.indexOf(']'));
|
||||
} else {
|
||||
return void 0;
|
||||
}
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.getSignature = function(dataType, models) {
|
||||
var isPrimitive, listType;
|
||||
listType = this.isListType(dataType);
|
||||
isPrimitive = ((listType != null) && models[listType]) || (models[dataType] != null) ? false : true;
|
||||
if (isPrimitive) {
|
||||
return dataType;
|
||||
} else {
|
||||
if (listType != null) {
|
||||
return models[listType].getMockSignature(dataType);
|
||||
} else {
|
||||
return models[dataType].getMockSignature(dataType);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype["do"] = function(args, callback, error) {
|
||||
var body, headers;
|
||||
if (args == null) {
|
||||
args = {};
|
||||
}
|
||||
if ((typeof args) === "function") {
|
||||
error = callback;
|
||||
callback = args;
|
||||
args = {};
|
||||
}
|
||||
if (error == null) {
|
||||
error = function(xhr, textStatus, error) {
|
||||
return console.log(xhr, textStatus, error);
|
||||
};
|
||||
}
|
||||
if (callback == null) {
|
||||
callback = function(data) {
|
||||
return console.log(data);
|
||||
};
|
||||
}
|
||||
if (args.headers != null) {
|
||||
headers = args.headers;
|
||||
delete args.headers;
|
||||
}
|
||||
if (args.body != null) {
|
||||
body = args.body;
|
||||
delete args.body;
|
||||
}
|
||||
return new SwaggerRequest(this.httpMethod, this.urlify(args), headers, body, callback, error, this);
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.pathJson = function() {
|
||||
return this.path.replace("{format}", "json");
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.pathXml = function() {
|
||||
return this.path.replace("{format}", "xml");
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.urlify = function(args, includeApiKey) {
|
||||
var param, queryParams, url, _i, _len, _ref;
|
||||
if (includeApiKey == null) {
|
||||
includeApiKey = true;
|
||||
}
|
||||
url = this.resource.basePath + this.pathJson();
|
||||
_ref = this.parameters;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
param = _ref[_i];
|
||||
if (param.paramType === 'path') {
|
||||
if (args[param.name]) {
|
||||
url = url.replace("{" + param.name + "}", encodeURIComponent(args[param.name]));
|
||||
delete args[param.name];
|
||||
} else {
|
||||
throw "" + param.name + " is a required path param.";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (includeApiKey && (this.resource.api.api_key != null) && this.resource.api.api_key.length > 0) {
|
||||
args[this.apiKeyName] = this.resource.api.api_key;
|
||||
}
|
||||
if (this.supportHeaderParams()) {
|
||||
queryParams = jQuery.param(this.getQueryParams(args, includeApiKey));
|
||||
} else {
|
||||
queryParams = jQuery.param(this.getQueryAndHeaderParams(args, includeApiKey));
|
||||
}
|
||||
if ((queryParams != null) && queryParams.length > 0) {
|
||||
url += "?" + queryParams;
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.supportHeaderParams = function() {
|
||||
return this.resource.api.supportHeaderParams;
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.supportedSubmitMethods = function() {
|
||||
return this.resource.api.supportedSubmitMethods;
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.getQueryAndHeaderParams = function(args, includeApiKey) {
|
||||
if (includeApiKey == null) {
|
||||
includeApiKey = true;
|
||||
}
|
||||
return this.getMatchingParams(['query', 'header'], args, includeApiKey);
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.getQueryParams = function(args, includeApiKey) {
|
||||
if (includeApiKey == null) {
|
||||
includeApiKey = true;
|
||||
}
|
||||
return this.getMatchingParams(['query'], args, includeApiKey);
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.getHeaderParams = function(args, includeApiKey) {
|
||||
if (includeApiKey == null) {
|
||||
includeApiKey = true;
|
||||
}
|
||||
return this.getMatchingParams(['header'], args, includeApiKey);
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.getMatchingParams = function(paramTypes, args, includeApiKey) {
|
||||
var matchingParams, name, param, value, _i, _len, _ref, _ref1;
|
||||
matchingParams = {};
|
||||
_ref = this.parameters;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
param = _ref[_i];
|
||||
if ((jQuery.inArray(param.paramType, paramTypes) >= 0) && args[param.name]) {
|
||||
matchingParams[param.name] = args[param.name];
|
||||
}
|
||||
}
|
||||
if (includeApiKey && (this.resource.api.api_key != null) && this.resource.api.api_key.length > 0) {
|
||||
matchingParams[this.resource.api.apiKeyName] = this.resource.api.api_key;
|
||||
}
|
||||
if (jQuery.inArray('header', paramTypes) >= 0) {
|
||||
_ref1 = this.resource.api.headers;
|
||||
for (name in _ref1) {
|
||||
value = _ref1[name];
|
||||
matchingParams[name] = value;
|
||||
}
|
||||
}
|
||||
return matchingParams;
|
||||
};
|
||||
|
||||
SwaggerOperation.prototype.help = function() {
|
||||
var parameter, _i, _len, _ref;
|
||||
_ref = this.parameters;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
parameter = _ref[_i];
|
||||
console.log(" " + parameter.name + (parameter.required ? ' (required)' : '') + " - " + parameter.description);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
return SwaggerOperation;
|
||||
|
||||
})();
|
||||
|
||||
SwaggerRequest = (function() {
|
||||
|
||||
function SwaggerRequest(type, url, headers, body, successCallback, errorCallback, operation) {
|
||||
var obj,
|
||||
_this = this;
|
||||
this.type = type;
|
||||
this.url = url;
|
||||
this.headers = headers;
|
||||
this.body = body;
|
||||
this.successCallback = successCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
this.operation = operation;
|
||||
if (this.type == null) {
|
||||
throw "SwaggerRequest type is required (get/post/put/delete).";
|
||||
}
|
||||
if (this.url == null) {
|
||||
throw "SwaggerRequest url is required.";
|
||||
}
|
||||
if (this.successCallback == null) {
|
||||
throw "SwaggerRequest successCallback is required.";
|
||||
}
|
||||
if (this.errorCallback == null) {
|
||||
throw "SwaggerRequest error callback is required.";
|
||||
}
|
||||
if (this.operation == null) {
|
||||
throw "SwaggerRequest operation is required.";
|
||||
}
|
||||
if (this.operation.resource.api.verbose) {
|
||||
console.log(this.asCurl());
|
||||
}
|
||||
this.headers || (this.headers = {});
|
||||
if (this.operation.resource.api.api_key != null) {
|
||||
this.headers[this.apiKeyName] = this.operation.resource.api.api_key;
|
||||
}
|
||||
if (this.headers.mock == null) {
|
||||
obj = {
|
||||
type: this.type,
|
||||
url: this.url,
|
||||
data: JSON.stringify(this.body),
|
||||
dataType: 'json',
|
||||
error: function(xhr, textStatus, error) {
|
||||
return _this.errorCallback(xhr, textStatus, error);
|
||||
},
|
||||
success: function(data) {
|
||||
return _this.successCallback(data);
|
||||
}
|
||||
};
|
||||
if (obj.type.toLowerCase() === "post" || obj.type.toLowerCase() === "put") {
|
||||
obj.contentType = "application/json";
|
||||
}
|
||||
jQuery.ajax(obj);
|
||||
}
|
||||
}
|
||||
|
||||
SwaggerRequest.prototype.asCurl = function() {
|
||||
var header_args, k, v;
|
||||
header_args = (function() {
|
||||
var _ref, _results;
|
||||
_ref = this.headers;
|
||||
_results = [];
|
||||
for (k in _ref) {
|
||||
v = _ref[k];
|
||||
_results.push("--header \"" + k + ": " + v + "\"");
|
||||
}
|
||||
return _results;
|
||||
}).call(this);
|
||||
return "curl " + (header_args.join(" ")) + " " + this.url;
|
||||
};
|
||||
|
||||
return SwaggerRequest;
|
||||
|
||||
})();
|
||||
|
||||
window.SwaggerApi = SwaggerApi;
|
||||
|
||||
window.SwaggerResource = SwaggerResource;
|
||||
|
||||
window.SwaggerOperation = SwaggerOperation;
|
||||
|
||||
window.SwaggerRequest = SwaggerRequest;
|
||||
|
||||
}).call(this);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user