Merge remote-tracking branch 'Upstream/develop' into develop-md

This commit is contained in:
aspangaro
2015-05-14 06:42:28 +02:00
167 changed files with 22041 additions and 721 deletions

View File

@@ -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
View 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;

View 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;
}
}

View File

@@ -873,7 +873,7 @@ if ($rowid)
}
});
';
if (GETPOST('paymentsave')) print '$("#'.GETPOST('paymentsave').'").attr("checked",true);';
if (GETPOST('paymentsave')) print '$("#'.GETPOST('paymentsave').'").prop("checked",true);';
print '});';
print '</script>'."\n";
}

View File

@@ -165,7 +165,7 @@ jQuery(document).ready(function() {
var row_num = field_id.split("_");
jQuery("#updateconst").show();
jQuery("#action").val('update');
jQuery("#check_" + row_num[1]).attr("checked",true);
jQuery("#check_" + row_num[1]).prop("checked",true);
});
});
</script>

View File

@@ -271,12 +271,12 @@ if ($action == 'edit')
{
jQuery(".drag").hide();
jQuery("#MAIN_MAIL_EMAIL_TLS").val(0);
jQuery("#MAIN_MAIL_EMAIL_TLS").attr(\'disabled\', \'disabled\');
jQuery("#MAIN_MAIL_EMAIL_TLS").prop("disabled", true);
';
if ($linuxlike)
{
print ' jQuery("#MAIN_MAIL_SMTP_SERVER").attr(\'disabled\', \'disabled\');';
print ' jQuery("#MAIN_MAIL_SMTP_PORT").attr(\'disabled\', \'disabled\');';
print ' jQuery("#MAIN_MAIL_SMTP_SERVER").prop("disabled", true);';
print ' jQuery("#MAIN_MAIL_SMTP_PORT").prop("disabled", true);';
}
print '
}
@@ -284,9 +284,9 @@ if ($action == 'edit')
{
jQuery(".drag").show();
jQuery("#MAIN_MAIL_EMAIL_TLS").val('.$conf->global->MAIN_MAIL_EMAIL_TLS.');
jQuery("#MAIN_MAIL_EMAIL_TLS").removeAttr(\'disabled\');
jQuery("#MAIN_MAIL_SMTP_SERVER").removeAttr(\'disabled\');
jQuery("#MAIN_MAIL_SMTP_PORT").removeAttr(\'disabled\');
jQuery("#MAIN_MAIL_EMAIL_TLS").removeAttr("disabled");
jQuery("#MAIN_MAIL_SMTP_SERVER").removeAttr("disabled");
jQuery("#MAIN_MAIL_SMTP_PORT").removeAttr("disabled");
}
}
initfields();

View File

@@ -255,12 +255,12 @@ if ($action == 'create')
{
if (jQuery("#topleft").val() == \'top\')
{
jQuery("#menuId").attr(\'disabled\',\'disabled\');
jQuery("#menuId").prop("disabled", true);
jQuery("#menuId").val(\'\');
}
else
{
jQuery("#menuId").removeAttr(\'disabled\');
jQuery("#menuId").removeAttr("disabled");
}
}
init_topleft();

View File

@@ -118,7 +118,7 @@ foreach($sortorder as $numero=>$name)
$idperms="";
$var=!$var;
// Module
print "<tr ".$bc[$var]."><td width=\"300\" nowrap=\"nowrap\">";
print "<tr ".$bc[$var].'><td width="300" class="nowrap">';
$alt=$name.' - '.$modules_files[$numero];
if (! empty($picto[$numero]))
{

View File

@@ -96,7 +96,7 @@ jQuery(document).ready(function() {
jQuery("#select_sql_compat").click(function() {
if (jQuery("#select_sql_compat").val() == 'POSTGRESQL')
{
jQuery("#checkbox_dump_disable-add-locks").attr('checked',true);
jQuery("#checkbox_dump_disable-add-locks").prop('checked',true);
}
});

50
htdocs/api/README.md Normal file
View 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

138
htdocs/api/admin/api.php Normal file
View 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>&nbsp;</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 '&nbsp;'.img_picto($langs->trans('Generate'), 'refresh', 'id="generate_token" class="linkobject"');
print '</td>';
print '<td>&nbsp;</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();

View 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
)
);
}
}

View 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
View 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.
*/

View File

@@ -32,7 +32,8 @@
include '../master.inc.php';
$phone = GETPOST('phone');
$notfound = "Not found";
$error = "Error"
// Security check
if (empty($conf->clicktodial->enabled)) {
@@ -64,13 +65,15 @@ if ($resql)
if ($obj)
{
$found = $obj->name;
} else {
$found = $notfound;
}
$db->free($resql);
}
else
{
dol_print_error($db,'Error');
$found = $error;
}
echo $found;

View File

@@ -313,28 +313,28 @@ jQuery(document).ready(function() {
{
if (jQuery("#fillmanually:checked").val() == "fillmanually")
{
jQuery("#submitproduct").attr(\'disabled\',\'disabled\');
jQuery("#submitthirdparty").attr(\'disabled\',\'disabled\');
jQuery("#search_productid").attr(\'disabled\',\'disabled\');
jQuery("#socid").attr(\'disabled\',\'disabled\');
jQuery("#submitproduct").prop("disabled", true);
jQuery("#submitthirdparty").prop("disabled", true);
jQuery("#search_productid").prop("disabled", true);
jQuery("#socid").prop("disabled", true);
jQuery(".showforproductselector").hide();
jQuery(".showforthirdpartyselector").hide();
}
if (jQuery("#fillfromproduct:checked").val() == "fillfromproduct")
{
jQuery("#submitproduct").removeAttr(\'disabled\');
jQuery("#submitthirdparty").attr(\'disabled\',\'disabled\');
jQuery("#search_productid").removeAttr(\'disabled\');
jQuery("#socid").attr(\'disabled\',\'disabled\');
jQuery("#submitproduct").removeAttr("disabled");
jQuery("#submitthirdparty").prop("disabled", true);
jQuery("#search_productid").removeAttr("disabled");
jQuery("#socid").prop("disabled", true);
jQuery(".showforproductselector").show();
jQuery(".showforthirdpartyselector").hide();
}
if (jQuery("#fillfromthirdparty:checked").val() == "fillfromthirdparty")
{
jQuery("#submitproduct").attr(\'disabled\',\'disabled\');
jQuery("#submitthirdparty").removeAttr(\'disabled\');
jQuery("#search_productid").attr(\'disabled\',\'disabled\');
jQuery("#socid").removeAttr(\'disabled\');
jQuery("#submitproduct").prop("disabled", true);
jQuery("#submitthirdparty").removeAttr("disabled");
jQuery("#search_productid").prop("disabled", true);
jQuery("#socid").removeAttr("disabled");
jQuery(".showforproductselector").hide();
jQuery(".showforthirdpartyselector").show();
}
@@ -348,11 +348,11 @@ jQuery(document).ready(function() {
{
if (jQuery("#select_fk_barcode_type").val() > 0 && jQuery("#forbarcode").val())
{
jQuery("#submitformbarcodegen").removeAttr(\'disabled\');
jQuery("#submitformbarcodegen").removeAttr("disabled");
}
else
{
jQuery("#submitformbarcodegen").attr(\'disabled\',\'disabled\');
jQuery("#submitformbarcodegen").prop("disabled", true);
}
}
init_gendoc_button();

View 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;
}
}

View File

@@ -254,7 +254,7 @@ else
{
$var=!$var;
print "\t<tr ".$bc[$var].">\n";
print "\t\t<td nowrap=\"nowrap\">";
print "\t\t".'<td class="nowrap">';
print "<a href='viewcat.php?id=".$cat->id."&amp;type=".$type."'>".$cat->label."</a>";
print "</td>\n";
print "\t\t".'<td colspan="2">'.$cat->description."</td>\n";

View File

@@ -570,11 +570,11 @@ if ($action == 'create')
$(".fulldayendmin").removeAttr("disabled");
$("#p2").removeAttr("disabled");
} else {
$(".fulldaystarthour").attr("disabled","disabled").val("00");
$(".fulldaystartmin").attr("disabled","disabled").val("00");
$(".fulldayendhour").attr("disabled","disabled").val("23");
$(".fulldayendmin").attr("disabled","disabled").val("59");
$("#p2").removeAttr("disabled");
$(".fulldaystarthour").prop("disabled", true).val("00");
$(".fulldaystartmin").prop("disabled", true).val("00");
$(".fulldayendhour").prop("disabled", true).val("23");
$(".fulldayendmin").prop("disabled", true).val("59");
$("#p2").removeAttr("disabled");
}
}
setdatefields();
@@ -841,10 +841,10 @@ if ($id > 0)
$(".fulldayendhour").removeAttr("disabled");
$(".fulldayendmin").removeAttr("disabled");
} else {
$(".fulldaystarthour").attr("disabled","disabled").val("00");
$(".fulldaystartmin").attr("disabled","disabled").val("00");
$(".fulldayendhour").attr("disabled","disabled").val("23");
$(".fulldayendmin").attr("disabled","disabled").val("59");
$(".fulldaystarthour").prop("disabled", true).val("00");
$(".fulldaystartmin").prop("disabled", true).val("00");
$(".fulldayendhour").prop("disabled", true).val("23");
$(".fulldayendmin").prop("disabled", true).val("59");
}
}
setdatefields();
@@ -986,7 +986,7 @@ if ($id > 0)
}
// Priority
print '<tr><td nowrap width="30%">'.$langs->trans("Priority").'</td><td colspan="3">';
print '<tr><td class="nowrap" width="30%">'.$langs->trans("Priority").'</td><td colspan="3">';
print '<input type="text" name="priority" value="'.($object->priority?$object->priority:'').'" size="5">';
print '</td></tr>';
@@ -1174,7 +1174,7 @@ if ($id > 0)
}
// Priority
print '<tr><td nowrap width="30%">'.$langs->trans("Priority").'</td><td colspan="3">';
print '<tr><td class="nowrap" width="30%">'.$langs->trans("Priority").'</td><td colspan="3">';
print ($object->priority?$object->priority:'');
print '</td></tr>';

View 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;
}
}

View File

@@ -512,10 +512,10 @@ if (($action != 'create' && $action != 'add') || !$error)
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery("#checkall").click(function() {
jQuery(".checkformerge").attr('checked', true);
jQuery(".checkformerge").prop('checked', true);
});
jQuery("#checknone").click(function() {
jQuery(".checkformerge").attr('checked', false);
jQuery(".checkformerge").prop('checked', false);
});
});
</script>
@@ -655,12 +655,12 @@ if (($action != 'create' && $action != 'add') || !$error)
print '<td>'.$objp->ref_client.'</td>';
// Order date
print '<td align="center" nowrap>';
print '<td align="center" class="nowrap">';
print dol_print_date($db->jdate($objp->date_commande),'day');
print '</td>';
//Delivery date
print '<td align="center" nowrap>';
print '<td align="center" class="nowrap">';
print dol_print_date($db->jdate($objp->date_livraison),'day');
print '</td>';

View File

@@ -776,11 +776,11 @@ if ($id > 0 || ! empty($ref))
{
if ($total >= 0)
{
print '<td align="right" nowrap>&nbsp;'.price($total).'</td>';
print '<td align="right" class="nowrap">&nbsp;'.price($total).'</td>';
}
else
{
print '<td align="right" class="error" nowrap>&nbsp;'.price($total).'</td>';
print '<td align="right" class="error nowrap">&nbsp;'.price($total).'</td>';
}
}
else
@@ -791,7 +791,7 @@ if ($id > 0 || ! empty($ref))
// Transaction reconciliated or edit link
if ($objp->rappro && $object->canBeConciliated() > 0) // If line not conciliated and account can be conciliated
{
print '<td align="center" nowrap>';
print '<td align="center" class="nowrap">';
print '<a href="'.DOL_URL_ROOT.'/compta/bank/ligne.php?rowid='.$objp->rowid.'&amp;account='.$object->id.'&amp;page='.$page.'">';
print img_edit();
print '</a>';
@@ -844,7 +844,7 @@ if ($id > 0 || ! empty($ref))
if ($sep > 0) print '&nbsp;'; // If we had at least one line in future
else print $langs->trans("CurrentBalance");
print ' '.$object->currency_code.'</td>';
print '<td align="right" nowrap><b>'.price($total, 0, $langs, 0, 0, -1, $object->currency_code).'</b></td>';
print '<td align="right" class="nowrap"><b>'.price($total, 0, $langs, 0, 0, -1, $object->currency_code).'</b></td>';
print '<td>&nbsp;</td>';
print '</tr>';
}

View File

@@ -489,15 +489,15 @@ else
if ($objp->amount < 0)
{
$totald = $totald + abs($objp->amount);
print '<td align="right" nowrap=\"nowrap\">'.price($objp->amount * -1)."</td><td>&nbsp;</td>\n";
print '<td align="right" class="nowrap">'.price($objp->amount * -1)."</td><td>&nbsp;</td>\n";
}
else
{
$totalc = $totalc + abs($objp->amount);
print "<td>&nbsp;</td><td align=\"right\" nowrap=\"nowrap\">".price($objp->amount)."</td>\n";
print '<td>&nbsp;</td><td align="right" class="nowrap">'.price($objp->amount)."</td>\n";
}
print "<td align=\"right\" nowrap=\"nowrap\">".price($total)."</td>\n";
print '<td align="right" class="nowrap">'.price($total)."</td>\n";
if ($user->rights->banque->modifier || $user->rights->banque->consolidate)
{

View File

@@ -134,13 +134,13 @@ if ($_REQUEST["account"] || $_REQUEST["ref"])
$var=!$var;
print '<tr class="liste_total">';
print '<td align="left" colspan="5">'.$langs->trans("CurrentBalance").'</td>';
print '<td align="right" nowrap>'.price($solde).'</td>';
print '<td align="right" class="nowrap">'.price($solde).'</td>';
print '</tr>';
$var=!$var;
print '<tr class="liste_total">';
print '<td align="left" colspan="5">'.$langs->trans("RemainderToPay").'</td>';
print '<td align="right" nowrap>&nbsp;</td>';
print '<td align="right" class="nowrap">&nbsp;</td>';
print '</tr>';
@@ -330,7 +330,7 @@ if ($_REQUEST["account"] || $_REQUEST["ref"])
$var=!$var;
print '<tr class="liste_total">';
print '<td align="left" colspan="5">'.$langs->trans("FutureBalance").' ('.$acct->currency_code.')</td>';
print '<td align="right" nowrap>'.price($solde, 0, $langs, 0, 0, -1, $acct->currency_code).'</td>';
print '<td align="right" class="nowrap">'.price($solde, 0, $langs, 0, 0, -1, $acct->currency_code).'</td>';
print '</tr>';
print "</table>";

View File

@@ -2099,7 +2099,7 @@ if ($action == 'create')
print '<script type="text/javascript" language="javascript">
jQuery(document).ready(function() {
jQuery("#typedeposit, #valuedeposit").click(function() {
jQuery("#radio_deposit").attr(\'checked\',\'checked\');
jQuery("#radio_deposit").prop("checked", true);
});
});
</script>';
@@ -2154,7 +2154,7 @@ if ($action == 'create')
print '<script type="text/javascript" language="javascript">
jQuery(document).ready(function() {
jQuery("#fac_replacement").change(function() {
jQuery("#radio_replacement").attr(\'checked\',\'checked\');
jQuery("#radio_replacement").prop("checked", true);
});
});
</script>';
@@ -2200,7 +2200,7 @@ if ($action == 'create')
// Show credit note options only if we checked credit note
print '<script type="text/javascript" language="javascript">
jQuery(document).ready(function() {
if (! jQuery("#radio_creditnote").attr(\'checked\'))
if (! jQuery("#radio_creditnote").is(":checked"))
{
jQuery("#credit_note_options").hide();
}
@@ -2229,8 +2229,8 @@ if ($action == 'create')
print $desc;
print '<div id="credit_note_options">';
print '&nbsp;&nbsp;&nbsp; <input type="checkbox" name="invoiceAvoirWithLines" id="invoiceAvoirWithLines" value="1" onclick="if($(this).is(\':checked\') ) { $(\'#radio_creditnote\').attr(\'checked\',\'checked\'); $(\'#invoiceAvoirWithPaymentRestAmount\').removeAttr(\'checked\'); }" '.(GETPOST('invoiceAvoirWithLines','int')>0 ? 'checked':'').' /> <label for="invoiceAvoirWithLines">'.$langs->trans('invoiceAvoirWithLines')."</label>";
print '<br>&nbsp;&nbsp;&nbsp; <input type="checkbox" name="invoiceAvoirWithPaymentRestAmount" id="invoiceAvoirWithPaymentRestAmount" value="1" onclick="if($(this).is(\':checked\') ) { $(\'#radio_creditnote\').attr(\'checked\',\'checked\'); $(\'#invoiceAvoirWithLines\').removeAttr(\'checked\'); }" '.(GETPOST('invoiceAvoirWithPaymentRestAmount','int')>0 ? 'checked':'').' /> <label for="invoiceAvoirWithPaymentRestAmount">'.$langs->trans('invoiceAvoirWithPaymentRestAmount')."</label>";
print '&nbsp;&nbsp;&nbsp; <input type="checkbox" name="invoiceAvoirWithLines" id="invoiceAvoirWithLines" value="1" onclick="if($(this).is(\':checked\') ) { $(\'#radio_creditnote\').prop("checked", true); $(\'#invoiceAvoirWithPaymentRestAmount\').removeAttr(\'checked\'); }" '.(GETPOST('invoiceAvoirWithLines','int')>0 ? 'checked':'').' /> <label for="invoiceAvoirWithLines">'.$langs->trans('invoiceAvoirWithLines')."</label>";
print '<br>&nbsp;&nbsp;&nbsp; <input type="checkbox" name="invoiceAvoirWithPaymentRestAmount" id="invoiceAvoirWithPaymentRestAmount" value="1" onclick="if($(this).is(\':checked\') ) { $(\'#radio_creditnote\').prop("checked", true); $(\'#invoiceAvoirWithLines\').removeAttr(\'checked\'); }" '.(GETPOST('invoiceAvoirWithPaymentRestAmount','int')>0 ? 'checked':'').' /> <label for="invoiceAvoirWithPaymentRestAmount">'.$langs->trans('invoiceAvoirWithPaymentRestAmount')."</label>";
print '</div>';
print '</td></tr>' . "\n";
@@ -3137,28 +3137,28 @@ if ($action == 'create')
// Paye partiellement 'escompte'
if (($object->statut == 2 || $object->statut == 3) && $object->close_code == 'discount_vat') {
print '<tr><td colspan="' . $nbcols . '" align="right" nowrap="1">';
print '<tr><td colspan="' . $nbcols . '" align="right" class="nowrap">';
print $form->textwithpicto($langs->trans("Discount") . ':', $langs->trans("HelpEscompte"), - 1);
print '</td><td align="right">' . price($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye) . '</td><td>&nbsp;</td></tr>';
$resteapayeraffiche = 0;
}
// Paye partiellement ou Abandon 'badcustomer'
if (($object->statut == 2 || $object->statut == 3) && $object->close_code == 'badcustomer') {
print '<tr><td colspan="' . $nbcols . '" align="right" nowrap="1">';
print '<tr><td colspan="' . $nbcols . '" align="right" class="nowrap">';
print $form->textwithpicto($langs->trans("Abandoned") . ':', $langs->trans("HelpAbandonBadCustomer"), - 1);
print '</td><td align="right">' . price($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye) . '</td><td>&nbsp;</td></tr>';
// $resteapayeraffiche=0;
}
// Paye partiellement ou Abandon 'product_returned'
if (($object->statut == 2 || $object->statut == 3) && $object->close_code == 'product_returned') {
print '<tr><td colspan="' . $nbcols . '" align="right" nowrap="1">';
print '<tr><td colspan="' . $nbcols . '" align="right" class="nowrap">';
print $form->textwithpicto($langs->trans("ProductReturned") . ':', $langs->trans("HelpAbandonProductReturned"), - 1);
print '</td><td align="right">' . price($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye) . '</td><td>&nbsp;</td></tr>';
$resteapayeraffiche = 0;
}
// Paye partiellement ou Abandon 'abandon'
if (($object->statut == 2 || $object->statut == 3) && $object->close_code == 'abandon') {
print '<tr><td colspan="' . $nbcols . '" align="right" nowrap="1">';
print '<tr><td colspan="' . $nbcols . '" align="right" class="nowrap">';
$text = $langs->trans("HelpAbandonOther");
if ($object->close_note)
$text .= '<br><br><b>' . $langs->trans("Reason") . '</b>:' . $object->close_note;
@@ -3319,7 +3319,7 @@ if ($action == 'create')
print $object->situation_counter;
print '</td>';
print '<td align="right" colspan="2" nowrap>';
print '<td align="right" colspan="2" class="nowrap">';
$prevsits_total_amount = 0;
foreach ($prevsits as $situation) {
@@ -3340,7 +3340,7 @@ if ($action == 'create')
print $prevsits[$i]->situation_counter;
print '</a></td>';
print '<td align="right" colspan="2" nowrap>';
print '<td align="right" colspan="2" class="nowrap">';
print '- ' . price($prevsits[$i]->total_ht);
print '</td>';
print '<td>' . $langs->trans('Currency' . $conf->currency) . '</td></tr>';
@@ -3350,20 +3350,20 @@ if ($action == 'create')
// Amount
print '<tr><td>' . $langs->trans('AmountHT') . '</td>';
print '<td colspan="3" nowrap>' . price($object->total_ht, 1, '', 1, - 1, - 1, $conf->currency) . '</td></tr>';
print '<tr><td>' . $langs->trans('AmountVAT') . '</td><td colspan="3" nowrap>' . price($object->total_tva, 1, '', 1, - 1, - 1, $conf->currency) . '</td></tr>';
print '<td colspan="3" class="nowrap">' . price($object->total_ht, 1, '', 1, - 1, - 1, $conf->currency) . '</td></tr>';
print '<tr><td>' . $langs->trans('AmountVAT') . '</td><td colspan="3" class="nowrap">' . price($object->total_tva, 1, '', 1, - 1, - 1, $conf->currency) . '</td></tr>';
print '</tr>';
// Amount Local Taxes
if (($mysoc->localtax1_assuj == "1" && $mysoc->useLocalTax(1)) || $object->total_localtax1 != 0) // Localtax1
{
print '<tr><td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td>';
print '<td colspan="3" nowrap>' . price($object->total_localtax1, 1, '', 1, - 1, - 1, $conf->currency) . '</td></tr>';
print '<td colspan="3" class="nowrap">' . price($object->total_localtax1, 1, '', 1, - 1, - 1, $conf->currency) . '</td></tr>';
}
if (($mysoc->localtax2_assuj == "1" && $mysoc->useLocalTax(2)) || $object->total_localtax2 != 0) // Localtax2
{
print '<tr><td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td>';
print '<td colspan="3" nowrap>' . price($object->total_localtax2, 1, '', 1, - 1, - 1, $conf->currency) . '</td></tr>';
print '<td colspan="3" class=nowrap">' . price($object->total_localtax2, 1, '', 1, - 1, - 1, $conf->currency) . '</td></tr>';
}
// Revenue stamp

View 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;
}
}

View File

@@ -393,16 +393,16 @@ llxHeader('',$title);
<script type="text/javascript">
$(document).ready(function() {
$("#checkall").click(function() {
$(".checkformerge").attr('checked', true);
$(".checkformerge").prop('checked', true);
});
$("#checknone").click(function() {
$(".checkformerge").attr('checked', false);
$(".checkformerge").prop('checked', false);
});
$("#checkallsend").click(function() {
$(".checkforsend").attr('checked', true);
$(".checkforsend").prop('checked', true);
});
$("#checknonesend").click(function() {
$(".checkforsend").attr('checked', false);
$(".checkforsend").prop('checked', false);
});
});
</script>

View File

@@ -399,27 +399,27 @@ if ($object->id > 0)
// Montants
print '<tr><td>'.$langs->trans('AmountHT').'</td>';
print '<td align="right" colspan="2" nowrap>'.price($object->total_ht).'</td>';
print '<td align="right" colspan="2" class="nowrap">'.price($object->total_ht).'</td>';
print '<td>'.$langs->trans('Currency'.$conf->currency).'</td></tr>';
print '<tr><td>'.$langs->trans('AmountVAT').'</td><td align="right" colspan="2" nowrap>'.price($object->total_tva).'</td>';
print '<tr><td>'.$langs->trans('AmountVAT').'</td><td align="right" colspan="2" class="nowrap">'.price($object->total_tva).'</td>';
print '<td>'.$langs->trans('Currency'.$conf->currency).'</td></tr>';
// Amount Local Taxes
if ($mysoc->localtax1_assuj=="1") //Localtax1
{
print '<tr><td>'.$langs->transcountry("AmountLT1",$mysoc->country_code).'</td>';
print '<td align="right" colspan="2" nowrap>'.price($object->total_localtax1).'</td>';
print '<td align="right" colspan="2" class="nowrap">'.price($object->total_localtax1).'</td>';
print '<td>'.$langs->trans("Currency".$conf->currency).'</td></tr>';
}
if ($mysoc->localtax2_assuj=="1") //Localtax2
{
print '<tr><td>'.$langs->transcountry("AmountLT2",$mysoc->country_code).'</td>';
print '<td align="right" colspan="2" nowrap>'.price($object->total_localtax2).'</td>';
print '<td align="right" colspan="2" class="nowrap">'.price($object->total_localtax2).'</td>';
print '<td>'.$langs->trans("Currency".$conf->currency).'</td></tr>';
}
print '<tr><td>'.$langs->trans('AmountTTC').'</td><td align="right" colspan="2" nowrap>'.price($object->total_ttc).'</td>';
print '<tr><td>'.$langs->trans('AmountTTC').'</td><td align="right" colspan="2" class="nowrap">'.price($object->total_ttc).'</td>';
print '<td>'.$langs->trans('Currency'.$conf->currency).'</td></tr>';
// We can also use bcadd to avoid pb with floating points
@@ -428,7 +428,7 @@ if ($object->id > 0)
//$resteapayer=bcadd($resteapayer,$totalavoir,$conf->global->MAIN_MAX_DECIMALS_TOT);
$resteapayer = price2num($object->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits,'MT');
print '<tr><td>'.$langs->trans('RemainderToPay').'</td><td align="right" colspan="2" nowrap>'.price($resteapayer).'</td>';
print '<tr><td>'.$langs->trans('RemainderToPay').'</td><td align="right" colspan="2" class="nowrap">'.price($resteapayer).'</td>';
print '<td>'.$langs->trans('Currency'.$conf->currency).'</td></tr>';
// Statut

View File

@@ -86,7 +86,7 @@ function pt ($db, $sql, $date)
$i++;
}
print '<tr class="liste_total"><td align="right">'.$langs->trans("Total")." :</td><td nowrap=\"nowrap\" align=\"right\"><b>".price($total)."</b></td><td>&nbsp;</td></tr>";
print '<tr class="liste_total"><td align="right">'.$langs->trans("Total")." :</td><td class=\"nowrap\" align=\"right\"><b>".price($total)."</b></td><td>&nbsp;</td></tr>";
print "</table>";
$db->free($result);
@@ -253,7 +253,7 @@ for ($m = 1 ; $m < 13 ; $m++ ) {
$subtotalcoll=0; $subtotalpaye=0; $subtotal=0;
}
}
print '<tr class="liste_total"><td align="right" colspan="3">'.$langs->trans("TotalToPay").':</td><td nowrap align="right">'.price($total).'</td>';
print '<tr class="liste_total"><td align="right" colspan="3">'.$langs->trans("TotalToPay").':</td><td class="nowrap" align="right">'.price($total).'</td>';
print "<td>&nbsp;</td>\n";
print '</tr>';

View File

@@ -414,11 +414,11 @@ if ($action == 'new')
{
jQuery("#checkall_'.$bid.'").click(function()
{
jQuery(".checkforremise_'.$bid.'").attr(\'checked\', true);
jQuery(".checkforremise_'.$bid.'").prop(\'checked\', true);
});
jQuery("#checknone_'.$bid.'").click(function()
{
jQuery(".checkforremise_'.$bid.'").attr(\'checked\', false);
jQuery(".checkforremise_'.$bid.'").prop(\'checked\', false);
});
});
</script>

View File

@@ -93,7 +93,7 @@ function pt ($db, $sql, $date)
$i++;
}
print '<tr class="liste_total"><td align="right">'.$langs->trans("Total")." :</td><td nowrap=\"nowrap\" align=\"right\"><b>".price($total)."</b></td><td>&nbsp;</td></tr>";
print '<tr class="liste_total"><td align="right">'.$langs->trans("Total")." :</td><td class=\"nowrap\" align=\"right\"><b>".price($total)."</b></td><td>&nbsp;</td></tr>";
print "</table>";
$db->free($result);
@@ -216,7 +216,7 @@ for ($m = 1 ; $m < 13 ; $m++ )
$subtotalcoll=0; $subtotalpaye=0; $subtotal=0;
}
}
print '<tr class="liste_total"><td align="right" colspan="3">'.$langs->trans("TotalToPay").':</td><td nowrap align="right">'.price($total).'</td>';
print '<tr class="liste_total"><td align="right" colspan="3">'.$langs->trans("TotalToPay").':</td><td class="nowrap" align="right">'.price($total).'</td>';
print "<td>&nbsp;</td>\n";
print '</tr>';

View File

@@ -437,7 +437,7 @@ if ($result)
print '</form>';
if ($num > $limit) print_barre_liste('', $page, $_SERVER["PHP_SELF"], '&amp;begin='.$begin.'&amp;view='.$view.'&amp;userid='.$userid, $sortfield, $sortorder, '', $num, $nbtotalofrecords, '');
if ($num > $limit) print_barre_liste('', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, '');
$db->free($result);
}

View File

@@ -1021,12 +1021,12 @@ if ($action == 'create')
}
// Commercial suivi
print '<tr><td width="20%" nowrap><span class="fieldrequired">'.$langs->trans("TypeContact_contrat_internal_SALESREPFOLL").'</span></td><td>';
print '<tr><td width="20%" class="nowrap"><span class="fieldrequired">'.$langs->trans("TypeContact_contrat_internal_SALESREPFOLL").'</span></td><td>';
print $form->select_dolusers(GETPOST("commercial_suivi_id")?GETPOST("commercial_suivi_id"):$user->id,'commercial_suivi_id',1,'');
print '</td></tr>';
// Commercial signature
print '<tr><td width="20%" nowrap><span class="fieldrequired">'.$langs->trans("TypeContact_contrat_internal_SALESREPSIGN").'</span></td><td>';
print '<tr><td width="20%" class="nowrap"><span class="fieldrequired">'.$langs->trans("TypeContact_contrat_internal_SALESREPSIGN").'</span></td><td>';
print $form->select_dolusers(GETPOST("commercial_signature_id")?GETPOST("commercial_signature_id"):$user->id,'commercial_signature_id',1,'');
print '</td></tr>';

View File

@@ -1124,7 +1124,7 @@ class ExtraFields
if (!empty($value)) {
$checked=' checked ';
}
$value='<input type="checkbox" '.$checked.' '.($moreparam?$moreparam:'').' readonly="readonly" disabled>';
$value='<input type="checkbox" '.$checked.' '.($moreparam?$moreparam:'').' readonly disabled>';
}
elseif ($type == 'mail')
{

View File

@@ -4356,7 +4356,7 @@ class Form
</script>';
}
$out.='<select id="'.preg_replace('/^\./','',$htmlname).'" '.($disabled?'disabled="disabled" ':'').'class="flat '.(preg_replace('/^\./','',$htmlname)).($morecss?' '.$morecss:'').'" name="'.preg_replace('/^\./','',$htmlname).'" '.($moreparam?$moreparam:'').'>';
$out.='<select id="'.preg_replace('/^\./','',$htmlname).'" '.($disabled?'disabled ':'').'class="flat '.(preg_replace('/^\./','',$htmlname)).($morecss?' '.$morecss:'').'" name="'.preg_replace('/^\./','',$htmlname).'" '.($moreparam?$moreparam:'').'>';
if ($show_empty)
{
@@ -4539,7 +4539,7 @@ class Form
// Try also magic suggest
// Add data-role="none" to disable jmobile decoration
$out = '<select data-role="none" id="'.$htmlname.'" class="multiselect'.($morecss?' '.$morecss:'').'" multiple="multiple" name="'.$htmlname.'[]"'.($moreattrib?' '.$moreattrib:'').($width?' style="width: '.(preg_match('/%/',$width)?$width:$width.'px').'"':'').'>'."\n";
$out = '<select data-role="none" id="'.$htmlname.'" class="multiselect'.($morecss?' '.$morecss:'').'" multiple name="'.$htmlname.'[]"'.($moreattrib?' '.$moreattrib:'').($width?' style="width: '.(preg_match('/%/',$width)?$width:$width.'px').'"':'').'>'."\n";
if (is_array($array) && ! empty($array))
{
if ($value_as_key) $array=array_combine($array, $array);

View File

@@ -95,17 +95,17 @@ class FormActions
percentage.val(value);
if (defaultvalue == -1) {
percentage.attr('disabled', 'disabled');
percentage.prop('disabled', true);
$('.hideifna').hide();
}
else if (defaultvalue == 0) {
percentage.val(0);
percentage.attr('disabled', 'disabled');
percentage.prop('disabled', true);
$('.hideifna').show();
}
else if (defaultvalue == 100) {
percentage.val(100);
percentage.attr('disabled', 'disabled');
percentage.prop('disabled', true);
$('.hideifna').show();
}
else {

View File

@@ -5,7 +5,8 @@
* Copyright (C) 2013 Charles-Fr BENKE <charles.fr@benke.fr>
* Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
* Copyright (C) 2014 Marcos García <marcosgdf@gmail.com>
*
* Copyright (C) 2015 Bahfir Abbes <bafbes@gmail.com>
* 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

View File

@@ -4,7 +4,7 @@
* Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
* Copyright (C) 2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2014-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.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
@@ -21,7 +21,7 @@
*/
/**
* Class to manage Dolibarr database access for a Mysql database
* Class to manage Dolibarr database access for an SQL database
*/
interface Database
{
@@ -111,13 +111,6 @@ interface Database
*/
function error();
/**
* Return label of manager
*
* @return string Label
*/
function getLabel();
/**
* List tables into a database
*
@@ -141,7 +134,7 @@ interface Database
* @param string $sortorder Sort order
* @return string String to provide syntax of a sort sql string
*/
function order($sortfield = 0, $sortorder = 0);
function order($sortfield = null, $sortorder = null);
/**
* Decrypt sensitive data in database
@@ -216,7 +209,7 @@ interface Database
* @param string $login login
* @param string $passwd password
* @param string $name name of database (not used for mysql, used for pgsql)
* @param string $port Port of database server
* @param int $port Port of database server
* @return resource Database access handler
* @see close
*/
@@ -293,10 +286,10 @@ interface Database
* @param string $type Type de la table
* @param array $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur
* @param array $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext
* @param string $keys Tableau des champs cles noms => valeur
* @param array $keys Tableau des champs cles noms => valeur
* @return int <0 if KO, >=0 if OK
*/
function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = "", $fulltext_keys = "", $keys = "");
function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null);
/**
* Return list of available charset that can be used to store data in database
@@ -382,15 +375,15 @@ interface Database
);
/**
* Convert (by PHP) a PHP server TZ string date into a Timestamps date (GMT if gm=true)
* 19700101020000 -> 3600 with TZ+1 and gmt=0
* 19700101020000 -> 7200 whaterver is TZ if gmt=1
*
* Convert (by PHP) a PHP server TZ string date into a Timestamps date (GMT if gm=true)
* 19700101020000 -> 3600 with TZ+1 and gmt=0
* 19700101020000 -> 7200 whaterver is TZ if gmt=1
*
* @param string $string Date in a string (YYYYMMDDHHMMSS, YYYYMMDD, YYYY-MM-DD HH:MM:SS)
* @param int $gm 1=Input informations are GMT values, otherwise local to server TZ
* @param bool $gm 1=Input informations are GMT values, otherwise local to server TZ
* @return int|string Date TMS or ''
*/
function jdate($string, $gm=false);
*/
function jdate($string, $gm=false);
/**
* Encrypt sensitive data in database
@@ -424,7 +417,7 @@ interface Database
* @param resource $resultset Fre cursor
* @return void
*/
function free($resultset = 0);
function free($resultset = null);
/**
* Close database connexion

View File

@@ -1,6 +1,6 @@
<?php
/*
* Copyright (C) 2013-2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2013-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2014-2015 Laurent Destailleur <eldy@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
@@ -29,41 +29,43 @@ require_once DOL_DOCUMENT_ROOT .'/core/db/Database.interface.php';
*/
abstract class DoliDB implements Database
{
//! Database handler
public $db;
//! Database type
public $type;
//! Charset used to force charset when creating database
public $forcecharset='utf8';
//! Collate used to force collate when creating database
public $forcecollate='utf8_general_ci';
//! Resultset of last query
private $_results;
//! 1 if connected, else 0
public $connected;
//! 1 if database selected, else 0
public $database_selected;
//! Selected database name
public $database_name;
//! Database username
public $database_user;
//! Database host
public $database_host;
//! Database port
public $database_port;
//! >=1 if a transaction is opened, 0 otherwise
public $transaction_opened;
//! Last successful query
public $lastquery;
//! Last failed query
public $lastqueryerror;
//! Last error message
public $lasterror;
//! Last error number
public $lasterrno;
/** @var resource Database handler */
public $db;
/** @var string Database type */
public $type;
/** @var string Charset used to force charset when creating database */
public $forcecharset='utf8';
/** @var string Collate used to force collate when creating database */
public $forcecollate='utf8_general_ci';
/** @var resource Resultset of last query */
private $_results;
/** @var bool true if connected, else false */
public $connected;
/** @var bool true if database selected, else false */
public $database_selected;
/** @var string Selected database name */
public $database_name;
/** @var string Database username */
public $database_user;
/** @var string Database host */
public $database_host;
/** @var int Database port */
public $database_port;
/** @var int >=1 if a transaction is opened, 0 otherwise */
public $transaction_opened;
/** @var string Last successful query */
public $lastquery;
/** @ar string Last failed query */
public $lastqueryerror;
/** @var string Last error message */
public $lasterror;
/** @var int Last error number */
public $lasterrno;
public $ok;
public $error;
/** @var bool Status */
public $ok;
/** @var string */
public $error;
/**
* Format a SQL IF
@@ -205,16 +207,6 @@ abstract class DoliDB implements Database
return preg_split("/[\.,-]/",$this->getVersion());
}
/**
* Return label of manager
*
* @return string Label
*/
function getLabel()
{
return $this->label;
}
/**
* Return last request executed with query()
*
@@ -232,9 +224,9 @@ abstract class DoliDB implements Database
* @param string $sortorder Sort order
* @return string String to provide syntax of a sort sql string
*/
function order($sortfield=0,$sortorder=0)
function order($sortfield=null,$sortorder=null)
{
if ($sortfield)
if (isset($sortfield))
{
$return='';
$fields=explode(',',$sortfield);
@@ -244,7 +236,9 @@ abstract class DoliDB implements Database
else $return.=',';
$return.=preg_replace('/[^0-9a-z_\.]/i','',$val);
if ($sortorder) $return.=' '.preg_replace('/[^0-9a-z]/i','',$sortorder);
if (isset($sortorder)) {
$return.=' '.preg_replace('/[^0-9a-z]/i','',$sortorder);
}
}
return $return;
}
@@ -270,7 +264,7 @@ abstract class DoliDB implements Database
* 19700101020000 -> 7200 whaterver is TZ if gmt=1
*
* @param string $string Date in a string (YYYYMMDDHHMMSS, YYYYMMDD, YYYY-MM-DD HH:MM:SS)
* @param int $gm 1=Input informations are GMT values, otherwise local to server TZ
* @param bool $gm 1=Input informations are GMT values, otherwise local to server TZ
* @return int|string Date TMS or ''
*/
function jdate($string, $gm=false)

View File

@@ -21,7 +21,7 @@
/**
* \file htdocs/core/db/mssql.class.php
* \brief Fichier de la classe permettant de gerer une base mssql
* \brief Fichier de la classe permettant de gerer une base MSSQL
*/
require_once DOL_DOCUMENT_ROOT .'/core/db/DoliDB.class.php';
@@ -41,7 +41,7 @@ class DoliDBMssql extends DoliDB
var $forcecollate='latin1_swedish_ci'; // Can't be static as it may be forced with a dynamic value
//! Version min database
const VERSIONMIN='2000';
//! Resultset of last query
/** @var resource Resultset of last query */
private $_results;
/**
@@ -57,7 +57,7 @@ class DoliDBMssql extends DoliDB
*/
function __construct($type, $host, $user, $pass, $name='', $port=0)
{
global $conf,$langs;
global $langs;
$this->database_user=$user;
$this->database_host=$host;
@@ -66,8 +66,8 @@ class DoliDBMssql extends DoliDB
if (! function_exists("mssql_connect"))
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error="Mssql PHP functions for using MSSql driver are not available in this version of PHP";
dol_syslog(get_class($this)."::DoliDBMssql : MSsql PHP functions for using MSsql driver are not available in this version of PHP",LOG_ERR);
return $this->ok;
@@ -75,8 +75,8 @@ class DoliDBMssql extends DoliDB
if (! $host)
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error=$langs->trans("ErrorWrongHostParameter");
dol_syslog(get_class($this)."::DoliDBMssql : Erreur Connect, wrong host parameters",LOG_ERR);
return $this->ok;
@@ -88,14 +88,14 @@ class DoliDBMssql extends DoliDB
{
// Si client connecte avec charset different de celui de la base Dolibarr
// (La base Dolibarr a ete forcee en this->forcecharset a l'install)
$this->connected = 1;
$this->ok = 1;
$this->connected = true;
$this->ok = true;
}
else
{
// host, login ou password incorrect
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error=mssql_get_last_message();
dol_syslog(get_class($this)."::DoliDBMssql : Erreur Connect mssql_get_last_message=".$this->error,LOG_ERR);
}
@@ -105,15 +105,15 @@ class DoliDBMssql extends DoliDB
{
if ($this->select_db($name))
{
$this->database_selected = 1;
$this->database_selected = true;
$this->database_name = $name;
$this->ok = 1;
$this->ok = true;
}
else
{
$this->database_selected = 0;
$this->database_selected = false;
$this->database_name = '';
$this->ok = 0;
$this->ok = false;
$this->error=$this->error();
dol_syslog(get_class($this)."::DoliDBMssql : Erreur Select_db ".$this->error,LOG_ERR);
}
@@ -121,7 +121,7 @@ class DoliDBMssql extends DoliDB
else
{
// Pas de selection de base demandee, ok ou ko
$this->database_selected = 0;
$this->database_selected = false;
}
return $this->ok;
@@ -157,8 +157,8 @@ class DoliDBMssql extends DoliDB
* @param string $login login
* @param string $passwd password
* @param string $name name of database (not used for mysql, used for pgsql)
* @param string $port Port of database server
* @return resource Database access handler
* @param int $port Port of database server
* @return false|resource|true Database access handler
* @see close
*/
function connect($host, $login, $passwd, $name, $port=0)
@@ -212,7 +212,7 @@ class DoliDBMssql extends DoliDB
/**
* Close database connexion
*
* @return boolean True if disconnect successfull, false otherwise
* @return bool True if disconnect successfull, false otherwise
* @see connect
*/
function close()
@@ -220,7 +220,7 @@ class DoliDBMssql extends DoliDB
if ($this->db)
{
if ($this->transaction_opened > 0) dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened,LOG_ERR);
$this->connected=0;
$this->connected=false;
return mssql_close($this->db);
}
return false;
@@ -230,7 +230,7 @@ class DoliDBMssql extends DoliDB
/**
* Start transaction
*
* @return int 1 if transaction successfuly opened or already opened, 0 if error
* @return bool true if transaction successfuly opened or already opened, false if error
*/
function begin()
{
@@ -250,7 +250,7 @@ class DoliDBMssql extends DoliDB
}
else
{
return 1;
return true;
}
}
@@ -258,7 +258,7 @@ class DoliDBMssql extends DoliDB
* Validate a database transaction
*
* @param string $log Add more log to default log line
* @return int 1 if validation is OK or transaction level no started, 0 if ERROR
* @return bool true if validation is OK or transaction level no started, false if ERROR
*/
function commit($log='')
{
@@ -272,25 +272,26 @@ class DoliDBMssql extends DoliDB
if ($ret)
{
dol_syslog("COMMIT Transaction",LOG_DEBUG);
return 1;
return true;
}
else
{
return 0;
return false;
}
}
elseif ($this->transaction_opened > 1)
{
return 1;
} else
trigger_error("Commit requested but no transaction remain");
return true;
}
trigger_error("Commit requested but no transaction remain");
return false;
}
/**
* Annulation d'une transaction et retour aux anciennes valeurs
*
* @param string $log Add more log to default log line
* @return int 1 si annulation ok ou transaction non ouverte, 0 en cas d'erreur
* @return bool true si annulation ok ou transaction non ouverte, false en cas d'erreur
*/
function rollback($log='')
{
@@ -305,9 +306,10 @@ class DoliDBMssql extends DoliDB
}
elseif ($this->transaction_opened > 1)
{
return 1;
} else
trigger_error("Rollback requested but no transaction remain");
return true;
}
trigger_error("Rollback requested but no transaction remain");
return false;
}
/**
@@ -317,7 +319,7 @@ class DoliDBMssql extends DoliDB
* @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollbock to savepoint if error (this allow to have some request with errors inside global transactions).
* Note that with Mysql, this parameter is not used as Myssql can already commit a transaction even if one request is in error, without using savepoints.
* @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
* @return resource Resultset of answer
* @return false|resource|true Resultset of answer
*/
function query($query,$usesavepoint=0,$type='auto')
{
@@ -459,8 +461,8 @@ class DoliDBMssql extends DoliDB
/**
* Renvoie la ligne courante (comme un objet) pour le curseur resultset
*
* @param Resultset $resultset Curseur de la requete voulue
* @return Object Object result line or false if KO or end of cursor
* @param resource $resultset Curseur de la requete voulue
* @return object|false Object result line or false if KO or end of cursor
*/
function fetch_object($resultset)
{
@@ -472,8 +474,8 @@ class DoliDBMssql extends DoliDB
/**
* Return datas as an array
*
* @param Resultset $resultset Resultset of request
* @return array Array
* @param resource $resultset Resultset of request
* @return array|false Array or false if KO or end of cursor
*/
function fetch_array($resultset)
{
@@ -486,8 +488,8 @@ class DoliDBMssql extends DoliDB
/**
* Return datas as an array
*
* @param Resultset $resultset Resultset of request
* @return array Array
* @param resource $resultset Resultset of request
* @return array|false Array or false if KO or end of cursor
*/
function fetch_row($resultset)
{
@@ -499,7 +501,7 @@ class DoliDBMssql extends DoliDB
/**
* Return number of lines for result of a SELECT
*
* @param Resultset $resultset Resulset of requests
* @param resource $resultset Resulset of requests
* @return int Nb of lines
* @see affected_rows
*/
@@ -513,7 +515,7 @@ class DoliDBMssql extends DoliDB
/**
* Renvoie le nombre de lignes dans le resultat d'une requete INSERT, DELETE ou UPDATE
*
* @param resultset $resultset Curseur de la requete voulue
* @param resource $resultset Curseur de la requete voulue
* @return int Nombre de lignes
* @see num_rows
*/
@@ -532,10 +534,10 @@ class DoliDBMssql extends DoliDB
/**
* Free last resultset used.
*
* @param resultset $resultset Curseur de la requete voulue
* @return void
* @param resource $resultset Curseur de la requete voulue
* @return bool
*/
function free($resultset=0)
function free($resultset=null)
{
// Si le resultset n'est pas fourni, on prend le dernier utilise sur cette connexion
if (! is_resource($resultset)) { $resultset=$this->_results; }
@@ -641,7 +643,7 @@ class DoliDBMssql extends DoliDB
*
* @param string $tab Table name concerned by insert. Ne sert pas sous MySql mais requis pour compatibilite avec Postgresql
* @param string $fieldid Field name
* @return int Id of row
* @return int Id of row or -1 on error
*/
function last_insert_id($tab,$fieldid='rowid')
{
@@ -662,7 +664,7 @@ class DoliDBMssql extends DoliDB
*
* @param string $fieldorvalue Field name or value to encrypt
* @param int $withQuotes Return string with quotes
* @return return XXX(field) or XXX('value') or field or 'value'
* @return string XXX(field) or XXX('value') or field or 'value'
*/
function encrypt($fieldorvalue, $withQuotes=0)
{
@@ -720,7 +722,7 @@ class DoliDBMssql extends DoliDB
* @param string $charset Charset used to store data
* @param string $collation Charset used to sort data
* @param string $owner Username of database owner
* @return resource resource defined if OK, null if KO
* @return false|resource|true resource defined if OK, false if KO
*/
function DDLCreateDb($database,$charset='',$collation='',$owner='')
{
@@ -786,11 +788,13 @@ class DoliDBMssql extends DoliDB
* @param string $type Type de la table
* @param array $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur
* @param array $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext
* @param string $keys Tableau des champs cles noms => valeur
* @param array $keys Tableau des champs cles noms => valeur
* @return int <0 if KO, >=0 if OK
*/
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys="",$fulltext_keys="",$keys="")
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys=null,$fulltext_keys=null,$keys=null)
{
// FIXME: $fulltext_keys parameter is unused
// cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra
// ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
$sql = "create table ".$table."(";
@@ -820,7 +824,7 @@ class DoliDBMssql extends DoliDB
if($primary_key != "")
$pk = "primary key(".$primary_key.")";
if($unique_keys != "")
if(is_array($unique_keys))
{
$i = 0;
foreach($unique_keys as $key => $value)
@@ -829,7 +833,7 @@ class DoliDBMssql extends DoliDB
$i++;
}
}
if($keys != "")
if(is_array($keys))
{
$i = 0;
foreach($keys as $key => $value)
@@ -841,9 +845,9 @@ class DoliDBMssql extends DoliDB
$sql .= implode(',',$sqlfields);
if($primary_key != "")
$sql .= ",".$pk;
if($unique_keys != "")
if(is_array($unique_keys))
$sql .= ",".implode(',',$sqluq);
if($keys != "")
if(is_array($keys))
$sql .= ",".implode(',',$sqlk);
$sql .=") type=".$type;
@@ -859,7 +863,7 @@ class DoliDBMssql extends DoliDB
*
* @param string $table Name of table
* @param string $field Optionnel : Name of field if we want description of field
* @return resource Resource
* @return false|resource|true Resource
*/
function DDLDescTable($table,$field="")
{
@@ -1047,7 +1051,7 @@ class DoliDBMssql extends DoliDB
// FIXME: Dummy method
// TODO: Implement
return '';
return array();
}
/**
@@ -1123,7 +1127,7 @@ class DoliDBMssql extends DoliDB
*
* @param string $table Table name which contains fields
* @param mixed $fields String for one field or array of string for multiple field
* @return boolean|multitype:object
* @return false|object
*/
function GetFieldInformation($table,$fields) {
$sql="SELECT * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='".$this->escape($table)."' AND COLUMN_NAME";

View File

@@ -21,13 +21,13 @@
/**
* \file htdocs/core/db/mysql.class.php
* \brief Class file to manage Dolibarr database access for a Mysql database
* \brief Class file to manage Dolibarr database access for a MySQL database
*/
require_once DOL_DOCUMENT_ROOT .'/core/db/DoliDB.class.php';
/**
* Class to manage Dolibarr database access for a Mysql database
* Class to manage Dolibarr database access for a MySQL database using the mysql extension
*/
class DoliDBMysql extends DoliDB
{
@@ -37,7 +37,7 @@ class DoliDBMysql extends DoliDB
const LABEL='MySQL';
//! Version min database
const VERSIONMIN='4.1.0';
//! Resultset of last query
/** @var resource Resultset of last query */
private $_results;
/**
@@ -69,8 +69,8 @@ class DoliDBMysql extends DoliDB
if (! function_exists("mysql_connect"))
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error="Mysql PHP functions for using MySql driver are not available in this version of PHP. Try to use another driver.";
dol_syslog(get_class($this)."::DoliDBMysql : Mysql PHP functions for using Mysql driver are not available in this version of PHP. Try to use another driver.",LOG_ERR);
return $this->ok;
@@ -78,8 +78,8 @@ class DoliDBMysql extends DoliDB
if (! $host)
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error=$langs->trans("ErrorWrongHostParameter");
dol_syslog(get_class($this)."::DoliDBMysql : Erreur Connect, wrong host parameters",LOG_ERR);
return $this->ok;
@@ -89,14 +89,14 @@ class DoliDBMysql extends DoliDB
$this->db = $this->connect($host, $user, $pass, $name, $port);
if ($this->db)
{
$this->connected = 1;
$this->ok = 1;
$this->connected = true;
$this->ok = true;
}
else
{
// host, login ou password incorrect
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error=mysql_error();
dol_syslog(get_class($this)."::DoliDBMysql : Erreur Connect mysql_error=".$this->error,LOG_ERR);
}
@@ -106,9 +106,9 @@ class DoliDBMysql extends DoliDB
{
if ($this->select_db($name))
{
$this->database_selected = 1;
$this->database_selected = true;
$this->database_name = $name;
$this->ok = 1;
$this->ok = true;
// If client connected with different charset than Dolibarr HTML output
$clientmustbe='';
@@ -122,9 +122,9 @@ class DoliDBMysql extends DoliDB
}
else
{
$this->database_selected = 0;
$this->database_selected = false;
$this->database_name = '';
$this->ok = 0;
$this->ok = false;
$this->error=$this->error();
dol_syslog(get_class($this)."::DoliDBMysql : Erreur Select_db ".$this->error,LOG_ERR);
}
@@ -132,7 +132,7 @@ class DoliDBMysql extends DoliDB
else
{
// Pas de selection de base demandee, ok ou ko
$this->database_selected = 0;
$this->database_selected = false;
if ($this->connected)
{
@@ -177,14 +177,14 @@ class DoliDBMysql extends DoliDB
}
/**
* Connexion to server
* Connection to server
*
* @param string $host database server host
* @param string $login login
* @param string $passwd password
* @param string $name name of database (not used for mysql, used for pgsql)
* @param integer $port Port of database server
* @return resource Database access handler
* @return resource|false Database access handler
* @see close
*/
function connect($host, $login, $passwd, $name, $port=0)
@@ -219,7 +219,7 @@ class DoliDBMysql extends DoliDB
*/
function getDriverInfo()
{
return mysqli_get_client_info();
return mysql_get_client_info();
}
@@ -234,7 +234,7 @@ class DoliDBMysql extends DoliDB
if ($this->db)
{
if ($this->transaction_opened > 0) dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened,LOG_ERR);
$this->connected=0;
$this->connected=false;
return mysql_close($this->db);
}
return false;
@@ -247,7 +247,7 @@ class DoliDBMysql extends DoliDB
* @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollbock to savepoint if error (this allow to have some request with errors inside global transactions).
* Note that with Mysql, this parameter is not used as Myssql can already commit a transaction even if one request is in error, without using savepoints.
* @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
* @return resource Resultset of answer
* @return resource|true|false Resultset of answer
*/
function query($query,$usesavepoint=0,$type='auto')
{
@@ -287,8 +287,8 @@ class DoliDBMysql extends DoliDB
/**
* Renvoie la ligne courante (comme un objet) pour le curseur resultset
*
* @param Resultset $resultset Curseur de la requete voulue
* @return Object Object result line or false if KO or end of cursor
* @param resource $resultset Curseur de la requete voulue
* @return resource|false Object result line or false if KO or end of cursor
*/
function fetch_object($resultset)
{
@@ -300,7 +300,7 @@ class DoliDBMysql extends DoliDB
/**
* Return datas as an array
*
* @param Resultset $resultset Resultset of request
* @param resource $resultset Resultset of request
* @return array Array
*/
function fetch_array($resultset)
@@ -327,7 +327,7 @@ class DoliDBMysql extends DoliDB
/**
* Return number of lines for result of a SELECT
*
* @param Resultset $resultset Resulset of requests
* @param resource $resultset Resulset of requests
* @return int Nb of lines
* @see affected_rows
*/
@@ -341,7 +341,7 @@ class DoliDBMysql extends DoliDB
/**
* Renvoie le nombre de lignes dans le resultat d'une requete INSERT, DELETE ou UPDATE
*
* @param resultset $resultset Curseur de la requete voulue
* @param resource $resultset Curseur de la requete voulue
* @return int Nombre de lignes
* @see num_rows
*/
@@ -358,10 +358,10 @@ class DoliDBMysql extends DoliDB
/**
* Free last resultset used.
*
* @param resultset $resultset Curseur de la requete voulue
* @param resource $resultset Curseur de la requete voulue
* @return void
*/
function free($resultset=0)
function free($resultset=null)
{
// If resultset not provided, we take the last used by connexion
if (! is_resource($resultset)) { $resultset=$this->_results; }
@@ -558,7 +558,7 @@ class DoliDBMysql extends DoliDB
* @param string $charset Charset used to store data
* @param string $collation Charset used to sort data
* @param string $owner Username of database owner
* @return resource resource defined if OK, null if KO
* @return false|resource|true resource defined if OK, null if KO
*/
function DDLCreateDb($database,$charset='',$collation='',$owner='')
{
@@ -634,11 +634,13 @@ class DoliDBMysql extends DoliDB
* @param string $type Type de la table
* @param array $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur
* @param array $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext
* @param string $keys Tableau des champs cles noms => valeur
* @param array $keys Tableau des champs cles noms => valeur
* @return int <0 if KO, >=0 if OK
*/
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys="",$fulltext_keys="",$keys="")
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys=null,$fulltext_keys=null,$keys=null)
{
// FIXME: $fulltext_keys parameter is unused
// cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra
// ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
$sql = "CREATE TABLE ".$table."(";
@@ -673,7 +675,7 @@ class DoliDBMysql extends DoliDB
if($primary_key != "")
$pk = "primary key(".$primary_key.")";
if($unique_keys != "")
if(is_array($unique_keys))
{
$i = 0;
foreach($unique_keys as $key => $value)
@@ -682,7 +684,7 @@ class DoliDBMysql extends DoliDB
$i++;
}
}
if($keys != "")
if(is_array($keys))
{
$i = 0;
foreach($keys as $key => $value)
@@ -694,9 +696,9 @@ class DoliDBMysql extends DoliDB
$sql .= implode(',',$sqlfields);
if($primary_key != "")
$sql .= ",".$pk;
if($unique_keys != "")
if(is_array($unique_keys))
$sql .= ",".implode(',',$sqluq);
if($keys != "")
if(is_array($keys))
$sql .= ",".implode(',',$sqlk);
$sql .=") engine=".$type;
@@ -712,7 +714,7 @@ class DoliDBMysql extends DoliDB
*
* @param string $table Name of table
* @param string $field Optionnel : Name of field if we want description of field
* @return resource Resource
* @return false|resource|true Resource
*/
function DDLDescTable($table,$field="")
{

View File

@@ -21,13 +21,13 @@
/**
* \file htdocs/core/db/mysqli.class.php
* \brief Class file to manage Dolibarr database access for a Mysql database
* \brief Class file to manage Dolibarr database access for a MySQL database
*/
require_once DOL_DOCUMENT_ROOT .'/core/db/DoliDB.class.php';
/**
* Class to manage Dolibarr database access for a Mysql database
* Class to manage Dolibarr database access for a MySQL database using the MySQLi extension
*/
class DoliDBMysqli extends DoliDB
{
@@ -36,8 +36,8 @@ class DoliDBMysqli extends DoliDB
//! Database label
const LABEL='MySQL';
//! Version min database
const VERSIONMIN='4.1.0';
//! Resultset of last query
const VERSIONMIN='4.1.3';
/** @var mysqli_result Resultset of last query */
private $_results;
/**
@@ -69,8 +69,8 @@ class DoliDBMysqli extends DoliDB
if (! function_exists("mysqli_connect"))
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error="Mysqli PHP functions for using Mysqli driver are not available in this version of PHP. Try to use another driver.";
dol_syslog(get_class($this)."::DoliDBMysqli : Mysqli PHP functions for using Mysqli driver are not available in this version of PHP. Try to use another driver.",LOG_ERR);
return $this->ok;
@@ -78,8 +78,8 @@ class DoliDBMysqli extends DoliDB
if (! $host)
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error=$langs->trans("ErrorWrongHostParameter");
dol_syslog(get_class($this)."::DoliDBMysqli : Erreur Connect, wrong host parameters",LOG_ERR);
return $this->ok;
@@ -91,14 +91,14 @@ class DoliDBMysqli extends DoliDB
if ($this->db)
{
$this->connected = 1;
$this->ok = 1;
$this->connected = true;
$this->ok = true;
}
else
{
// host, login ou password incorrect
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error=mysqli_connect_error();
dol_syslog(get_class($this)."::DoliDBMysqli : Erreur Connect mysqli_connect_error=".$this->error,LOG_ERR);
}
@@ -108,9 +108,9 @@ class DoliDBMysqli extends DoliDB
{
if ($this->select_db($name))
{
$this->database_selected = 1;
$this->database_selected = true;
$this->database_name = $name;
$this->ok = 1;
$this->ok = true;
// If client connected with different charset than Dolibarr HTML output
$clientmustbe='';
@@ -124,9 +124,9 @@ class DoliDBMysqli extends DoliDB
}
else
{
$this->database_selected = 0;
$this->database_selected = false;
$this->database_name = '';
$this->ok = 0;
$this->ok = false;
$this->error=$this->error();
dol_syslog(get_class($this)."::DoliDBMysqli : Erreur Select_db ".$this->error,LOG_ERR);
}
@@ -134,7 +134,7 @@ class DoliDBMysqli extends DoliDB
else
{
// Pas de selection de base demandee, ok ou ko
$this->database_selected = 0;
$this->database_selected = false;
if ($this->connected)
{
@@ -187,7 +187,7 @@ class DoliDBMysqli extends DoliDB
* @param string $passwd password
* @param string $name name of database (not used for mysql, used for pgsql)
* @param integer $port Port of database server
* @return resource Database access handler
* @return mysqli Database access handler
* @see close
*/
function connect($host, $login, $passwd, $name, $port=0)
@@ -230,7 +230,7 @@ class DoliDBMysqli extends DoliDB
/**
* Close database connexion
*
* @return boolean True if disconnect successfull, false otherwise
* @return bool True if disconnect successfull, false otherwise
* @see connect
*/
function close()
@@ -238,7 +238,7 @@ class DoliDBMysqli extends DoliDB
if ($this->db)
{
if ($this->transaction_opened > 0) dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened,LOG_ERR);
$this->connected=0;
$this->connected=false;
return mysqli_close($this->db);
}
return false;
@@ -251,7 +251,7 @@ class DoliDBMysqli extends DoliDB
* @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollbock to savepoint if error (this allow to have some request with errors inside global transactions).
* Note that with Mysql, this parameter is not used as Myssql can already commit a transaction even if one request is in error, without using savepoints.
* @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
* @return resource Resultset of answer
* @return bool|mysqli_result Resultset of answer
*/
function query($query,$usesavepoint=0,$type='auto')
{
@@ -290,8 +290,8 @@ class DoliDBMysqli extends DoliDB
/**
* Renvoie la ligne courante (comme un objet) pour le curseur resultset
*
* @param Resultset $resultset Curseur de la requete voulue
* @return Object Object result line or false if KO or end of cursor
* @param mysqli_result $resultset Curseur de la requete voulue
* @return object|null Object result line or null if KO or end of cursor
*/
function fetch_object($resultset)
{
@@ -304,8 +304,8 @@ class DoliDBMysqli extends DoliDB
/**
* Return datas as an array
*
* @param Resultset $resultset Resultset of request
* @return array Array
* @param mysqli_result $resultset Resultset of request
* @return array|null Array or null if KO or end of cursor
*/
function fetch_array($resultset)
{
@@ -317,8 +317,8 @@ class DoliDBMysqli extends DoliDB
/**
* Return datas as an array
*
* @param resource $resultset Resultset of request
* @return array Array
* @param mysqli_result $resultset Resultset of request
* @return array|null|0 Array or null if KO or end of cursor or 0 if resultset is bool
*/
function fetch_row($resultset)
{
@@ -338,8 +338,8 @@ class DoliDBMysqli extends DoliDB
/**
* Return number of lines for result of a SELECT
*
* @param Resultset $resultset Resulset of requests
* @return int Nb of lines
* @param mysqli_result $resultset Resulset of requests
* @return int Nb of lines
* @see affected_rows
*/
function num_rows($resultset)
@@ -352,8 +352,8 @@ class DoliDBMysqli extends DoliDB
/**
* Renvoie le nombre de lignes dans le resultat d'une requete INSERT, DELETE ou UPDATE
*
* @param resultset $resultset Curseur de la requete voulue
* @return int Nombre de lignes
* @param mysqli_result $resultset Curseur de la requete voulue
* @return int Nombre de lignes
* @see num_rows
*/
function affected_rows($resultset)
@@ -369,10 +369,10 @@ class DoliDBMysqli extends DoliDB
/**
* Libere le dernier resultset utilise sur cette connexion
*
* @param resultset $resultset Curseur de la requete voulue
* @param mysqli_result $resultset Curseur de la requete voulue
* @return void
*/
function free($resultset=0)
function free($resultset=null)
{
// If resultset not provided, we take the last used by connexion
if (! is_object($resultset)) { $resultset=$this->_results; }
@@ -401,8 +401,7 @@ class DoliDBMysqli extends DoliDB
if (! $this->connected) {
// Si il y a eu echec de connexion, $this->db n'est pas valide.
return 'DB_ERROR_FAILED_TO_CONNECT';
}
else {
} else {
// Constants to convert a MySql error code to a generic Dolibarr error code
$errorcode_map = array(
1004 => 'DB_ERROR_CANNOT_CREATE',
@@ -434,8 +433,7 @@ class DoliDBMysqli extends DoliDB
1451 => 'DB_ERROR_CHILD_EXISTS'
);
if (isset($errorcode_map[mysqli_errno($this->db)]))
{
if (isset($errorcode_map[mysqli_errno($this->db)])) {
return $errorcode_map[mysqli_errno($this->db)];
}
$errno=mysqli_errno($this->db);
@@ -464,7 +462,7 @@ class DoliDBMysqli extends DoliDB
*
* @param string $tab Table name concerned by insert. Ne sert pas sous MySql mais requis pour compatibilite avec Postgresql
* @param string $fieldid Field name
* @return int Id of row
* @return int|string Id of row
*/
function last_insert_id($tab,$fieldid='rowid')
{
@@ -562,7 +560,7 @@ class DoliDBMysqli extends DoliDB
* @param string $charset Charset used to store data
* @param string $collation Charset used to sort data
* @param string $owner Username of database owner
* @return resource resource defined if OK, null if KO
* @return bool|mysqli_result resource defined if OK, null if KO
*/
function DDLCreateDb($database,$charset='',$collation='',$owner='')
{
@@ -638,11 +636,13 @@ class DoliDBMysqli extends DoliDB
* @param string $type Type de la table
* @param array $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur
* @param array $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext
* @param string $keys Tableau des champs cles noms => valeur
* @param array $keys Tableau des champs cles noms => valeur
* @return int <0 if KO, >=0 if OK
*/
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys="",$fulltext_keys="",$keys="")
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys=null,$fulltext_keys=null,$keys=null)
{
// FIXME: $fulltext_keys parameter is unused
// cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra
// ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
$sql = "CREATE TABLE ".$table."(";
@@ -677,8 +677,7 @@ class DoliDBMysqli extends DoliDB
if($primary_key != "")
$pk = "primary key(".$primary_key.")";
if($unique_keys != "")
{
if(is_array($unique_keys)) {
$i = 0;
foreach($unique_keys as $key => $value)
{
@@ -686,7 +685,7 @@ class DoliDBMysqli extends DoliDB
$i++;
}
}
if($keys != "")
if(is_array($keys))
{
$i = 0;
foreach($keys as $key => $value)
@@ -700,7 +699,7 @@ class DoliDBMysqli extends DoliDB
$sql .= ",".$pk;
if($unique_keys != "")
$sql .= ",".implode(',',$sqluq);
if($keys != "")
if(is_array($keys))
$sql .= ",".implode(',',$sqlk);
$sql .=") engine=".$type;
@@ -716,7 +715,7 @@ class DoliDBMysqli extends DoliDB
*
* @param string $table Name of table
* @param string $field Optionnel : Name of field if we want description of field
* @return resource Resultset x (x->Field, x->Type, ...)
* @return bool|mysqli_result Resultset x (x->Field, x->Type, ...)
*/
function DDLDescTable($table,$field="")
{
@@ -763,14 +762,10 @@ class DoliDBMysqli extends DoliDB
$sql.= " ".$field_position;
dol_syslog(get_class($this)."::DDLAddField ".$sql,LOG_DEBUG);
if(! $this->query($sql))
{
return -1;
}
else
{
if($this->query($sql)) {
return 1;
}
return -1;
}
/**
@@ -808,12 +803,11 @@ class DoliDBMysqli extends DoliDB
{
$sql= "ALTER TABLE ".$table." DROP COLUMN `".$field_name."`";
dol_syslog(get_class($this)."::DDLDropField ".$sql,LOG_DEBUG);
if (! $this->query($sql))
{
$this->error=$this->lasterror();
return -1;
if ($this->query($sql)) {
return 1;
}
else return 1;
$this->error=$this->lasterror();
return -1;
}
@@ -883,7 +877,7 @@ class DoliDBMysqli extends DoliDB
/**
* Return list of available charset that can be used to store data in database
*
* @return array List of Charset
* @return array|null List of Charset
*/
function getListOfCharacterSet()
{
@@ -926,7 +920,7 @@ class DoliDBMysqli extends DoliDB
/**
* Return list of available collation that can be used for database
*
* @return array Liste of Collation
* @return array|null Liste of Collation
*/
function getListOfCollation()
{

View File

@@ -45,7 +45,7 @@ class DoliDBPgsql extends DoliDB
var $forcecollate=''; // Can't be static as it may be forced with a dynamic value
//! Version min database
const VERSIONMIN='8.4.0'; // Version min database
//! Resultset of last query
/** @var resource Resultset of last query */
private $_results;
public $unescapeslashquot;
@@ -80,8 +80,8 @@ class DoliDBPgsql extends DoliDB
if (! function_exists("pg_connect"))
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error="Pgsql PHP functions are not available in this version of PHP";
dol_syslog(get_class($this)."::DoliDBPgsql : Pgsql PHP functions are not available in this version of PHP",LOG_ERR);
return $this->ok;
@@ -89,8 +89,8 @@ class DoliDBPgsql extends DoliDB
if (! $host)
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error=$langs->trans("ErrorWrongHostParameter");
dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Connect, wrong host parameters",LOG_ERR);
return $this->ok;
@@ -102,14 +102,14 @@ class DoliDBPgsql extends DoliDB
if ($this->db)
{
$this->connected = 1;
$this->ok = 1;
$this->connected = true;
$this->ok = true;
}
else
{
// host, login ou password incorrect
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error='Host, login or password incorrect';
dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Connect ".$this->error,LOG_ERR);
}
@@ -119,15 +119,15 @@ class DoliDBPgsql extends DoliDB
{
if ($this->select_db($name))
{
$this->database_selected = 1;
$this->database_selected = true;
$this->database_name = $name;
$this->ok = 1;
$this->ok = true;
}
else
{
$this->database_selected = 0;
$this->database_selected = false;
$this->database_name = '';
$this->ok = 0;
$this->ok = false;
$this->error=$this->error();
dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Select_db ".$this->error,LOG_ERR);
}
@@ -135,7 +135,7 @@ class DoliDBPgsql extends DoliDB
else
{
// Pas de selection de base demandee, ok ou ko
$this->database_selected = 0;
$this->database_selected = false;
}
return $this->ok;
@@ -147,10 +147,10 @@ class DoliDBPgsql extends DoliDB
*
* @param string $line SQL request line to convert
* @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
* @param string $unescapeslashquot Unescape slash quote with quote quote
* @param bool $unescapeslashquot Unescape slash quote with quote quote
* @return string SQL request line converted
*/
static function convertSQLFromMysql($line,$type='auto',$unescapeslashquot=0)
static function convertSQLFromMysql($line,$type='auto',$unescapeslashquot=false)
{
// Removed empty line if this is a comment line for SVN tagging
if (preg_match('/^--\s\$Id/i',$line)) {
@@ -353,7 +353,7 @@ class DoliDBPgsql extends DoliDB
* On compare juste manuellement si la database choisie est bien celle activee par la connexion
*
* @param string $database Name of database
* @return boolean true if OK, false if KO
* @return bool true if OK, false if KO
*/
function select_db($database)
{
@@ -369,7 +369,7 @@ class DoliDBPgsql extends DoliDB
* @param string $passwd Password
* @param string $name Name of database (not used for mysql, used for pgsql)
* @param integer $port Port of database server
* @return resource Database access handler
* @return false|resource Database access handler
* @see close
*/
function connect($host, $login, $passwd, $name, $port=0)
@@ -451,7 +451,7 @@ class DoliDBPgsql extends DoliDB
if ($this->db)
{
if ($this->transaction_opened > 0) dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened,LOG_ERR);
$this->connected=0;
$this->connected=false;
return pg_close($this->db);
}
return false;
@@ -463,7 +463,7 @@ class DoliDBPgsql extends DoliDB
* @param string $query SQL query string
* @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollback to savepoint if error (this allow to have some request with errors inside global transactions).
* @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
* @return resource Resultset of answer
* @return false|resource Resultset of answer
*/
function query($query,$usesavepoint=0,$type='auto')
{
@@ -530,8 +530,8 @@ class DoliDBPgsql extends DoliDB
/**
* Renvoie la ligne courante (comme un objet) pour le curseur resultset
*
* @param Resultset $resultset Curseur de la requete voulue
* @return Object Object result line or false if KO or end of cursor
* @param resource $resultset Curseur de la requete voulue
* @return false|object Object result line or false if KO or end of cursor
*/
function fetch_object($resultset)
{
@@ -544,7 +544,7 @@ class DoliDBPgsql extends DoliDB
* Return datas as an array
*
* @param resource $resultset Resultset of request
* @return array Array
* @return false|array Array
*/
function fetch_array($resultset)
{
@@ -557,7 +557,7 @@ class DoliDBPgsql extends DoliDB
* Return datas as an array
*
* @param resource $resultset Resultset of request
* @return array Array
* @return false|array Array
*/
function fetch_row($resultset)
{
@@ -569,8 +569,8 @@ class DoliDBPgsql extends DoliDB
/**
* Return number of lines for result of a SELECT
*
* @param Resultset $resultset Resulset of requests
* @return int Nb of lines
* @param resourse $resultset Resulset of requests
* @return int Nb of lines, -1 on error
* @see affected_rows
*/
function num_rows($resultset)
@@ -583,7 +583,7 @@ class DoliDBPgsql extends DoliDB
/**
* Renvoie le nombre de lignes dans le resultat d'une requete INSERT, DELETE ou UPDATE
*
* @param Resultset $resultset Result set of request
* @param resource $resultset Result set of request
* @return int Nb of lines
* @see num_rows
*/
@@ -600,10 +600,10 @@ class DoliDBPgsql extends DoliDB
/**
* Libere le dernier resultset utilise sur cette connexion
*
* @param Resultset $resultset Result set of request
* @param resource $resultset Result set of request
* @return void
*/
function free($resultset=0)
function free($resultset=null)
{
// If resultset not provided, we take the last used by connexion
if (! is_resource($resultset)) { $resultset=$this->_results; }
@@ -746,7 +746,7 @@ class DoliDBPgsql extends DoliDB
*
* @param string $tab Table name concerned by insert. Ne sert pas sous MySql mais requis pour compatibilite avec Postgresql
* @param string $fieldid Field name
* @return int Id of row
* @return string Id of row
*/
function last_insert_id($tab,$fieldid='rowid')
{
@@ -827,7 +827,7 @@ class DoliDBPgsql extends DoliDB
* @param string $charset Charset used to store data
* @param string $collation Charset used to sort data
* @param string $owner Username of database owner
* @return resource resource defined if OK, null if KO
* @return false|resource resource defined if OK, null if KO
*/
function DDLCreateDb($database,$charset='',$collation='',$owner='')
{
@@ -910,11 +910,13 @@ class DoliDBPgsql extends DoliDB
* @param string $type Type de la table
* @param array $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur
* @param array $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext
* @param string $keys Tableau des champs cles noms => valeur
* @param array $keys Tableau des champs cles noms => valeur
* @return int <0 if KO, >=0 if OK
*/
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys="",$fulltext_keys="",$keys="")
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys=null,$fulltext_keys=null,$keys=null)
{
// FIXME: $fulltext_keys parameter is unused
// cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra
// ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
$sql = "create table ".$table."(";
@@ -944,7 +946,7 @@ class DoliDBPgsql extends DoliDB
if($primary_key != "")
$pk = "primary key(".$primary_key.")";
if($unique_keys != "")
if(is_array($unique_keys))
{
$i = 0;
foreach($unique_keys as $key => $value)
@@ -953,7 +955,7 @@ class DoliDBPgsql extends DoliDB
$i++;
}
}
if($keys != "")
if(is_array($keys))
{
$i = 0;
foreach($keys as $key => $value)
@@ -965,9 +967,9 @@ class DoliDBPgsql extends DoliDB
$sql .= implode(',',$sqlfields);
if($primary_key != "")
$sql .= ",".$pk;
if($unique_keys != "")
if(is_array($unique_keys))
$sql .= ",".implode(',',$sqluq);
if($keys != "")
if(is_array($keys))
$sql .= ",".implode(',',$sqlk);
$sql .=") type=".$type;
@@ -1007,7 +1009,7 @@ class DoliDBPgsql extends DoliDB
*
* @param string $table Name of table
* @param string $field Optionnel : Name of field if we want description of field
* @return resource Resultset x (x->attname)
* @return false|resource Resultset x (x->attname)
*/
function DDLDescTable($table,$field="")
{
@@ -1052,8 +1054,7 @@ class DoliDBPgsql extends DoliDB
dol_syslog($sql,LOG_DEBUG);
if(! $this -> query($sql))
return -1;
else
return -1;
return 1;
}
@@ -1078,8 +1079,7 @@ class DoliDBPgsql extends DoliDB
dol_syslog($sql,LOG_DEBUG);
if (! $this->query($sql))
return -1;
else
return -1;
return 1;
}
@@ -1099,7 +1099,7 @@ class DoliDBPgsql extends DoliDB
$this->error=$this->lasterror();
return -1;
}
else return 1;
return 1;
}
/**

View File

@@ -37,7 +37,7 @@ class DoliDBSqlite extends DoliDB
const LABEL='PDO Sqlite';
//! Version min database
const VERSIONMIN='3.0.0';
//! Resultset of last query
/** @var PDOStatement Resultset of last query */
private $_results;
/**
@@ -54,7 +54,7 @@ class DoliDBSqlite extends DoliDB
*/
function __construct($type, $host, $user, $pass, $name='', $port=0)
{
global $conf,$langs;
global $conf;
// Note that having "static" property for "$forcecharset" and "$forcecollate" will make error here in strict mode, so they are not static
if (! empty($conf->db->character_set)) $this->forcecharset=$conf->db->character_set;
@@ -70,8 +70,8 @@ class DoliDBSqlite extends DoliDB
/*if (! function_exists("sqlite_query"))
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error="Sqlite PHP functions for using Sqlite driver are not available in this version of PHP. Try to use another driver.";
dol_syslog(get_class($this)."::DoliDBSqlite : Sqlite PHP functions for using Sqlite driver are not available in this version of PHP. Try to use another driver.",LOG_ERR);
return $this->ok;
@@ -79,8 +79,8 @@ class DoliDBSqlite extends DoliDB
/*if (! $host)
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error=$langs->trans("ErrorWrongHostParameter");
dol_syslog(get_class($this)."::DoliDBSqlite : Erreur Connect, wrong host parameters",LOG_ERR);
return $this->ok;
@@ -92,9 +92,9 @@ class DoliDBSqlite extends DoliDB
if ($this->db)
{
$this->connected = 1;
$this->ok = 1;
$this->database_selected = 1;
$this->connected = true;
$this->ok = true;
$this->database_selected = true;
$this->database_name = $name;
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
@@ -102,15 +102,15 @@ class DoliDBSqlite extends DoliDB
else
{
// host, login ou password incorrect
$this->connected = 0;
$this->ok = 0;
$this->database_selected = 0;
$this->connected = false;
$this->ok = false;
$this->database_selected = false;
$this->database_name = '';
//$this->error=sqlite_connect_error();
dol_syslog(get_class($this)."::DoliDBSqlite : Erreur Connect ".$this->error,LOG_ERR);
}
return $this->ok;
return (int) $this->ok;
}
@@ -285,6 +285,7 @@ class DoliDBSqlite extends DoliDB
function select_db($database)
{
dol_syslog(get_class($this)."::select_db database=".$database, LOG_DEBUG);
// FIXME: sqlite_select_db() does not exist
return sqlite_select_db($this->db,$database);
}
@@ -297,12 +298,12 @@ class DoliDBSqlite extends DoliDB
* @param string $passwd password
* @param string $name name of database (not used for mysql, used for pgsql)
* @param integer $port Port of database server
* @return resource Database access handler
* @return PDO Database access handler
* @see close
*/
function connect($host, $login, $passwd, $name, $port=0)
{
global $conf,$main_data_dir;
global $main_data_dir;
dol_syslog(get_class($this)."::connect name=".$name,LOG_DEBUG);
@@ -352,7 +353,7 @@ class DoliDBSqlite extends DoliDB
/**
* Close database connexion
*
* @return boolean True if disconnect successfull, false otherwise
* @return bool True if disconnect successfull, false otherwise
* @see connect
*/
function close()
@@ -360,7 +361,7 @@ class DoliDBSqlite extends DoliDB
if ($this->db)
{
if ($this->transaction_opened > 0) dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened,LOG_ERR);
$this->connected=0;
$this->connected=false;
$this->db=null; // Clean this->db
return true;
}
@@ -374,13 +375,11 @@ class DoliDBSqlite extends DoliDB
* @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollbock to savepoint if error (this allow to have some request with errors inside global transactions).
* Note that with Mysql, this parameter is not used as Myssql can already commit a transaction even if one request is in error, without using savepoints.
* @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
* @return resource Resultset of answer
* @return PDOStatement Resultset of answer
*/
function query($query,$usesavepoint=0,$type='auto')
{
$errmsg='';
$ret='';
$ret=null;
$query = trim($query);
$this->error = 0;
@@ -429,8 +428,8 @@ class DoliDBSqlite extends DoliDB
/**
* Renvoie la ligne courante (comme un objet) pour le curseur resultset
*
* @param Resultset $resultset Curseur de la requete voulue
* @return Object Object result line or false if KO or end of cursor
* @param PDOStatement $resultset Curseur de la requete voulue
* @return false|object Object result line or false if KO or end of cursor
*/
function fetch_object($resultset)
{
@@ -443,8 +442,8 @@ class DoliDBSqlite extends DoliDB
/**
* Return datas as an array
*
* @param Resultset $resultset Resultset of request
* @return array Array
* @param PDOStatement $resultset Resultset of request
* @return false|array Array or false if KO or end of cursor
*/
function fetch_array($resultset)
{
@@ -456,8 +455,8 @@ class DoliDBSqlite extends DoliDB
/**
* Return datas as an array
*
* @param resource $resultset Resultset of request
* @return array Array
* @param PDOStatement $resultset Resultset of request
* @return false|array Array or false if KO or end of cursor
*/
function fetch_row($resultset)
{
@@ -477,7 +476,7 @@ class DoliDBSqlite extends DoliDB
/**
* Return number of lines for result of a SELECT
*
* @param Resultset $resultset Resulset of requests
* @param PDOStatement $resultset Resulset of requests
* @return int Nb of lines
* @see affected_rows
*/
@@ -491,7 +490,7 @@ class DoliDBSqlite extends DoliDB
/**
* Return number of lines for result of a SELECT
*
* @param Resultset $resultset Resulset of requests
* @param PDOStatement $resultset Resulset of requests
* @return int Nb of lines
* @see affected_rows
*/
@@ -508,10 +507,10 @@ class DoliDBSqlite extends DoliDB
/**
* Free last resultset used.
*
* @param integer $resultset Curseur de la requete voulue
* @param PDOStatement $resultset Curseur de la requete voulue
* @return void
*/
function free($resultset=0)
function free($resultset=null)
{
// If resultset not provided, we take the last used by connexion
if (! is_object($resultset)) { $resultset=$this->_results; }
@@ -709,7 +708,7 @@ class DoliDBSqlite extends DoliDB
* @param string $charset Charset used to store data
* @param string $collation Charset used to sort data
* @param string $owner Username of database owner
* @return resource resource defined if OK, null if KO
* @return PDOStatement resource defined if OK, null if KO
*/
function DDLCreateDb($database,$charset='',$collation='',$owner='')
{
@@ -786,11 +785,13 @@ class DoliDBSqlite extends DoliDB
* @param string $type Type de la table
* @param array $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur
* @param array $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext
* @param string $keys Tableau des champs cles noms => valeur
* @param array $keys Tableau des champs cles noms => valeur
* @return int <0 if KO, >=0 if OK
*/
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys="",$fulltext_keys="",$keys="")
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys=null,$fulltext_keys=null,$keys=null)
{
// FIXME: $fulltext_keys parameter is unused
// cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra
// ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
$sql = "create table ".$table."(";
@@ -820,7 +821,7 @@ class DoliDBSqlite extends DoliDB
if($primary_key != "")
$pk = "primary key(".$primary_key.")";
if($unique_keys != "")
if(is_array($unique_keys))
{
$i = 0;
foreach($unique_keys as $key => $value)
@@ -829,7 +830,7 @@ class DoliDBSqlite extends DoliDB
$i++;
}
}
if($keys != "")
if(is_array($keys))
{
$i = 0;
foreach($keys as $key => $value)
@@ -841,9 +842,9 @@ class DoliDBSqlite extends DoliDB
$sql .= implode(',',$sqlfields);
if($primary_key != "")
$sql .= ",".$pk;
if($unique_keys != "")
if(is_array($unique_keys))
$sql .= ",".implode(',',$sqluq);
if($keys != "")
if(is_array($keys))
$sql .= ",".implode(',',$sqlk);
$sql .=") type=".$type;

View File

@@ -4,6 +4,7 @@
* Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
* Copyright (C) 2005-2009 Regis Houssin <regis.houssin@capnetworks.com>
* Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.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
@@ -21,13 +22,13 @@
/**
* \file htdocs/core/db/sqlite.class.php
* \brief Class file to manage Dolibarr database access for a Sqlite database
* \brief Class file to manage Dolibarr database access for a SQLite database
*/
require_once DOL_DOCUMENT_ROOT .'/core/db/DoliDB.class.php';
/**
* Class to manage Dolibarr database access for a Sqlite database
* Class to manage Dolibarr database access for a SQLite database
*/
class DoliDBSqlite3 extends DoliDB
{
@@ -37,8 +38,8 @@ class DoliDBSqlite3 extends DoliDB
const LABEL='Sqlite3';
//! Version min database
const VERSIONMIN='3.0.0';
//! Resultset of last query
private $_results;
/** @var SQLite3Result Resultset of last query */
private $_results;
const WEEK_MONDAY_FIRST=1;
const WEEK_YEAR = 2;
@@ -58,7 +59,7 @@ class DoliDBSqlite3 extends DoliDB
*/
function __construct($type, $host, $user, $pass, $name='', $port=0)
{
global $conf,$langs;
global $conf;
// Note that having "static" property for "$forcecharset" and "$forcecollate" will make error here in strict mode, so they are not static
if (! empty($conf->db->character_set)) $this->forcecharset=$conf->db->character_set;
@@ -74,8 +75,8 @@ class DoliDBSqlite3 extends DoliDB
/*if (! function_exists("sqlite_query"))
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error="Sqlite PHP functions for using Sqlite driver are not available in this version of PHP. Try to use another driver.";
dol_syslog(get_class($this)."::DoliDBSqlite3 : Sqlite PHP functions for using Sqlite driver are not available in this version of PHP. Try to use another driver.",LOG_ERR);
return $this->ok;
@@ -83,8 +84,8 @@ class DoliDBSqlite3 extends DoliDB
/*if (! $host)
{
$this->connected = 0;
$this->ok = 0;
$this->connected = false;
$this->ok = false;
$this->error=$langs->trans("ErrorWrongHostParameter");
dol_syslog(get_class($this)."::DoliDBSqlite3 : Erreur Connect, wrong host parameters",LOG_ERR);
return $this->ok;
@@ -96,9 +97,9 @@ class DoliDBSqlite3 extends DoliDB
if ($this->db)
{
$this->connected = 1;
$this->ok = 1;
$this->database_selected = 1;
$this->connected = true;
$this->ok = true;
$this->database_selected = true;
$this->database_name = $name;
$this->addCustomFunction('IF');
@@ -114,9 +115,9 @@ class DoliDBSqlite3 extends DoliDB
else
{
// host, login ou password incorrect
$this->connected = 0;
$this->ok = 0;
$this->database_selected = 0;
$this->connected = false;
$this->ok = false;
$this->database_selected = false;
$this->database_name = '';
//$this->error=sqlite_connect_error();
dol_syslog(get_class($this)."::DoliDBSqlite3 : Error Connect ".$this->error,LOG_ERR);
@@ -304,6 +305,7 @@ class DoliDBSqlite3 extends DoliDB
function select_db($database)
{
dol_syslog(get_class($this)."::select_db database=".$database, LOG_DEBUG);
// FIXME: sqlite_select_db() does not exist
return sqlite_select_db($this->db,$database);
}
@@ -316,12 +318,12 @@ class DoliDBSqlite3 extends DoliDB
* @param string $passwd password
* @param string $name name of database (not used for mysql, used for pgsql)
* @param integer $port Port of database server
* @return resource Database access handler
* @return SQLite3 Database access handler
* @see close
*/
function connect($host, $login, $passwd, $name, $port=0)
{
global $conf,$main_data_dir;
global $main_data_dir;
dol_syslog(get_class($this)."::connect name=".$name,LOG_DEBUG);
@@ -372,7 +374,7 @@ class DoliDBSqlite3 extends DoliDB
/**
* Close database connexion
*
* @return boolean True if disconnect successfull, false otherwise
* @return bool True if disconnect successfull, false otherwise
* @see connect
*/
function close()
@@ -380,9 +382,9 @@ class DoliDBSqlite3 extends DoliDB
if ($this->db)
{
if ($this->transaction_opened > 0) dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened,LOG_ERR);
$this->connected=0;
$this->connected=false;
$this->db->close();
$this->db=null; // Clean this->db
unset($this->db); // Clean this->db
return true;
}
return false;
@@ -395,13 +397,11 @@ class DoliDBSqlite3 extends DoliDB
* @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollbock to savepoint if error (this allow to have some request with errors inside global transactions).
* Note that with Mysql, this parameter is not used as Myssql can already commit a transaction even if one request is in error, without using savepoints.
* @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
* @return resource Resultset of answer
* @return SQLite3Result Resultset of answer
*/
function query($query,$usesavepoint=0,$type='auto')
{
$errmsg='';
$ret='';
$ret=null;
$query = trim($query);
$this->error = 0;
@@ -492,8 +492,8 @@ class DoliDBSqlite3 extends DoliDB
/**
* Renvoie la ligne courante (comme un objet) pour le curseur resultset
*
* @param Resultset $resultset Curseur de la requete voulue
* @return Object Object result line or false if KO or end of cursor
* @param SQLite3Result $resultset Curseur de la requete voulue
* @return false|object Object result line or false if KO or end of cursor
*/
function fetch_object($resultset)
{
@@ -504,14 +504,15 @@ class DoliDBSqlite3 extends DoliDB
if ($ret) {
return (object) $ret;
}
return false;
}
/**
* Return datas as an array
*
* @param Resultset $resultset Resultset of request
* @return array Array
* @param SQLite3Result $resultset Resultset of request
* @return false|array Array or false if KO or end of cursor
*/
function fetch_array($resultset)
{
@@ -519,16 +520,14 @@ class DoliDBSqlite3 extends DoliDB
if (! is_object($resultset)) { $resultset=$this->_results; }
//return $resultset->fetch(PDO::FETCH_ASSOC);
$ret = $resultset->fetchArray(SQLITE3_ASSOC);
if ($ret) {
return (array) $ret;
}
return $ret;
}
/**
* Return datas as an array
*
* @param resource $resultset Resultset of request
* @return array Array
* @param SQLite3Result $resultset Resultset of request
* @return false|array Array or false if KO or end of cursor
*/
function fetch_row($resultset)
{
@@ -541,19 +540,21 @@ class DoliDBSqlite3 extends DoliDB
else
{
// si le curseur est un booleen on retourne la valeur 0
return 0;
return false;
}
}
/**
* Return number of lines for result of a SELECT
*
* @param Resultset $resultset Resulset of requests
* @param SQLite3Result $resultset Resulset of requests
* @return int Nb of lines
* @see affected_rows
*/
function num_rows($resultset)
{
// FIXME: SQLite3Result does not have a queryString member
// If resultset not provided, we take the last used by connexion
if (! is_object($resultset)) { $resultset=$this->_results; }
if (preg_match("/^SELECT/i", $resultset->queryString)) {
@@ -565,12 +566,14 @@ class DoliDBSqlite3 extends DoliDB
/**
* Return number of lines for result of a SELECT
*
* @param Resultset $resultset Resulset of requests
* @param SQLite3Result $resultset Resulset of requests
* @return int Nb of lines
* @see affected_rows
*/
function affected_rows($resultset)
{
// FIXME: SQLite3Result does not have a queryString member
// If resultset not provided, we take the last used by connexion
if (! is_object($resultset)) { $resultset=$this->_results; }
if (preg_match("/^SELECT/i", $resultset->queryString)) {
@@ -585,10 +588,10 @@ class DoliDBSqlite3 extends DoliDB
/**
* Free last resultset used.
*
* @param integer $resultset Curseur de la requete voulue
* @param SQLite3Result $resultset Curseur de la requete voulue
* @return void
*/
function free($resultset=0)
function free($resultset=null)
{
// If resultset not provided, we take the last used by connexion
if (! is_object($resultset)) { $resultset=$this->_results; }
@@ -789,7 +792,7 @@ class DoliDBSqlite3 extends DoliDB
* @param string $charset Charset used to store data
* @param string $collation Charset used to sort data
* @param string $owner Username of database owner
* @return resource resource defined if OK, null if KO
* @return SQLite3Result resource defined if OK, null if KO
*/
function DDLCreateDb($database,$charset='',$collation='',$owner='')
{
@@ -866,11 +869,13 @@ class DoliDBSqlite3 extends DoliDB
* @param string $type Type de la table
* @param array $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur
* @param array $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext
* @param string $keys Tableau des champs cles noms => valeur
* @param array $keys Tableau des champs cles noms => valeur
* @return int <0 if KO, >=0 if OK
*/
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys="",$fulltext_keys="",$keys="")
function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys=null,$fulltext_keys=null,$keys=null)
{
// FIXME: $fulltext_keys parameter is unused
// cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra
// ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
$sql = "create table ".$table."(";
@@ -900,7 +905,7 @@ class DoliDBSqlite3 extends DoliDB
if($primary_key != "")
$pk = "primary key(".$primary_key.")";
if($unique_keys != "")
if(is_array($unique_keys))
{
$i = 0;
foreach($unique_keys as $key => $value)
@@ -909,7 +914,7 @@ class DoliDBSqlite3 extends DoliDB
$i++;
}
}
if($keys != "")
if(is_array($keys))
{
$i = 0;
foreach($keys as $key => $value)
@@ -921,16 +926,15 @@ class DoliDBSqlite3 extends DoliDB
$sql .= implode(',',$sqlfields);
if($primary_key != "")
$sql .= ",".$pk;
if($unique_keys != "")
if(is_array($unique_keys))
$sql .= ",".implode(',',$sqluq);
if($keys != "")
if(is_array($keys))
$sql .= ",".implode(',',$sqlk);
$sql .=") type=".$type;
dol_syslog($sql,LOG_DEBUG);
if(! $this -> query($sql))
return -1;
else
return -1;
return 1;
}
@@ -939,7 +943,7 @@ class DoliDBSqlite3 extends DoliDB
*
* @param string $table Name of table
* @param string $field Optionnel : Name of field if we want description of field
* @return resource Resource
* @return SQLite3Result Resource
*/
function DDLDescTable($table,$field="")
{
@@ -990,10 +994,7 @@ class DoliDBSqlite3 extends DoliDB
{
return -1;
}
else
{
return 1;
}
return 1;
}
/**
@@ -1014,8 +1015,7 @@ class DoliDBSqlite3 extends DoliDB
dol_syslog(get_class($this)."::DDLUpdateField ".$sql,LOG_DEBUG);
if (! $this->query($sql))
return -1;
else
return -1;
return 1;
}
@@ -1035,7 +1035,7 @@ class DoliDBSqlite3 extends DoliDB
$this->error=$this->lasterror();
return -1;
}
else return 1;
return 1;
}
@@ -1082,7 +1082,6 @@ class DoliDBSqlite3 extends DoliDB
{
return -1;
}
return 1;
}
@@ -1141,6 +1140,7 @@ class DoliDBSqlite3 extends DoliDB
*/
function getPathOfDump()
{
// FIXME: not for SQLite
$fullpathofdump='/pathtomysqldump/mysqldump';
$resql=$this->query('SHOW VARIABLES LIKE \'basedir\'');
@@ -1160,6 +1160,7 @@ class DoliDBSqlite3 extends DoliDB
*/
function getPathOfRestore()
{
// FIXME: not for SQLite
$fullpathofimport='/pathtomysql/mysql';
$resql=$this->query('SHOW VARIABLES LIKE \'basedir\'');
@@ -1425,10 +1426,10 @@ class DoliDBSqlite3 extends DoliDB
/**
* calc_daynr
*
* @param string $year Year
* @param string $month Month
* @param string $day Day
* @return string La date formatee.
* @param int $year Year
* @param int $month Month
* @param int $day Day
* @return int Formatted date
*/
private static function calc_daynr($year, $month, $day) {
$y = $year;
@@ -1446,8 +1447,9 @@ class DoliDBSqlite3 extends DoliDB
/**
* calc_weekday
*
* @param string $daynr ???
* @param string $sunday_first_day_of_week ???
* @param int $daynr ???
* @param bool $sunday_first_day_of_week ???
* @return int
*/
private static function calc_weekday($daynr, $sunday_first_day_of_week) {
$ret = floor(($daynr + 5 + ($sunday_first_day_of_week ? 1 : 0)) % 7);

View File

@@ -106,7 +106,7 @@ window.onload = function()
<tr>
<td><?php echo img_picto_common('','treemenu/folder.gif','width="16" height="16"'); ?></td>
<td>&nbsp;</td>
<td id="tdName" width="100%" nowrap class="ActualFolder">/</td>
<td id="tdName" width="100%" class="ActualFolder nowrap">/</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>

View File

@@ -220,7 +220,7 @@ window.onload = function()
<table id="tableFiles" cellSpacing="0" cellPadding="0" width="100%" border="0">
<tr id="trUp" style="DISPLAY: none">
<td width="16"><a id="linkUpIcon" href="#"><img alt="" src="images/FolderUp.gif" width="16" height="16" border="0"></a></td>
<td nowrap width="100%">&nbsp;<a id="linkUp" href="#">..</a></td>
<td class="nowrap" width="100%">&nbsp;<a id="linkUp" href="#">..</a></td>
</tr>
</table>
</body>

View File

@@ -77,7 +77,7 @@ oListManager.GetFolderRowHtml = function( folderName, folderPath )
'<td width="16">' +
sLink +
'<img alt="" src="images/Folder.gif" width="16" height="16" border="0"><\/a>' +
'<\/td><td nowrap colspan="2">&nbsp;' +
'<\/td><td class="nowrap" colspan="2">&nbsp;' +
sLink +
folderName +
'<\/a>' +
@@ -100,7 +100,7 @@ oListManager.GetFileRowHtml = function( fileName, fileUrl, fileSize )
sLink +
fileName +
'<\/a>' +
'<\/td><td align="right" nowrap>&nbsp;' +
'<\/td><td align="right" class="nowrap">&nbsp;' +
fileSize +
' KB' +
'<\/td><\/tr>' ;

View File

@@ -132,7 +132,7 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLengt
// Disable an element
if (options.option_disabled) {
if (ui.item.disabled) {
$("#" + options.option_disabled).attr("disabled", "disabled");
$("#" + options.option_disabled).prop("disabled", true);
if (options.error) {
$.jnotify(options.error, "error", true); // Output with jnotify the error message
}
@@ -145,7 +145,7 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLengt
}
if (options.disabled) {
$.each(options.disabled, function(key, value) {
$("#" + value).attr("disabled", "disabled");
$("#" + value).prop("disabled", true);
});
}
if (options.show) {
@@ -524,7 +524,7 @@ function ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input=a
// Disable another element
if (input.disabled && input.disabled.length > 0) {
$.each(input.disabled, function(key,value) {
$("#" + value).attr("disabled", true);
$("#" + value).prop("disabled", true);
if ($("#" + value).hasClass("butAction") == true) {
$("#" + value).removeClass("butAction");
$("#" + value).addClass("butActionRefused");

View File

@@ -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

View 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);
}
}

View File

@@ -42,27 +42,27 @@
if (GETPOST('type') == "separate")
{
print "jQuery('#size, #unique, #required, #default_value').val('').attr('disabled','disabled');";
print "jQuery('#size, #unique, #required, #default_value').val('').prop('disabled', true);";
print 'jQuery("#value_choice").hide();';
}
?>
if (type == 'date') { size.val('').attr('disabled','disabled'); unique.removeAttr('disabled','disabled'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); }
else if (type == 'datetime') { size.val('').attr('disabled','disabled'); unique.removeAttr('disabled','disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();}
if (type == 'date') { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); }
else if (type == 'datetime') { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();}
else if (type == 'double') { size.val('24,8').removeAttr('disabled'); unique.removeAttr('disabled','disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();}
else if (type == 'int') { size.val('10').removeAttr('disabled'); unique.removeAttr('disabled','disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();}
else if (type == 'text') { size.val('2000').removeAttr('disabled'); unique.attr('disabled','disabled').removeAttr('checked'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); }
else if (type == 'int') { size.val('10').removeAttr('disabled'); unique.removeAttr('disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();}
else if (type == 'text') { size.val('2000').removeAttr('disabled'); unique.prop('disabled', true).removeAttr('checked'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); }
else if (type == 'varchar') { size.val('255').removeAttr('disabled'); unique.removeAttr('disabled','disabled'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); }
else if (type == 'boolean') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide();}
else if (type == 'price') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide();}
else if (type == 'select') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled'); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
else if (type == 'link') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled'); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").show();}
else if (type == 'sellist') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled'); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").show();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
else if (type == 'radio') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled'); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
else if (type == 'checkbox') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled'); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
else if (type == 'chkbxlst') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled'); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").show();jQuery("#helplink").hide();}
else if (type == 'separate') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled'); required.val('').attr('disabled','disabled'); default_value.val('').attr('disabled','disabled'); jQuery("#value_choice").hide();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
else size.val('').attr('disabled','disabled');
else if (type == 'boolean') { size.val('').prop('disabled', true); unique.prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide();}
else if (type == 'price') { size.val('').prop('disabled', true); unique.prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide();}
else if (type == 'select') { size.val('').prop('disabled', true); unique.prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
else if (type == 'link') { size.val('').prop('disabled', true); unique.prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").show();}
else if (type == 'sellist') { size.val('').prop('disabled', true); unique.prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").show();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
else if (type == 'radio') { size.val('').prop('disabled', true); unique.prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
else if (type == 'checkbox') { size.val('').prop('disabled', true); unique.prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
else if (type == 'chkbxlst') { size.val('').prop('disabled', true); unique.prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").show();jQuery("#helplink").hide();}
else if (type == 'separate') { size.val('').prop('disabled', true); unique.prop('disabled', true); required.val('').prop('disabled', true); default_value.val('').prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
else size.val('').prop('disabled', true);
}
init_typeoffields('<?php echo GETPOST('type'); ?>');
jQuery("#type").change(function() {

View File

@@ -25,15 +25,15 @@
var size = jQuery("#size");
var unique = jQuery("#unique");
var required = jQuery("#required");
if (type == 'date') { size.attr('disabled','disabled'); }
else if (type == 'datetime') { size.attr('disabled','disabled'); }
if (type == 'date') { size.prop('disabled', true); }
else if (type == 'datetime') { size.prop('disabled', true); }
else if (type == 'double') { size.removeAttr('disabled'); }
else if (type == 'int') { size.removeAttr('disabled'); }
else if (type == 'text') { size.removeAttr('disabled'); unique.attr('disabled','disabled').removeAttr('checked'); }
else if (type == 'text') { size.removeAttr('disabled'); unique.prop('disabled', true).removeAttr('checked'); }
else if (type == 'varchar') { size.removeAttr('disabled'); }
else if (type == 'boolean') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled');}
else if (type == 'price') { size.val('').attr('disabled','disabled'); unique.attr('disabled','disabled');}
else size.val('').attr('disabled','disabled');
else if (type == 'boolean') { size.val('').prop('disabled', true); unique.prop('disabled', true);}
else if (type == 'price') { size.val('').prop('disabled', true); unique.prop('disabled', true);}
else size.val('').prop('disabled', true);
}
init_typeoffields(jQuery("#type").val());
});

View File

@@ -583,8 +583,8 @@ function setforfree() {
jQuery("#idprod").val('');
jQuery("#idprodfournprice").val('0'); // Set cursor on not selected product
jQuery("#search_idprodfournprice").val('');
jQuery("#prod_entry_mode_free").attr('checked',true);
jQuery("#prod_entry_mode_predef").attr('checked',false);
jQuery("#prod_entry_mode_free").prop('checked',true);
jQuery("#prod_entry_mode_predef").prop('checked',false);
jQuery("#price_ht").show();
jQuery("#price_ttc").show(); // May no exists
jQuery("#tva_tx").show();
@@ -601,8 +601,8 @@ function setforfree() {
}
function setforpredef() {
jQuery("#select_type").val(-1);
jQuery("#prod_entry_mode_free").attr('checked',false);
jQuery("#prod_entry_mode_predef").attr('checked',true);
jQuery("#prod_entry_mode_free").prop('checked',false);
jQuery("#prod_entry_mode_predef").prop('checked',true);
jQuery("#price_ht").hide();
jQuery("#title_up_ht").hide();
jQuery("#price_ttc").hide(); // May no exists

View File

@@ -99,7 +99,7 @@ $coldisplay=-1; // We remove first td
$doleditor=new DolEditor('product_desc',$line->description,'',164,$toolbarname,'',false,true,$enable,$nbrows,'98%');
$doleditor->Create();
} else {
print '<textarea id="desc" class="flat" name="desc" readonly="readonly" style="width: 200px; height:80px;">' . $line->description . '</textarea>';
print '<textarea id="desc" class="flat" name="desc" readonly style="width: 200px; height:80px;">' . $line->description . '</textarea>';
}
?>
</td>
@@ -113,19 +113,19 @@ $coldisplay=-1; // We remove first td
if ($this->situation_counter == 1 || !$this->situation_cycle_ref) {
print '<td align="right">' . $form->load_tva('tva_tx',$line->tva_tx,$seller,$buyer,0,$line->info_bits,$line->product_type) . '</td>';
} else {
print '<td align="right"><input size="1" type="text" class="flat" name="tva_tx" value="' . price($line->tva_tx) . '" readonly="readonly" />%</td>';
print '<td align="right"><input size="1" type="text" class="flat" name="tva_tx" value="' . price($line->tva_tx) . '" readonly />%</td>';
}
$coldisplay++;
print '<td align="right"><input type="text" class="flat" size="8" id="price_ht" name="price_ht" value="' . (isset($line->pu_ht)?price($line->pu_ht,0,'',0):price($line->subprice,0,'',0)) . '"';
if ($this->situation_counter > 1) print ' readonly="readonly"';
if ($this->situation_counter > 1) print ' readonly';
print '></td>';
if ($inputalsopricewithtax)
{
$coldisplay++;
print '<td align="right"><input type="text" class="flat" size="8" id="price_ttc" name="price_ttc" value="'.(isset($line->pu_ttc)?price($line->pu_ttc,0,'',0):'').'"';
if ($this->situation_counter > 1) print ' readonly="readonly"';
if ($this->situation_counter > 1) print ' readonly';
print '></td>';
}
?>
@@ -136,7 +136,7 @@ $coldisplay=-1; // We remove first td
// must also not be output for most entities (proposal, intervention, ...)
//if($line->qty > $line->stock) print img_picto($langs->trans("StockTooLow"),"warning", 'style="vertical-align: bottom;"')." ";
print '<input size="3" type="text" class="flat" name="qty" id="qty" value="' . $line->qty . '"';
if ($this->situation_counter > 1) print ' readonly="readonly"';
if ($this->situation_counter > 1) print ' readonly';
print '>';
} else { ?>
&nbsp;
@@ -152,10 +152,10 @@ $coldisplay=-1; // We remove first td
}
?>
<td align="right" nowrap><?php $coldisplay++; ?>
<td align="right" class="nowrap"><?php $coldisplay++; ?>
<?php if (($line->info_bits & 2) != 2) {
print '<input size="1" type="text" class="flat" name="remise_percent" id="remise_percent" value="' . $line->remise_percent . '"';
if ($this->situation_counter > 1) print ' readonly="readonly"';
if ($this->situation_counter > 1) print ' readonly';
print '>%';
} else { ?>
&nbsp;
@@ -164,7 +164,7 @@ $coldisplay=-1; // We remove first td
<?php
if ($this->situation_cycle_ref) {
$coldisplay++;
print '<td align="right" nowrap><input type="text" size="1" value="' . $line->situation_percent . '" name="progress">%</td>';
print '<td align="right" class="nowrap"><input type="text" size="1" value="' . $line->situation_percent . '" name="progress">%</td>';
}
if (! empty($usemargins))
{

View File

@@ -148,7 +148,7 @@ if (empty($usemargins)) $usemargins=0;
<?php
if($conf->global->PRODUCT_USE_UNITS)
{
print '<td align="left" nowrap="nowrap">';
print '<td align="left" class="nowrap">';
$label = $line->getLabelOfUnit('short');
if ($label !== '') {
print $langs->trans($label);
@@ -169,7 +169,7 @@ if (empty($usemargins)) $usemargins=0;
if ($this->situation_cycle_ref) {
$coldisplay++;
print '<td align="right" nowrap="nowrap">' . $line->situation_percent . '%</td>';
print '<td align="right" class="nowrap">' . $line->situation_percent . '%</td>';
}
if ($usemargins && ! empty($conf->margin->enabled) && empty($user->societe_id))

View File

@@ -93,7 +93,7 @@ if ($resql)
{
$var=!$var;
$obj = $db->fetch_object($resql);
print "<tr ".$bc[$var]."><td nowrap=\"nowrap\">";
print "<tr ".$bc[$var].'><td class="nowrap">';
$shipment->id=$obj->rowid;
$shipment->ref=$obj->ref;
print $shipment->getNomUrl(1);

View File

@@ -386,10 +386,10 @@ if (($action != 'create' && $action != 'add') || ! empty($mesgs)) {
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery("#checkall").click(function() {
jQuery(".checkformerge").attr('checked', true);
jQuery(".checkformerge").prop('checked', true);
});
jQuery("#checknone").click(function() {
jQuery(".checkformerge").attr('checked', false);
jQuery(".checkformerge").prop('checked', false);
});
});
</script>

View File

@@ -7,7 +7,8 @@
* Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
* Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2015 juanjo Menent <jmenent@2byte.es>
*
* Copyright (C) 2015 Abbes Bahfir <bafbes@gmail.com>
* 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

View 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);
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}

View 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;
}
}

View 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
{
}

View 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]);
}
}

View 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
)
);
}
}

View 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;
}
}

View 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;
}
}
}

View 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;
}
}

View 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);
}

View 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();
}

View 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;
}
}

View 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);
}
}

View 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.";
}
}

View 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();
}
}

View 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();
}
}

View 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;
}
}

View 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();
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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();
}
}

View 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();
}
}

View 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;
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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);
}

View 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();
}

View 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.'
);
}
}

View 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)

View 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();
}
}

File diff suppressed because it is too large Load Diff

View 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';
}
}

File diff suppressed because it is too large Load Diff

View 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;
}
}

View 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;
}
}

Some files were not shown because too many files have changed in this diff Show More