Merge branch 'develop' of github.com:Dolibarr/dolibarr into develop

This commit is contained in:
Laurent Destailleur 2016-05-14 00:44:38 +02:00
commit cba0ef5693
107 changed files with 13433 additions and 69311 deletions

1
.gitignore vendored
View File

@ -19,6 +19,7 @@ Thumbs.db
# Vagrant generated files
.vagrant
# Composer installed repositories
composer.lock
/htdocs/includes/**/.git
# Composer autoloader and unwanted files
htdocs/includes/autoload.php

View File

@ -27,7 +27,7 @@ PHPExcel 1.8.1 LGPL-2.1+ Yes
php-iban 1.4.7 LGPL-3+ Yes Parse and validate IBAN (and IIBAN) bank account information in PHP
PHPoAuthLib 0.8.2 MIT License Yes Library to provide oauth1 and oauth2 to different service
PHPPrintIPP 1.3 GPL-2+ Yes Library to send print IPP requests
Restler 3.0 LGPL-3+ Yes Library to develop REST Web services
Restler 3.0.0RC6 LGPL-3+ Yes Library to develop REST Web services
TCPDF 6.2.12 LGPL-3+ Yes PDF generation
TCPDI 1.0.0 LGPL-3+ / Apache 2.0 Yes FPDI replacement
Swift Mailer 5.4.2-DEV MIT license Yes Comprehensive mailing tools for PHP

View File

@ -20,7 +20,7 @@
"mike42/escpos-php": "dev-master",
"mobiledetect/mobiledetectlib": "2.8.17",
"phpoffice/phpexcel": "1.8.1",
"restler/framework": "^3.0",
"restler/framework": "3.0.0-RC6",
"tecnickcom/tcpdf": "6.2.12"
},
"require-dev": {

420
composer.lock generated
View File

@ -1,420 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "c2b53c577364dbe3a56137043081b511",
"content-hash": "8f7a86cfbc13f45e13b73c49531818cb",
"packages": [
{
"name": "ccampbell/chromephp",
"version": "4.1.0",
"source": {
"type": "git",
"url": "https://github.com/ccampbell/chromephp.git",
"reference": "c3c297615d48ae5b2a86a82311152d1ed095fcef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ccampbell/chromephp/zipball/c3c297615d48ae5b2a86a82311152d1ed095fcef",
"reference": "c3c297615d48ae5b2a86a82311152d1ed095fcef",
"shasum": ""
},
"require": {
"php": ">=5.0.0"
},
"type": "library",
"autoload": {
"psr-0": {
"ChromePhp": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Craig Campbell",
"email": "iamcraigcampbell@gmail.com",
"homepage": "http://craig.is",
"role": "Developer"
}
],
"description": "Log variables to the Chrome console (via Chrome Logger Google Chrome extension).",
"homepage": "http://github.com/ccampbell/chromephp",
"keywords": [
"log",
"logging"
],
"time": "2013-06-26 03:44:33"
},
{
"name": "ckeditor/ckeditor",
"version": "dev-full/stable",
"source": {
"type": "git",
"url": "https://github.com/ckeditor/ckeditor-releases.git",
"reference": "e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ckeditor/ckeditor-releases/zipball/e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0",
"reference": "e3eb254641c4c349ffc19e49bd4a1a6831b5b6d0",
"shasum": ""
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0+",
"LGPL-2.1+",
"MPL-1.1+"
],
"authors": [
{
"name": "CKSource",
"homepage": "http://cksource.com"
}
],
"description": "JavaScript WYSIWYG web text editor.",
"homepage": "http://ckeditor.com",
"keywords": [
"CKEditor",
"editor",
"fckeditor",
"html",
"javascript",
"richtext",
"text",
"wysiwyg"
],
"time": "2016-03-31 16:19:25"
},
{
"name": "mike42/escpos-php",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/mike42/escpos-php.git",
"reference": "96f05cbf460f5b67c2184ee4e91aedfbcedeb788"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mike42/escpos-php/zipball/96f05cbf460f5b67c2184ee4e91aedfbcedeb788",
"reference": "96f05cbf460f5b67c2184ee4e91aedfbcedeb788",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "4.5.*"
},
"type": "library",
"autoload": {
"psr-4": {
"Mike42\\": "src/Mike42"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Roni Saha",
"email": "roni.cse@gmail.com"
},
{
"name": "Michael Billington",
"email": "michael.billington@gmail.com"
},
{
"name": "Gergely Radics",
"email": "gerifield@ustream.tv"
},
{
"name": "Warren Doyle",
"email": "w.doyle@fuelled.co"
},
{
"name": "vharo",
"email": "vharo@geepok.com"
}
],
"description": "Thermal receipt printer library, for use with ESC/POS compatible printers",
"homepage": "https://github.com/mike42/escpos-php",
"keywords": [
"ESC-POS",
"driver",
"escpos",
"print",
"receipt"
],
"time": "2016-03-27 23:08:27"
},
{
"name": "mobiledetect/mobiledetectlib",
"version": "2.8.17",
"source": {
"type": "git",
"url": "https://github.com/serbanghita/Mobile-Detect.git",
"reference": "b87da5f63a76e9615a0c74fcf168657b1ea7e41d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/b87da5f63a76e9615a0c74fcf168657b1ea7e41d",
"reference": "b87da5f63a76e9615a0c74fcf168657b1ea7e41d",
"shasum": ""
},
"require": {
"php": ">=5.0.0"
},
"require-dev": {
"codeclimate/php-test-reporter": "dev-master",
"johnkary/phpunit-speedtrap": "~1.0@dev",
"phpunit/phpunit": "*"
},
"type": "library",
"autoload": {
"classmap": [
"Mobile_Detect.php"
],
"psr-0": {
"Detection": "namespaced/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Serban Ghita",
"email": "serbanghita@gmail.com",
"homepage": "http://mobiledetect.net",
"role": "Developer"
}
],
"description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.",
"homepage": "https://github.com/serbanghita/Mobile-Detect",
"keywords": [
"detect mobile devices",
"mobile",
"mobile detect",
"mobile detector",
"php mobile detect"
],
"time": "2015-09-17 14:45:21"
},
{
"name": "phpoffice/phpexcel",
"version": "1.8.1",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PHPExcel.git",
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
"shasum": ""
},
"require": {
"ext-xml": "*",
"ext-xmlwriter": "*",
"php": ">=5.2.0"
},
"type": "library",
"autoload": {
"psr-0": {
"PHPExcel": "Classes/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL"
],
"authors": [
{
"name": "Maarten Balliauw",
"homepage": "http://blog.maartenballiauw.be"
},
{
"name": "Mark Baker"
},
{
"name": "Franck Lefevre",
"homepage": "http://blog.rootslabs.net"
},
{
"name": "Erik Tilt"
}
],
"description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
"homepage": "http://phpexcel.codeplex.com",
"keywords": [
"OpenXML",
"excel",
"php",
"spreadsheet",
"xls",
"xlsx"
],
"time": "2015-05-01 07:00:55"
},
{
"name": "restler/framework",
"version": "3.0.0",
"target-dir": "Luracast/Restler",
"source": {
"type": "git",
"url": "https://github.com/Luracast/Restler-Framework.git",
"reference": "6ee10b3e5dbc6376916fed55ec2340a37cce436b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Luracast/Restler-Framework/zipball/6ee10b3e5dbc6376916fed55ec2340a37cce436b",
"reference": "6ee10b3e5dbc6376916fed55ec2340a37cce436b",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"replace": {
"luracast/restler": "3.*"
},
"require-dev": {
"bshaffer/oauth2-server-php": "v1.0",
"luracast/explorer": "*",
"mustache/mustache": "dev-master",
"rodneyrehm/plist": "dev-master",
"symfony/yaml": "*",
"twig/twig": "v1.13.0",
"zendframework/zendamf": "dev-master"
},
"suggest": {
"bshaffer/oauth2-server-php": "Restler can provide OAuth2 authentication using this library (see require-dev for details)",
"luracast/explorer": "Restler's very own api explorer (see require-dev for details)",
"mustache/mustache": "Restler can render HtmlView using mustache/handlebar templates (see require-dev for details)",
"rodneyrehm/plist": "Restler supports tho Apple plist xml format (see require-dev for details)",
"symfony/yaml": "Restler can produce content in yaml format as well (see require-dev for details)",
"twig/twig": "Restler can render HtmlView using twig templates (see require-dev for details)",
"zendframework/zendamf": "Support for the amf document format (see require-dev for details)"
},
"type": "library",
"extra": {
"branch-alias": {
"master": "v3.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Luracast\\Restler": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "Luracast",
"email": "arul@luracast.com"
},
{
"name": "Nick nickl- Lombard",
"email": "github@jigsoft.co.za"
}
],
"description": "Just the Restler Framework without the tests and examples",
"homepage": "http://luracast.com/products/restler/",
"keywords": [
"api",
"framework",
"rest",
"server"
],
"time": "2015-08-04 07:52:49"
},
{
"name": "tecnickcom/tcpdf",
"version": "6.2.12",
"source": {
"type": "git",
"url": "https://github.com/tecnickcom/TCPDF.git",
"reference": "2f732eaa91b5665274689b1d40b285a7bacdc37f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/2f732eaa91b5665274689b1d40b285a7bacdc37f",
"reference": "2f732eaa91b5665274689b1d40b285a7bacdc37f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"autoload": {
"classmap": [
"fonts",
"config",
"include",
"tcpdf.php",
"tcpdf_parser.php",
"tcpdf_import.php",
"tcpdf_barcodes_1d.php",
"tcpdf_barcodes_2d.php",
"include/tcpdf_colors.php",
"include/tcpdf_filters.php",
"include/tcpdf_font_data.php",
"include/tcpdf_fonts.php",
"include/tcpdf_images.php",
"include/tcpdf_static.php",
"include/barcodes/datamatrix.php",
"include/barcodes/pdf417.php",
"include/barcodes/qrcode.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPLv3"
],
"authors": [
{
"name": "Nicola Asuni",
"email": "info@tecnick.com",
"homepage": "http://nicolaasuni.tecnick.com"
}
],
"description": "TCPDF is a PHP class for generating PDF documents and barcodes.",
"homepage": "http://www.tcpdf.org/",
"keywords": [
"PDFD32000-2008",
"TCPDF",
"barcodes",
"datamatrix",
"pdf",
"pdf417",
"qrcode"
],
"time": "2015-09-12 10:08:34"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"ckeditor/ckeditor": 20,
"mike42/escpos-php": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.3.0",
"ext-curl": "*"
},
"platform-dev": []
}

View File

@ -49,6 +49,9 @@ $search_zip=GETPOST("search_zip");
$search_town=GETPOST("search_town");
$search_state=GETPOST("search_state");
$search_country=GETPOST("search_country");
$search_phone=GETPOST("search_phone");
$search_phone_perso=GETPOST("search_phone_perso");
$search_phone_mobile=GETPOST("search_phone_mobile");
$type=GETPOST("type");
$search_email=GETPOST("search_email");
$search_categ = GETPOST("search_categ",'int');
@ -104,6 +107,9 @@ $arrayfields=array(
'd.address'=>array('label'=>$langs->trans("Address"), 'checked'=>0),
'd.zip'=>array('label'=>$langs->trans("Zip"), 'checked'=>0),
'd.town'=>array('label'=>$langs->trans("Town"), 'checked'=>0),
'd.phone'=>array('label'=>$langs->trans("Phone"), 'checked'=>0),
'd.phone_perso'=>array('label'=>$langs->trans("PhonePerso"), 'checked'=>0),
'd.phone_mobile'=>array('label'=>$langs->trans("PhoneMobile"), 'checked'=>0),
'state.nom'=>array('label'=>$langs->trans("State"), 'checked'=>0),
'country.code_iso'=>array('label'=>$langs->trans("Country"), 'checked'=>0),
/*'d.note_public'=>array('label'=>$langs->trans("NotePublic"), 'checked'=>0),
@ -151,6 +157,9 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP
$search_town="";
$search_state="";
$search_country='';
$search_phone='';
$search_phone_perso='';
$search_phone_mobile='';
$search_morphy="";
$search_categ="";
$catid="";
@ -290,6 +299,9 @@ if ($resql)
if ($search_zip != '') $param.= "&search_zip=".urlencode($search_zip);
if ($search_state != '') $param.= "&search_state=".urlencode($search_state);
if ($search_country != '') $param.= "&search_country=".urlencode($search_country);
if ($search_phone != '') $param.= "&search_phone=".urlencode($search_phone);
if ($search_phone_perso != '') $param.= "&search_phone_perso=".urlencode($search_phone_perso);
if ($search_phone_mobile != '') $param.= "&search_phone_mobile=".urlencode($search_phone_mobile);
if ($filter) $param.="&filter=".urlencode($filter);
if ($type > 0) $param.="&type=".urlencode($type);
if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss);
@ -347,7 +359,7 @@ if ($resql)
print '<tr class="liste_titre">';
if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER))
{
print '<td width="5" align="center">&nbsp;</td>';
print '<td colspan="1" align="center">'.$langs->trans("NumberingShort").'</td>';
}
if (! empty($arrayfields['d.ref']['checked'])) print_liste_field_titre($arrayfields['d.ref']['label'],$_SERVER["PHP_SELF"],'d.rowid','',$param,'',$sortfield,$sortorder);
if (! empty($arrayfields['d.firstname']['checked'])) print_liste_field_titre($arrayfields['d.firstname']['label'],$_SERVER["PHP_SELF"],'d.firstname','',$param,'',$sortfield,$sortorder);
@ -361,6 +373,9 @@ if ($resql)
if (! empty($arrayfields['d.town']['checked'])) print_liste_field_titre($arrayfields['d.town']['label'],$_SERVER["PHP_SELF"],'d.town','',$param,'',$sortfield,$sortorder);
if (! empty($arrayfields['state.nom']['checked'])) print_liste_field_titre($langs->trans("StateShort"),$_SERVER["PHP_SELF"],"state.nom","",$param,'',$sortfield,$sortorder);
if (! empty($arrayfields['country.code_iso']['checked'])) print_liste_field_titre($langs->trans("Country"),$_SERVER["PHP_SELF"],"country.code_iso","",$param,'align="center"',$sortfield,$sortorder);
if (! empty($arrayfields['d.phone']['checked'])) print_liste_field_titre($arrayfields['d.phone']['label'],$_SERVER["PHP_SELF"],'d.phone','',$param,'',$sortfield,$sortorder);
if (! empty($arrayfields['d.phone_perso']['checked'])) print_liste_field_titre($arrayfields['d.phone_perso']['label'],$_SERVER["PHP_SELF"],'d.phone_perso','',$param,'',$sortfield,$sortorder);
if (! empty($arrayfields['d.phone_mobile']['checked'])) print_liste_field_titre($arrayfields['d.phone_mobile']['label'],$_SERVER["PHP_SELF"],'d.phone_mobile','',$param,'',$sortfield,$sortorder);
if (! empty($arrayfields['d.email']['checked'])) print_liste_field_titre($arrayfields['d.email']['label'],$_SERVER["PHP_SELF"],'d.email','',$param,'',$sortfield,$sortorder);
if (! empty($arrayfields['d.datefin']['checked'])) print_liste_field_titre($arrayfields['d.datefin']['label'],$_SERVER["PHP_SELF"],'d.datefin','',$param,'align="center"',$sortfield,$sortorder);
// Extra fields
@ -388,6 +403,12 @@ if ($resql)
// Line for filters fields
print '<tr class="liste_titre">';
// Line numbering
if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER))
{
print '<td class="liste_titre">&nbsp;</td>';
}
// Ref
if (! empty($arrayfields['d.ref']['checked']))
{
@ -464,6 +485,24 @@ if ($resql)
print $form->select_country($search_country,'search_country','',0,'maxwidth100');
print '</td>';
}
// Phone pro
if (! empty($arrayfields['d.phone']['checked']))
{
print '<td class="liste_titre" align="left">';
print '<input class="flat" type="text" name="search_phone" value="'.$search_phone.'" size="5"></td>';
}
// Phone perso
if (! empty($arrayfields['d.phone_perso']['checked']))
{
print '<td class="liste_titre" align="left">';
print '<input class="flat" type="text" name="search_phone_perso" value="'.$search_phone_perso.'" size="5"></td>';
}
// Phone mobile
if (! empty($arrayfields['d.phone_mobile']['checked']))
{
print '<td class="liste_titre" align="left">';
print '<input class="flat" type="text" name="search_phone_mobile" value="'.$search_phone_mobile.'" size="5"></td>';
}
// Email
if (! empty($arrayfields['d.email']['checked']))
{
@ -619,14 +658,6 @@ if ($resql)
print $obj->address;
print '</td>';
}
// Town
if (! empty($arrayfields['d.town']['checked']))
{
print '<td class="nocellnopadd">';
print $obj->town;
print '</td>';
if (! $i) $totalarray['nbfield']++;
}
// Zip
if (! empty($arrayfields['d.zip']['checked']))
{
@ -635,6 +666,14 @@ if ($resql)
print '</td>';
if (! $i) $totalarray['nbfield']++;
}
// Town
if (! empty($arrayfields['d.town']['checked']))
{
print '<td class="nocellnopadd">';
print $obj->town;
print '</td>';
if (! $i) $totalarray['nbfield']++;
}
// State
if (! empty($arrayfields['state.nom']['checked']))
{
@ -650,6 +689,30 @@ if ($resql)
print '</td>';
if (! $i) $totalarray['nbfield']++;
}
// Phone pro
if (! empty($arrayfields['d.phone']['checked']))
{
print '<td class="nocellnopadd">';
print $obj->phone;
print '</td>';
if (! $i) $totalarray['nbfield']++;
}
// Phone perso
if (! empty($arrayfields['d.phone_perso']['checked']))
{
print '<td class="nocellnopadd">';
print $obj->phone_perso;
print '</td>';
if (! $i) $totalarray['nbfield']++;
}
// Phone mobile
if (! empty($arrayfields['d.phone_mobile']['checked']))
{
print '<td class="nocellnopadd">';
print $obj->phone_mobile;
print '</td>';
if (! $i) $totalarray['nbfield']++;
}
// EMail
if (! empty($arrayfields['d.email']['checked']))
{

View File

@ -3987,7 +3987,7 @@ abstract class CommonObject
function call_trigger($trigger_name, $user)
{
global $langs,$conf;
include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
$interface=new Interfaces($this->db);
$result=$interface->run_triggers($trigger_name,$this,$user,$langs,$conf);

View File

@ -77,7 +77,7 @@ class Interfaces
$modules = array();
$orders = array();
$i=0;
$dirtriggers=array_merge(array('/core/triggers'),$conf->modules_parts['triggers']);
foreach($dirtriggers as $reldir)
{
@ -100,9 +100,9 @@ class Interfaces
$part3=$reg[3];
$nbfile++;
$modName = "Interface".ucfirst($reg[3]);
//print "file=$file"; print "modName=$modName"; exit;
//print "file=$file - modName=$modName\n";
if (in_array($modName,$modules))
{
$langs->load("errors");
@ -111,9 +111,17 @@ class Interfaces
}
else
{
include_once $newdir.'/'.$file;
try {
//print 'Todo for '.$modName." : ".$newdir.'/'.$file."\n";
include_once $newdir.'/'.$file;
//print 'Done for '.$modName."\n";
}
catch(Exception $e)
{
dol_syslog('ko for '.$modName." ".$e->getMessage()."\n", LOG_ERROR);
}
}
// Check if trigger file is disabled by name
if (preg_match('/NORUN$/i',$file)) continue;
// Check if trigger file is for a particular module
@ -142,7 +150,7 @@ class Interfaces
}
asort($orders);
// Loop on each trigger
foreach ($orders as $key => $value)
{

View File

@ -35,7 +35,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/menubase.class.php';
* @param int $type_user 0=Menu for backoffice, 1=Menu for front office
* @param array $tabMenu If array with menu entries already loaded, we put this array here (in most cases, it's empty)
* @param array $menu Object Menu to return back list of menu entries
* @param int $noout Disable output (Initialise &$menu only).
* @param int $noout 1=Disable output (Initialise &$menu only).
* @return int 0
*/
function print_eldy_menu($db,$atarget,$type_user,&$tabMenu,&$menu,$noout=0)
@ -50,6 +50,19 @@ function print_eldy_menu($db,$atarget,$type_user,&$tabMenu,&$menu,$noout=0)
if (empty($noout)) print_start_menu_array();
// Show/Hide vertical menu
if (GETPOST('testhidemenu') && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
{
$showmode=1;
$classname = 'class="tmenu"';
$idsel='home';
if (empty($noout)) print_start_menu_entry($idsel,$classname,$showmode);
if (empty($noout)) print_text_menu_entry($langs->trans("XXX"), 1, '#', $id, $idsel, $classname, $atarget);
if (empty($noout)) print_end_menu_entry($showmode);
$menu->add('#', $langs->trans("XXX"), 0, $showmode, $atarget, "xxx", '');
}
// Home
$showmode=1;
$classname="";
@ -463,7 +476,7 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
print '</div>'."\n";
}
if (is_array($moredata) && ! empty($moredata['searchform']))
if (is_array($moredata) && ! empty($moredata['searchform']) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
{
print "\n";
print "<!-- Begin SearchForm -->\n";

View File

@ -144,7 +144,7 @@ class MenuManager
if ($mode == 'topnb')
{
print_eldy_menu($this->db,$this->atarget,$this->type_user,$this->tabMenu,$this->menu,1);
print_eldy_menu($this->db,$this->atarget,$this->type_user,$this->tabMenu,$this->menu,1); // no output
return $this->menu->getNbOfVisibleMenuEntries();
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,7 @@ use Luracast\Restler\iCache;
* @copyright 2013 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class ApcCache implements iCache
{

View File

@ -12,7 +12,7 @@ namespace Luracast\Restler {
* @subpackage helper
* @author Nick Lombard <github@jigsoft.co.za>
* @copyright 2012 Luracast
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class AutoLoader
{
@ -263,6 +263,12 @@ class AutoLoader
* @return bool false unless className now exists
*/
private function loadLastResort($className, $loader = null) {
// DOL_LDR Add protection to avoid conflict with other autouploader
/*print 'Try to load '.$className."\n";
if (in_array($className, array('Google_Client')))
{
return false;
}*/
$loaders = array_unique(static::$rogueLoaders);
if (isset($loader)) {
if (false === array_search($loader, $loaders))
@ -301,8 +307,6 @@ class AutoLoader
*/
private function alias($className, $currentClass)
{
if ($className == 'Luracast\Restler\string') return;
if ($className == 'Luracast\Restler\mixed') return;
if ($className != $currentClass
&& false !== strpos($className, $currentClass))
if (!class_exists($currentClass, false)

View File

@ -2,6 +2,7 @@
namespace Luracast\Restler;
use Exception;
use Luracast\Restler\Data\Text;
/**
* Parses the PHPDoc comments for metadata. Inspired by `Documentor` code base.
@ -13,7 +14,7 @@ use Exception;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class CommentParser
{
@ -50,6 +51,15 @@ class CommentParser
*/
public static $arrayDelimiter = ',';
/**
* @var array annotations that support array value
*/
public static $allowsArrayValue = array(
'choice' => true,
'select' => true,
'properties' => true,
);
/**
* character sequence used to escape \@
*/
@ -195,7 +205,7 @@ class CommentParser
list(, $param, $value) = preg_split('/\@|\s/', $line, 3)
+ array('', '', '');
list($value, $embedded) = $this->parseEmbeddedData($value);
$value = array_filter(preg_split('/\s+/msu', $value));
$value = array_filter(preg_split('/\s+/msu', $value), 'strlen');
$this->parseParam($param, $value, $embedded);
}
return $this->_data;
@ -214,6 +224,9 @@ class CommentParser
$allowMultiple = false;
switch ($param) {
case 'param' :
case 'property' :
case 'property-read' :
case 'property-write' :
$value = $this->formatParam($value);
$allowMultiple = true;
break;
@ -280,7 +293,12 @@ class CommentParser
$value[self::$embeddedDataName]
+= $data[$param][self::$embeddedDataName];
}
$data[$param] = $value + $data[$param];
if (!is_array($data[$param])) {
$data[$param] = array('description' => (string) $data[$param]);
}
if (is_array($value)) {
$data[$param] = $value + $data[$param];
}
}
}
@ -303,14 +321,15 @@ class CommentParser
}
while (preg_match('/{@(\w+)\s?([^}]*)}/ms', $subject, $matches)) {
$subject = str_replace($matches[0], '', $subject);
if ($matches[2] == 'true' || $matches[2] == 'false') {
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 (isset(static::$allowsArrayValue[$matches[1]])) {
$matches[2] = explode(static::$arrayDelimiter, $matches[2]);
} elseif ($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)) {
} elseif ($matches[1] == 'required') {
$matches[2] = explode(static::$arrayDelimiter, $matches[2]);
}
$data[$matches[1]] = $matches[2];
@ -376,12 +395,36 @@ class CommentParser
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;
$code = 500;
$exception = 'Exception';
if(count($value)>1){
$v1 = $value[0];
$v2 = $value[1];
if(is_numeric($v1)){
$code = $v1;
$exception = $v2;
array_shift($value);
array_shift($value);
} elseif(is_numeric($v2)){
$code = $v2;
$exception = $v1;
array_shift($value);
array_shift($value);
} else {
$exception = $v1;
array_shift($value);
}
} elseif(count($value) && is_numeric($value[0])) {
$code = $value[0];
array_shift($value);
}
$message = implode(' ', $value);
if(!isset(RestException::$codes[$code])){
$code = 500;
} elseif(empty($message)){
$message = RestException::$codes[$code];
}
return compact('code','message','exception');
}
private function formatClass(array $value)
@ -439,6 +482,10 @@ class CommentParser
$r['name'] = substr($data, 1);
}
}
if (isset($r['type']) && Text::endsWith($r['type'], '[]')) {
$r[static::$embeddedDataName]['type'] = substr($r['type'], 0, -2);
$r['type'] = 'array';
}
if ($value) {
$r['description'] = implode(' ', $value);
}
@ -458,6 +505,10 @@ class CommentParser
$data = explode('|', $data);
$r['type'] = count($data) == 1 ? $data[0] : $data;
}
if (isset($r['type']) && Text::endsWith($r['type'], '[]')) {
$r[static::$embeddedDataName]['type'] = substr($r['type'], 0, -2);
$r['type'] = 'array';
}
if ($value) {
$r['description'] = implode(' ', $value);
}

View File

@ -11,6 +11,7 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc6
*/
class Compose implements iCompose
{

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler\Data;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class ApiMethodInfo extends ValueObject
{

View File

@ -10,6 +10,7 @@ namespace Luracast\Restler\Data;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc6
*/
class Arr
{

View File

@ -12,7 +12,7 @@ use Exception;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Invalid extends Exception
{

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler\Data;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Object
{

View File

@ -10,15 +10,16 @@ namespace Luracast\Restler\Data;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc6
*/
class String
class Text
{
/**
* Given haystack contains the needle or not?
*
* @param string $haystack
* @param string $needle
* @param bool $caseSensitive
* @param bool $caseSensitive
*
* @return bool
*/
@ -75,10 +76,21 @@ class String
return
ucwords(
preg_replace(
array('/(?<=[^A-Z])([A-Z])/', '/(?<=[^0-9])([0-9])/', '/(_)/'),
array(' $0', ' $0', ' '),
array('/(?<=[^A-Z])([A-Z])/', '/(?<=[^0-9])([0-9])/', '/([_-])/', '/[^a-zA-Z0-9\s]|\s\s+/'),
array(' $0', ' $0', ' ', ' '),
$name
)
);
}
/**
* Convert given string to be used as a slug or css class
*
* @param string $name
* @return string
*/
public static function slug($name)
{
return preg_replace('/[^a-zA-Z]+/', '-', strtolower(strip_tags($name)));
}
}

View File

@ -15,7 +15,7 @@ use Luracast\Restler\Util;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class ValidationInfo implements iValueObject
{

View File

@ -17,13 +17,24 @@ use Luracast\Restler\Util;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Validator implements iValidate
{
public static $holdException = false;
public static $exceptions = array();
public static $preFilters = array(
'*' => 'trim',
//'string' => 'strip_tags',
//'string' => 'htmlspecialchars',
//'int' => 'abs',
//'float' => 'abs',
//'CustomClass' => 'MyFilterClass::custom',
// please note that you wont get an instance
// of CustomClass. you will get an array instead
);
/**
* Validate alphabetic characters.
*
@ -116,6 +127,25 @@ class Validator implements iValidate
throw new Invalid('Expecting only hexadecimal digits.');
}
/**
* Color specified as hexadecimals
*
* Check that given value contains only color.
*
* @param $input
* @param ValidationInfo|null $info
*
* @return string
* @throws Invalid
*/
public static function color($input, ValidationInfo $info = null)
{
if (preg_match('/^#[a-f0-9]{6}$/i', $input)) {
return $input;
}
throw new Invalid('Expecting color as hexadecimal digits.');
}
/**
* Validate Telephone number
*
@ -360,6 +390,20 @@ class Validator implements iValidate
{
$html = Scope::get('Restler')->responseFormat instanceof HtmlFormat;
$name = $html ? "<strong>$info->label</strong>" : "`$info->name`";
if (
isset(static::$preFilters['*']) &&
is_scalar($input) &&
is_callable($func = static::$preFilters['*'])
) {
$input = $func($input);
}
if (
isset(static::$preFilters[$info->type]) &&
(is_scalar($input) || !empty($info->children)) &&
is_callable($func = static::$preFilters[$info->type])
) {
$input = $func($input);
}
try {
if (is_null($input)) {
if ($info->required) {
@ -470,6 +514,8 @@ class Validator implements iValidate
return $r;
case 'string' :
case 'password' : //password fields with string
case 'search' : //search field with string
if (!is_string($input)) {
$error .= '. Expecting alpha numeric value';
break;
@ -602,13 +648,13 @@ class Validator implements iValidate
}
foreach ($info->children as $key => $value) {
$cv = new ValidationInfo($value);
$cv->name = "{$info->name}[$key]";
if (array_key_exists($key, $input) || $cv->required) {
$instance->{$key} = static::validate(
Util::nestedValue($input, $key),
$cv
);
}
}
}
return $instance;
@ -616,7 +662,7 @@ class Validator implements iValidate
}
throw new RestException (400, $error);
} catch (\Exception $e) {
static::$exceptions[] = $e;
static::$exceptions[$info->name] = $e;
if (static::$holdException) {
return null;
}

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler\Data;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class ValueObject implements iValueObject
{

View File

@ -10,7 +10,7 @@ namespace Luracast\Restler\Data;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
interface iValidate {

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler\Data;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
interface iValueObject
{

View File

@ -15,7 +15,7 @@ use Luracast\Restler\Data\Validator;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Defaults
{
@ -166,6 +166,12 @@ class Defaults
*/
public static $emptyBodyForNullResponse = true;
/**
* @var bool when set to true, the response will not be outputted directly into the buffer.
* If set, Restler::handle() will return the response as a string.
*/
public static $returnResponse = false;
/**
* @var bool enables CORS support
*/

View File

@ -9,7 +9,7 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
use Closure;

View File

@ -0,0 +1,582 @@
<?php
namespace Luracast\Restler;
use stdClass;
use Luracast\Restler\Data\Text;
use Luracast\Restler\Data\ValidationInfo;
use Luracast\Restler\Scope;
/**
* Class Explorer
*
* @package Luracast\Restler
*
* @access hybrid
* @version 3.0.0rc6
*/
class Explorer implements iProvideMultiVersionApi
{
const SWAGGER_VERSION = '1.2';
/**
* @var bool should protected resources be shown to unauthenticated users?
*/
public static $hideProtected = true;
/**
* @var bool should we use format as extension?
*/
public static $useFormatAsExtension = true;
/*
* @var bool can we accept scalar values (string, int, float etc) as the request body?
*/
public static $allowScalarValueOnRequestBody = false;
/**
* @var array all http methods specified here will be excluded from
* documentation
*/
public static $excludedHttpMethods = array('OPTIONS');
/**
* @var array all paths beginning with any of the following will be excluded
* from documentation
*/
public static $excludedPaths = array();
/**
* @var bool
*/
public static $placeFormatExtensionBeforeDynamicParts = true;
/**
* @var bool should we group all the operations with the same url or not
*/
public static $groupOperations = false;
/**
* @var string class that holds metadata as static properties
*/
public static $infoClass = 'Luracast\Restler\ExplorerInfo';
/**
* Injected at runtime
*
* @var Restler instance of restler
*/
public $restler;
/**
* @var string when format is not used as the extension this property is
* used to set the extension manually
*/
public $formatString = '';
/**
* @var array type mapping for converting data types to JSON-Schema Draft 4
* Which is followed by swagger 1.2 spec
*/
public static $dataTypeAlias = array(
//'string' => 'string',
'int' => 'integer',
'number' => 'number',
'float' => array('number', 'float'),
'bool' => 'boolean',
//'boolean' => 'boolean',
//'NULL' => 'null',
'array' => 'array',
//'object' => 'object',
'stdClass' => 'object',
'mixed' => 'string',
'date' => array('string', 'date'),
'datetime' => array('string', 'date-time'),
);
/**
* @var array configurable symbols to differentiate public, hybrid and
* protected api
*/
public static $apiDescriptionSuffixSymbols = array(
0 => '&nbsp; <i class="fa fa-lg fa-unlock-alt"></i>', //public api
1 => '&nbsp; <i class="fa fa-lg fa-adjust"></i>', //hybrid api
2 => '&nbsp; <i class="fa fa-lg fa-lock"></i>', //protected api
);
protected $models = array();
/**
* @var bool|stdClass
*/
protected $_fullDataRequested = false;
protected $crud = array(
'POST' => 'create',
'GET' => 'retrieve',
'PUT' => 'update',
'DELETE' => 'delete',
'PATCH' => 'partial update'
);
protected static $prefixes = array(
'get' => 'retrieve',
'index' => 'list',
'post' => 'create',
'put' => 'update',
'patch' => 'modify',
'delete' => 'remove',
);
protected $_authenticated = false;
protected $cacheName = '';
public function __construct()
{
}
/**
* Serve static files for exploring
*
* Serves explorer html, css, and js files
*
* @url GET *
*/
public function get()
{
if (func_num_args() > 1 && func_get_arg(0) == 'resources') {
/**
* BUGFIX:
* If we use common resourcePath (e.g. $r->addAPIClass([api-class], 'api/shop')), than we must determine resource-ID of e.g. 'api/shop'!
*/
$arguments = func_get_args();
// remove first entry
array_shift($arguments);
// create ID
$id = implode('/', $arguments);
return $this->getResources($id);
}
$filename = implode('/', func_get_args());
$redirect = false;
if (
(empty($filename) && substr($_SERVER['REQUEST_URI'], -1, 1) != '/') ||
$filename == 'index.html'
) {
$status = 302;
$url = $this->restler->getBaseUrl() . '/' . $this->base() . '/';
header("{$_SERVER['SERVER_PROTOCOL']} $status " . RestException::$codes[$status]);
header("Location: $url");
exit;
}
if (
isset($this->restler->responseFormat) &&
$this->restler->responseFormat->getExtension() == 'js'
) {
$filename .= '.js';
}
PassThrough::file(__DIR__ . '/explorer/' . (empty($filename) ? 'index.html' : $filename), false, 0); //60 * 60 * 24);
}
public function resources()
{
$r = new stdClass();
$r->apiVersion = (string)$this->restler->getRequestedApiVersion();
$r->swaggerVersion = static::SWAGGER_VERSION;
$r->apis = $this->apis($r->apiVersion);
$r->authorizations = $this->authorizations();
$r->info = array_filter(get_class_vars(static::$infoClass));
return $r;
}
public function getResources($id)
{
$r = new stdClass();
$r->apiVersion = (string)$this->restler->getRequestedApiVersion();
$r->swaggerVersion = static::SWAGGER_VERSION;
$r->basePath = $this->restler->getBaseUrl();
$r->resourcePath = "/$id";
$r->apis = $this->apis($r->apiVersion, $id);
$r->models = (object)$this->models;
$r->produces = $this->restler->getWritableMimeTypes();
$r->consumes = $this->restler->getReadableMimeTypes();
$r->authorizations = $this->authorizations();
return $r;
}
private function apis($version = 1, $resource = false)
{
$map = Routes::findAll(static::$excludedPaths + array($this->base()), static::$excludedHttpMethods, $version);
$r = array();
$a = array();
foreach ($map as $path => $data) {
$route = $data[0]['route'];
$access = $data[0]['access'];
if ($access && !Text::contains($path, '{')) {
$r[] = array(
'path' => empty($path) ? '/root' : "/$path",
//'description' => ''
//TODO: Util::nestedValue($route, 'metadata', 'classDescription') ? : ''
);
}
if (static::$hideProtected && !$access)
continue;
$grouper = array();
foreach ($data as $item) {
$route = $item['route'];
$access = $item['access'];
if (static::$hideProtected && !$access)
continue;
$url = $route['url'];
if (isset($grouper[$url])) {
$grouper[$url]['operations'][] = $this->operation($route);
} else {
$api = array(
'path' => "/$url",
'description' =>
Util::nestedValue($route, 'metadata', 'classDescription') ? : '',
'operations' => array($this->operation($route))
);
static::$groupOperations
? $grouper[$url] = $api
: $a[$path][] = $api;
}
}
if (!empty($grouper)) {
$a[$path] = array_values($grouper);
// sort REST-endpoints by path
foreach ($a as & $b) {
usort(
$b,
function ($x, $y) {
return $x['path'] > $y['path'];
}
);
}
} else {
$order = array(
'GET' => 1,
'POST' => 2,
'PUT' => 3,
'PATCH' => 4,
'DELETE' => 5
);
foreach ($a as & $b) {
usort(
$b,
function ($x, $y) use ($order) {
return
$x['operations'][0]->method ==
$y['operations'][0]->method
? $x['path'] > $y['path']
: $order[$x['operations'][0]->method] >
$order[$y['operations'][0]->method];
}
);
}
}
}
if (false !== $resource) {
if ($resource == 'root') $resource = '';
if (isset($a[$resource])) return $a[$resource];
}
return $r;
}
private function operation($route)
{
$r = new stdClass();
$r->method = $route['httpMethod'];
$r->nickname = $this->nickname($route);
$r->parameters = $this->parameters($route);
$m = $route['metadata'];
$r->summary = isset($m['description'])
? $m['description']
: '';
$r->summary .= $route['accessLevel'] > 2
? static::$apiDescriptionSuffixSymbols[2]
: static::$apiDescriptionSuffixSymbols[$route['accessLevel']];
$r->notes = isset($m['longDescription'])
? $m['longDescription']
: '';
$r->responseMessages = $this->responseMessages($route);
$this->setType(
$r,
new ValidationInfo(Util::nestedValue($m, 'return') ? : array())
);
if (is_null($r->type) || 'mixed' == $r->type) {
$r->type = 'array';
} elseif ($r->type == 'null') {
$r->type = 'void';
} elseif (Text::contains($r->type, '|')) {
$r->type = 'array';
}
//TODO: add $r->authorizations
//A list of authorizations required to execute this operation. While not mandatory, if used, it overrides
//the value given at the API Declaration's authorizations. In order to completely remove API Declaration's
//authorizations completely, an empty object ({}) may be applied.
//TODO: add $r->produces
//TODO: add $r->consumes
//A list of MIME types this operation can produce/consume. This is overrides the global produces definition at the root of the API Declaration. Each string value SHOULD represent a MIME type.
//TODO: add $r->deprecated
//Declares this operation to be deprecated. Usage of the declared operation should be refrained. Valid value MUST be either "true" or "false". Note: This field will change to type boolean in the future.
return $r;
}
private function parameters(array $route)
{
$r = array();
$children = array();
$required = false;
foreach ($route['metadata']['param'] as $param) {
$info = new ValidationInfo($param);
$description = isset($param['description']) ? $param['description'] : '';
if ('body' == $info->from) {
if ($info->required)
$required = true;
$param['description'] = $description;
$children[] = $param;
} else {
$r[] = $this->parameter($info, $description);
}
}
if (!empty($children)) {
if (
1 == count($children) &&
(static::$allowScalarValueOnRequestBody || !empty($children[0]['children']))
) {
$firstChild = $children[0];
if (empty($firstChild['children'])) {
$description = $firstChild['description'];
} else {
$description = '<section class="body-param">';
foreach ($firstChild['children'] as $child) {
$description .= isset($child['required']) && $child['required']
? '<strong>' . $child['name'] . '</strong> (required)<br/>'
: $child['name'] . '<br/>';
}
$description .= '</section>';
}
$r[] = $this->parameter(new ValidationInfo($firstChild), $description);
} else {
$description = '<section class="body-param">';
foreach ($children as $child) {
$description .= isset($child['required']) && $child['required']
? '<strong>' . $child['name'] . '</strong> (required)<br/>'
: $child['name'] . '<br/>';
}
$description .= '</section>';
//lets group all body parameters under a generated model name
$name = $this->nameModel($route);
$r[] = $this->parameter(
new ValidationInfo(array(
'name' => $name,
'type' => $name,
'from' => 'body',
'required' => $required,
'children' => $children
)),
$description
);
}
}
return $r;
}
private function parameter(ValidationInfo $info, $description = '')
{
$p = new stdClass();
if(isset($info->rules['model'])){
$info->type = $info->rules['model'];
}
$p->name = $info->name;
$this->setType($p, $info);
if (empty($info->children) || $info->type != 'array') {
//primitives
if ($info->default)
$p->defaultValue = $info->default;
if ($info->choice)
$p->enum = $info->choice;
if ($info->min)
$p->minimum = $info->min;
if ($info->max)
$p->maximum = $info->max;
//TODO: $p->items and $p->uniqueItems boolean
}
$p->description = $description;
$p->paramType = $info->from; //$info->from == 'body' ? 'form' : $info->from;
$p->required = $info->required;
$p->allowMultiple = false;
return $p;
}
private function responseMessages(array $route)
{
$r = array();
if (is_array($throws = Util::nestedValue($route, 'metadata', 'throws'))) {
foreach ($throws as $message) {
$m = (object)$message;
//TODO: add $m->responseModel from composer class
$r[] = $m;
}
}
return $r;
}
private function model($type, array $children)
{
/**
* Bugfix:
* If we use namespaces, than the model will not be correct, if we use a short name for the type!
*
* Example (phpDoc/annotations in API-class, which uses custom domain-model with namespace):
* @param Car $car {@from body} {@type Aoe\RestServices\Domain\Model\Car}
* @return Car {@type Aoe\RestServices\Domain\Model\Car}
* Than, the model (in swagger-spec) must also be 'Aoe\RestServices\Domain\Model\Car' and not 'Car'
*
* When we use namespaces, than we must use the @type-annotation, otherwise the automatic reconstitution
* from request-data (e.g. when it is a POST-request) to custom domain-model-object will not work!
*
* Summary:
* - When we use no namespaces, than the type would not be changed, if we would call 'Util::getShortName'
* - When we use namespaces, than the model will not be correct, if we would call 'Util::getShortName'
* ...so this method-call is either needless or will create a bug/error
*/
//$type = Util::getShortName($type);
if (isset($this->models[$type]))
return $this->models[$type];
$r = new stdClass();
$r->id = $type;
$r->description = "$type Model"; //TODO: enhance this on Router
$r->required = array();
$r->properties = array();
foreach ($children as $child) {
$info = new ValidationInfo($child);
$p = new stdClass();
$this->setType($p, $info);
$p->description = isset($child['description']) ? $child['description'] : '';
if ($info->default)
$p->defaultValue = $info->default;
if ($info->choice)
$p->enum = $info->choice;
if ($info->min)
$p->minimum = $info->min;
if ($info->max)
$p->maximum = $info->max;
if ($info->required)
$r->required[] = $info->name;
$r->properties[$info->name] = $p;
}
//TODO: add $r->subTypes https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#527-model-object
//TODO: add $r->discriminator https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#527-model-object
$this->models[$type] = $r;
return $r;
}
private function setType(&$object, ValidationInfo $info)
{
//TODO: proper type management
if ($info->type == 'array') {
if ($info->children) {
$this->model($info->contentType, $info->children);
$object->items = (object)array(
'$ref' => $info->contentType
);
} elseif ($info->contentType && $info->contentType == 'associative') {
unset($info->contentType);
$this->model($info->type = 'Object', array(
array(
'name' => 'property',
'type' => 'string',
'default' => '',
'required' => false,
'description' => ''
)
));
} elseif ($info->contentType && $info->contentType != 'indexed') {
$object->items = (object)array(
'type' => $info->contentType
);
} else {
$object->items = (object)array(
'type' => 'string'
);
}
} elseif ($info->children) {
$this->model($info->type, $info->children);
} elseif (is_string($info->type) && $t = Util::nestedValue(static::$dataTypeAlias, strtolower($info->type))) {
if (is_array($t)) {
list($info->type, $object->format) = $t;
} else {
$info->type = $t;
}
} else {
$info->type = 'string';
}
$object->type = $info->type;
$has64bit = PHP_INT_MAX > 2147483647;
if ($object->type == 'integer') {
$object->format = $has64bit
? 'int64'
: 'int32';
} elseif ($object->type == 'number') {
$object->format = $has64bit
? 'double'
: 'float';
}
}
private function nickname(array $route)
{
static $hash = array();
$method = $route['methodName'];
if (isset(static::$prefixes[$method])) {
$method = static::$prefixes[$method];
} else {
$method = str_replace(
array_keys(static::$prefixes),
array_values(static::$prefixes),
$method
);
}
while (isset($hash[$method]) && $route['url'] != $hash[$method]) {
//create another one
$method .= '_';
}
$hash[$method] = $route['url'];
return $method;
}
private function nameModel(array $route)
{
static $hash = array();
$count = 1;
//$name = str_replace('/', '-', $route['url']) . 'Model';
$name = $route['className'] . 'Model';
while (isset($hash[$name . $count])) {
//create another one
$count++;
}
$name .= $count;
$hash[$name] = $route['url'];
return $name;
}
private function authorizations()
{
$r = new stdClass();
$r->apiKey = (object)array(
'type' => 'apiKey',
'passAs' => 'query',
'keyname' => 'api_key',
);
return $r;
}
private function base()
{
return strtok($this->restler->url, '/');
}
/**
* Maximum api version supported by the api class
* @return int
*/
public static function __getMaximumSupportedVersion()
{
return Scope::get('Restler')->getApiVersion();
}
}

View File

@ -0,0 +1,17 @@
<?php namespace Luracast\Restler;
/**
* Class ExplorerInfo
* @package Luracast\Restler
*
* @version 3.0.0rc6
*/
class ExplorerInfo
{
public static $title = 'Restler API Explorer';
public static $description = 'Live API Documentation';
public static $termsOfServiceUrl = null;
public static $contact = 'arul@luracast.com';
public static $license = 'LGPL-2.1';
public static $licenseUrl = 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html';
}

View File

@ -15,7 +15,7 @@ use Luracast\Restler\RestException;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class RateLimit implements iFilter, iUseAuthentication
{
@ -53,7 +53,7 @@ class RateLimit implements iFilter, iUseAuthentication
* @var array all paths beginning with any of the following will be excluded
* from documentation
*/
public static $excludedPaths = array('resources');
public static $excludedPaths = array('explorer');
/**

View File

@ -2,6 +2,7 @@
namespace Luracast\Restler;
use Luracast\Restler\Format\HtmlFormat;
use ArrayAccess;
/**
* Storing and retrieving a message or array of key value pairs for one time use using $_SESSION
@ -14,9 +15,9 @@ use Luracast\Restler\Format\HtmlFormat;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Flash //implements \JsonSerializable
class Flash implements ArrayAccess
{
const SUCCESS = 'success';
const INFO = 'info';
@ -104,18 +105,22 @@ class Flash //implements \JsonSerializable
*/
public static function set(array $data)
{
if (!static::$instance)
if (!static::$instance) {
static::$instance = new Flash();
if (!isset($_SESSION['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);
}
@ -126,8 +131,9 @@ class Flash //implements \JsonSerializable
public function __destruct()
{
if ($this->usedOnce)
if ($this->usedOnce) {
unset($_SESSION['flash']);
}
}
/**
@ -139,8 +145,30 @@ class Flash //implements \JsonSerializable
public function jsonSerialize()
{
$this->usedOnce = true;
return isset($_SESSION['flash'])
? $_SESSION['flash']
: array();
}
public function offsetExists($offset)
{
return $this->__isset($offset);
}
public function offsetGet($offset)
{
return $this->__get($offset);
}
public function offsetSet($offset, $value)
{
//not implemented
}
public function offsetUnset($offset)
{
//not implemented
}
}

View File

@ -17,13 +17,16 @@ use ZendAmf\Parser\OutputStream;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class AmfFormat extends Format
class AmfFormat extends DependentFormat
{
const MIME = 'application/x-amf';
const EXTENSION = 'amf';
const PACKAGE_NAME = 'zendframework/zendamf:dev-master';
const EXTERNAL_CLASS = 'ZendAmf\\Parser\\Amf3\\Deserializer';
public function encode($data, $humanReadable = false)
{

View File

@ -15,7 +15,7 @@ use Luracast\Restler\RestException;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class CsvFormat extends Format implements iDecodeStream
{

View File

@ -0,0 +1,56 @@
<?php
namespace Luracast\Restler\Format;
use Luracast\Restler\RestException;
abstract class DependentFormat extends Format
{
/**
* override in the extending class
*
* @example symfony/yaml:*
*/
const PACKAGE_NAME = 'vendor/project:version';
/**
* override in the extending class
*
* fully qualified class name
*/
const EXTERNAL_CLASS = 'Namespace\\ClassName';
/**
* Get external class => packagist package name as an associative array
*
* @return array list of dependencies for the format
*/
public function getDependencyMap()
{
return array(
static::EXTERNAL_CLASS => static::PACKAGE_NAME
);
}
protected function checkDependency($class = null)
{
if (empty($class)) {
$class = key($this->getDependencyMap());
}
if (!class_exists($class, true)) {
$map = $this->getDependencyMap();
$package = $map[$class];
throw new RestException(
500,
get_called_class() . ' has external dependency. Please run `composer require ' .
$package . '` from the project root. Read https://getcomposer.org for more info'
);
}
}
public function __construct()
{
$this->checkDependency();
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Luracast\Restler\Format;
use Luracast\Restler\RestException;
abstract class DependentMultiFormat extends MultiFormat
{
/**
* Get external class => packagist package name as an associative array
*
* @return array list of dependencies for the format
*
* @example return ['Illuminate\\View\\View' => 'illuminate/view:4.2.*']
*/
abstract public function getDependencyMap();
protected function checkDependency($class = null)
{
if (empty($class)) {
$class = key($this->getDependencyMap());
}
if (!class_exists($class, true)) {
$map = $this->getDependencyMap();
$package = $map[$class];
throw new RestException(
500,
get_called_class() . ' has external dependency. Please run `composer require ' .
$package . '` from the project root. Read https://getcomposer.org for more info'
);
}
}
public function __construct()
{
$this->checkDependency();
}
}

View File

@ -10,7 +10,7 @@ namespace Luracast\Restler\Format;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
abstract class Format implements iFormat
{

View File

@ -6,11 +6,11 @@ use Illuminate\Events\Dispatcher;
use Illuminate\Filesystem\Filesystem;
use Illuminate\View\Compilers\BladeCompiler;
use Illuminate\View\Engines\CompilerEngine;
use Illuminate\View\Engines\PhpEngine;
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;
@ -29,9 +29,9 @@ use Luracast\Restler\Util;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class HtmlFormat extends Format
class HtmlFormat extends DependentFormat
{
public static $mime = 'text/html';
public static $extension = 'html';
@ -39,6 +39,7 @@ class HtmlFormat extends Format
public static $errorView = 'debug.php';
public static $template = 'php';
public static $handleSession = true;
public static $convertResponseToArray = false;
public static $useSmartViews = true;
/**
@ -83,11 +84,16 @@ class HtmlFormat extends Format
}
}
public function getDependencyMap(){
return array(
'Illuminate\View\View' => 'illuminate/view:4.2.*',
'Twig_Environment' => 'twig/twig:v1.13.*',
'Mustache_Engine' => 'mustache/mustache:dev-master',
);
}
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);
@ -95,6 +101,10 @@ class HtmlFormat extends Format
$resolver->register('blade', function () use ($engine) {
return $engine;
});
$phpEngine = new PhpEngine();
$resolver->register('php', function () use ($phpEngine) {
return $phpEngine;
});
/** @var Restler $restler */
$restler = Scope::get('Restler');
@ -109,7 +119,7 @@ class HtmlFormat extends Format
}
return false;
}, true, true);
$viewFinder = new FileViewFinder($files, array(static::$viewPath));
$factory = new Factory($resolver, $viewFinder, new Dispatcher());
$path = $viewFinder->find(self::$view);
@ -120,9 +130,6 @@ class HtmlFormat extends Format
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,
@ -176,27 +183,21 @@ class HtmlFormat extends Format
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())
),
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
);
},
$params = explode(',', $m->render($text));
return call_user_func_array(
'Luracast\Restler\UI\Forms::get',
$params
);
},
)
);
if (!$debug)
@ -313,7 +314,9 @@ class HtmlFormat extends Format
$success = is_null($exception);
$error = $success ? null : $exception->getMessage();
$data = array(
'response' => Object::toArray($data),
'response' => static::$convertResponseToArray
? Object::toArray($data)
: $data,
'stages' => $this->restler->getEvents(),
'success' => $success,
'error' => $error
@ -334,10 +337,7 @@ class HtmlFormat extends Format
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;
self::$view = static::guessViewName($this->restler->url);
}
if (
isset($metadata['param'])
@ -368,12 +368,19 @@ class HtmlFormat extends Format
if (!static::$cacheDirectory) {
static::$cacheDirectory = Defaults::$cacheDirectory . DIRECTORY_SEPARATOR . $template;
if (!file_exists(static::$cacheDirectory)) {
if (!mkdir(static::$cacheDirectory)) {
if (!mkdir(static::$cacheDirectory, 0770, true)) {
throw new RestException(500, 'Unable to create cache directory `' . static::$cacheDirectory . '`');
}
}
}
if (method_exists($class = get_called_class(), $template)) {
if ($template == 'blade') {
$this->checkDependency('Illuminate\View\View');
} elseif ($template == 'twig') {
$this->checkDependency('Twig_Environment');
} elseif ($template == 'mustache' || $template == 'handlebar') {
$this->checkDependency('Mustache_Engine');
}
return call_user_func("$class::$template", $data, $humanReadable);
}
throw new RestException(500, "Unsupported template system `$template`");
@ -384,6 +391,20 @@ class HtmlFormat extends Format
}
}
public static function guessViewName($path)
{
if (empty($path)) {
$path = 'index';
} elseif (strpos($path, '/')) {
$path .= '/index';
}
$file = static::$viewPath . '/' . $path . '.' . static::getViewExtension();
return static::$useSmartViews && is_readable($file)
? $path
: static::$errorView;
}
public static function getViewExtension()
{
return isset(static::$customTemplateExtensions[static::$template])

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler\Format;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class JsFormat extends JsonFormat
{

View File

@ -14,7 +14,7 @@ use Luracast\Restler\RestException;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class JsonFormat extends Format
{
@ -57,37 +57,60 @@ class JsonFormat extends Format
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
);
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;
}
$result = json_encode(Object::toArray($data, true), $options);
$this->handleJsonError();
return $result;
}
$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);
$this->handleJsonError();
if ($humanReadable) {
$result = $this->formatJson($result);
}
if (self::$unEscapedSlashes) $result = str_replace('\/', '/', $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;
}
@ -102,39 +125,21 @@ class JsonFormat extends Format
} else {
$data = preg_replace(
'/:\s*(\-?\d+(\.\d+)?([e|E][\-|\+]\d+)?)/',
': "$1"', $data
': "$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');
try {
$decoded = json_decode($data, $options);
$this->handleJsonError();
} catch (\RuntimeException $e) {
throw new RestException(400, $e->getMessage());
}
if (strlen($data) && $decoded === null || $decoded === $data) {
throw new RestException(400, 'Error parsing JSON');
}
return Object::toArray($decoded);
@ -206,5 +211,52 @@ class JsonFormat extends Format
return $newJson;
}
}
/**
* Throws an exception if an error occurred during the last JSON encoding/decoding
*
* @return void
* @throws \RuntimeException
*/
protected function handleJsonError()
{
if (function_exists('json_last_error_msg') && json_last_error() !== JSON_ERROR_NONE) {
// PHP >= 5.5.0
$message = json_last_error_msg();
} elseif (function_exists('json_last_error')) {
// PHP >= 5.3.0
switch (json_last_error()) {
case JSON_ERROR_NONE:
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;
}
}
if (isset($message)) {
throw new \RuntimeException('Error encoding/decoding JSON: '. $message);
}
}
}

View File

@ -9,7 +9,7 @@ namespace Luracast\Restler\Format;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
abstract class MultiFormat implements iFormat
{

View File

@ -18,9 +18,9 @@ use CFPropertyList\CFPropertyList;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class PlistFormat extends MultiFormat
class PlistFormat extends DependentMultiFormat
{
/**
* @var boolean set it to true binary plist is preferred
@ -81,11 +81,24 @@ class PlistFormat extends MultiFormat
*/
public function decode($data)
{
//require_once 'CFPropertyList.php';
$plist = new CFPropertyList ();
$plist->parse($data);
return $plist->toArray();
}
/**
* Get external class => packagist package name as an associative array
*
* @return array list of dependencies for the format
*
* @example return ['Illuminate\\View\\View' => 'illuminate/view:4.2.*']
*/
public function getDependencyMap()
{
return array(
'CFPropertyList\CFPropertyList' => 'rodneyrehm/plist:dev-master'
);
}
}

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler\Format;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class TsvFormat extends CsvFormat
{

View File

@ -13,7 +13,7 @@ use Luracast\Restler\RestException;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class UploadFormat extends Format
{

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler\Format;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class UrlEncodedFormat extends Format
{

View File

@ -16,7 +16,7 @@ use XMLWriter;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class XmlFormat extends Format
{
@ -110,14 +110,16 @@ class XmlFormat extends Format
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();
}
@ -151,30 +153,15 @@ class XmlFormat extends Format
&& !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();
}
$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';
@ -217,6 +204,7 @@ class XmlFormat extends Format
return array();
}
libxml_use_internal_errors(true);
libxml_disable_entity_loader(true);
$xml = simplexml_load_string($data,
"SimpleXMLElement", LIBXML_NOBLANKS | LIBXML_NOCDATA | LIBXML_COMPACT);
if (false === $xml) {
@ -241,8 +229,10 @@ class XmlFormat extends Format
}
}
$data = $this->read($xml);
if (count($data) == 1 && isset($data[static::$textNodeName]))
if (count($data) == 1 && isset($data[static::$textNodeName])) {
$data = $data[static::$textNodeName];
}
return $data;
} catch (\RuntimeException $e) {
throw new RestException(400,
@ -268,10 +258,13 @@ class XmlFormat extends Format
}
$children = $xml->children();
foreach ($children as $key => $value) {
if (isset($r[$key])) {
if ($key == static::$defaultTagName) {
$r[] = $this->read($value);
} elseif (isset($r[$key])) {
if (is_array($r[$key])) {
if ($r[$key] != array_values($r[$key]))
if ($r[$key] != array_values($r[$key])) {
$r[$key] = array($r[$key]);
}
} else {
$r[$key] = array($r[$key]);
}
@ -282,8 +275,9 @@ class XmlFormat extends Format
}
if (static::$parseNamespaces) {
if (is_null($namespaces))
if (is_null($namespaces)) {
$namespaces = $xml->getDocNamespaces(true);
}
foreach ($namespaces as $prefix => $ns) {
static::$namespaces[$prefix] = $ns;
if (static::$parseAttributes) {
@ -303,12 +297,14 @@ class XmlFormat extends Format
}
$children = $xml->children($ns);
foreach ($children as $key => $value) {
if (static::$importSettingsFromXml)
if (static::$importSettingsFromXml) {
static::$namespacedProperties[$key] = $prefix;
}
if (isset($r[$key])) {
if (is_array($r[$key])) {
if ($r[$key] != array_values($r[$key]))
if ($r[$key] != array_values($r[$key])) {
$r[$key] = array($r[$key]);
}
} else {
$r[$key] = array($r[$key]);
}
@ -321,7 +317,9 @@ class XmlFormat extends Format
}
if (empty($text) && $text !== '0') {
if (empty($r)) return null;
if (empty($r)) {
return null;
}
} else {
empty($r)
? $r = static::setType($text)
@ -331,18 +329,25 @@ class XmlFormat extends Format
: $r[] = static::setType($text)
);
}
return $r;
}
public static function setType($value)
{
if (empty($value) && $value !== '0')
if (empty($value) && $value !== '0') {
return null;
if ($value == 'true')
}
if ($value == 'true') {
return true;
if ($value == 'false')
}
if ($value == 'false') {
return true;
}
if (is_numeric($value)) {
return 0 + $value;
}
return $value;
}
}

View File

@ -14,22 +14,23 @@ use Luracast\Restler\Data\Object;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class YamlFormat extends Format
class YamlFormat extends DependentFormat
{
const MIME = 'text/plain';
const EXTENSION = 'yaml';
const PACKAGE_NAME = 'symfony/yaml:*';
const EXTERNAL_CLASS = 'Symfony\Component\Yaml\Yaml';
public function encode($data, $humanReadable = false)
{
// require_once 'sfyaml.php';
return @Yaml::dump(Object::toArray($data));
return @Yaml::dump(Object::toArray($data), $humanReadable ? 10 : 4);
}
public function decode($data)
{
// require_once 'sfyaml.php';
return Yaml::parse($data);
}
}

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler\Format;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
interface iDecodeStream
{

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler\Format;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
interface iFormat
{

View File

@ -10,7 +10,7 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class HumanReadableCache implements iCache
{

View File

@ -0,0 +1,22 @@
<?php
namespace Luracast\Restler;
/**
* Special RestException for forcing the exception even when
* in hybrid method
*
* @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/
* @version 3.0.0rc6
*/
class InvalidAuthCredentials extends RestException
{
}

View File

@ -0,0 +1,140 @@
<?php
namespace Luracast\Restler;
/**
* Class MemcacheCache provides a memcache based cache for Restler
*
* @category Framework
* @package Restler
* @author Dave Drager <ddrager@gmail.com>
* @copyright 2014 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
*/
class MemcacheCache 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;
/**
* @var string the memcache server hostname / IP address. For the memcache
* cache method.
*/
static public $memcacheServer = '127.0.0.1';
/**
* @var int the memcache server port. For the memcache cache method.
*/
static public $memcachePort = 11211;
private $memcache;
/**
* @param string $namespace
*/
function __construct($namespace = 'restler')
{
self::$namespace = $namespace;
if (function_exists('memcache_connect')) {
$this->memcache = new \Memcache;
$this->memcache->connect(self::$memcacheServer, self::$memcachePort);
} else {
$this->memcacheNotAvailable('Memcache is not available for use as Restler Cache. Please make sure the the memcache php extension is installed.');
}
}
/**
* store data in the cache
*
*
* @param string $name
* @param mixed $data
*
* @return boolean true if successful
*/
public function set($name, $data)
{
function_exists('memcache_set') || $this->memcacheNotAvailable();
try {
return $this->memcache->set(self::$namespace . "-" . $name, $data);
} catch
(\Exception $exception) {
return false;
}
}
private function memcacheNotAvailable($message = 'Memcache is not available.')
{
throw new \Exception($message);
}
/**
* retrieve data from the cache
*
*
* @param string $name
* @param bool $ignoreErrors
*
* @throws \Exception
* @return mixed
*/
public function get($name, $ignoreErrors = false)
{
function_exists('memcache_get') || $this->memcacheNotAvailable();
try {
return $this->memcache->get(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('memcache_delete') || $this->memcacheNotAvailable();
try {
$this->memcache->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('memcache_get') || $this->memcacheNotAvailable();
$data = $this->memcache->get(self::$namespace . "-" . $name);
return !empty($data);
}
}

View File

@ -0,0 +1,91 @@
<?php
namespace Luracast\Restler;
/**
* Static Class to pass through content outside of web root
*
* @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.0rc6
*/
class PassThrough
{
public static $mimeTypes = array(
'js' => 'text/javascript',
'css' => 'text/css',
'png' => 'image/png',
'jpg' => 'image/jpeg',
'gif' => 'image/gif',
'html' => 'text/html',
);
/**
* Serve a file outside web root
*
* Respond with a file stored outside web accessible path
*
* @param string $filename full path for the file to be served
* @param bool $forceDownload should the we download instead of viewing
* @param int $expires cache expiry in number of seconds
* @param bool $isPublic cache control, is it public or private
*
* @throws RestException
* @internal param string $pragma
*
*/
public static function file($filename, $forceDownload = false, $expires = 0, $isPublic = true)
{
if (!is_file($filename))
throw new RestException(404);
if (!is_readable($filename))
throw new RestException(403);
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (!$mime = Util::nestedValue(static::$mimeTypes, $extension)) {
if (!function_exists('finfo_open')) {
throw new RestException(
500,
'Unable to find media type of ' .
basename($filename) .
' either enable fileinfo php extension or update ' .
'PassThrough::$mimeTypes to include mime type for ' . $extension .
' extension'
);
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $filename);
}
if (!is_array(Defaults::$headerCacheControl))
Defaults::$headerCacheControl = array(Defaults::$headerCacheControl);
$cacheControl = Defaults::$headerCacheControl[0];
if ($expires > 0) {
$cacheControl = $isPublic ? 'public' : 'private';
$cacheControl .= end(Defaults::$headerCacheControl);
$cacheControl = str_replace('{expires}', $expires, $cacheControl);
$expires = gmdate('D, d M Y H:i:s \G\M\T', time() + $expires);
}
header('Cache-Control: ' . $cacheControl);
header('Expires: ' . $expires);
$lastModified = filemtime($filename);
if (
isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $lastModified
) {
header("{$_SERVER['SERVER_PROTOCOL']} 304 Not Modified");
exit;
}
header('Last-Modified: ' . date('r', $lastModified));
header('X-Powered-By: Luracast Restler v' . Restler::VERSION);
header('Content-type: ' . $mime);
header("Content-Length: " . filesize($filename));
if ($forceDownload) {
header("Content-Transfer-Encoding: binary");
header('Content-Disposition: attachment; filename="' . $filename . '"');
}
readfile($filename);
exit;
}
}

View File

@ -12,7 +12,7 @@ use Luracast\Restler\Format\JsonFormat;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Redirect
{
@ -32,11 +32,13 @@ class Redirect
/** @var $r Restler */
$r = Scope::get('Restler');
$base = $r->getBaseUrl() . '/';
if (0 !== strpos($url, 'http'))
if (0 !== strpos($url, 'http')) {
$url = $base . $url;
}
if (!empty($flashData) || $base . $r->url !== $url || Util::getRequestMethod() != 'GET') {
if ($r->responseFormat instanceof JsonFormat)
if ($r->responseFormat instanceof JsonFormat) {
return array('redirect' => $url);
}
if (!empty($params)) {
$url .= '?' . http_build_query($params);
}
@ -48,6 +50,19 @@ class Redirect
header("Location: $url");
die('');
}
return array();
}
/**
* Redirect back to the previous page
*
* Makes use of http referrer for redirection
*
* @return array
*/
public static function back()
{
return static::to($_SERVER['HTTP_REFERER']);
}
}

View File

@ -1,7 +1,7 @@
<?php
namespace Luracast\Restler;
use Luracast\Restler\Data\String;
use Luracast\Restler\Data\Text;
use Luracast\Restler\Scope;
use stdClass;
@ -15,7 +15,7 @@ use stdClass;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Resources implements iUseAuthentication, iProvideMultiVersionApi
{
@ -226,14 +226,14 @@ class Resources implements iUseAuthentication, iProvideMultiVersionApi
continue;
}
$fullPath = $route['url'];
if ($fullPath !== $target && !String::beginsWith($fullPath, $target)) {
if ($fullPath !== $target && !Text::beginsWith($fullPath, $target)) {
continue;
}
$fLen = strlen($fullPath);
if ($tSlash) {
if ($fLen != $tLen && !String::beginsWith($fullPath, $target . '/'))
if ($fLen != $tLen && !Text::beginsWith($fullPath, $target . '/'))
continue;
} elseif ($fLen > $tLen + 1 && $fullPath{$tLen + 1} != '{' && !String::beginsWith($fullPath, '{')) {
} elseif ($fLen > $tLen + 1 && $fullPath{$tLen + 1} != '{' && !Text::beginsWith($fullPath, '{')) {
//when mapped to root exclude paths that have static parts
//they are listed else where under that static part name
continue;
@ -246,7 +246,7 @@ class Resources implements iUseAuthentication, iProvideMultiVersionApi
if (empty($exclude)) {
if ($fullPath == $exclude)
continue 2;
} elseif (String::beginsWith($fullPath, $exclude)) {
} elseif (Text::beginsWith($fullPath, $exclude)) {
continue 2;
}
}
@ -317,7 +317,7 @@ class Resources implements iUseAuthentication, iProvideMultiVersionApi
if (isset($m['throws'])) {
foreach ($m['throws'] as $exception) {
$operation->errorResponses[] = array(
'reason' => $exception['reason'],
'reason' => $exception['message'],
'code' => $exception['code']);
}
}
@ -370,7 +370,7 @@ class Resources implements iUseAuthentication, iProvideMultiVersionApi
if (static::$groupOperations) {
foreach ($r->apis as $a) {
if ($a->path == "/$fullPath") {
if ($a->path == "$prefix/$fullPath") {
$api = $a;
break;
}
@ -774,14 +774,14 @@ class Resources implements iUseAuthentication, iProvideMultiVersionApi
$this->_model($itemType);
$itemType = $this->_noNamespace($itemType);
}
$properties[$key]['item'] = array(
$properties[$key]['items'] = array(
'type' => $itemType,
/*'description' => '' */ //TODO: add description
);
} else if (preg_match('/^Array\[(.+)\]$/', $type, $matches)) {
$itemType = $matches[1];
$properties[$key]['type'] = 'Array';
$properties[$key]['item']['type'] = $itemType;
$properties[$key]['items']['type'] = $this->_noNamespace($itemType);
if (class_exists($itemType)) {
$this->_model($itemType);
@ -891,7 +891,7 @@ class Resources implements iUseAuthentication, iProvideMultiVersionApi
}
$this->_mapResources($allRoutes, $map, $version);
foreach ($map as $path => $description) {
if (!String::contains($path, '{')) {
if (!Text::contains($path, '{')) {
//add id
$r->apis[] = array(
'path' => $path . $this->formatString,
@ -934,7 +934,7 @@ class Resources implements iUseAuthentication, iProvideMultiVersionApi
foreach ($allRoutes as $fullPath => $routes) {
$path = explode('/', $fullPath);
$resource = isset($path[0]) ? $path[0] : '';
if ($resource == 'resources' || String::endsWith($resource, 'index'))
if ($resource == 'resources' || Text::endsWith($resource, 'index'))
continue;
foreach ($routes as $httpMethod => $route) {
if (in_array($httpMethod, static::$excludedHttpMethods)) {
@ -948,7 +948,7 @@ class Resources implements iUseAuthentication, iProvideMultiVersionApi
if (empty($exclude)) {
if ($fullPath == $exclude)
continue 2;
} elseif (String::beginsWith($fullPath, $exclude)) {
} elseif (Text::beginsWith($fullPath, $exclude)) {
continue 2;
}
}

View File

@ -14,6 +14,7 @@ use Exception;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc6
*/
class RestException extends Exception

View File

@ -21,11 +21,37 @@ use Luracast\Restler\Format\UrlEncodedFormat;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*
* @method static void onGet() onGet(Callable $function) fired before reading the request details
* @method static void onRoute() onRoute(Callable $function) fired before finding the api method
* @method static void onNegotiate() onNegotiate(Callable $function) fired before content negotiation
* @method static void onPreAuthFilter() onPreAuthFilter(Callable $function) fired before pre auth filtering
* @method static void onAuthenticate() onAuthenticate(Callable $function) fired before auth
* @method static void onPostAuthFilter() onPostAuthFilter(Callable $function) fired before post auth filtering
* @method static void onValidate() onValidate(Callable $function) fired before validation
* @method static void onCall() onCall(Callable $function) fired before api method call
* @method static void onCompose() onCompose(Callable $function) fired before composing response
* @method static void onRespond() onRespond(Callable $function) fired before sending response
* @method static void onComplete() onComplete(Callable $function) fired after sending response
* @method static void onMessage() onMessage(Callable $function) fired before composing error response
*
* @method void onGet() onGet(Callable $function) fired before reading the request details
* @method void onRoute() onRoute(Callable $function) fired before finding the api method
* @method void onNegotiate() onNegotiate(Callable $function) fired before content negotiation
* @method void onPreAuthFilter() onPreAuthFilter(Callable $function) fired before pre auth filtering
* @method void onAuthenticate() onAuthenticate(Callable $function) fired before auth
* @method void onPostAuthFilter() onPostAuthFilter(Callable $function) fired before post auth filtering
* @method void onValidate() onValidate(Callable $function) fired before validation
* @method void onCall() onCall(Callable $function) fired before api method call
* @method void onCompose() onCompose(Callable $function) fired before composing response
* @method void onRespond() onRespond(Callable $function) fired before sending response
* @method void onComplete() onComplete(Callable $function) fired after sending response
* @method void onMessage() onMessage(Callable $function) fired before composing error response
*/
class Restler extends EventDispatcher
{
const VERSION = '3.0.0rc5';
const VERSION = '3.0.0rc6';
// ==================================================================
//
@ -78,9 +104,9 @@ class Restler extends EventDispatcher
/**
* Http status code
*
* @var int
* @var int|null when specified it will override @status comment
*/
public $responseCode=200;
public $responseCode=null;
/**
* @var string base url of the api service
*/
@ -278,11 +304,20 @@ class Restler extends EventDispatcher
$this->call();
$this->compose();
$this->postCall();
if (Defaults::$returnResponse) {
return $this->respond();
}
$this->respond();
} catch (Exception $e) {
try{
if (Defaults::$returnResponse) {
return $this->message($e);
}
$this->message($e);
} catch (Exception $e2) {
if (Defaults::$returnResponse) {
return $this->message($e2);
}
$this->message($e2);
}
}
@ -426,6 +461,31 @@ class Restler extends EventDispatcher
$this->formatOverridesMap['extensions'] = array_keys($extensions);
}
/**
* Set one or more string to be considered as the base url
*
* When more than one base url is provided, restler will make
* use of $_SERVER['HTTP_HOST'] to find the right one
*
* @param string ,... $url
*/
public function setBaseUrls($url /*[, $url2...$urlN]*/)
{
if (func_num_args() > 1) {
$urls = func_get_args();
usort($urls, function ($a, $b) {
return strlen($a) - strlen($b);
});
foreach ($urls as $u) {
if (0 === strpos($_SERVER['HTTP_HOST'], parse_url($u, PHP_URL_HOST))) {
$this->baseUrl = $u;
return;
}
}
}
$this->baseUrl = $url;
}
/**
* Parses the request url and get the api path
*
@ -436,35 +496,43 @@ class Restler extends EventDispatcher
// fix SCRIPT_NAME for PHP 5.4 built-in web server
if (false === strpos($_SERVER['SCRIPT_NAME'], '.php'))
$_SERVER['SCRIPT_NAME']
= '/' . Util::removeCommonPath($_SERVER['SCRIPT_FILENAME'], $_SERVER['DOCUMENT_ROOT']);
= '/' . substr($_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT']) + 1);
$fullPath = urldecode($_SERVER['REQUEST_URI']);
$path = Util::removeCommonPath(
$fullPath,
list($base, $path) = Util::splitCommonPath(
strtok(urldecode($_SERVER['REQUEST_URI']), '?'), //remove query string
$_SERVER['SCRIPT_NAME']
);
$port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : '80';
$https = $port == '443' ||
(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') || // Amazon ELB
(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on');
$baseUrl = ($https ? 'https://' : 'http://') . $_SERVER['SERVER_NAME'];
if (!$this->baseUrl) {
// Fix port number retrieval if port is specified in HOST header.
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
$portPos = strpos($host,":");
if ($portPos){
$port = substr($host,$portPos+1);
} else {
$port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : '80';
$port = isset($_SERVER['HTTP_X_FORWARDED_PORT']) ? $_SERVER['HTTP_X_FORWARDED_PORT'] : $port; // Amazon ELB
}
$https = $port == '443' ||
(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') || // Amazon ELB
(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on');
$baseUrl = ($https ? 'https://' : 'http://') . $_SERVER['SERVER_NAME'];
if (!$https && $port != '80' || $https && $port != '443')
$baseUrl .= ':' . $port;
$this->baseUrl = $baseUrl . $base;
} elseif (!empty($base) && false === strpos($this->baseUrl, $base)) {
$this->baseUrl .= $base;
}
if (!$https && $port != '80' || $https && $port != '443')
$baseUrl .= ':' . $port;
$this->baseUrl = rtrim($baseUrl
. substr($fullPath, 0, strlen($fullPath) - strlen($path)), '/');
$path = rtrim(strtok($path, '?'), '/'); //remove query string and trailing slash if found any
$path = str_replace(
array_merge(
$this->formatMap['extensions'],
$this->formatOverridesMap['extensions']
),
'',
$path
rtrim($path, '/') //remove trailing slash if found
);
if (Defaults::$useUrlBasedVersioning && strlen($path) && $path{0} == 'v') {
$version = intval(substr($path, 1));
if ($version && $version <= $this->apiVersion) {
@ -865,23 +933,21 @@ class Restler extends EventDispatcher
protected function authenticate()
{
$o = & $this->apiMethodInfo;
$accessLevel = max(Defaults::$apiAccessLevel,
$o->accessLevel);
try {
if ($accessLevel || count($this->postAuthFilterClasses)) {
$this->dispatch('authenticate');
if (!count($this->authClasses)) {
throw new RestException(
403,
'at least one Authentication Class is required'
);
}
foreach ($this->authClasses as $authClass) {
$o = &$this->apiMethodInfo;
$accessLevel = max(Defaults::$apiAccessLevel, $o->accessLevel);
if ($accessLevel || count($this->postAuthFilterClasses)) {
$this->dispatch('authenticate');
if (!count($this->authClasses) && $accessLevel > 1) {
throw new RestException(
403,
'at least one Authentication Class is required'
);
}
$unauthorized = false;
foreach ($this->authClasses as $authClass) {
try {
$authObj = Scope::get($authClass);
if (!method_exists($authObj,
Defaults::$authenticationMethod)
) {
if (!method_exists($authObj, Defaults::$authenticationMethod)) {
throw new RestException (
500, 'Authentication Class ' .
'should implement iAuthenticate');
@ -890,16 +956,26 @@ class Restler extends EventDispatcher
) {
throw new RestException(401);
}
$unauthorized = false;
break;
} catch (InvalidAuthCredentials $e) {
$this->authenticated = false;
throw $e;
} catch (RestException $e) {
if (!$unauthorized) {
$unauthorized = $e;
}
}
$this->authenticated = true;
}
$this->authVerified = true;
} catch (RestException $e) {
$this->authVerified = true;
if ($accessLevel > 1) { //when it is not a hybrid api
throw ($e);
if ($unauthorized) {
if ($accessLevel > 1) { //when it is not a hybrid api
throw $unauthorized;
} else {
$this->authenticated = false;
}
} else {
$this->authenticated = false;
$this->authenticated = true;
}
}
}
@ -936,6 +1012,8 @@ class Restler extends EventDispatcher
}
//convert to instance of ValidationInfo
$info = new ValidationInfo($param);
//initialize validator
Scope::get(Defaults::$validatorClass);
$validator = Defaults::$validatorClass;
//if(!is_subclass_of($validator, 'Luracast\\Restler\\Data\\iValidate')) {
//changed the above test to below for addressing this php bug
@ -964,6 +1042,8 @@ class Restler extends EventDispatcher
$o = & $this->apiMethodInfo;
$accessLevel = max(Defaults::$apiAccessLevel,
$o->accessLevel);
if (function_exists('newrelic_name_transaction'))
newrelic_name_transaction("{$o->className}/{$o->methodName}");
$object = Scope::get($o->className);
switch ($accessLevel) {
case 3 : //protected method
@ -1057,6 +1137,8 @@ class Restler extends EventDispatcher
if (!Defaults::$suppressResponseCode) {
if ($e) {
$code = $e->getCode();
} elseif ($this->responseCode) {
$code = $this->responseCode;
} elseif (isset($this->apiMethodInfo->metadata['status'])) {
$code = $this->apiMethodInfo->metadata['status'];
}
@ -1084,9 +1166,13 @@ class Restler extends EventDispatcher
: 'Unknown';
@header('WWW-Authenticate: ' . $authString, false);
}
echo $this->responseData;
$this->dispatch('complete');
exit;
if (Defaults::$returnResponse) {
return $this->responseData;
} else {
echo $this->responseData;
exit;
}
}
protected function message(Exception $exception)
@ -1128,6 +1214,9 @@ class Restler extends EventDispatcher
$compose->message($exception),
!$this->productionMode
);
if (Defaults::$returnResponse) {
return $this->respond();
}
$this->respond();
}

View File

@ -2,7 +2,7 @@
namespace Luracast\Restler;
use Luracast\Restler\Data\ApiMethodInfo;
use Luracast\Restler\Data\String;
use Luracast\Restler\Data\Text;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
@ -17,13 +17,32 @@ use Exception;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Routes
{
public static $prefixingParameterNames = array(
'id'
);
public static $fieldTypesByName = array(
'email' => 'email',
'password' => 'password',
'phone' => 'tel',
'mobile' => 'tel',
'tel' => 'tel',
'search' => 'search',
'date' => 'date',
'created_at' => 'datetime',
'modified_at' => 'datetime',
'url' => 'url',
'link' => 'url',
'href' => 'url',
'website' => 'url',
'color' => 'color',
'colour' => 'color',
);
protected static $routes = array();
protected static $models = array();
@ -56,6 +75,7 @@ class Routes
* - Do not include it in URL
*/
$class = new ReflectionClass($className);
$dataName = CommentParser::$embeddedDataName;
try {
$classMetadata = CommentParser::parse($class->getDocComment());
} catch (Exception $e) {
@ -92,7 +112,7 @@ class Routes
= (isset($metadata['smart-auto-routing'])
&& $metadata['smart-auto-routing'] != 'true')
|| !Defaults::$smartAutoRouting;
$metadata['resourcePath'] = $resourcePath;
$metadata['resourcePath'] = trim($resourcePath, '/');
if (isset($classMetadata['description'])) {
$metadata['classDescription'] = $classMetadata['description'];
}
@ -123,44 +143,51 @@ class Routes
}
$m = & $metadata ['param'] [$position];
$m ['name'] = $param->getName();
if (!isset($m[$dataName])) {
$m[$dataName] = array();
}
$p = &$m[$dataName];
if (empty($m['label']))
$m['label'] = static::label($m['name']);
$m['label'] = Text::title($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';
if (isset(static::$fieldTypesByName[$m['name']]) && empty($p['type']) && $type == 'string') {
$p['type'] = static::$fieldTypesByName[$m['name']];
}
$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
$contentType = Util::nestedValue($p,'type');
if ($type == 'array' && $contentType && $qualified = Scope::resolve($contentType, $scope)) {
list($p['type'], $children, $modelName) = static::getTypeAndModel(
new ReflectionClass($qualified), $scope,
$className . Text::title($methodUrl), $p
);
}
if ($type instanceof ReflectionClass) {
list($type, $children) = static::getTypeAndModel($type, $scope);
list($type, $children, $modelName) = static::getTypeAndModel($type, $scope,
$className . Text::title($methodUrl), $p);
} elseif ($type && is_string($type) && $qualified = Scope::resolve($type, $scope)) {
list($type, $children)
= static::getTypeAndModel(new ReflectionClass($qualified), $scope);
list($type, $children, $modelName)
= static::getTypeAndModel(new ReflectionClass($qualified), $scope,
$className . Text::title($methodUrl), $p);
}
if (isset($type)) {
$m['type'] = $type;
}
$m['children'] = $children;
$m['children'] = $children;
if (isset($modelName)) {
$m['model'] = $modelName;
}
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'];
} elseif (isset($p['from'])) {
$from = $p['from'];
} else {
if ((isset($type) && Util::isObjectOrArray($type))
) {
@ -174,7 +201,7 @@ class Routes
$from = 'body';
}
}
$m[CommentParser::$embeddedDataName]['from'] = $from;
$p['from'] = $from;
if (!isset($m['type'])) {
$type = $m['type'] = static::type($defaults[$position]);
}
@ -229,21 +256,21 @@ class Routes
strpos($url, '{' . $p['name'] . '}') ||
strpos($url, ':' . $p['name']);
if ($inPath) {
$copy['metadata']['param'][$i][CommentParser::$embeddedDataName]['from'] = 'path';
$copy['metadata']['param'][$i][$dataName]['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';
$copy['metadata']['param'][$i][$dataName]['from'] = 'query';
} elseif (empty($p[$dataName]['from']) || $p[$dataName]['from'] == 'path') {
$copy['metadata']['param'][$i][$dataName]['from'] = 'body';
}
}
$url = preg_replace_callback('/{[^}]+}|:[^\/]+/',
function ($matches) use ($call) {
function ($matches) use ($copy) {
$match = trim($matches[0], '{}:');
$index = $call['arguments'][$match];
$index = $copy['arguments'][$match];
return '{' .
Routes::typeChar(isset(
$call['metadata']['param'][$index]['type'])
? $call['metadata']['param'][$index]['type']
$copy['metadata']['param'][$index]['type'])
? $copy['metadata']['param'][$index]['type']
: null)
. $index . '}';
}, $url);
@ -269,11 +296,11 @@ class Routes
$lastPathParam = array_keys($pathParams);
$lastPathParam = end($lastPathParam);
for ($position = 0; $position < count($params); $position++) {
$from = $metadata['param'][$position][CommentParser::$embeddedDataName]['from'];
$from = $metadata['param'][$position][$dataName]['from'];
if ($from == 'body' && ($httpMethod == 'GET' ||
$httpMethod == 'DELETE')
) {
$call['metadata']['param'][$position][CommentParser::$embeddedDataName]['from']
$call['metadata']['param'][$position][$dataName]['from']
= 'query';
}
}
@ -313,7 +340,7 @@ class Routes
}
protected static function addPath($path, array $call,
$httpMethod = 'GET', $version = 1)
$httpMethod = 'GET', $version = 1)
{
$call['url'] = preg_replace_callback(
"/\{\S(\d+)\}/",
@ -347,7 +374,7 @@ class Routes
* @return ApiMethodInfo
*/
public static function find($path, $httpMethod,
$version = 1, array $data = array())
$version = 1, array $data = array())
{
$p = Util::nestedValue(static::$routes, "v$version");
if (!$p) {
@ -431,6 +458,62 @@ class Routes
throw new RestException($status, $message);
}
public static function findAll(array $excludedPaths = array(), array $excludedHttpMethods = array(), $version = 1)
{
$map = array();
$all = Util::nestedValue(self::$routes, "v$version");
$filter = array();
if (isset($all['*'])) {
$all = $all['*'] + $all;
unset($all['*']);
}
if(is_array($all)){
foreach ($all as $fullPath => $routes) {
foreach ($routes as $httpMethod => $route) {
if (in_array($httpMethod, $excludedHttpMethods)) {
continue;
}
foreach ($excludedPaths as $exclude) {
if (empty($exclude)) {
if ($fullPath == $exclude || $fullPath == 'index')
continue 2;
} elseif (Text::beginsWith($fullPath, $exclude)) {
continue 2;
}
}
$hash = "$httpMethod " . $route['url'];
if (!isset($filter[$hash])) {
$route['httpMethod'] = $httpMethod;
$map[$route['metadata']['resourcePath']][]
= array('access' => static::verifyAccess($route), 'route' => $route, 'hash' => $hash);
$filter[$hash] = true;
}
}
}
}
return $map;
}
public static function verifyAccess($route)
{
if ($route['accessLevel'] < 2)
return true;
/** @var Restler $r */
$r = Scope::get('Restler');
$authenticated = $r->_authenticated;
if (!$authenticated && $route['accessLevel'] > 1)
return false;
if (
$authenticated &&
Defaults::$accessControlFunction &&
(!call_user_func(Defaults::$accessControlFunction, $route['metadata']))
) {
return false;
}
return true;
}
/**
* Populates the parameter values
*
@ -445,14 +528,14 @@ class Routes
{
$call['parameters'] = $call['defaults'];
$p = & $call['parameters'];
$dataName = CommentParser::$embeddedDataName;
foreach ($data as $key => $value) {
if (isset($call['arguments'][$key])) {
$p[$call['arguments'][$key]] = $value;
}
}
if (Defaults::$smartParameterParsing && 'post' != (string)Util::$restler->requestFormat) {
if (Defaults::$smartParameterParsing) {
if (
count($p) == 1 &&
($m = Util::nestedValue($call, 'metadata', 'param', 0)) &&
!array_key_exists($m['name'], $data) &&
array_key_exists(Defaults::$fullRequestDataName, $data) &&
@ -466,7 +549,7 @@ class Routes
$lastBodyParamIndex = -1;
$lastM = null;
foreach ($call['metadata']['param'] as $k => $m) {
if ($m[CommentParser::$embeddedDataName]['from'] == 'body') {
if ($m[$dataName]['from'] == 'body') {
$bodyParamCount++;
$lastBodyParamIndex = $k;
$lastM = $m;
@ -526,6 +609,21 @@ class Routes
return true;
}
protected static function parseMagic(ReflectionClass $class, $forResponse = true)
{
if (!$c = CommentParser::parse($class->getDocComment())) {
return false;
}
$p = 'property';
$r = empty($c[$p]) ? array() : $c[$p];
$p .= '-' . ($forResponse ? 'read' : 'write');
if (!empty($c[$p])) {
$r = array_merge($r, $c[$p]);
}
return $r;
}
/**
* Get the type and associated model
*
@ -538,62 +636,105 @@ class Routes
*
* @access protected
*/
protected static function getTypeAndModel(ReflectionClass $class, array $scope)
protected static function getTypeAndModel(ReflectionClass $class, array $scope, $prefix='', array $rules=array())
{
$className = $class->getName();
if (isset(static::$models[$className])) {
return static::$models[$className];
$dataName = CommentParser::$embeddedDataName;
if (isset(static::$models[$prefix.$className])) {
return static::$models[$prefix.$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') ?: array();
} 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);
if ($magic_properties = static::parseMagic($class, empty($prefix))) {
foreach ($magic_properties as $prop) {
if (!isset($prop['name'])) {
throw new Exception('@property comment is not properly defined in ' . $className . ' class');
}
if (!isset($prop[$dataName]['label'])) {
$prop[$dataName]['label'] = Text::title($prop['name']);
}
if (isset(static::$fieldTypesByName[$prop['name']]) && $prop['type'] == 'string' && !isset($prop[$dataName]['type'])) {
$prop[$dataName]['type'] = static::$fieldTypesByName[$prop['name']];
}
$children[$prop['name']] = $prop;
}
} else {
$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') ?: array();
} 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' => isset(static::$fieldTypesByName[$child['name']])
? static::$fieldTypesByName[$child['name']]
: 'string',
'label' => Text::title($child['name'])
);
isset($child[$dataName])
? $child[$dataName] += array('required' => true)
: $child[$dataName]['required'] = true;
if ($prop->class != $className && $qualified = Scope::resolve($child['type'], $scope)) {
list($child['type'], $child['children'])
= static::getTypeAndModel(new ReflectionClass($qualified), $scope);
} elseif (
($contentType = Util::nestedValue($child, $dataName, 'type')) &&
($qualified = Scope::resolve($contentType, $scope))
) {
list($child['contentType'], $child['children'])
= static::getTypeAndModel(new ReflectionClass($qualified), $scope);
}
$children[$name] = $child;
}
$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')) {
if (Text::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];
if ($properties = Util::nestedValue($rules, 'properties')) {
if (is_string($properties)) {
$properties = array($properties);
}
$c = array();
foreach ($properties as $property) {
if (isset($children[$property])) {
$c[$property] = $children[$property];
}
}
$children = $c;
}
if ($required = Util::nestedValue($rules, 'required')) {
//override required on children
if (is_bool($required)) {
// true means all are required false means none are required
$required = $required ? array_keys($children) : array();
} elseif (is_string($required)) {
$required = array($required);
}
$required = array_fill_keys($required, true);
foreach ($children as $name => $child) {
$children[$name][$dataName]['required'] = isset($required[$name]);
}
}
static::$models[$prefix.$className] = array($className, $children, $prefix.$className);
return static::$models[$prefix.$className];
}
/**
@ -625,20 +766,6 @@ class Routes
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();
@ -693,4 +820,4 @@ class Routes
}
return $imports;
}
}
}

View File

@ -11,57 +11,70 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Scope
{
public static $classAliases = array(
//Core
'Restler' => 'Luracast\Restler\Restler',
'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',
'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',
'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',
'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',
'Resources' => 'Luracast\Restler\Resources',
'Explorer' => 'Luracast\Restler\Explorer',
//Cache classes
'HumanReadableCache' => 'Luracast\Restler\HumanReadableCache',
'ApcCache' => 'Luracast\Restler\ApcCache',
'ApcCache' => 'Luracast\Restler\ApcCache',
'MemcacheCache' => 'Luracast\Restler\MemcacheCache',
//Utility classes
'Object' => 'Luracast\Restler\Data\Object',
'String' => 'Luracast\Restler\Data\String',
'Arr' => 'Luracast\Restler\Data\Arr',
'Object' => 'Luracast\Restler\Data\Object',
'Text' => 'Luracast\Restler\Data\Text',
'Arr' => 'Luracast\Restler\Data\Arr',
//Exception
'RestException' => 'Luracast\Restler\RestException'
'RestException' => 'Luracast\Restler\RestException'
);
/**
* @var null|Callable adding a resolver function that accepts
* the class name as the parameter and returns an instance of the class
* as a singleton. Allows the use of your favourite DI container
*/
public static $resolver = null;
public static $properties = array();
protected static $instances = array();
protected static $registry = array();
public static function register($name, Callable $function, $singleton = true)
/**
* @param string $name
* @param callable $function
* @param bool $singleton
*/
public static function register($name, $function, $singleton = true)
{
static::$registry[$name] = (object)compact('function', 'singleton');
}
@ -82,8 +95,19 @@ class Scope
} elseif (!empty(static::$registry[$name])) {
$function = static::$registry[$name]->function;
$r = $function();
if (static::$registry[$name]->singleton)
if (static::$registry[$name]->singleton) {
static::$instances[$name] = (object)array('instance' => $r);
}
} elseif (is_callable(static::$resolver) && false === stristr($name, 'Luracast\Restler')) {
$fullName = $name;
if (isset(static::$classAliases[$name])) {
$fullName = static::$classAliases[$name];
}
/** @var Callable $function */
$function = static::$resolver;
$r = $function($fullName);
static::$instances[$name] = (object)array('instance' => $r);
static::$instances[$name]->initPending = true;
} else {
$fullName = $name;
if (isset(static::$classAliases[$name])) {
@ -100,10 +124,10 @@ class Scope
$properties = Util::nestedValue(
$m, 'class', $fullName,
CommentParser::$embeddedDataName
) ? : (Util::nestedValue(
) ?: (Util::nestedValue(
$m, 'class', $shortName,
CommentParser::$embeddedDataName
) ? : array());
) ?: array());
} else {
static::$instances[$name]->initPending = true;
}
@ -117,7 +141,7 @@ class Scope
) {
static::$instances[$name]->authVerified = true;
$r->__setAuthenticationStatus
(static::get('Restler')->_authenticated);
(static::get('Restler')->_authenticated);
}
if (isset(static::$instances[$name]->initPending)) {
$m = Util::nestedValue(static::get('Restler'), 'apiMethodInfo', 'metadata');
@ -126,17 +150,18 @@ class Scope
$shortName = Util::getShortName($name);
} else {
$shortName = $name;
if (isset(static::$classAliases[$name]))
if (isset(static::$classAliases[$name])) {
$fullName = static::$classAliases[$name];
}
}
if ($m) {
$properties = Util::nestedValue(
$m, 'class', $fullName,
CommentParser::$embeddedDataName
) ? : (Util::nestedValue(
) ?: (Util::nestedValue(
$m, 'class', $shortName,
CommentParser::$embeddedDataName
) ? : array());
) ?: array());
unset(static::$instances[$name]->initPending);
$initialized = false;
}

View File

@ -4,6 +4,12 @@ namespace Luracast\Restler\UI;
use Luracast\Restler\UI\Tags as T;
use Luracast\Restler\Util;
/**
* Class Emmet
* @package Luracast\Restler\UI
*
* @version 3.0.0rc6
*/
class Emmet
{
const DELIMITERS = '.#*>+^[=" ]{$@-#}';

View File

@ -10,32 +10,32 @@ namespace Luracast\Restler\UI;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class FormStyles
{
public static $html = array(
'form' => 'form[role=form id=$id# name=$name# method=$method# action=$action# enctype=$enctype#]',
'input' => '.row>section>label{$label#}^input[name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept#]',
'textarea' => '.row>label{$label#}^textarea[name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3]{$value#}',
'radio' => '.row>section>label{$label#}^span>label*options>input[name=$name# value=$value# type=radio checked=$selected# required=$required#]+{ $text#}',
'select' => '.row>label{$label#}^select[name=$name# required=$required#]>option[value]+option[value=$value# selected=$selected#]{$text#}*options',
'submit' => '.row>label{ &nbsp; }^button[type=submit]{$label#}',
'input' => '.row>section>label{$label#}^input[id=$id# name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept# disabled=$disabled#]',
'textarea' => '.row>label{$label#}^textarea[id=$id# name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3 disabled=$disabled#]{$value#}',
'radio' => '.row>section>label{$label#}^span>label*options>input[id=$id# name=$name# value=$value# type=radio checked=$selected# required=$required# disabled=$disabled#]+{ $text#}',
'select' => '.row>label{$label#}^select[id=$id# name=$name# required=$required#]>option[value]+option[value=$value# selected=$selected# disabled=$disabled#]{$text#}*options',
'submit' => '.row>label{ &nbsp; }^button[id=$id# type=submit disabled=$disabled#]{$label#}',
'fieldset' => 'fieldset>legend{$label#}',
'checkbox' => '.row>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{$label#}',
'checkbox' => '.row>label>input[id=$id# name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept# disabled=$disabled#]+{$label#}',
//------------- TYPE BASED STYLES ---------------------//
'checkbox-array' => 'fieldset>legend{$label#}+section*options>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{ $text#}',
'select-array' => 'label{$label#}+select[name=$name# required=$required# multiple style="height: auto;background-image: none; outline: inherit;"]>option[value=$value# selected=$selected#]{$text#}*options',
);
public static $bootstrap3 = array(
'form' => 'form[role=form id=$id# name=$name# method=$method# action=$action# enctype=$enctype#]',
'input' => '.form-group>label{$label#}+input.form-control[name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept#]',
'textarea' => '.form-group>label{$label#}+textarea.form-control[name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3]{$value#}',
'radio' => 'fieldset>legend{$label#}>.radio*options>label>input.radio[name=$name# value=$value# type=radio checked=$selected# required=$required#]{$text#}',
'select' => '.form-group>label{$label#}+select.form-control[name=$name# multiple=$multiple# required=$required#]>option[value]+option[value=$value# selected=$selected#]{$text#}*options',
'submit' => 'button.btn.btn-primary[type=submit]{$label#}',
'input' => '.form-group.$error#>label{$label#}+input.form-control[id=$id# name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept# disabled=$disabled#]+small.help-block>{$message#}',
'textarea' => '.form-group>label{$label#}+textarea.form-control[id=$id# name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3 disabled=$disabled#]{$value#}+small.help-block>{$message#}',
'radio' => 'fieldset>legend{$label#}>.radio*options>label>input.radio[name=$name# value=$value# type=radio checked=$selected# required=$required# disabled=$disabled#]{$text#}+p.help-block>{$message#}',
'select' => '.form-group>label{$label#}+select.form-control[id=$id# name=$name# multiple=$multiple# required=$required#]>option[value]+option[value=$value# selected=$selected# disabled=$disabled#]{$text#}*options',
'submit' => 'button.btn.btn-primary[id=$id# type=submit]{$label#} disabled=$disabled#',
'fieldset' => 'fieldset>legend{$label#}',
'checkbox' => '.checkbox>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{$label#}',
'checkbox' => '.checkbox>label>input[id=$id# name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# disabled=$disabled#]+{$label#}^p.help-block>{$error#}',
//------------- TYPE BASED STYLES ---------------------//
'checkbox-array' => 'fieldset>legend{$label#}>.checkbox*options>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required#]{$text#}',
'select-array' => '.form-group>label{$label#}+select.form-control[name=$name# multiple=$multiple# required=$required#] size=$options#>option[value=$value# selected=$selected#]{$text#}*options',
@ -44,15 +44,15 @@ class FormStyles
);
public static $foundation5 = array(
'form' => 'form[id=$id# name=$name# method=$method# action=$action# enctype=$enctype#]',
'input' => 'label{$label#}+input[name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept#]',
'textarea' => 'label{$label#}+textarea[name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3]{$value#}',
'radio' => 'label{$label# : &nbsp;}+label.radio-inline*options>input.radio[name=$name# value=$value# type=radio checked=$selected# required=$required#]+{$text#}',
'select' => 'label{$label#}+select[name=$name# required=$required#]>option[value]+option[value=$value# selected=$selected#]{$text#}*options',
'submit' => 'button.button[type=submit]{$label#}',
'input' => 'label{$label#}+input[id=$id# name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept# disabled=$disabled#]',
'textarea' => 'label{$label#}+textarea[id=$id# name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3 disabled=$disabled#]{$value#}',
'radio' => 'label{$label# : &nbsp;}+label.radio-inline*options>input.radio[name=$name# value=$value# type=radio checked=$selected# required=$required# disabled=$disabled#]+{$text#}',
'select' => 'label{$label#}+select[id=$id# name=$name# required=$required#]>option[value]+option[value=$value# selected=$selected# disabled=$disabled#]{$text#}*options',
'submit' => 'button.button[id=$id# type=submit disabled=$disabled#]{$label#}',
'fieldset' => 'fieldset>legend{$label#}',
'checkbox' => 'label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{ $label#}',
'checkbox' => 'label>input[id=$id# name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# disabled=$disabled#]+{ $label#}',
//------------- TYPE BASED STYLES ---------------------//
'checkbox-array' => 'fieldset>legend{$label#}+label*options>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{ $text#}',
'checkbox-array' => 'fieldset>legend{$label#}+label*options>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus#]+{ $text#}',
'select-array' => 'label{$label#}+select[name=$name# required=$required# multiple style="height: auto;background-image: none; outline: inherit;"]>option[value=$value# selected=$selected#]{$text#}*options',
//------------- CUSTOM STYLES ---------------------//
);

View File

@ -3,8 +3,9 @@ namespace Luracast\Restler\UI;
use Luracast\Restler\CommentParser;
use Luracast\Restler\Data\ApiMethodInfo;
use Luracast\Restler\Data\String;
use Luracast\Restler\Data\Text;
use Luracast\Restler\Data\ValidationInfo;
use Luracast\Restler\Data\Validator;
use Luracast\Restler\Defaults;
use Luracast\Restler\Format\UploadFormat;
use Luracast\Restler\Format\UrlEncodedFormat;
@ -28,7 +29,7 @@ use Luracast\Restler\Util;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Forms implements iFilter
{
@ -190,6 +191,9 @@ class Forms implements iFilter
static::$fileUpload = false;
$t['enctype'] = 'multipart/form-data';
}
if (isset($m[CommentParser::$embeddedDataName])) {
$t += $m[CommentParser::$embeddedDataName];
}
if (!$dataOnly) {
$t = Emmet::make(static::style('form', $m), $t);
$t->prefix = $prefix;
@ -292,14 +296,14 @@ class Forms implements iFilter
$options[] = $option;
}
} elseif ($p->type == 'boolean' || $p->type == 'bool') {
if (String::beginsWith($type, 'radio')) {
if (Text::beginsWith($type, 'radio') || Text::beginsWith($type, 'select')) {
$options[] = array('name' => $p->name, 'text' => ' Yes ',
'value' => 'true');
$options[] = array('name' => $p->name, 'text' => ' No ',
'value' => 'false');
if ($p->value || $p->default)
$options[0]['selected'] = true;
} else {
} else { //checkbox
$r = array(
'tag' => $tag,
'name' => $name,
@ -312,6 +316,9 @@ class Forms implements iFilter
if ($p->default) {
$r['selected'] = true;
}
if (isset($p->rules)) {
$r += $p->rules;
}
}
}
if (empty($r)) {
@ -325,16 +332,25 @@ class Forms implements iFilter
'options' => & $options,
'multiple' => $multiple,
);
if (isset($p->rules)) {
$r += $p->rules;
}
}
if ($type == 'file') {
static::$fileUpload = true;
$r['accept'] = implode(', ', UploadFormat::$allowedMimeTypes);
if (empty($r['accept'])) {
$r['accept'] = implode(', ', UploadFormat::$allowedMimeTypes);
}
}
if (!empty(Validator::$exceptions[$name]) && static::$info->url == Scope::get('Restler')->url) {
$r['error'] = 'has-error';
$r['message'] = Validator::$exceptions[$p->name]->getMessage();
}
if (true === $p->required)
$r['required'] = true;
$r['required'] = 'required';
if (isset($p->rules['autofocus']))
$r['autofocus'] = true;
$r['autofocus'] = 'autofocus';
/*
echo "<pre>";
print_r($r);
@ -411,7 +427,7 @@ class Forms implements iFilter
if (empty($exclude)) {
if ($url == $exclude)
return true;
} elseif (String::beginsWith($url, $exclude)) {
} elseif (Text::beginsWith($url, $exclude)) {
return true;
}
}

View File

@ -2,7 +2,7 @@
namespace Luracast\Restler\UI;
use Luracast\Restler\CommentParser;
use Luracast\Restler\Defaults;
use Luracast\Restler\Data\Text;
use Luracast\Restler\Restler;
use Luracast\Restler\Routes;
use Luracast\Restler\Scope;
@ -19,18 +19,13 @@ use Luracast\Restler\Util;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Nav
{
protected static $tree = array();
public static $root = 'home';
/**
* @var null|callable if the api methods are under access control mechanism
* you can attach a function here that returns true or false to determine
* visibility of a protected api method. this function will receive method
* info as the only parameter.
*/
public static $accessControlFunction = null;
/**
* @var array all paths beginning with any of the following will be excluded
* from documentation. if an empty string is given it will exclude the root
@ -40,169 +35,188 @@ class Nav
* @var array prefix additional menu items with one of the following syntax
* [$path => $text]
* [$path]
* [$path => ['text' => $text, 'url' => $url]]
* [$path => ['text' => $text, 'url' => $url, 'trail'=> $trail]]
*/
public static $prepends = array();
/**
* @var array suffix additional menu items with one of the following syntax
* [$path => $text]
* [$path]
* [$path => ['text' => $text, 'url' => $url]]
* [$path => ['text' => $text, 'url' => $url, 'trail'=> $trail]]
*/
public static $appends = array();
public static $addExtension = true;
protected static $extension = '';
protected static $activeTrail = '';
protected static $url;
public static function get($for = '', $activeUrl = null)
public static function get($for = '', $activeTrail = null)
{
if (!static::$accessControlFunction && Defaults::$accessControlFunction)
static::$accessControlFunction = Defaults::$accessControlFunction;
/** @var Restler $restler */
$restler = Scope::get('Restler');
if (static::$addExtension)
static::$extension = '.' . $restler->responseFormat->getExtension();
if (is_null($activeUrl))
$activeUrl = $restler->url;
$tree = array();
foreach (static::$prepends as $path => $text) {
$url = null;
if (is_array($text)) {
if (isset($text['url'])) {
$url = $text['url'];
$text = $text['text'];
} else {
$url = current(array_keys($text));
$text = current($text);
}
}
if (is_numeric($path)) {
$path = $text;
$text = null;
}
if (empty($for) || 0 === strpos($path, "$for/"))
static::build($tree, $path, $url, $text, $activeUrl);
}
$routes = Routes::toArray();
$routes = $routes['v' . $restler->getRequestedApiVersion()];
foreach ($routes as $value) {
foreach ($value as $httpMethod => $route) {
if ($httpMethod != 'GET') {
continue;
}
$path = $route['url'];
if (false !== strpos($path, '{'))
continue;
if ($route['accessLevel'] > 1 && !Util::$restler->_authenticated)
continue;
foreach (static::$excludedPaths as $exclude) {
if (empty($exclude)) {
if (empty($path))
continue 2;
} elseif (0 === strpos($path, $exclude)) {
continue 2;
if (empty(static::$tree)) {
/** @var Restler $restler */
$restler = Scope::get('Restler');
if (static::$addExtension)
static::$extension = isset($restler->responseFormat)
? '.' . $restler->responseFormat->getExtension()
: '.html';
static::$url = $restler->getBaseUrl();
if (empty(static::$url))
static::$url = '';
static::$activeTrail = $activeTrail = empty($activeTrail)
? (empty($restler->url) || $restler->url == 'index'
? static::$root
: $restler->url
)
: $activeTrail;
if (static::$addExtension)
static::$extension = isset($restler->responseFormat)
? '.' . $restler->responseFormat->getExtension()
: '.html';
static::addUrls(static::$prepends);
$map = Routes::findAll(
static::$excludedPaths,
array('POST', 'DELETE', 'PUT', 'PATCH'),
$restler->getRequestedApiVersion()
);
foreach ($map as $path => $data) {
foreach ($data as $item) {
$access = $item['access'];
$route = $item['route'];
$url = $route['url'];
if ($access && !Text::contains($url, '{')) {
$label = Util::nestedValue(
$route,
'metadata',
CommentParser::$embeddedDataName,
'label'
);
if (!empty($url)) {
$url .= static::$extension;
}
static::add($url, $label);
}
}
if ($restler->_authenticated
&& static::$accessControlFunction
&& (!call_user_func(
static::$accessControlFunction, $route['metadata']))
) {
continue;
}
$text = Util::nestedValue(
$route,
'metadata',
CommentParser::$embeddedDataName,
'label'
);
if (empty($for) || 0 === strpos($path, "$for/"))
static::build($tree, $path, null, $text, $activeUrl);
}
static::addUrls(static::$appends);
} elseif (empty($activeTrail)) {
$activeTrail = static::$activeTrail;
}
foreach (static::$appends as $path => $text) {
$url = null;
if (is_array($text)) {
if (isset($text['url'])) {
$url = $text['url'];
$text = $text['text'];
} else {
$url = current(array_keys($text));
$text = current($text);
}
}
if (is_numeric($path)) {
$path = $text;
$text = null;
}
if (empty($for) || 0 === strpos($path, "$for/"))
static::build($tree, $path, $url, $text, $activeUrl);
$tree = static::$tree;
$activeTrail = explode('/', $activeTrail);
$nested = & static::nested($tree, $activeTrail);
if (is_array($nested)) {
$nested['active'] = true;
}
if (!empty($for)) {
$for = explode('/', $for);
$p = & $tree;
foreach ($for as $f) {
if (isset($p[$f]['children'])) {
$p = & $p[$f]['children'];
} else {
return array();
}
}
return $p;
$tree = static::nested($tree, $for)['children'];
}
return $tree;
return array_filter($tree);
}
protected static function build(&$tree, $path,
$url = null, $text = null, $activeUrl = null)
protected static function & nested(array & $tree, array $parts)
{
$parts = explode('/', $path);
if (!empty($parts)) {
$part = array_shift($parts);
if (empty($tree[$part])) {
return $tree[$part];
} elseif (empty($parts)) {
return static::nested($tree[$part], $parts);
} elseif (!empty($tree[$part]['children'])) {
return static::nested($tree[$part]['children'], $parts);
}
} else {
return $tree;
}
return null;
}
public static function addUrls(array $urls)
{
foreach ($urls as $url => $label) {
$trail = null;
if (is_array($label)) {
if (isset($label['trail'])) {
$trail = $label['trail'];
}
if (isset($label['url'])) {
$url = $label['url'];
$label = isset($label['label']) ? $label['label'] : null;
} else {
$url = current(array_keys($label));
$label = current($label);
}
}
if (is_numeric($url)) {
$url = $label;
$label = null;
}
static::add($url, $label, $trail);
}
return static::$tree;
}
public static function add($url, $label = null, $trail = null)
{
$r = parse_url($url);
if (is_null($trail)) {
$trail = isset($r['path']) ? $r['path'] : static::$root;
}
//remove / prefix and / suffixes and any extension
$trail = strtok(trim($trail, '/'), '.');
$parts = explode('/', $trail);
if (count($parts) == 1 && empty($parts[0]))
$parts = array(static::$root);
$p = & $tree;
$end = end($parts);
foreach ($parts as $part) {
if (!isset($p[$part])) {
$p[$part] = array(
'href' => '#',
'text' => static::title($part)
);
if ($part == $end) {
$p[$part]['class'] = $part;
if ($text)
$p[$part]['text'] = $text;
if (is_null($url)) {
if (empty($path) && !empty(static::$extension))
$path = 'index';
$p[$part]['href'] = Util::$restler->getBaseUrl()
. '/' . $path . static::$extension;
} else {
if (empty($url) && !empty(static::$extension))
$url = 'index';
$p[$part]['href'] = $url . static::$extension;
}
if ($path == $activeUrl) {
$p[$part]['active'] = true;
}
}
$p[$part]['children'] = array();
if (isset($r['fragment'])) {
$parts[] = $r['fragment'];
if (is_null($label)) {
$label = Text::title($r['fragment']);
}
}
if (empty($r['scheme'])) {
//relative url found
if (empty($url)) {
$label = Text::title(static::$root);
$url = static::$url;
} else {
$url = static::$url . '/' . ltrim($url, '/');
}
}
if (is_null($label)) {
$label = Text::title(strtok(end($parts), '.'));
}
$r['url'] = $url;
$r['path'] = $trail;
$r['parts'] = $parts;
$r['label'] = $label;
static::build($r);
return $r;
}
public static function build(array $r)
{
$p = & static::$tree;
$parts = $r['parts'];
$last = count($parts) - 1;
foreach ($parts as $i => $part) {
if ($i == $last) {
$p[$part]['text'] = $r['label'];
$p[$part]['href'] = $r['url'];
$p[$part]['class'] = Text::slug($part);
/* dynamically do it at run time instead
if ($r['path'] == static::$activeTrail)
$p[$part]['active'] = true;
*/
} elseif (!isset($p[$part])) {
$p[$part] = array();
$p[$part]['text'] = Text::title($part);
$p[$part]['href'] = '#';
$p[$part]['children'] = array();
}
$p = & $p[$part]['children'];
}
}
protected static function title($name)
{
if (empty($name)) {
$name = static::$root;
} else {
$name = ltrim($name, '#');
}
return ucfirst(preg_replace(array('/(?<=[^A-Z])([A-Z])/', '/(?<=[^0-9])([0-9])/'), ' $0', $name));
}
}
}

View File

@ -14,7 +14,7 @@ use Luracast\Restler\Util;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*
* ============================ magic properties ==============================
* @property Tags parent parent tag
@ -154,10 +154,10 @@ class Tags implements ArrayAccess, Countable
*/
public function id($value)
{
$this->attributes['id'] = isset($value)
? (string)$value
: Util::nestedValue($this->attributes, 'name');
static::$instances[$value] = $this;
if (!empty($value) && is_string($value)) {
$this->attributes['id'] = $value;
static::$instances[$value] = $this;
}
return $this;
}

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class User implements iIdentifyUser
{

View File

@ -9,7 +9,7 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
class Util
{
@ -50,8 +50,8 @@ class Util
* When the deeply nested property is found its value is returned, otherwise
* false is returned.
*
* @param array $from array to extract the value from
* @param string|array $key ... pass more to go deeply inside the array
* @param array $from array to extract the value from
* @param string|array $key ... pass more to go deeply inside the array
* alternatively you can pass a single array
*
* @return null|mixed null when not found, value otherwise
@ -129,6 +129,38 @@ class Util
return implode($char, $fromPath);
}
/**
* Compare two strings and split the common
* sub string from the first string and return it as array
*
* @static
*
* @param string $fromPath
* @param string $usingPath
* @param string $char
* optional, set it as
* blank string for char by char comparison
*
* @return array with 2 strings first is the common string and second is the remaining in $fromPath
*/
public static function splitCommonPath($fromPath, $usingPath, $char = '/')
{
if (empty($fromPath))
return array('', '');
$fromPath = explode($char, $fromPath);
$usingPath = explode($char, $usingPath);
$commonPath = array();
while (count($usingPath)) {
if (count($fromPath) && $fromPath[0] == $usingPath[0]) {
$commonPath [] = array_shift($fromPath);
} else {
break;
}
array_shift($usingPath);
}
return array(implode($char, $commonPath), implode($char, $fromPath));
}
/**
* Parses the request to figure out the http request type
*

View File

@ -9,10 +9,6 @@
{
"name":"Luracast",
"email":"arul@luracast.com"
},
{
"name":"Nick nickl- Lombard",
"email":"github@jigsoft.co.za"
}
],
"extra":{
@ -21,41 +17,23 @@
}
},
"suggest":{
"luracast/explorer":"Restler's very own api explorer (see require-dev for details)",
"rodneyrehm/plist":"Restler supports tho Apple plist xml format (see require-dev for details)",
"zendframework/zendamf":"Support for the amf document format (see require-dev for details)",
"symfony/yaml":"Restler can produce content in yaml format as well (see require-dev for details)",
"twig/twig":"Restler can render HtmlView using twig templates (see require-dev for details)",
"mustache/mustache":"Restler can render HtmlView using mustache/handlebar templates (see require-dev for details)",
"bshaffer/oauth2-server-php":"Restler can provide OAuth2 authentication using this library (see require-dev for details)"
"rodneyrehm/plist":"If you need Apple plist binary/xml format",
"zendframework/zendamf":"If you need AMF format",
"symfony/yaml":"If you need YAML format",
"twig/twig":"If you want to use twig templates with Html format",
"mustache/mustache":"If you want to use mustache/handlebar templates with Html format",
"illuminate/view":"If you want to use laravel blade templates with Html format",
"bshaffer/oauth2-server-php":"If you want to use OAuth2 for authentication"
},
"require":{
"php":">=5.3.0"
},
"require-dev":{
"luracast/explorer":"*",
"rodneyrehm/plist":"dev-master",
"zendframework/zendamf":"dev-master",
"symfony/yaml":"*",
"mustache/mustache": "dev-master",
"twig/twig": "v1.13.0",
"bshaffer/oauth2-server-php":"v1.0"
},
"repositories":[
{
"type":"vcs",
"url":"https://github.com/zendframework/ZendAmf.git"
},
{
"type":"package",
"package":{
"name":"luracast/explorer",
"version":"v3.0.0",
"dist":{
"type":"zip",
"url":"https://github.com/Luracast/Restler-API-Explorer/zipball/v3.0.0"
}
}
}
],
"autoload":{

View File

@ -0,0 +1,125 @@
/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html>
<head>
<title>Api Explorer</title>
<link href='https://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='print' rel='stylesheet' type='text/css'/>
<script type="text/javascript" src="lib/shred.bundle.js"></script>
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='lib/handlebars-1.0.0.js' type='text/javascript'></script>
<script src='lib/underscore-min.js' type='text/javascript'></script>
<script src='lib/backbone-min.js' type='text/javascript'></script>
<script src='lib/swagger.js' type='text/javascript'></script>
<script src='swagger-ui.js' type='text/javascript'></script>
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
<!-- enabling this will enable oauth2 implicit scope support -->
<script src='lib/swagger-oauth.js' type='text/javascript'></script>
<script type="text/javascript">
$(function () {
window.swaggerUi = new SwaggerUi({
url: "resources.json",
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'patch', 'delete'],
onComplete: function(swaggerApi, swaggerUi){
log("Loaded Api Explorer");
if(typeof initOAuth == "function") {
/*
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
*/
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load Api Explorer");
},
docExpansion: "none"
});
$('#input_apiKey').change(function() {
var key = $('#input_apiKey')[0].value;
log("key: " + key);
if(key && key.trim() != "") {
log("added key " + key);
window.authorizations.add("key", new ApiKeyAuthorization("api_key", key, "query"));
}
})
window.swaggerUi.load();
});
</script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="#">API Explorer</a>
<form id='api_selector'>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="hidden" value="resources.json"/></div>
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
<div class='input'><a id="explore" href="#">Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>

View File

@ -0,0 +1,38 @@
// Backbone.js 0.9.2
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks=
{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g=
z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent=
{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null==
b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent:
b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};
a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error,
h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t();
return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending=
{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||
!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator);
this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c<d;c++){if(!(e=a[c]=this._prepareModel(a[c],b)))throw Error("Can't add an invalid model to a collection");g=e.cid;i=e.id;j[g]||this._byCid[g]||null!=i&&(k[i]||this._byId[i])?
l.push(c):j[g]=k[i]=e}for(c=l.length;c--;)a.splice(l[c],1);c=0;for(d=a.length;c<d;c++)(e=a[c]).on("all",this._onModelEvent,this),this._byCid[e.cid]=e,null!=e.id&&(this._byId[e.id]=e);this.length+=d;A.apply(this.models,[null!=b.at?b.at:this.models.length,0].concat(a));this.comparator&&this.sort({silent:!0});if(b.silent)return this;c=0;for(d=this.models.length;c<d;c++)if(j[(e=this.models[c]).cid])b.index=c,e.trigger("add",e,this,b);return this},remove:function(a,b){var c,d,e,g;b||(b={});a=f.isArray(a)?
a.slice():[a];c=0;for(d=a.length;c<d;c++)if(g=this.getByCid(a[c])||this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],e=this.indexOf(g),this.models.splice(e,1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,b);return a},pop:function(a){var b=this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:0},b));return a},
shift:function(a){var b=this.at(0);this.remove(b,a);return b},get:function(a){return null==a?void 0:this._byId[null!=a.id?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},where:function(a){return f.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");var b=f.bind(this.comparator,this);1==this.comparator.length?
this.models=this.sortBy(b):this.models.sort(b);a.silent||this.trigger("reset",this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},reset:function(a,b){a||(a=[]);b||(b={});for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);this._reset();this.add(a,f.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d,
e,f){b[a.add?"add":"reset"](b.parse(d,f),a);c&&c(b,d)};a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},create:function(a,b){var c=this,b=b?f.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(e,f){b.wait&&c.add(e,b);d?d(e,f):e.trigger("sync",a,f,b)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId=
{};this._byCid={}},_prepareModel:function(a,b){b||(b={});a instanceof o?a.collection||(a.collection=this):(b.collection=this,a=new this.model(a,b),a._validate(a.attributes,b)||(a=!1));return a},_removeReference:function(a){this==a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"==a||"remove"==a)&&c!=this||("destroy"==a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,
arguments))}});f.each("forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split(","),function(a){r.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});var u=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},B=/:\w+/g,
C=/\*\w+/g,D=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(u.prototype,k,{initialize:function(){},route:function(a,b,c){g.history||(g.history=new m);f.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,
this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(D,"\\$&").replace(B,"([^/]+)").replace(C,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var m=g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")},s=/^[#\/]/,E=/msie [\w.]+/;m.started=!1;f.extend(m.prototype,k,{interval:50,getHash:function(a){return(a=(a?a.location:window.location).href.match(/#(.*)$/))?a[1]:
""},getFragment:function(a,b){if(null==a)if(this._hasPushState||b){var a=window.location.pathname,c=window.location.search;c&&(a+=c)}else a=this.getHash();a.indexOf(this.options.root)||(a=a.substr(this.options.root.length));return a.replace(s,"")},start:function(a){if(m.started)throw Error("Backbone.history has already been started");m.started=!0;this.options=f.extend({},{root:"/"},this.options,a);this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=
!(!this.options.pushState||!window.history||!window.history.pushState);var a=this.getFragment(),b=document.documentMode;if(b=E.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b))this.iframe=i('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?i(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?i(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,
this.interval));this.fragment=a;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&(this.fragment=this.getHash().replace(s,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},
stop:function(){i(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a==this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,
function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(s,"");this.fragment!=c&&(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.getHash(this.iframe))&&(b.replace||
this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,"")+"#"+b):a.hash=b}});var v=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},F=/^(\S+)\s*(.*)$/,w="model,collection,el,id,attributes,className,tagName".split(",");
f.extend(v.prototype,k,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);b&&i(a).attr(b);c&&i(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof i?a:i(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=n(this,"events"))){this.undelegateEvents();
for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(F),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=w.length;b<c;b++){var d=w[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el)this.setElement(this.el,
!1);else{var a=n(this,"attributes")||{};this.id&&(a.id=this.id);this.className&&(a["class"]=this.className);this.setElement(this.make(this.tagName,a),!1)}}});o.extend=r.extend=u.extend=v.extend=function(a,b){var c=G(this,a,b);c.extend=this.extend;return c};var H={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=H[a];c||(c={});var e={type:d,dataType:"json"};c.url||(e.url=n(b,"url")||t());if(!c.data&&b&&("create"==a||"update"==a))e.contentType="application/json",
e.data=JSON.stringify(b.toJSON());g.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(g.emulateHTTP&&("PUT"===d||"DELETE"===d))g.emulateJSON&&(e.data._method=d),e.type="POST",e.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d)};"GET"!==e.type&&!g.emulateJSON&&(e.processData=!1);return i.ajax(f.extend(e,c))};g.wrapError=function(a,b,c){return function(d,e){e=d===b?e:d;a?a(b,e,c):b.trigger("error",b,e,c)}};var x=function(){},G=function(a,
b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};f.extend(d,a);x.prototype=a.prototype;d.prototype=new x;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},n=function(a,b){return!a||!a[b]?null:f.isFunction(a[b])?a[b]():a[b]},t=function(){throw Error('A "url" property or function must be specified');}}).call(this);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,18 @@
/*
* jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
* http://benalman.com/projects/jquery-bbq-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M<N?O[P]||(R[M+1]&&isNaN(R[M+1])?{}:[]):J}}else{if($.isArray(H[P])){H[P].push(J)}else{if(H[P]!==i){H[P]=[H[P],J]}else{H[P]=J}}}}else{if(P){H[P]=F?i:""}}});return H};function z(H,F,G){if(F===i||typeof F==="boolean"){G=F;F=a[H?D:A]()}else{F=E(F)?F.replace(H?w:x,""):F}return l(F,G)}l[A]=B(z,0);l[D]=v=B(z,1);$[y]||($[y]=function(F){return $.extend(C,F)})({a:k,base:k,iframe:t,img:t,input:t,form:"action",link:k,script:t});j=$[y];function s(I,G,H,F){if(!E(H)&&typeof H!=="object"){F=H;H=G;G=i}return this.each(function(){var L=$(this),J=G||j()[(this.nodeName||"").toLowerCase()]||"",K=J&&L.attr(J)||"";L.attr(J,a[I](K,H,F))})}$.fn[A]=B(s,A);$.fn[D]=B(s,D);b.pushState=q=function(I,F){if(E(I)&&/^#/.test(I)&&F===i){F=2}var H=I!==i,G=c(p[g][k],H?I:{},H?F:2);p[g][k]=G+(/#/.test(G)?"":"#")};b.getState=u=function(F,G){return F===i||typeof F==="boolean"?v(F):v(G)[F]};b.removeState=function(F){var G={};if(F!==i){G=u();$.each($.isArray(F)?F:arguments,function(I,H){delete G[H]})}q(G,2)};e[d]=$.extend(e[d],{add:function(F){var H;function G(J){var I=J[D]=c();J.getState=function(K,L){return K===i||typeof K==="boolean"?l(I,K):l(I,L)[K]};H.apply(this,arguments)}if($.isFunction(F)){H=F;return G}else{H=F.handler;F.handler=G}}})})(jQuery,this);
/*
* jQuery hashchange event - v1.2 - 2/11/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,i,b){var j,k=$.event.special,c="location",d="hashchange",l="href",f=$.browser,g=document.documentMode,h=f.msie&&(g===b||g<8),e="on"+d in i&&!h;function a(m){m=m||i[c][l];return m.replace(/^[^#]*#?(.*)$/,"$1")}$[d+"Delay"]=100;k[d]=$.extend(k[d],{setup:function(){if(e){return false}$(j.start)},teardown:function(){if(e){return false}$(j.stop)}});j=(function(){var m={},r,n,o,q;function p(){o=q=function(s){return s};if(h){n=$('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);

View File

@ -0,0 +1 @@
(function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery);

View File

@ -0,0 +1,8 @@
/*
jQuery Wiggle
Author: WonderGroup, Jordan Thomas
URL: http://labs.wondergroup.com/demos/mini-ui/index.html
License: MIT (http://en.wikipedia.org/wiki/MIT_License)
*/
jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('<div class="wiggle-wrap"></div>').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,193 @@
// The purpose of the `Content` object is to abstract away the data conversions
// to and from raw content entities as strings. For example, you want to be able
// to pass in a Javascript object and have it be automatically converted into a
// JSON string if the `content-type` is set to a JSON-based media type.
// Conversely, you want to be able to transparently get back a Javascript object
// in the response if the `content-type` is a JSON-based media-type.
// One limitation of the current implementation is that it [assumes the `charset` is UTF-8](https://github.com/spire-io/shred/issues/5).
// The `Content` constructor takes an options object, which *must* have either a
// `body` or `data` property and *may* have a `type` property indicating the
// media type. If there is no `type` attribute, a default will be inferred.
var Content = function(options) {
this.body = options.body;
this.data = options.data;
this.type = options.type;
};
Content.prototype = {
// Treat `toString()` as asking for the `content.body`. That is, the raw content entity.
//
// toString: function() { return this.body; }
//
// Commented out, but I've forgotten why. :/
};
// `Content` objects have the following attributes:
Object.defineProperties(Content.prototype,{
// - **type**. Typically accessed as `content.type`, reflects the `content-type`
// header associated with the request or response. If not passed as an options
// to the constructor or set explicitly, it will infer the type the `data`
// attribute, if possible, and, failing that, will default to `text/plain`.
type: {
get: function() {
if (this._type) {
return this._type;
} else {
if (this._data) {
switch(typeof this._data) {
case "string": return "text/plain";
case "object": return "application/json";
}
}
}
return "text/plain";
},
set: function(value) {
this._type = value;
return this;
},
enumerable: true
},
// - **data**. Typically accessed as `content.data`, reflects the content entity
// converted into Javascript data. This can be a string, if the `type` is, say,
// `text/plain`, but can also be a Javascript object. The conversion applied is
// based on the `processor` attribute. The `data` attribute can also be set
// directly, in which case the conversion will be done the other way, to infer
// the `body` attribute.
data: {
get: function() {
if (this._body) {
return this.processor.parser(this._body);
} else {
return this._data;
}
},
set: function(data) {
if (this._body&&data) Errors.setDataWithBody(this);
this._data = data;
return this;
},
enumerable: true
},
// - **body**. Typically accessed as `content.body`, reflects the content entity
// as a UTF-8 string. It is the mirror of the `data` attribute. If you set the
// `data` attribute, the `body` attribute will be inferred and vice-versa. If
// you attempt to set both, an exception is raised.
body: {
get: function() {
if (this._data) {
return this.processor.stringify(this._data);
} else {
return this._body.toString();
}
},
set: function(body) {
if (this._data&&body) Errors.setBodyWithData(this);
this._body = body;
return this;
},
enumerable: true
},
// - **processor**. The functions that will be used to convert to/from `data` and
// `body` attributes. You can add processors. The two that are built-in are for
// `text/plain`, which is basically an identity transformation and
// `application/json` and other JSON-based media types (including custom media
// types with `+json`). You can add your own processors. See below.
processor: {
get: function() {
var processor = Content.processors[this.type];
if (processor) {
return processor;
} else {
// Return the first processor that matches any part of the
// content type. ex: application/vnd.foobar.baz+json will match json.
var main = this.type.split(";")[0];
var parts = main.split(/\+|\//);
for (var i=0, l=parts.length; i < l; i++) {
processor = Content.processors[parts[i]]
}
return processor || {parser:identity,stringify:toString};
}
},
enumerable: true
},
// - **length**. Typically accessed as `content.length`, returns the length in
// bytes of the raw content entity.
length: {
get: function() {
if (typeof Buffer !== 'undefined') {
return Buffer.byteLength(this.body);
}
return this.body.length;
}
}
});
Content.processors = {};
// The `registerProcessor` function allows you to add your own processors to
// convert content entities. Each processor consists of a Javascript object with
// two properties:
// - **parser**. The function used to parse a raw content entity and convert it
// into a Javascript data type.
// - **stringify**. The function used to convert a Javascript data type into a
// raw content entity.
Content.registerProcessor = function(types,processor) {
// You can pass an array of types that will trigger this processor, or just one.
// We determine the array via duck-typing here.
if (types.forEach) {
types.forEach(function(type) {
Content.processors[type] = processor;
});
} else {
// If you didn't pass an array, we just use what you pass in.
Content.processors[types] = processor;
}
};
// Register the identity processor, which is used for text-based media types.
var identity = function(x) { return x; }
, toString = function(x) { return x.toString(); }
Content.registerProcessor(
["text/html","text/plain","text"],
{ parser: identity, stringify: toString });
// Register the JSON processor, which is used for JSON-based media types.
Content.registerProcessor(
["application/json; charset=utf-8","application/json","json"],
{
parser: function(string) {
return JSON.parse(string);
},
stringify: function(data) {
return JSON.stringify(data); }});
var qs = require('querystring');
// Register the post processor, which is used for JSON-based media types.
Content.registerProcessor(
["application/x-www-form-urlencoded"],
{ parser : qs.parse, stringify : qs.stringify });
// Error functions are defined separately here in an attempt to make the code
// easier to read.
var Errors = {
setDataWithBody: function(object) {
throw new Error("Attempt to set data attribute of a content object " +
"when the body attributes was already set.");
},
setBodyWithData: function(object) {
throw new Error("Attempt to set body attribute of a content object " +
"when the data attributes was already set.");
}
}
module.exports = Content;

View File

@ -0,0 +1,218 @@
var appName;
var popupMask;
var popupDialog;
var clientId;
var realm;
function handleLogin() {
var scopes = [];
if(window.swaggerUi.api.authSchemes
&& window.swaggerUi.api.authSchemes.oauth2
&& window.swaggerUi.api.authSchemes.oauth2.scopes) {
scopes = window.swaggerUi.api.authSchemes.oauth2.scopes;
}
if(window.swaggerUi.api
&& window.swaggerUi.api.info) {
appName = window.swaggerUi.api.info.title;
}
if(popupDialog.length > 0)
popupDialog = popupDialog.last();
else {
popupDialog = $(
[
'<div class="api-popup-dialog">',
'<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
'<div class="api-popup-content">',
'<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
'<a href="#">Learn how to use</a>',
'</p>',
'<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
'<ul class="api-popup-scopes">',
'</ul>',
'<p class="error-msg"></p>',
'<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
'</div>',
'</div>'].join(''));
$(document.body).append(popupDialog);
popup = popupDialog.find('ul.api-popup-scopes').empty();
for (i = 0; i < scopes.length; i ++) {
scope = scopes[i];
str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"/>' + '<label for="scope_' + i + '">' + scope.scope;
if (scope.description) {
str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
}
str += '</label></li>';
popup.append(str);
}
var $win = $(window),
dw = $win.width(),
dh = $win.height(),
st = $win.scrollTop(),
dlgWd = popupDialog.outerWidth(),
dlgHt = popupDialog.outerHeight(),
top = (dh -dlgHt)/2 + st,
left = (dw - dlgWd)/2;
popupDialog.css({
top: (top < 0? 0 : top) + 'px',
left: (left < 0? 0 : left) + 'px'
});
popupDialog.find('button.api-popup-cancel').click(function() {
popupMask.hide();
popupDialog.hide();
});
popupDialog.find('button.api-popup-authbtn').click(function() {
popupMask.hide();
popupDialog.hide();
var authSchemes = window.swaggerUi.api.authSchemes;
var location = window.location;
var locationUrl = location.protocol + '//' + location.host + location.pathname;
var redirectUrl = locationUrl.replace("index.html","").concat("/o2c.html").replace("//o2c.html","/o2c.html");
var url = null;
var p = window.swaggerUi.api.authSchemes;
for (var key in p) {
if (p.hasOwnProperty(key)) {
var o = p[key].grantTypes;
for(var t in o) {
if(o.hasOwnProperty(t) && t === 'implicit') {
var dets = o[t];
url = dets.loginEndpoint.url + "?response_type=token";
window.swaggerUi.tokenName = dets.tokenName;
}
}
}
}
var scopes = [];
var scopeForUrl='';
var o = $('.api-popup-scopes').find('input:checked');
for(var k =0; k < o.length; k++) {
scopes.push($(o[k]).attr("scope"));
if(k > 0){
scopeForUrl+=' ';
}
scopeForUrl+=$(o[k]).attr("scope");
}
window.enabledScopes=scopes;
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopeForUrl);
window.open(url);
});
}
popupMask.show();
popupDialog.show();
return;
}
function handleLogout() {
for(key in window.authorizations.authz){
window.authorizations.remove(key)
}
window.enabledScopes = null;
$('.api-ic.ic-on').addClass('ic-off');
$('.api-ic.ic-on').removeClass('ic-on');
// set the info box
$('.api-ic.ic-warning').addClass('ic-error');
$('.api-ic.ic-warning').removeClass('ic-warning');
}
function initOAuth(opts) {
var o = (opts||{});
var errors = [];
appName = (o.appName||errors.push("missing appName"));
popupMask = (o.popupMask||$('#api-common-mask'));
popupDialog = (o.popupDialog||$('.api-popup-dialog'));
clientId = (o.clientId||errors.push("missing client id"));
realm = (o.realm||errors.push("missing realm"));
if(errors.length > 0){
log("auth unable initialize oauth: " + errors);
return;
}
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
$('.api-ic').click(function(s) {
if($(s.target).hasClass('ic-off'))
handleLogin();
else {
handleLogout();
}
false;
});
}
function onOAuthComplete(token) {
if(token) {
if(token.error) {
var checkbox = $('input[type=checkbox],.secured')
checkbox.each(function(pos){
checkbox[pos].checked = false;
});
alert(token.error);
}
else {
var b = token[window.swaggerUi.tokenName];
if(b){
// if all roles are satisfied
var o = null;
$.each($('.auth #api_information_panel'), function(k, v) {
var children = v;
if(children && children.childNodes) {
var requiredScopes = [];
$.each((children.childNodes), function (k1, v1){
var inner = v1.innerHTML;
if(inner)
requiredScopes.push(inner);
});
var diff = [];
for(var i=0; i < requiredScopes.length; i++) {
var s = requiredScopes[i];
if(window.enabledScopes && window.enabledScopes.indexOf(s) == -1) {
diff.push(s);
}
}
if(diff.length > 0){
o = v.parentNode;
$(o.parentNode).find('.api-ic.ic-on').addClass('ic-off');
$(o.parentNode).find('.api-ic.ic-on').removeClass('ic-on');
// sorry, not all scopes are satisfied
$(o).find('.api-ic').addClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
}
else {
o = v.parentNode;
$(o.parentNode).find('.api-ic.ic-off').addClass('ic-on');
$(o.parentNode).find('.api-ic.ic-off').removeClass('ic-off');
// all scopes are satisfied
$(o).find('.api-ic').addClass('ic-info');
$(o).find('.api-ic').removeClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
}
}
});
window.authorizations.add("key", new ApiKeyAuthorization("Authorization", "Bearer " + b, "header"));
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
// Underscore.js 1.3.3
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break;
g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,
c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&&
a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,
c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,
a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&
(e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};
j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,
0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,
e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=
i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<=
1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=
i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h=
g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));
return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&
c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=
function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"};
b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,
b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId=
function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape||
u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};
b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d,
this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);

View File

@ -0,0 +1,15 @@
<script>
var qp = null;
if(window.location.hash) {
qp = location.hash.substring(1);
}
else {
qp = location.search.substring(1);
}
qp = qp ? JSON.parse('{"' + qp.replace(/&/g, '","').replace(/=/g,'":"') + '"}',
function(key, value) {
return key===""?value:decodeURIComponent(value) }
):{}
window.opener.onOAuthComplete(qp);
window.close();
</script>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
interface iAuthenticate extends iFilter
{

View File

@ -9,7 +9,7 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
interface iCache
{

View File

@ -13,7 +13,7 @@ use Exception;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
interface iCompose {
/**

View File

@ -12,7 +12,7 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
interface iFilter
{

View File

@ -12,7 +12,7 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
interface iIdentifyUser
{

View File

@ -1,8 +1,14 @@
<?php
namespace Luracast\Restler;
interface iProvideMultiVersionApi {
/**
* Interface iProvideMultiVersionApi
* @package Luracast\Restler
*
* @version 3.0.0rc6
*/
interface iProvideMultiVersionApi
{
/**
* Maximum api version supported by the api class
* @return int

View File

@ -11,7 +11,7 @@ namespace Luracast\Restler;
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
* @version 3.0.0rc6
*/
interface iUseAuthentication
{

View File

@ -64,7 +64,7 @@ if ($success && isset($api)) {
} else {
if (isset($response['error']['message'])) {
$icon = '<icon class="denied"></icon>';
$title = end(explode(':',$response['error']['message']));
$title = end(explode(':',$response['error']['message'],2));
} else {
$icon = '<icon class="warning"></icon>';
$title = 'No Matching Resource';

View File

@ -438,6 +438,7 @@ ALTER TABLE llx_resource ADD COLUMN extraparams varchar(255);
ALTER TABLE llx_element_resources ADD COLUMN duree real; -- total duration of using ressource
UPDATE llx_element_resources SET resource_type = 'dolresource' WHERE resource_type = 'resource';
CREATE TABLE llx_advtargetemailing
(

View File

@ -1,6 +1,7 @@
# Dolibarr language file - Source file is en_US - other
SecurityCode=Security code
Calendar=Calendar
NumberingShort=N°
Tools=Tools
ToolsDesc=This area is dedicated to group miscellaneous tools not available into other menu entries.<br><br>Those tools can be reached from menu on the side.
Birthday=Birthday

View File

@ -563,7 +563,11 @@ if (! defined('NOLOGIN'))
$reshook=$hookmanager->executeHooks('afterLoginFailed',$parameters,$user,$action); // Note that $action and $object may have been modified by some hooks
if ($reshook < 0) $error++;
header('Location: '.DOL_URL_ROOT.'/index.php');
$paramsurl=array();
if (GETPOST('textbrowser')) $paramsurl[]='textbrowser='.GETPOST('textbrowser','int');
if (GETPOST('nojs')) $paramsurl[]='nojs='.GETPOST('nojs','int');
if (GETPOST('lang')) $paramsurl[]='lang='.GETPOST('lang','alpha');
header('Location: '.DOL_URL_ROOT.'/index.php'.($count(paramsurl)?'?'.join('&',$paramsurl):''));
exit;
}
}
@ -617,7 +621,11 @@ if (! defined('NOLOGIN'))
$reshook=$hookmanager->executeHooks('afterLoginFailed',$parameters,$user,$action); // Note that $action and $object may have been modified by some hooks
if ($reshook < 0) $error++;
header('Location: '.DOL_URL_ROOT.'/index.php');
$paramsurl=array();
if (GETPOST('textbrowser')) $paramsurl[]='textbrowser='.GETPOST('textbrowser','int');
if (GETPOST('nojs')) $paramsurl[]='nojs='.GETPOST('nojs','int');
if (GETPOST('lang')) $paramsurl[]='lang='.GETPOST('lang','alpha');
header('Location: '.DOL_URL_ROOT.'/index.php'.($count(paramsurl)?'?'.join('&',$paramsurl):''));
exit;
}
else
@ -756,6 +764,11 @@ if (! GETPOST('nojs')) // If javascript was not disabled on URL
}
}
else $conf->use_javascript_ajax=0;
// Set MAIN_OPTIMIZEFORTEXTBROWSER
if (GETPOST('textbrowser') || ! empty($user->conf->MAIN_OPTIMIZEFORTEXTBROWSER)) // If text browser was enabled on URL
{
$conf->global->MAIN_OPTIMIZEFORTEXTBROWSER=1;
}
// Set terminal output option according to conf->browser.
if (GETPOST('dol_hide_leftmenu') || ! empty($_SESSION['dol_hide_leftmenu'])) $conf->dol_hide_leftmenu=1;
@ -1006,9 +1019,9 @@ function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs
$favicon=dol_buildpath('/theme/'.$conf->theme.'/img/favicon.ico',1);
if (! empty($conf->global->MAIN_FAVICON_URL)) $favicon=$conf->global->MAIN_FAVICON_URL;
print '<link rel="shortcut icon" type="image/x-icon" href="'.$favicon.'"/>'."\n";
if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) print '<link rel="top" title="'.$langs->trans("Home").'" href="'.(DOL_URL_ROOT?DOL_URL_ROOT:'/').'">'."\n";
if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) print '<link rel="copyright" title="GNU General Public License" href="http://www.gnu.org/copyleft/gpl.html#SEC1">'."\n";
if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) print '<link rel="author" title="Dolibarr Development Team" href="http://www.dolibarr.org">'."\n";
if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && ! GETPOST('textbrowser')) print '<link rel="top" title="'.$langs->trans("Home").'" href="'.(DOL_URL_ROOT?DOL_URL_ROOT:'/').'">'."\n";
if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && ! GETPOST('textbrowser')) print '<link rel="copyright" title="GNU General Public License" href="http://www.gnu.org/copyleft/gpl.html#SEC1">'."\n";
if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && ! GETPOST('textbrowser')) print '<link rel="author" title="Dolibarr Development Team" href="http://www.dolibarr.org">'."\n";
// Displays title
$appli=constant('DOL_APPLICATION_TITLE');
@ -1460,7 +1473,7 @@ function top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $a
else $toprightmenu.=$result; // For backward compatibility
// Link to print main content area
if (empty($conf->global->MAIN_PRINT_DISABLELINK) && empty($conf->browser->phone))
if (empty($conf->global->MAIN_PRINT_DISABLELINK) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && empty($conf->browser->phone))
{
$qs=$_SERVER["QUERY_STRING"];
$qs.=(($qs && $morequerystring)?'&':'').$morequerystring;
@ -1471,7 +1484,7 @@ function top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $a
}
// Link to Dolibarr wiki pages
if (empty($conf->global->MAIN_HELP_DISABLELINK))
if (empty($conf->global->MAIN_HELP_DISABLELINK) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
{
$langs->load("help");

View File

@ -1435,7 +1435,7 @@ else
print '<table width="100%" class="nobordernopadding"><tr><td class="nowrap">';
print $langs->trans("BarcodeType");
print '</td>';
if (($action != 'editbarcodetype') && ! empty($user->rights->barcode->creer)) print '<td align="right"><a href="'.$_SERVER["PHP_SELF"].'?action=editbarcodetype&amp;id='.$object->id.'">'.img_edit($langs->trans('Edit'),1).'</a></td>';
if (($action != 'editbarcodetype') && ! empty($user->rights->produit->creer) && $createbarcode) print '<td align="right"><a href="'.$_SERVER["PHP_SELF"].'?action=editbarcodetype&amp;id='.$object->id.'">'.img_edit($langs->trans('Edit'),1).'</a></td>';
print '</tr></table>';
print '</td><td colspan="2">';
if ($action == 'editbarcodetype')
@ -1456,7 +1456,7 @@ else
print '<table width="100%" class="nobordernopadding"><tr><td class="nowrap">';
print $langs->trans("BarcodeValue");
print '</td>';
if (($action != 'editbarcode') && ! empty($user->rights->barcode->creer)) print '<td align="right"><a href="'.$_SERVER["PHP_SELF"].'?action=editbarcode&amp;id='.$object->id.'">'.img_edit($langs->trans('Edit'),1).'</a></td>';
if (($action != 'editbarcode') && ! empty($user->rights->produit->creer) && $createbarcode) print '<td align="right"><a href="'.$_SERVER["PHP_SELF"].'?action=editbarcode&amp;id='.$object->id.'">'.img_edit($langs->trans('Edit'),1).'</a></td>';
print '</tr></table>';
print '</td><td colspan="2">';
if ($action == 'editbarcode')

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