Просмотр исходного кода

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

Regis Houssin 8 лет назад
Родитель
Сommit
fea60e2675
32 измененных файлов с 932 добавлено и 708 удалено
  1. 6 131
      COPYRIGHT
  2. 4 2
      htdocs/contact/list.php
  3. 58 50
      htdocs/core/actions_extrafields.inc.php
  4. 69 28
      htdocs/core/class/extrafields.class.php
  5. 18 6
      htdocs/core/lib/functions.lib.php
  6. 52 15
      htdocs/core/tpl/admin_extrafields_add.tpl.php
  7. 52 7
      htdocs/core/tpl/admin_extrafields_edit.tpl.php
  8. 5 1
      htdocs/core/tpl/admin_extrafields_view.tpl.php
  9. 12 31
      htdocs/includes/restler/framework/Luracast/Restler/explorer/css/screen.css
  10. BIN
      htdocs/includes/restler/framework/Luracast/Restler/explorer/images/logo_small.png
  11. BIN
      htdocs/includes/restler/framework/Luracast/Restler/explorer/images/pet_store_api.png
  12. BIN
      htdocs/includes/restler/framework/Luracast/Restler/explorer/images/wordnik_api.png
  13. 25 9
      htdocs/includes/restler/framework/Luracast/Restler/explorer/index.html
  14. 54 61
      htdocs/includes/restler/framework/Luracast/Restler/explorer/lib/swagger-oauth.js
  15. 269 154
      htdocs/includes/restler/framework/Luracast/Restler/explorer/lib/swagger.js
  16. 190 141
      htdocs/includes/restler/framework/Luracast/Restler/explorer/swagger-ui.js
  17. 0 0
      htdocs/includes/restler/framework/Luracast/Restler/explorer/swagger-ui.min.js
  18. 4 1
      htdocs/install/mysql/migration/5.0.0-6.0.0.sql
  19. 4 2
      htdocs/install/mysql/tables/llx_extrafields.sql
  20. 7 5
      htdocs/langs/en_US/admin.lang
  21. 1 0
      htdocs/langs/en_US/main.lang
  22. 12 8
      htdocs/modulebuilder/skeletons/skeleton_list.php
  23. 1 1
      htdocs/projet/admin/project.php
  24. 1 1
      htdocs/projet/admin/project_extrafields.php
  25. 1 1
      htdocs/projet/admin/project_task_extrafields.php
  26. 25 25
      htdocs/projet/list.php
  27. 20 17
      htdocs/projet/tasks/list.php
  28. 2 2
      htdocs/projet/tasks/time.php
  29. 20 1
      htdocs/societe/class/api_thirdparties.class.php
  30. 0 2
      htdocs/societe/class/societe.class.php
  31. 4 2
      htdocs/societe/list.php
  32. 16 4
      htdocs/user/class/api_users.class.php

+ 6 - 131
COPYRIGHT

@@ -21,13 +21,13 @@ GeoIP                  1.4           LGPL-2.1+                   Yes
 Mobiledetect           2.8.17        MIT License                 Yes             Detect mobile devices browsers
 NuSoap                 0.9.5         LGPL 2.1+                   Yes             Library to develop SOAP Web services (not into rpm and deb package)
 PEAR Mail_MIME         1.8.9         BSD                         Yes             NuSoap dependency
-odtPHP                 1.0.1         GPL-2+  b                   Yes             Library to build/edit ODT files
+odtPHP                 1.0.1         GPL-2+                      Yes             Library to build/edit ODT files
 ParseDown              1.6           MIT License                 Yes             Markdown parser
 PHPExcel               1.8.1         LGPL-2.1+                   Yes             Read/Write XLS files, read ODS files
 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.0RC6      LGPL-3+                     Yes             Library to develop REST Web services
+Restler                3.0.0RC6      LGPL-3+                     Yes             Library to develop REST Web services (+ swagger-ui js lib into dir explorer)
 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
@@ -52,138 +52,13 @@ jQuery Timepicker      1.1.0         GPL and MIT License         Yes
 jQuery Tiptip          1.3           GPL and MIT License         Yes             JS library for tooltips
 jsGanttImproved        1.7.5.2       BSD License                 Yes             JS library (to build Gantt reports)
 JsTimezoneDetect       1.0.6         MIT License                 Yes             JS library to detect user timezone
+SwaggerUI			   2.0.24		 GPL-2+                      Yes             JS library to offer the REST API explorer
 
 For licenses compatibility informations:
 http://www.gnu.org/licenses/licenses.en.html
 
 
-Copyright
----------
-
-Copyright (C) 2016
-
-Copyright (C) 2015
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Marcos García <marcosgdf@gmail.com>
-- Alexandre Spangaro <aspangaro.dolibarr@gmail.com>
-- Frederic France <frederic.france@free.fr>
-- Regis Houssin <regis.houssin@capnetworks.com>
-
-Copyright (C) 2014
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
-- Jean-François Ferry <jfefe@aternatik.fr>
-- Marcos García <marcosgdf@gmail.com>
-- Philippe Grand <philippe.grand@atoo-net.com>
-- Florian Henry <florian.henry@open-concept.pro>
-- Regis Houssin <regis.houssin@capnetworks.com>
-- Maxime Kohlhaas <mko@atm-consulting.fr>
-- Juanjo Menent <jmenent@2byte.es>
-- Alexandre Spangaro <aspangaro.dolibarr@gmail.com>
-- Frederic France <frederic.france@free.fr>
-
-Copyright (C) 2013
-- Christophe Battarel <christophe.battarel@altairis.fr>
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Jean-François Ferry <jfefe@aternatik.fr>
-- Marcos García <marcosgdf@gmail.com>
-- Philippe Grand <philippe.grand@atoo-net.com>
-- Florian Henry <florian.henry@open-concept.pro>
-- Regis Houssin <regis.houssin@capnetworks.com>
-- Maxime Kohlhaas <mko@atm-consulting.fr>
-- Juanjo Menent <jmenent@2byte.es>
-- Adolfo Segura <adolfo.segura@gmail.com>
-- Alexandre Spangaro <aspangaro.dolibarr@gmail.com>
-
-Copyright (C) 2012
-- Christophe Battarel <christophe.battarel@altairis.fr>
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Jean-François Ferry <jfefe@aternatik.fr>
-- Marcos García <marcosgdf@gmail.com>
-- Philippe Grand <philippe.grand@atoo-net.com>
-- Jean Heimburger <jean@tiaris.info>
-- Florian Henry <florian.henry@open-concept.pro>
-- Regis Houssin <regis.houssin@capnetworks.com>
-- Maxime Kohlhaas <mko@atm-consulting.fr>
-- Juanjo Menent <jmenent@2byte.es>
-- Nicolas Péré <nicolas@amarok2.net>
-- Alexandre Spangaro <aspangaro.dolibarr@gmail.com>
-
-Copyright (C) 2011
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Regis Houssin <regis.houssin@capnetworks.com>
-- Juanjo Menent <jmenent@2byte.es>
-- Philippe Grand <philippe.grand@atoo-net.com>
-- Jean Heimburger <jean@tiaris.info>
-
-Copyright (C) 2010
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Regis Houssin <regis.houssin@capnetworks.com>
-- Juanjo Menent <jmenent@2byte.es>
-- r2gnl
-- meos
-
-Copyright (C) 2009
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Regis Houssin <regis.houssin@capnetworks.com>
-- Juanjo Menent <jmenent@2byte.es>
-
-Copyright (C) 2008
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Regis Houssin <regis.houssin@capnetworks.com>
-- Rodolphe Quiedeville <rodolphe@quiedeville.org>
-- Jeremie Ollivier <jeremie.o@laposte.net>
-
-Copyright (C) 2007
-- Rodolphe Quiedeville <rodolphe@quiedeville.org>
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Regis Houssin <regis.houssin@capnetworks.com>
-- Auguria SARL <info@auguria.org>
-- Jean Heimburger <jean@tiaris.info>
-- Jeremie Ollivier <jeremie.o@laposte.net>
-
-Copyright (C) 2006
-- Auguria SARL <info@auguria.org>
-- Marc Barilley/Ocebo <marc@ocebo.com>
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Rodolphe Quiedeville <rodolphe@quiedeville.org>
-- Regis Houssin <regis.houssin@capnetworks.com>
-- Andre Cianfarani <acianfa@free.fr>
-- Yannick Warnier <ywarnier@beeznest.org>
-- Jean Heimburger <jean@tiaris.info>
-
-Copyright (C) 2005
-- Brice Davoleau <brice.davoleau@gmail.com>
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Benoit Mortier <benoit.mortier@opensides.be>
-- Rodolphe Quiedeville <rodolphe@quiedeville.org>
-- Eric Seigne <erics@rycks.com>
-- Matthieu Valleton <mv@seeschloss.org>
-- Regis Houssin <regis.houssin@capnetworks.com>
-
-Copyright (C) 2004
-- Laurent Destailleur <eldy@users.sourceforge.net>
-- Rodolphe Quiedeville <rodolphe@quiedeville.org>
-- Eric Seigne <erics@rycks.com>
-- Benoit Mortier <benoit.mortier@opensides.be>
-- Christophe Combelles <ccomb@free.fr>
-- Sebastien Di Cintio <sdicintio@ressource-toi.org>
-
-Copyright (C) 2003
-- Jean-Louis Bergamo <jlb@j1b.org>
-- Xavier Dutoit <doli@sydesy.com>
-- Rodolphe Quiedeville <rodolphe@quiedeville.org>
-- Emmanuel Raviart <eraviart@entrouvert.com>
-- Eric Seigne <erics@rycks.com>
-
-Copyright (C) 2002
-- Jean-Louis Bergamo <jlb@j1b.org>
-- Rodolphe Quiedeville <rodolphe@quiedeville.org>
-
-Copyright (C) 2001
-- Rodolphe Quiedeville <rodolphe@quiedeville.org>
-
-
-
-
+Copyright / Authors
+-------------------
 
+See page https://github.com/Dolibarr/dolibarr/graphs/contributors

+ 4 - 2
htdocs/contact/list.php

@@ -597,7 +597,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
             $align=$extrafields->getAlignFlag($key);
             $typeofextrafield=$extrafields->attribute_type[$key];
             print '<td class="liste_titre'.($align?' '.$align:'').'">';
-		    if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select')))
+		    if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select')) && empty($extrafields->attribute_computed[$key]))
 			{
 			    $crit=$val;
 				$tmpkey=preg_replace('/search_options_/','',$key);
@@ -664,7 +664,9 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
        if (! empty($arrayfields["ef.".$key]['checked']))
        {
 			$align=$extrafields->getAlignFlag($key);
-			print_liste_field_titre($langs->trans($extralabels[$key]),$_SERVER["PHP_SELF"],"ef.".$key,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder);
+			$sortonfield = "ef.".$key;
+			if (! empty($extrafields->attribute_computed[$key])) $sortonfield='';
+			print_liste_field_titre($langs->trans($extralabels[$key]),$_SERVER["PHP_SELF"],$sortonfield,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder);
        }
    }
 }

+ 58 - 50
htdocs/core/actions_extrafields.inc.php

@@ -14,6 +14,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  * or see http://www.gnu.org/
+ * 
+ * $elementype must be defined.
  */
 
 /**
@@ -24,11 +26,14 @@
 $maxsizestring=255;
 $maxsizeint=10;
 
-$extrasize=GETPOST('size');
-if (GETPOST('type')=='double' && strpos($extrasize,',')===false) $extrasize='24,8';
-if (GETPOST('type')=='date')     $extrasize='';
-if (GETPOST('type')=='datetime') $extrasize='';
-if (GETPOST('type')=='select')   $extrasize='';
+$extrasize=GETPOST('size','int');
+$type=GETPOST('type','alpha');
+$param=GETPOST('param','alpha');;
+
+if ($type=='double' && strpos($extrasize,',')===false) $extrasize='24,8';
+if ($type=='date')     $extrasize='';
+if ($type=='datetime') $extrasize='';
+if ($type=='select')   $extrasize='';
 
 
 // Add attribute
@@ -37,73 +42,73 @@ if ($action == 'add')
 	if ($_POST["button"] != $langs->trans("Cancel"))
 	{
 	    // Check values
-		if (! GETPOST('type'))
+		if (! $type)
 		{
 			$error++;
 			$langs->load("errors");
 			$mesg[]=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Type"));
 			$action = 'create';
 		}
-		if (GETPOST('type')=='varchar' && $extrasize <= 0)
+		if ($type=='varchar' && $extrasize <= 0)
 		{
 		    $error++;
 		    $langs->load("errors");
 		    $mesg[]=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Size"));
 		    $action = 'edit';
 		}
-        if (GETPOST('type')=='varchar' && $extrasize > $maxsizestring)
+        if ($type=='varchar' && $extrasize > $maxsizestring)
         {
             $error++;
             $langs->load("errors");
             $mesg[]=$langs->trans("ErrorSizeTooLongForVarcharType",$maxsizestring);
             $action = 'create';
         }
-        if (GETPOST('type')=='int' && $extrasize > $maxsizeint)
+        if ($type=='int' && $extrasize > $maxsizeint)
         {
             $error++;
             $langs->load("errors");
             $mesg[]=$langs->trans("ErrorSizeTooLongForIntType",$maxsizeint);
             $action = 'create';
         }
-        if (GETPOST('type')=='select' && !GETPOST('param'))
+        if ($type=='select' && !$param)
         {
         	$error++;
         	$langs->load("errors");
         	$mesg[]=$langs->trans("ErrorNoValueForSelectType");
         	$action = 'create';
         }
-        if (GETPOST('type')=='sellist' && !GETPOST('param'))
+        if ($type=='sellist' && !$param)
         {
         	$error++;
         	$langs->load("errors");
         	$mesg[]=$langs->trans("ErrorNoValueForSelectListType");
         	$action = 'create';
         }
-        if (GETPOST('type')=='checkbox' && !GETPOST('param'))
+        if ($type=='checkbox' && !$param)
         {
         	$error++;
         	$langs->load("errors");
         	$mesg[]=$langs->trans("ErrorNoValueForCheckBoxType");
         	$action = 'create';
         }
-        if (GETPOST('type')=='link' && !GETPOST('param'))
+        if ($type=='link' && !$param)
         {
         	$error++;
         	$langs->load("errors");
         	$mesg[]=$langs->trans("ErrorNoValueForLinkType");
         	$action = 'create';
         }
-        if (GETPOST('type')=='radio' && !GETPOST('param'))
+        if ($type=='radio' && !$param)
         {
         	$error++;
         	$langs->load("errors");
         	$mesg[]=$langs->trans("ErrorNoValueForRadioType");
         	$action = 'create';
         }
-        if  (((GETPOST('type')=='radio') || (GETPOST('type')=='checkbox')) && GETPOST('param'))
+        if  ((($type=='radio') || ($type=='checkbox')) && $param)
         {
         	// Construct array for parameter (value of select list)
-    		$parameters = GETPOST('param');
+    		$parameters = $param;
     		$parameters_array = explode("\r\n",$parameters);
     		foreach($parameters_array as $param_ligne)
     		{
@@ -134,11 +139,11 @@ if ($action == 'add')
     		if (isset($_POST["attrname"]) && preg_match("/^[a-z0-9-_]+$/",$_POST['attrname']) && !is_numeric($_POST["attrname"]))
     		{
     			// Construct array for parameter (value of select list)
-        		$default_value = GETPOST('default_value');
-    			$parameters = GETPOST('param');
+        		$default_value = GETPOST('default_value','alpha');
+    			$parameters = $param;
     			$parameters_array = explode("\r\n",$parameters);
     			//In sellist we have only one line and it can have come to do SQL expression
-    			if (GETPOST('type')=='sellist') {
+    			if ($type=='sellist') {
     				foreach($parameters_array as $param_ligne)
     				{
     					$params['options'] = array($parameters=>null);
@@ -155,20 +160,21 @@ if ($action == 'add')
     			}
 
                 $result=$extrafields->addExtraField(
-                	GETPOST('attrname'),
-                	GETPOST('label'),
-                	GETPOST('type'),
-                	GETPOST('pos'),
+                	GETPOST('attrname', 'alpha'),
+                	GETPOST('label', 'alpha'),
+                	$type,
+                	GETPOST('pos', 'alpha'),
                 	$extrasize,
                 	$elementtype,
-                	(GETPOST('unique')?1:0),
-                	(GETPOST('required')?1:0),
+                	(GETPOST('unique', 'alpha')?1:0),
+                	(GETPOST('required', 'alpha')?1:0),
                 	$default_value,
                 	$params,
-                	(GETPOST('alwayseditable')?1:0),
-                	(GETPOST('perms')?GETPOST('perms'):''),
-                	(GETPOST('list')?1:0),
-					(GETPOST('ishidden')?1:0)
+                	(GETPOST('alwayseditable', 'alpha')?1:0),
+                	(GETPOST('perms', 'alpha')?GETPOST('perms', 'alpha'):''),
+                	(GETPOST('list', 'alpha')?1:0),
+					(GETPOST('ishidden', 'alpha')?1:0),
+                    GETPOST('computed_value','alpha')
                 );
     			if ($result > 0)
     			{
@@ -205,66 +211,66 @@ if ($action == 'update')
 	if ($_POST["button"] != $langs->trans("Cancel"))
 	{
         // Check values
-		if (! GETPOST('type'))
+		if (! $type)
 		{
 			$error++;
 			$langs->load("errors");
 			$mesg[]=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Type"));
 			$action = 'edit';
 		}
-		if (GETPOST('type')=='varchar' && $extrasize <= 0)
+		if ($type=='varchar' && $extrasize <= 0)
 		{
 		    $error++;
 		    $langs->load("errors");
 		    $mesg[]=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Size"));
 		    $action = 'edit';
 		}
-		if (GETPOST('type')=='varchar' && $extrasize > $maxsizestring)
+		if ($type=='varchar' && $extrasize > $maxsizestring)
         {
             $error++;
             $langs->load("errors");
             $mesg[]=$langs->trans("ErrorSizeTooLongForVarcharType",$maxsizestring);
             $action = 'edit';
         }
-        if (GETPOST('type')=='int' && $extrasize > $maxsizeint)
+        if ($type=='int' && $extrasize > $maxsizeint)
         {
             $error++;
             $langs->load("errors");
             $mesg[]=$langs->trans("ErrorSizeTooLongForIntType",$maxsizeint);
             $action = 'edit';
         }
-        if (GETPOST('type')=='select' && !GETPOST('param'))
+        if ($type=='select' && !$param)
         {
         	$error++;
         	$langs->load("errors");
         	$mesg[]=$langs->trans("ErrorNoValueForSelectType");
         	$action = 'edit';
         }
-        if (GETPOST('type')=='sellist' && !GETPOST('param'))
+        if ($type=='sellist' && !$param)
         {
         	$error++;
         	$langs->load("errors");
         	$mesg[]=$langs->trans("ErrorNoValueForSelectListType");
         	$action = 'edit';
         }
-        if (GETPOST('type')=='checkbox' && !GETPOST('param'))
+        if ($type=='checkbox' && !$param)
         {
         	$error++;
         	$langs->load("errors");
         	$mesg[]=$langs->trans("ErrorNoValueForCheckBoxType");
         	$action = 'edit';
         }
-        if (GETPOST('type')=='radio' && !GETPOST('param'))
+        if ($type=='radio' && !$param)
         {
         	$error++;
         	$langs->load("errors");
         	$mesg[]=$langs->trans("ErrorNoValueForRadioType");
         	$action = 'edit';
         }
-        if  (((GETPOST('type')=='radio') || (GETPOST('type')=='checkbox')) && GETPOST('param'))
+        if  ((($type=='radio') || ($type=='checkbox')) && $param)
         {
         	// Construct array for parameter (value of select list)
-        	$parameters = GETPOST('param');
+        	$parameters = $param;
         	$parameters_array = explode("\r\n",$parameters);
         	foreach($parameters_array as $param_ligne)
         	{
@@ -295,10 +301,10 @@ if ($action == 'update')
     		{
     			$pos = GETPOST('pos','int');
     			// Construct array for parameter (value of select list)
-    			$parameters = GETPOST('param');
+    			$parameters = $param;
     			$parameters_array = explode("\r\n",$parameters);
     			//In sellist we have only one line and it can have come to do SQL expression
-    			if (GETPOST('type')=='sellist') {
+    			if ($type=='sellist') {
     				foreach($parameters_array as $param_ligne)
     				{
     					$params['options'] = array($parameters=>null);
@@ -315,19 +321,21 @@ if ($action == 'update')
     			}
 
     			$result=$extrafields->update(
-    				GETPOST('attrname'),
-    				GETPOST('label'),
-    				GETPOST('type'),
+    				GETPOST('attrname', 'alpha'),
+    				GETPOST('label', 'alpha'),
+    				$type,
     				$extrasize,
     				$elementtype,
-    				(GETPOST('unique')?1:0),
-    				(GETPOST('required')?1:0),
+    				(GETPOST('unique', 'alpha')?1:0),
+    				(GETPOST('required', 'alpha')?1:0),
     				$pos,
     				$params,
-    				(GETPOST('alwayseditable')?1:0),
-    				(GETPOST('perms')?GETPOST('perms'):''),
-                	(GETPOST('list')?1:0),
-					(GETPOST('ishidden')?1:0)
+    				(GETPOST('alwayseditable', 'alpha')?1:0),
+    				(GETPOST('perms', 'alpha')?GETPOST('perms', 'alpha'):''),
+                	(GETPOST('list', 'alpha')?1:0),
+					(GETPOST('ishidden', 'alpha')?1:0),
+    			    GETPOST('default_value','alpha'),
+    			    GETPOST('computed_value','alpha')
     			);
     			if ($result > 0)
     			{

+ 69 - 28
htdocs/core/class/extrafields.class.php

@@ -36,14 +36,22 @@
 class ExtraFields
 {
 	var $db;
-	// Tableau contenant le nom des champs en clef et la definition de ces champs
+
+	// type of element (for what object is the extrafield)
+	var $attribute_elementtype;
+	
+	// Array with type of the extra field
 	var $attribute_type;
-	// Tableau contenant le nom des champs en clef et le label de ces champs en value
+	// Array with label of extra field
 	var $attribute_label;
-	// Tableau contenant le nom des champs en clef et la taille/longueur max de ces champs en value
+	// Array with size of extra field
 	var $attribute_size;
-	// Tableau contenant le nom des choix en clef et la valeur de ces choix en value
+	// array with list of possible values for some types of extra fields
 	var $attribute_choice;
+	// Array to store compute formula for computed fields
+	var $attribute_computed;
+	// Array to store default value
+	var $attribute_default;
 	// Array to store if attribute is unique or not
 	var $attribute_unique;
 	// Array to store if attribute is required or not
@@ -77,16 +85,17 @@ class ExtraFields
 	'phone'=>'ExtrafieldPhone',
 	'mail'=>'ExtrafieldMail',
 	'url'=>'ExtrafieldUrl',
+	'password' => 'ExtrafieldPassword',
 	'select' => 'ExtrafieldSelect',
 	'sellist' => 'ExtrafieldSelectList',
 	'radio' => 'ExtrafieldRadio',
 	'checkbox' => 'ExtrafieldCheckBox',
 	'chkbxlst' => 'ExtrafieldCheckBoxFromList',
 	'link' => 'ExtrafieldLink',
-	'password' => 'ExtrafieldPassword',
 	'separate' => 'ExtrafieldSeparator',
 	);
 
+	
 	/**
 	 *	Constructor
 	 *
@@ -96,10 +105,12 @@ class ExtraFields
 	{
 		$this->db = $db;
 		$this->error = array();
+		$this->attribute_elementtype = array();
 		$this->attribute_type = array();
 		$this->attribute_label = array();
 		$this->attribute_size = array();
-		$this->attribute_elementtype = array();
+		$this->attribute_computed = array();
+		$this->attribute_default = array();
 		$this->attribute_unique = array();
 		$this->attribute_required = array();
 		$this->attribute_perms = array();
@@ -118,15 +129,16 @@ class ExtraFields
 	 *  @param  string	$elementtype        Element type ('member', 'product', 'thirdparty', ...)
 	 *  @param	int		$unique				Is field unique or not
 	 *  @param	int		$required			Is field required or not
-	 *  @param	string	$default_value		Defaulted value (Example: '', '0', 'null', 'avalue')
+	 *  @param	string	$default_value		Defaulted value (In database. use the default_value feature for default value on screen. Example: '', '0', 'null', 'avalue')
 	 *  @param  array	$param				Params for field
 	 *  @param  int		$alwayseditable		Is attribute always editable regardless of the document status
 	 *  @param	string	$perms				Permission to check
 	 *  @param	int		$list				Into list view by default
 	 *  @param	int		$ishidden			Is hidden extrafield (warning, do not rely on this. If your module need a hidden data, it must use its own table)
+	 *  @param  string  $computed           Computed value
 	 *  @return int      					<=0 if KO, >0 if OK
 	 */
-	function addExtraField($attrname, $label, $type, $pos, $size, $elementtype, $unique=0, $required=0, $default_value='', $param=0, $alwayseditable=0, $perms='', $list=0, $ishidden=0)
+	function addExtraField($attrname, $label, $type, $pos, $size, $elementtype, $unique=0, $required=0, $default_value='', $param=0, $alwayseditable=0, $perms='', $list=0, $ishidden=0, $computed='')
 	{
 		if (empty($attrname)) return -1;
 		if (empty($label)) return -1;
@@ -137,13 +149,13 @@ class ExtraFields
 		// Create field into database except for separator type which is not stored in database
 		if ($type != 'separate')
 		{
-			$result=$this->create($attrname, $type, $size, $elementtype, $unique, $required, $default_value, $param, $perms, $list);
+			$result=$this->create($attrname, $type, $size, $elementtype, $unique, $required, $default_value, $param, $perms, $list, $copmputed);
 		}
 		$err1=$this->errno;
 		if ($result > 0 || $err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' || $type == 'separate')
 		{
 			// Add declaration of field into table
-			$result2=$this->create_label($attrname,$label,$type,$pos,$size,$elementtype, $unique, $required, $param, $alwayseditable, $perms, $list, $ishidden);
+			$result2=$this->create_label($attrname, $label, $type, $pos, $size, $elementtype, $unique, $required, $param, $alwayseditable, $perms, $list, $ishidden, $default, $computed);
 			$err2=$this->errno;
 			if ($result2 > 0 || ($err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS'))
 			{
@@ -169,13 +181,14 @@ class ExtraFields
 	 *  @param  string	$elementtype        Element type ('member', 'product', 'thirdparty', 'contact', ...)
 	 *  @param	int		$unique				Is field unique or not
 	 *  @param	int		$required			Is field required or not
-	 *  @param  string  $default_value		Default value for field
+	 *  @param  string  $default_value		Default value for field (in database)
 	 *  @param  array	$param				Params for field  (ex for select list : array('options'=>array('value'=>'label of option'))
 	 *  @param	string	$perms				Permission
 	 *	@param	int		$list				Into list view by default
+	 *  @param  string  $computed           Computed value
 	 *  @return int      	           		<=0 if KO, >0 if OK
 	 */
-	private function create($attrname, $type='varchar', $length=255, $elementtype='member', $unique=0, $required=0, $default_value='',$param='', $perms='', $list=0)
+	private function create($attrname, $type='varchar', $length=255, $elementtype='member', $unique=0, $required=0, $default_value='',$param='', $perms='', $list=0, $computed='')
 	{
 		if ($elementtype == 'thirdparty') $elementtype='societe';
 		if ($elementtype == 'contact') $elementtype='socpeople';
@@ -258,9 +271,11 @@ class ExtraFields
 	 *  @param	string			$perms			Permission to check
 	 *  @param	int				$list			Into list view by default
 	 *  @param	int				$ishidden		Is hidden extrafield (warning, do not rely on this. If your module need a hidden data, it must use its own table)
+	 *  @param  string          $default        Default value (in database. use the default_value feature for default value on screen).
+	 *  @param  string          $computed       Computed value
 	 *  @return	int								<=0 if KO, >0 if OK
 	 */
-	private function create_label($attrname, $label='', $type='', $pos=0, $size=0, $elementtype='member', $unique=0, $required=0, $param='', $alwayseditable=0, $perms='', $list=0, $ishidden=0)
+	private function create_label($attrname, $label='', $type='', $pos=0, $size=0, $elementtype='member', $unique=0, $required=0, $param='', $alwayseditable=0, $perms='', $list=0, $ishidden=0, $default='', $computed='')
 	{
 		global $conf;
 
@@ -286,7 +301,7 @@ class ExtraFields
 				$params='';
 			}
 
-			$sql = "INSERT INTO ".MAIN_DB_PREFIX."extrafields(name, label, type, pos, size, entity, elementtype, fieldunique, fieldrequired, param, alwayseditable, perms, list, ishidden)";
+			$sql = "INSERT INTO ".MAIN_DB_PREFIX."extrafields(name, label, type, pos, size, entity, elementtype, fieldunique, fieldrequired, param, alwayseditable, perms, list, ishidden, fielddefault, fieldcomputed)";
 			$sql.= " VALUES('".$attrname."',";
 			$sql.= " '".$this->db->escape($label)."',";
 			$sql.= " '".$type."',";
@@ -299,8 +314,10 @@ class ExtraFields
 			$sql.= " '".$params."',";
 			$sql.= " '".$alwayseditable."',";
 			$sql.= " ".($perms?"'".$this->db->escape($perms)."'":"null").",";
-			$sql.= " ".$list;
-			$sql.= ", ".$ishidden;
+			$sql.= " ".$list.",";
+			$sql.= " ".$ishidden.",";
+			$sql.= " ".($default?"'".$this->db->escape($default)."'":"null").",";
+			$sql.= " ".($computed?"'".$this->db->escape($computed)."'":"null");
 			$sql.=')';
 
 			dol_syslog(get_class($this)."::create_label", LOG_DEBUG);
@@ -430,9 +447,11 @@ class ExtraFields
 	 *  @param	string	$perms				Permission to check
 	 *  @param	int		$list				Into list view by default
 	 *  @param	int		$ishidden			Is hidden extrafield (warning, do not rely on this. If your module need a hidden data, it must use its own table)
+	 *  @param  string  $default            Default value (in database. use the default_value feature for default value on screen).
+	 *  @param  string  $computed           Computed value
 	 * 	@return	int							>0 if OK, <=0 if KO
 	 */
-	function update($attrname,$label,$type,$length,$elementtype,$unique=0,$required=0,$pos=0,$param='',$alwayseditable=0, $perms='',$list='',$ishidden=0)
+	function update($attrname,$label,$type,$length,$elementtype,$unique=0,$required=0,$pos=0,$param='',$alwayseditable=0, $perms='',$list='',$ishidden=0,$default='',$computed='')
 	{
 		if ($elementtype == 'thirdparty') $elementtype='societe';
 		if ($elementtype == 'contact') $elementtype='socpeople';
@@ -479,7 +498,7 @@ class ExtraFields
 			{
 				if ($label)
 				{
-					$result=$this->update_label($attrname,$label,$type,$length,$elementtype,$unique,$required,$pos,$param,$alwayseditable,$perms,$list,$ishidden);
+					$result=$this->update_label($attrname,$label,$type,$length,$elementtype,$unique,$required,$pos,$param,$alwayseditable,$perms,$list,$ishidden,$default,$computed);
 				}
 				if ($result > 0)
 				{
@@ -531,12 +550,14 @@ class ExtraFields
 	 *  @param	string	$perms				Permission to check
 	 *  @param	int		$list				Into list view by default
 	 *  @param	int		$ishidden			Is hidden extrafield (warning, do not rely on this. If your module need a hidden data, it must use its own table)
+	 *  @param  string  $default            Default value (in database. use the default_value feature for default value on screen).
+	 *  @param  string  $computed           Computed value
 	 *  @return	int							<=0 if KO, >0 if OK
 	 */
-	private function update_label($attrname,$label,$type,$size,$elementtype,$unique=0,$required=0,$pos=0,$param='',$alwayseditable=0,$perms='',$list=0,$ishidden=0)
+	private function update_label($attrname,$label,$type,$size,$elementtype,$unique=0,$required=0,$pos=0,$param='',$alwayseditable=0,$perms='',$list=0,$ishidden=0,$default='',$computed='')
 	{
 		global $conf;
-		dol_syslog(get_class($this)."::update_label ".$attrname.", ".$label.", ".$type.", ".$size.", ".$elementtype.", ".$unique.", ".$required.", ".$pos.", ".$alwayseditable.", ".$perms.", ".$list.", ".$ishidden);
+		dol_syslog(get_class($this)."::update_label ".$attrname.", ".$label.", ".$type.", ".$size.", ".$elementtype.", ".$unique.", ".$required.", ".$pos.", ".$alwayseditable.", ".$perms.", ".$list.", ".$ishidden.", ".$default.", ".$computed);
 
 		// Clean parameters
 		if ($elementtype == 'thirdparty') $elementtype='societe';
@@ -557,7 +578,7 @@ class ExtraFields
 			$sql_del.= " WHERE name = '".$attrname."'";
 			$sql_del.= " AND entity = ".$conf->entity;
 			$sql_del.= " AND elementtype = '".$elementtype."'";
-			dol_syslog(get_class($this)."::update_label", LOG_DEBUG);
+
 			$resql1=$this->db->query($sql_del);
 
 			$sql = "INSERT INTO ".MAIN_DB_PREFIX."extrafields(";
@@ -573,8 +594,10 @@ class ExtraFields
 			$sql.= " pos,";
 			$sql.= " alwayseditable,";
 			$sql.= " param,";
-			$sql.= " list";
-			$sql.= ", ishidden";
+			$sql.= " list,";
+			$sql.= " ishidden,";
+			$sql.= " fielddefault,";
+			$sql.= " fieldcomputed";
 			$sql.= ") VALUES (";
 			$sql.= "'".$attrname."',";
 			$sql.= " ".$conf->entity.",";
@@ -588,10 +611,12 @@ class ExtraFields
 			$sql.= " '".$pos."',";
 			$sql.= " '".$alwayseditable."',";
 			$sql.= " '".$param."',";
-			$sql.= " ".$list;
-			$sql.= ", ".$ishidden;
+			$sql.= " ".$list.", ";
+			$sql.= " ".$ishidden.", ";
+			$sql.= " ".($default?"'".$this->db->escape($default)."'":"null").",";
+			$sql.= " ".($computed?"'".$this->db->escape($computed)."'":"null");
 			$sql.= ")";
-			dol_syslog(get_class($this)."::update_label", LOG_DEBUG);
+
 			$resql2=$this->db->query($sql);
 
 			if ($resql1 && $resql2)
@@ -635,7 +660,7 @@ class ExtraFields
 		// For avoid conflicts with external modules
 		if (!$forceload && !empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return $array_name_label;
 
-		$sql = "SELECT rowid,name,label,type,size,elementtype,fieldunique,fieldrequired,param,pos,alwayseditable,perms,list,ishidden";
+		$sql = "SELECT rowid,name,label,type,size,elementtype,fieldunique,fieldrequired,param,pos,alwayseditable,perms,list,ishidden,fielddefault,fieldcomputed";
 		$sql.= " FROM ".MAIN_DB_PREFIX."extrafields";
 		$sql.= " WHERE entity IN (0,".$conf->entity.")";
 		if ($elementtype) $sql.= " AND elementtype = '".$elementtype."'";
@@ -658,6 +683,8 @@ class ExtraFields
 					$this->attribute_label[$tab->name]=$tab->label;
 					$this->attribute_size[$tab->name]=$tab->size;
 					$this->attribute_elementtype[$tab->name]=$tab->elementtype;
+					$this->attribute_default[$tab->name]=$tab->fielddefault;
+					$this->attribute_computed[$tab->name]=$tab->fieldcomputed;
 					$this->attribute_unique[$tab->name]=$tab->fieldunique;
 					$this->attribute_required[$tab->name]=$tab->fieldrequired;
 					$this->attribute_param[$tab->name]=($tab->param ? unserialize($tab->param) : '');
@@ -699,6 +726,8 @@ class ExtraFields
 		$type =$this->attribute_type[$key];
 		$size =$this->attribute_size[$key];
 		$elementtype=$this->attribute_elementtype[$key];
+		$default=$this->attribute_default[$key];
+		$computed=$this->attribute_computed[$key];
 		$unique=$this->attribute_unique[$key];
 		$required=$this->attribute_required[$key];
 		$param=$this->attribute_param[$key];
@@ -706,6 +735,8 @@ class ExtraFields
 		$list=$this->attribute_list[$key];
 		$hidden=$this->attribute_hidden[$key];
 
+		if ($computed) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
+		
 		if (empty($showsize))
 		{
     		if ($type == 'date')
@@ -1218,10 +1249,12 @@ class ExtraFields
 	{
 		global $conf,$langs;
 
+		$elementtype=$this->attribute_elementtype[$key];
 		$label=$this->attribute_label[$key];
 		$type=$this->attribute_type[$key];
 		$size=$this->attribute_size[$key];
-		$elementtype=$this->attribute_elementtype[$key];
+		$default=$this->attribute_default[$key];
+		$computed=$this->attribute_computed[$key];
 		$unique=$this->attribute_unique[$key];
 		$required=$this->attribute_required[$key];
 		$params=$this->attribute_param[$key];
@@ -1229,6 +1262,14 @@ class ExtraFields
 		$list=$this->attribute_list[$key];
 		$hidden=$this->attribute_hidden[$key];	// warning, do not rely on this. If your module need a hidden data, it must use its own table.
 
+		// If field is a computed field, value must become result of compute
+		if ($computed)
+		{
+		    // Make the eval of compute string
+		    //var_dump($computed);
+		    $value = dol_eval($computed, 1, 0);
+		}
+		
 		$showsize=0;
 		if ($type == 'date')
 		{

+ 18 - 6
htdocs/core/lib/functions.lib.php

@@ -5604,20 +5604,32 @@ function verifCond($strRights)
  *
  * @param 	string	$s				String to evaluate
  * @param	int		$returnvalue	0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)).
+ * @param   int     $hideerrors     1=Hide errors
  * @return	mixed					Nothing or return of eval
  */
-function dol_eval($s,$returnvalue=0)
+function dol_eval($s, $returnvalue=0, $hideerrors=1)
 {
 	// Only global variables can be changed by eval function and returned to caller
-	global $langs, $user, $conf;
-	global $leftmenu;
+	global $db, $langs, $user, $conf;
+	global $mainmenu, $leftmenu;
 	global $rights;
 	global $object;
-    global $soc;
+	global $mysoc;
+	
+	global $obj;       // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object      
+	global $soc;       // For backward compatibility
 
 	//print $s."<br>\n";
-	if ($returnvalue) return @eval('return '.$s.';');
-	else @eval($s);
+	if ($returnvalue)
+	{
+	    if ($hideerrors) return @eval('return '.$s.';');
+	    else return eval('return '.$s.';');
+	}
+	else
+	{
+	    if ($hideerrors) @eval($s);
+	    else eval($s);
+	}
 }
 
 /**

+ 52 - 15
htdocs/core/tpl/admin_extrafields_add.tpl.php

@@ -18,10 +18,12 @@
  */
 
 /**
- * The following vars must be defined
+ * The following vars must be defined:
  * $type2label
  * $form
  * $conf, $lang,
+ * The following vars may also be defined:
+ * $elementtype
  */
 
 ?>
@@ -31,15 +33,16 @@
     jQuery(document).ready(function() {
     	function init_typeoffields(type)
     	{
-        	console.log("select new type "+type);
+        	console.log("selected type is "+type);
     		var size = jQuery("#size");
+    		var computed_value = jQuery("#computed_value");
+    		var default_value = jQuery("#default_value");
     		var unique = jQuery("#unique");
     		var required = jQuery("#required");
-    		var default_value = jQuery("#default_value");
     		var alwayseditable = jQuery("#alwayseditable");
     		var list = jQuery("#list");
     		<?php
-    		if((GETPOST('type') != "select") && (GETPOST('type') != "sellist"))
+    		if ((GETPOST('type') != "select") && (GETPOST('type') != "sellist"))
     		{
     			print 'jQuery("#value_choice").hide();';
     		}
@@ -51,6 +54,28 @@
     		}
     		?>
 
+    		// Case of computed field
+    		console.log(type);
+    		if (type == '' || type == 'varchar' || type == 'int' || type == 'double' || type == 'price') { 
+    			jQuery("tr.extra_computed_value").show(); 
+    		} else {
+    			computed_value.val(''); jQuery("tr.extra_computed_value").hide();
+    		} 
+    		if (computed_value.val())
+    		{
+        		console.log("We enter a computed formula");
+        		jQuery("#default_value").val('');
+        		/* jQuery("#unique, #required, #alwayseditable, #ishidden, #list").removeAttr('checked'); */
+        		jQuery("#default_value, #unique, #required, #alwayseditable, #ishidden, #list").attr('disabled', true);
+        		jQuery("tr.extra_default_value, tr.extra_unique, tr.extra_required, tr.extra_alwayseditable, tr.extra_ishidden, tr.extra_list").hide();
+    		}
+    		else
+    		{
+        		console.log("No computed formula");
+        		jQuery("#default_value, #unique, #required, #alwayseditable, #ishidden, #list").attr('disabled', false);
+        		jQuery("tr.extra_default_value, tr.extra_unique, tr.extra_required, tr.extra_alwayseditable, tr.extra_ishidden, tr.extra_list").show();
+    		}
+    		
 			if (type == 'date')          { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); }
 			else if (type == 'datetime') { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();}
     		else if (type == 'double')   { size.val('24,8').removeAttr('disabled'); unique.removeAttr('disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();}
@@ -65,7 +90,10 @@
 			else if (type == 'checkbox') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
 			else if (type == 'chkbxlst') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").show();jQuery("#helplink").hide();}
 			else if (type == 'link')     { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").show();}
-			else if (type == 'separate') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); required.val('').prop('disabled', true); default_value.val('').prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();}
+			else if (type == 'separate') { 
+				size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); required.val('').prop('disabled', true); 
+				jQuery("#value_choice").hide();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();
+			}
 			else {	// type = string
 				size.val('').prop('disabled', true);
 				unique.removeAttr('disabled');
@@ -73,10 +101,12 @@
 
 			if (type == 'separate')
 			{
-				unique.removeAttr('checked').prop('disabled', true); required.removeAttr('checked').prop('disabled', true); alwayseditable.removeAttr('checked').prop('disabled', true); list.val('').prop('disabled', true); 
+				required.removeAttr('checked').prop('disabled', true); alwayseditable.removeAttr('checked').prop('disabled', true); list.val('').prop('disabled', true);
+				jQuery('#size, #default_value').val('').prop('disabled', true); 
 			}
 			else
 			{
+				default_value.removeAttr('disabled');
 				required.removeAttr('disabled'); alwayseditable.removeAttr('disabled'); list.val('').removeAttr('disabled'); 
 			}
     	}
@@ -84,6 +114,11 @@
     	jQuery("#type").change(function() {
     		init_typeoffields($(this).val());
     	});
+
+    	// If we enter a formula, we disable other fields
+    	jQuery("#computed_value").keyup(function() {
+    		init_typeoffields(jQuery('#type').val());
+    	});
     });
 </script>
 
@@ -103,9 +138,7 @@
 <?php print $form->selectarray('type',$type2label,GETPOST('type')); ?>
 </td></tr>
 <!-- Size -->
-<tr><td class="fieldrequired"><?php echo $langs->trans("Size"); ?></td><td class="valeur"><input id="size" type="text" name="size" size="5" value="<?php echo (GETPOST('size')?GETPOST('size'):''); ?>"></td></tr>
-<!-- Position -->
-<tr><td><?php echo $langs->trans("Position"); ?></td><td class="valeur"><input type="text" name="pos" size="5" value="<?php echo GETPOST('pos'); ?>"></td></tr>
+<tr class="extra_size"><td class="fieldrequired"><?php echo $langs->trans("Size"); ?></td><td class="valeur"><input id="size" type="text" name="size" size="5" value="<?php echo (GETPOST('size')?GETPOST('size'):''); ?>"></td></tr>
 <!-- Default Value (for select list / radio/ checkbox) -->
 <tr id="value_choice">
 <td>
@@ -124,17 +157,21 @@
     </table>
 </td>
 </tr>
-<!-- Default Value -->
-<tr><td><?php echo $langs->trans("DefaultValue"); ?></td><td class="valeur"><input id="default_value" type="text" name="default_value" size="5" value="<?php echo (GETPOST('"default_value"')?GETPOST('"default_value"'):''); ?>"></td></tr>
+<!-- Position -->
+<tr><td class="titlefield"><?php echo $langs->trans("Position"); ?></td><td class="valeur"><input type="text" name="pos" size="5" value="<?php echo GETPOST('pos'); ?>"></td></tr>
+<!-- Computed Value -->
+<tr class="extra_computed_value"><td><?php echo $form->textwithpicto($langs->trans("ComputedFormula"), $langs->trans("ComputedFormulaDesc"), 1, 'help', '', 0, 2, 'tooltipcompute'); ?></td><td class="valeur"><input id="computed_value" type="text" name="computed_value" class="quatrevingtpercent" value="<?php echo (GETPOST('"computed_value"')?GETPOST('"computed_value"'):''); ?>"></td></tr>
+<!-- Default Value (at sql setup level) -->
+<tr class="extra_default_value"><td><?php echo $langs->trans("DefaultValue").' ('.$langs->trans("Database").')'; ?></td><td class="valeur"><input id="default_value" type="text" name="default_value" size="5" value="<?php echo (GETPOST('"default_value"')?GETPOST('"default_value"'):''); ?>"></td></tr>
 <!-- Unique -->
-<tr><td><?php echo $langs->trans("Unique"); ?></td><td class="valeur"><input id="unique" type="checkbox" name="unique"<?php echo (GETPOST('unique')?' checked':''); ?>></td></tr>
+<tr class="extra_unique"><td><?php echo $langs->trans("Unique"); ?></td><td class="valeur"><input id="unique" type="checkbox" name="unique"<?php echo (GETPOST('unique')?' checked':''); ?>></td></tr>
 <!-- Required -->
-<tr><td><?php echo $langs->trans("Required"); ?></td><td class="valeur"><input id="required" type="checkbox" name="required"<?php echo (GETPOST('required')?' checked':''); ?>></td></tr>
+<tr class="extra_required"><td><?php echo $langs->trans("Required"); ?></td><td class="valeur"><input id="required" type="checkbox" name="required"<?php echo (GETPOST('required')?' checked':''); ?>></td></tr>
 <!-- Always editable -->
-<tr><td><?php echo $langs->trans("AlwaysEditable"); ?></td><td class="valeur"><input id="alwayseditable" type="checkbox" name="alwayseditable"<?php echo ((GETPOST('alwayseditable') || ! GETPOST('button'))?' checked':''); ?>></td></tr>
+<tr class="extra_alwayseditable"><td><?php echo $langs->trans("AlwaysEditable"); ?></td><td class="valeur"><input id="alwayseditable" type="checkbox" name="alwayseditable"<?php echo ((GETPOST('alwayseditable') || ! GETPOST('button'))?' checked':''); ?>></td></tr>
 <!-- Is visible or not -->
 <?php if (! empty($conf->global->MAIN_CAN_HIDE_EXTRAFIELDS)) { ?>
-<tr><td><?php echo $langs->trans("Hidden"); ?></td><td class="valeur"><input id="ishidden" type="checkbox" name="ishidden"<?php echo (GETPOST('ishidden') ?' checked' : ''); ?>></td></tr>
+<tr class="extra_ishidden"><td><?php echo $langs->trans("Hidden"); ?></td><td class="valeur"><input id="ishidden" type="checkbox" name="ishidden"<?php echo (GETPOST('ishidden') ?' checked' : ''); ?>></td></tr>
 <?php } ?>
 <?php if ($conf->global->MAIN_FEATURES_LEVEL >= 2) { ?>
 <!-- By default visible into list -->

+ 52 - 7
htdocs/core/tpl/admin_extrafields_edit.tpl.php

@@ -15,6 +15,16 @@
  * You should have received a copy of the GNU General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
+
+/**
+ * The following vars must be defined:
+ * $type2label
+ * $form
+ * $conf, $lang,
+ * The following vars may also be defined:
+ * $elementtype
+ */
+
 ?>
 
 <!-- BEGIN PHP TEMPLATE admin_extrafields_edit.tpl.php -->
@@ -24,9 +34,10 @@
     	{
         	console.log("select new type "+type);
     		var size = jQuery("#size");
+    		var computed_value = jQuery("#computed_value");
+    		var default_value = jQuery("#default_value");
     		var unique = jQuery("#unique");
     		var required = jQuery("#required");
-    		var default_value = jQuery("#default_value");
     		var alwayseditable = jQuery("#alwayseditable");
     		var list = jQuery("#list");
     		<?php
@@ -42,6 +53,27 @@
     		}
     		?>
 
+    		// Case of computed field
+    		if (type == 'varchar' || type == 'int' || type == 'double' || type == 'price') { 
+    			jQuery("tr.extra_computed_value").show(); 
+    		} else {
+    			computed_value.val(''); jQuery("tr.extra_computed_value").hide();
+    		} 
+    		if (computed_value.val())
+    		{
+        		console.log("We enter a computed formula");
+        		jQuery("#default_value").val('');
+        		/* jQuery("#unique, #required, #alwayseditable, #ishidden, #list").removeAttr('checked'); */
+        		jQuery("#default_value, #unique, #required, #alwayseditable, #ishidden, #list").attr('disabled', true);
+        		jQuery("tr.extra_default_value, tr.extra_unique, tr.extra_required, tr.extra_alwayseditable, tr.extra_ishidden, tr.extra_list").hide();
+    		}
+    		else
+    		{
+        		console.log("No computed formula");
+        		jQuery("#default_value, #unique, #required, #alwayseditable, #ishidden, #list").attr('disabled', false);
+        		jQuery("tr.extra_default_value, tr.extra_unique, tr.extra_required, tr.extra_alwayseditable, tr.extra_ishidden, tr.extra_list").show();
+    		}
+    		
 			if (type == 'date') { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); }
 			else if (type == 'datetime') { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();}
     		else if (type == 'double')   { size.removeAttr('disabled'); unique.removeAttr('disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();}
@@ -65,9 +97,11 @@
 			if (type == 'separate')
 			{
 				required.removeAttr('checked').prop('disabled', true); alwayseditable.removeAttr('checked').prop('disabled', true); list.val('').prop('disabled', true); 
+				jQuery('#size, #default_value').val('').prop('disabled', true); 
 			}
 			else
 			{
+				default_value.removeAttr('disabled');
 				required.removeAttr('disabled'); alwayseditable.removeAttr('disabled'); list.val('').removeAttr('disabled'); 
 			}			
     	}
@@ -75,6 +109,11 @@
     	jQuery("#type").change(function() {
     		init_typeoffields($(this).val());
     	});
+
+    	// If we enter a formula, we disable other fields
+    	jQuery("#computed_value").keyup(function() {
+    		init_typeoffields(jQuery('#type').val());
+    	});    	
     });
 </script>
 
@@ -92,6 +131,8 @@
 <?php
 $type=$extrafields->attribute_type[$attrname];
 $size=$extrafields->attribute_size[$attrname];
+$computed=$extrafields->attribute_computed[$attrname];
+$default=$extrafields->attribute_default[$attrname];
 $unique=$extrafields->attribute_unique[$attrname];
 $required=$extrafields->attribute_required[$attrname];
 $pos=$extrafields->attribute_pos[$attrname];
@@ -156,9 +197,7 @@ else
 ?>
 </td></tr>
 <!-- Size -->
-<tr><td class="fieldrequired"><?php echo $langs->trans("Size"); ?></td><td><input id="size" type="text" name="size" size="5" value="<?php echo $size; ?>"></td></tr>
-<!-- Position -->
-<tr><td><?php echo $langs->trans("Position"); ?></td><td class="valeur"><input type="text" name="pos" size="5" value="<?php  echo $extrafields->attribute_pos[$attrname];  ?>"></td></tr>
+<tr class="extra_size"><td class="fieldrequired"><?php echo $langs->trans("Size"); ?></td><td><input id="size" type="text" name="size" size="5" value="<?php echo $size; ?>"></td></tr>
 <!--  Value (for select list / radio) -->
 <tr id="value_choice">
 <td>
@@ -177,12 +216,18 @@ else
     </table>
 </td>
 </tr>
+<!-- Position -->
+<tr><td class="titlefield"><?php echo $langs->trans("Position"); ?></td><td class="valeur"><input type="text" name="pos" size="5" value="<?php echo dol_escape_htmltag($extrafields->attribute_pos[$attrname]);  ?>"></td></tr>
+<!-- Computed value -->
+<tr class="extra_computed_value"><td><?php echo $form->textwithpicto($langs->trans("ComputedFormula"), $langs->trans("ComputedFormulaDesc"), 1, 'help', '', 0, 2, 'tooltipcompute'); ?></td><td class="valeur"><input id="computed_value" class="quatrevingtpercent" type="text" name="computed_value" value="<?php echo dol_escape_htmltag($computed); ?>"></td></tr>
+<!-- Default value -->
+<!-- Edit of default into sql structure not yet supported -->
 <!-- Unique -->
-<tr><td><?php echo $langs->trans("Unique"); ?></td><td class="valeur"><input id="unique" type="checkbox" name="unique"<?php echo ($unique?' checked':''); ?>></td></tr>
+<tr class="extra_unique"><td><?php echo $langs->trans("Unique"); ?></td><td class="valeur"><input id="unique" type="checkbox" name="unique"<?php echo ($unique?' checked':''); ?>></td></tr>
 <!-- Required -->
-<tr><td><?php echo $langs->trans("Required"); ?></td><td class="valeur"><input id="required" type="checkbox" name="required"<?php echo ($required?' checked':''); ?>></td></tr>
+<tr class="extra_required"><td><?php echo $langs->trans("Required"); ?></td><td class="valeur"><input id="required" type="checkbox" name="required"<?php echo ($required?' checked':''); ?>></td></tr>
 <!-- Always editable -->
-<tr><td><?php echo $langs->trans("AlwaysEditable"); ?></td><td class="valeur"><input id="alwayseditable" type="checkbox" name="alwayseditable"<?php echo ($alwayseditable?' checked':''); ?>></td></tr>
+<tr class="extra_alwayseditable"><td><?php echo $langs->trans("AlwaysEditable"); ?></td><td class="valeur"><input id="alwayseditable" type="checkbox" name="alwayseditable"<?php echo ($alwayseditable?' checked':''); ?>></td></tr>
 <!-- Is visible or not -->
 <?php if (! empty($conf->global->MAIN_CAN_HIDE_EXTRAFIELDS)) { ?>
     <tr><td><?php echo $langs->trans("Hidden"); ?></td><td class="valeur"><input id="ishidden" type="checkbox" name="ishidden"<?php echo ($ishidden ?' checked':''); ?>></td></tr>

+ 5 - 1
htdocs/core/tpl/admin_extrafields_view.tpl.php

@@ -37,12 +37,15 @@ print '<div class="div-table-responsive">';
 print '<table summary="listofattributes" class="noborder" width="100%">';
 
 print '<tr class="liste_titre">';
-print '<td align="left">'.$langs->trans("Position").'</td>';
+print '<td align="left">'.$langs->trans("Position");
+print '<span class="nowrap"><img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/1downarrow.png" alt="" title="A-Z" class="imgdown"></span>';
+print '</td>';
 print '<td>'.$langs->trans("Label").'</td>';
 print '<td>'.$langs->trans("AttributeCode").'</td>';
 print '<td>'.$langs->trans("Type").'</td>';
 print '<td align="right">'.$langs->trans("Size").'</td>';
 print '<td align="center">'.$langs->trans("Unique").'</td>';
+print '<td>'.$langs->trans("ComputedFormula").'</td>';
 print '<td align="center">'.$langs->trans("Required").'</td>';
 print '<td align="center">'.$langs->trans("AlwaysEditable").'</td>';
 if (! empty($conf->global->MAIN_CAN_HIDE_EXTRAFIELDS)) print '<td align="center">'.$langs->trans("Hidden").'</td>';
@@ -61,6 +64,7 @@ if (count($extrafields->attribute_type))
         print "<td>".$type2label[$extrafields->attribute_type[$key]]."</td>\n";
         print '<td align="right">'.$extrafields->attribute_size[$key]."</td>\n";
         print '<td align="center">'.yn($extrafields->attribute_unique[$key])."</td>\n";
+        print '<td>'.dol_trunc($extrafields->attribute_computed[$key], 20)."</td>\n";
         print '<td align="center">'.yn($extrafields->attribute_required[$key])."</td>\n";
         print '<td align="center">'.yn($extrafields->attribute_alwayseditable[$key])."</td>\n";
     	if (! empty($conf->global->MAIN_CAN_HIDE_EXTRAFIELDS)) print '<td align="center">'.yn($extrafields->attribute_hidden[$key])."</td>\n";	// Add hidden option on not working feature. Why hide if user can't see it.

+ 12 - 31
htdocs/includes/restler/framework/Luracast/Restler/explorer/css/screen.css

@@ -350,7 +350,7 @@
   font-size: .85em;
   line-height: 1.2em;
   overflow: auto;
-  max-height: 400px;
+  max-height: 200px;
   cursor: pointer;
 }
 .swagger-section .swagger-ui-wrap .model-signature ul.signature-nav {
@@ -743,17 +743,20 @@
   display: inline-block;
   font-size: 0.9em;
 }
-.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header img {
-  display: block;
-  clear: none;
-  float: right;
-}
 .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header input.submit {
   display: block;
   clear: none;
   float: left;
   padding: 6px 8px;
 }
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber {
+  background-image: url('../images/throbber.gif');
+  width: 128px;
+  height: 16px;
+  display: block;
+  clear: none;
+  float: right;
+}
 .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form input[type='text'].error {
   outline: 2px solid black;
   outline-color: #cc0000;
@@ -1158,7 +1161,7 @@
   cursor: pointer;
 }
 .swagger-section #header {
-  background-color: #646257;
+  background-color: #89bf04;
   padding: 14px;
 }
 .swagger-section #header a#logo {
@@ -1193,7 +1196,7 @@
   padding: 6px 8px;
   font-size: 0.9em;
   color: white;
-  background-color: #000000;
+  background-color: #547f00;
   -moz-border-radius: 4px;
   -webkit-border-radius: 4px;
   -o-border-radius: 4px;
@@ -1202,35 +1205,13 @@
   border-radius: 4px;
 }
 .swagger-section #header form#api_selector .input a#explore:hover {
-  background-color: #a41e22;
+  background-color: #547f00;
 }
 .swagger-section #header form#api_selector .input input {
   font-size: 0.9em;
   padding: 3px;
   margin: 0;
 }
-.swagger-section #footer-nav {
-  margin-top: 50px;
-  color: #bbb;
-  float: left;
-  list-style: none;
-  font-size: 0.8em;
-}
-.swagger-section #footer-nav li {
-  display: block;
-  float: left;
-}
-.swagger-section #footer-nav a {
-  margin-right: 4px;
-  text-decoration: none;
-  font-weight: none;
-  padding: 4px 2px;
-  font-size: 0.9em;
-  color: #999;
-}
-.swagger-section #footer-nav a:hover {
-  color: #555;
-}
 .swagger-section #content_message {
   margin: 10px 15px;
   font-style: italic;

BIN
htdocs/includes/restler/framework/Luracast/Restler/explorer/images/logo_small.png


BIN
htdocs/includes/restler/framework/Luracast/Restler/explorer/images/pet_store_api.png


BIN
htdocs/includes/restler/framework/Luracast/Restler/explorer/images/wordnik_api.png


+ 25 - 9
htdocs/includes/restler/framework/Luracast/Restler/explorer/index.html

@@ -2,8 +2,7 @@
 <html>
 <head>
   <title>Api Explorer</title>
-  <!-- DOL_CHANGE LDR Remove external links <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">-->
+  <!-- DOL_CHANGE LDR Remove external links <link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>-->
   <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'/>
@@ -27,10 +26,11 @@
     $(function () {
       window.swaggerUi = new SwaggerUi({
       url: "resources.json",
+      validatorUrl: null,
       dom_id: "swagger-ui-container",
-      supportedSubmitMethods: ['get', 'post', 'put', 'patch', 'delete'],
+      supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
       onComplete: function(swaggerApi, swaggerUi){
-        log("Loaded Api Explorer");
+        log("Loaded API Explorer");
 
         if(typeof initOAuth == "function") {
           /*
@@ -45,15 +45,19 @@
           hljs.highlightBlock(e)
         });
       },
+      defaultModelRendering: 'model',
       onFailure: function(data) {
-        log("Unable to Load Api Explorer");
+        log("Unable to Load API Explorer");
       },
-      docExpansion: "none"
+      docExpansion: "none",
+      /*showRequestHeaders: true,
+      jsonEditor: true */
+      /*, sorter : "alpha"*/
     });
 
     $('#input_apiKey').change(function() {
       var key = $('#input_apiKey')[0].value;
-
+      log("key: " + key);
       if(key && key.trim() != "") {
         /* DOL_CHANGE LDR We set DOLAPIKEY into header */
         log("added key " + key);
@@ -67,11 +71,15 @@
         console.log("header DOLAPIKEY added with value "+key);
       }
     })
-    
     window.swaggerUi.load();
   });
-    
   </script>
+  
+  <style>
+	.info_title, .info_description, .info_contact, .info_license {
+		display: none;
+	}
+  </style>
 </head>
 
 <body class="swagger-section">
@@ -79,6 +87,14 @@
   <div class="swagger-ui-wrap">
     <a id="logo" href="#">API Explorer</a>
     <form id='api_selector'>
+      <!-- DOL_CHANGE LDR
+      <div class='input icon-btn'>
+        <img id="show-pet-store-icon" src="images/pet_store_api.png" title="Show Swagger Petstore Example Apis">
+      </div>
+      <div class='input icon-btn'>
+        <img id="show-wordnik-dev-icon" src="images/wordnik_api.png" title="Show Wordnik Developer Apis">
+      </div>
+      -->
       <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="DOLAPIKEY" id="input_apiKey" name="apiKey" type="text"/></div>
       <div class='input'><a id="explore" href="#">Explore</a></div>

+ 54 - 61
htdocs/includes/restler/framework/Luracast/Restler/explorer/lib/swagger-oauth.js

@@ -48,72 +48,65 @@ function handleLogin() {
       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 $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 host = window.location;
+    var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
+    var redirectUrl = host.protocol + '//' + host.host + pathname + "/o2c.html";
+    var url = null;
+
+    for (var key in authSchemes) {
+      if (authSchemes.hasOwnProperty(key)) {
+        var o = authSchemes[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");
-      }
+    }
+    var scopes = []
+    var o = $('.api-popup-scopes').find('input:checked');
 
-      window.enabledScopes=scopes;
-    
+    for(k =0; k < o.length; k++) {
+      scopes.push($(o[k]).attr("scope"));
+    }
 
-      url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
-      url += '&realm=' + encodeURIComponent(realm);
-      url += '&client_id=' + encodeURIComponent(clientId);
-      url += '&scope=' + encodeURIComponent(scopeForUrl);
+    window.enabledScopes=scopes;
+
+    url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
+    url += '&realm=' + encodeURIComponent(realm);
+    url += '&client_id=' + encodeURIComponent(clientId);
+    url += '&scope=' + encodeURIComponent(scopes);
+
+    window.open(url);
+  });
 
-      window.open(url);
-    });
-  }
   popupMask.show();
   popupDialog.show();
   return;
@@ -211,7 +204,7 @@ function onOAuthComplete(token) {
           }
         });
 
-        window.authorizations.add("key", new ApiKeyAuthorization("Authorization", "Bearer " + b, "header"));
+        window.authorizations.add("oauth2", new ApiKeyAuthorization("Authorization", "Bearer " + b, "header"));
       }
     }
   }

+ 269 - 154
htdocs/includes/restler/framework/Luracast/Restler/explorer/lib/swagger.js

@@ -1,5 +1,5 @@
 // swagger.js
-// version 2.0.30
+// version 2.0.39
 
 var __bind = function(fn, me){
   return function(){
@@ -11,10 +11,20 @@ log = function(){
   log.history = log.history || [];
   log.history.push(arguments);
   if(this.console){
-    console.log( Array.prototype.slice.call(arguments) );
+    console.log( Array.prototype.slice.call(arguments)[0] );
   }
 };
 
+// if you want to apply conditional formatting of parameter values
+parameterMacro = function(value) {
+  return value;
+}
+
+// if you want to apply conditional formatting of model property values
+modelPropertyMacro = function(value) {
+  return value;
+}
+
 if (!Array.prototype.indexOf) {
   Array.prototype.indexOf = function(obj, start) {
     for (var i = (start || 0), j = this.length; i < j; i++) {
@@ -45,48 +55,50 @@ if (!('map' in Array.prototype)) {
 }
 
 Object.keys = Object.keys || (function () {
-    var hasOwnProperty = Object.prototype.hasOwnProperty,
-        hasDontEnumBug = !{toString:null}.propertyIsEnumerable("toString"),
-        DontEnums = [
-            'toString',
-            'toLocaleString',
-            'valueOf',
-            'hasOwnProperty',
-            'isPrototypeOf',
-            'propertyIsEnumerable',
-            'constructor'
-        ],
-        DontEnumsLength = DontEnums.length;
-  
-    return function (o) {
-        if (typeof o != "object" && typeof o != "function" || o === null)
-            throw new TypeError("Object.keys called on a non-object");
-     
-        var result = [];
-        for (var name in o) {
-            if (hasOwnProperty.call(o, name))
-                result.push(name);
-        }
-     
-        if (hasDontEnumBug) {
-            for (var i = 0; i < DontEnumsLength; i++) {
-                if (hasOwnProperty.call(o, DontEnums[i]))
-                    result.push(DontEnums[i]);
-            }   
-        }
-     
-        return result;
-    };
-})();
+  var hasOwnProperty = Object.prototype.hasOwnProperty,
+    hasDontEnumBug = !{toString:null}.propertyIsEnumerable("toString"),
+    DontEnums = [
+      'toString',
+      'toLocaleString',
+      'valueOf',
+      'hasOwnProperty',
+      'isPrototypeOf',
+      'propertyIsEnumerable',
+      'constructor'
+    ],
+  DontEnumsLength = DontEnums.length;
+
+  return function (o) {
+    if (typeof o != "object" && typeof o != "function" || o === null)
+      throw new TypeError("Object.keys called on a non-object");
+
+    var result = [];
+    for (var name in o) {
+      if (hasOwnProperty.call(o, name))
+        result.push(name);
+    }
+
+    if (hasDontEnumBug) {
+      for (var i = 0; i < DontEnumsLength; i++) {
+        if (hasOwnProperty.call(o, DontEnums[i]))
+          result.push(DontEnums[i]);
+      }
+    }
 
+    return result;
+  };
+})();
 
 var SwaggerApi = function(url, options) {
+  this.isBuilt = false;
   this.url = null;
   this.debug = false;
   this.basePath = null;
   this.authorizations = null;
   this.authorizationScheme = null;
   this.info = null;
+  this.useJQuery = false;
+  this.modelsArray = [];
 
   options = (options||{});
   if (url)
@@ -103,13 +115,20 @@ var SwaggerApi = function(url, options) {
   if (options.success != null)
     this.success = options.success;
 
+  if (typeof options.useJQuery === 'boolean')
+    this.useJQuery = options.useJQuery;
+
   this.failure = options.failure != null ? options.failure : function() {};
   this.progress = options.progress != null ? options.progress : function() {};
-  if (options.success != null)
+  if (options.success != null) {
     this.build();
+    this.isBuilt = true;
+  }
 }
 
 SwaggerApi.prototype.build = function() {
+  if(this.isBuilt)
+    return this;
   var _this = this;
   this.progress('fetching resource list: ' + this.url);
   var obj = {
@@ -117,7 +136,7 @@ SwaggerApi.prototype.build = function() {
     url: this.url,
     method: "get",
     headers: {
-      accept: "application/json"
+      accept: "application/json,application/json;charset=\"utf-8\",*/*"
     },
     on: {
       error: function(response) {
@@ -154,6 +173,7 @@ SwaggerApi.prototype.buildFromSpec = function(response) {
   }
   this.apis = {};
   this.apisArray = [];
+  this.consumes = response.consumes;
   this.produces = response.produces;
   this.authSchemes = response.authorizations;
   if (response.info != null) {
@@ -171,13 +191,13 @@ SwaggerApi.prototype.buildFromSpec = function(response) {
       }
     }
   }
-  if (response.basePath) {
+  if (response.basePath)
     this.basePath = response.basePath;
-  } else if (this.url.indexOf('?') > 0) {
+  else if (this.url.indexOf('?') > 0)
     this.basePath = this.url.substring(0, this.url.lastIndexOf('?'));
-  } else {
+  else
     this.basePath = this.url;
-  }
+
   if (isApi) {
     var newName = response.resourcePath.replace(/\//g, '');
     this.resourcePath = response.resourcePath;
@@ -272,7 +292,6 @@ SwaggerApi.prototype.fail = function(message) {
 
 SwaggerApi.prototype.setConsolidatedModels = function() {
   var model, modelName, resource, resource_name, _i, _len, _ref, _ref1, _results;
-  this.modelsArray = [];
   this.models = {};
   _ref = this.apis;
   for (resource_name in _ref) {
@@ -317,8 +336,8 @@ var SwaggerResource = function(resourceObj, api) {
   var _this = this;
   this.api = api;
   this.api = this.api;
-  produces = [];
-  consumes = [];
+  consumes = (this.consumes | []);
+  produces = (this.produces | []);
   this.path = this.api.resourcePath != null ? this.api.resourcePath : resourceObj.path;
   this.description = resourceObj.description;
 
@@ -349,7 +368,7 @@ var SwaggerResource = function(resourceObj, api) {
       method: "get",
       useJQuery: this.useJQuery,
       headers: {
-        accept: "application/json"
+        accept: "application/json,application/json;charset=\"utf-8\",*/*"
       },
       on: {
         response: function(resp) {
@@ -368,22 +387,28 @@ var SwaggerResource = function(resourceObj, api) {
   }
 }
 
-SwaggerResource.prototype.getAbsoluteBasePath = function(relativeBasePath) {
-  var parts, pos, url;
+SwaggerResource.prototype.getAbsoluteBasePath = function (relativeBasePath) {
+  var pos, url;
   url = this.api.basePath;
   pos = url.lastIndexOf(relativeBasePath);
-  if (pos === -1) {
-    parts = url.split("/");
-    url = parts[0] + "//" + parts[2];
-    if (relativeBasePath.indexOf("/") === 0) {
-      return url + relativeBasePath;
-    } else {
-      return url + "/" + relativeBasePath;
-    }
-  } else if (relativeBasePath === "/") {
-    return url.substring(0, pos);
-  } else {
-    return url.substring(0, pos) + relativeBasePath;
+  var parts = url.split("/");
+  var rootUrl = parts[0] + "//" + parts[2];
+
+  if(relativeBasePath.indexOf("http") === 0)
+    return relativeBasePath;
+  if(relativeBasePath === "/")
+    return rootUrl;
+  if(relativeBasePath.substring(0, 1) == "/") {
+    // use root + relative
+    return rootUrl + relativeBasePath;
+  }
+  else {
+    var pos = this.basePath.lastIndexOf("/");
+    var base = this.basePath.substring(0, pos);
+    if(base.substring(base.length - 1) == "/")
+      return base + relativeBasePath;
+    else
+      return base + "/" + relativeBasePath;
   }
 };
 
@@ -478,8 +503,7 @@ SwaggerResource.prototype.addOperations = function(resource_path, ops, consumes,
 
 SwaggerResource.prototype.sanitize = function(nickname) {
   var op;
-  op = nickname.replace(/[\s!@#$%^&*()_+=\[{\]};:<>|./?,\\'""-]/g, '_');
-  //'
+  op = nickname.replace(/[\s!@#$%^&*()_+=\[{\]};:<>|.\/?,\\'""-]/g, '_');
   op = op.replace(/((_){2,})/g, '_');
   op = op.replace(/^(_)*/g, '');
   op = op.replace(/([_])*$/g, '');
@@ -584,6 +608,7 @@ var SwaggerModelProperty = function(name, obj) {
   this.isCollection = this.dataType && (this.dataType.toLowerCase() === 'array' || this.dataType.toLowerCase() === 'list' || this.dataType.toLowerCase() === 'set');
   this.descr = obj.description;
   this.required = obj.required;
+  this.defaultValue = modelPropertyMacro(obj.defaultValue);
   if (obj.items != null) {
     if (obj.items.type != null) {
       this.refDataType = obj.items.type;
@@ -629,7 +654,9 @@ SwaggerModelProperty.prototype.getSampleValue = function(modelsToIgnore) {
 
 SwaggerModelProperty.prototype.toSampleValue = function(value) {
   var result;
-  if (value === "integer") {
+  if ((typeof this.defaultValue !== 'undefined') && this.defaultValue !== null) {
+    result = this.defaultValue;
+  } else if (value === "integer") {
     result = 0;
   } else if (value === "boolean") {
     result = false;
@@ -759,6 +786,7 @@ var SwaggerOperation = function(nickname, path, method, parameters, summary, not
         }
       }
     }
+    param.defaultValue = parameterMacro(param.defaultValue);
   }
   this.resource[this.nickname] = function(args, callback, error) {
     return _this["do"](args, callback, error);
@@ -812,7 +840,7 @@ SwaggerOperation.prototype.getSampleJSON = function(type, models) {
       else
         return JSON.stringify(val, null, 2);
     }
-    else 
+    else
       return val;
   }
 };
@@ -1073,7 +1101,7 @@ SwaggerOperation.prototype.formatXml = function(xml) {
 var SwaggerRequest = function(type, url, params, opts, successCallback, errorCallback, operation, execution) {
   var _this = this;
   var errors = [];
-  this.useJQuery = (typeof operation.useJQuery !== 'undefined' ? operation.useJQuery : null);
+  this.useJQuery = (typeof operation.resource.useJQuery !== 'undefined' ? operation.resource.useJQuery : null);
   this.type = (type||errors.push("SwaggerRequest type is required (get/post/put/delete/patch/options)."));
   this.url = (url||errors.push("SwaggerRequest url is required."));
   this.params = params;
@@ -1090,96 +1118,59 @@ var SwaggerRequest = function(type, url, params, opts, successCallback, errorCal
 
   this.type = this.type.toUpperCase();
 
-  var myHeaders = {};
+  // set request, response content type headers
+  var headers = this.setHeaders(params, this.operation);
   var body = params.body;
-  var parent = params["parent"];
-  var requestContentType = "application/json";
-
-  var formParams = [];
-  var fileParams = [];
-  var params = this.operation.parameters;
 
-
-  for(var i = 0; i < params.length; i++) {
-    var param = params[i];
-    if(param.paramType === "form")
-      formParams.push(param);
-    else if(param.paramType === "file")
-      fileParams.push(param);
-  }
-
-
-  if (body && (this.type === "POST" || this.type === "PUT" || this.type === "PATCH")) {
-    if (this.opts.requestContentType) {
-      requestContentType = this.opts.requestContentType;
-    }
-  } else {
-    // if any form params, content type must be set
-    if(formParams.length > 0) {
-      if(fileParams.length > 0)
-        requestContentType = "multipart/form-data";
-      else
-        requestContentType = "application/x-www-form-urlencoded";
+  // encode the body for form submits
+  if (headers["Content-Type"]) {
+    var values = {};
+    var i;
+    var operationParams = this.operation.parameters;
+    for(i = 0; i < operationParams.length; i++) {
+      var param = operationParams[i];
+      if(param.paramType === "form")
+        values[param.name] = param;
     }
-    else if (this.type != "DELETE")
-      requestContentType = null;
-  }
 
-  if (requestContentType && this.operation.consumes) {
-    if (this.operation.consumes[requestContentType] === 'undefined') {
-      log("server doesn't consume " + requestContentType + ", try " + JSON.stringify(this.operation.consumes));
-      if (this.requestContentType === null) {
-        requestContentType = this.operation.consumes[0];
+    if(headers["Content-Type"].indexOf("application/x-www-form-urlencoded") === 0) {
+      var encoded = "";
+      var key;
+      for(key in values) {
+        value = this.params[key];
+        if(typeof value !== 'undefined'){
+          if(encoded !== "")
+            encoded += "&";
+          encoded += encodeURIComponent(key) + '=' + encodeURIComponent(value);
+        }
       }
+      body = encoded;
     }
-  }
-
-  var responseContentType = null;
-  if (this.opts.responseContentType) {
-    responseContentType = this.opts.responseContentType;
-  } else {
-    responseContentType = "application/json";
-  }
-  if (responseContentType && this.operation.produces) {
-    if (this.operation.produces[responseContentType] === 'undefined') {
-      log("server can't produce " + responseContentType);
-    }
-  }
-  if (requestContentType && requestContentType.indexOf("application/x-www-form-urlencoded") === 0) {
-    var fields = {};
-    var possibleParams = {};
-    var values = {};
-    var key;
-    for(key in formParams){
-      var param = formParams[key];
-      values[param.name] = param;
-    }
-
-    var encoded = "";
-    var key;
-    for(key in values) {
-      value = this.params[key];
-      if(typeof value !== 'undefined'){
-        if(encoded !== "")
-          encoded += "&";
-        encoded += encodeURIComponent(key) + '=' + encodeURIComponent(value);
+    else if (headers["Content-Type"].indexOf("multipart/form-data") === 0) {
+      // encode the body for form submits
+      var data = "";
+      var boundary = "----SwaggerFormBoundary" + Date.now();
+      var key;
+      for(key in values) {
+        value = this.params[key];
+        if(typeof value !== 'undefined') {
+          data += '--' + boundary + '\n';
+          data += 'Content-Disposition: form-data; name="' + key + '"';
+          data += '\n\n';
+          data += value + "\n";
+        }
       }
+      data += "--" + boundary + "--\n";
+      headers["Content-Type"] = "multipart/form-data; boundary=" + boundary;
+      body = data;
     }
-    body = encoded;
   }
-  var name;
-  for (name in this.headers)
-    myHeaders[name] = this.headers[name];
-  if ((requestContentType && body !== "") || (requestContentType === "application/x-www-form-urlencoded"))
-    myHeaders["Content-Type"] = requestContentType;
-  if (responseContentType)
-    myHeaders["Accept"] = responseContentType;
 
   if (!((this.headers != null) && (this.headers.mock != null))) {
     obj = {
       url: this.url,
       method: this.type,
-      headers: myHeaders,
+      headers: headers,
       body: body,
       useJQuery: this.useJQuery,
       on: {
@@ -1216,6 +1207,77 @@ var SwaggerRequest = function(type, url, params, opts, successCallback, errorCal
   }
 };
 
+SwaggerRequest.prototype.setHeaders = function(params, operation) {
+  // default type
+  var accepts = "application/json";
+  var consumes = "application/json";
+
+  var allDefinedParams = this.operation.parameters;
+  var definedFormParams = [];
+  var definedFileParams = [];
+  var body = params.body;
+  var headers = {};
+
+  // get params from the operation and set them in definedFileParams, definedFormParams, headers
+  var i;
+  for(i = 0; i < allDefinedParams.length; i++) {
+    var param = allDefinedParams[i];
+    if(param.paramType === "form")
+      definedFormParams.push(param);
+    else if(param.paramType === "file")
+      definedFileParams.push(param);
+    else if(param.paramType === "header" && this.params.headers) {
+      var key = param.name;
+      var headerValue = this.params.headers[param.name];
+      if(typeof this.params.headers[param.name] !== 'undefined')
+        headers[key] = headerValue;
+    }
+  }
+
+  // if there's a body, need to set the accepts header via requestContentType
+  if (body && (this.type === "POST" || this.type === "PUT" || this.type === "PATCH" || this.type === "DELETE")) {
+    if (this.opts.requestContentType)
+      consumes = this.opts.requestContentType;
+  } else {
+    // if any form params, content type must be set
+    if(definedFormParams.length > 0) {
+      if(definedFileParams.length > 0)
+        consumes = "multipart/form-data";
+      else
+        consumes = "application/x-www-form-urlencoded";
+    }
+    else if (this.type === "DELETE")
+      body = "{}";
+    else if (this.type != "DELETE")
+      accepts = null;
+  }
+
+  if (consumes && this.operation.consumes) {
+    if (this.operation.consumes.indexOf(consumes) === -1) {
+      log("server doesn't consume " + consumes + ", try " + JSON.stringify(this.operation.consumes));
+      consumes = this.operation.consumes[0];
+    }
+  }
+
+  if (this.opts.responseContentType) {
+    accepts = this.opts.responseContentType;
+  } else {
+    accepts = "application/json";
+  }
+  if (accepts && this.operation.produces) {
+    if (this.operation.produces.indexOf(accepts) === -1) {
+      log("server can't produce " + accepts);
+      accepts = this.operation.produces[0];
+    }
+  }
+
+  if ((consumes && body !== "") || (consumes === "application/x-www-form-urlencoded"))
+    headers["Content-Type"] = consumes;
+  if (accepts)
+    headers["Accept"] = accepts;
+  return headers;
+}
+
 SwaggerRequest.prototype.asCurl = function() {
   var results = [];
   if(this.headers) {
@@ -1259,9 +1321,16 @@ SwaggerHttp.prototype.isIE8 = function() {
 };
 
 /*
- * JQueryHttpClient lets a browser take advantage of JQuery's cross-browser magic
+ * JQueryHttpClient lets a browser take advantage of JQuery's cross-browser magic.
+ * NOTE: when jQuery is available it will export both '$' and 'jQuery' to the global space.
+ *       Since we are using closures here we need to alias it for internal use.
  */
-var JQueryHttpClient = function(options) {}
+var JQueryHttpClient = function(options) {
+  "use strict";
+  if(!jQuery){
+    var jQuery = window.jQuery;
+  }
+}
 
 JQueryHttpClient.prototype.execute = function(obj) {
   var cb = obj.on;
@@ -1335,8 +1404,8 @@ JQueryHttpClient.prototype.execute = function(obj) {
       return cb.response(out);
   };
 
-  $.support.cors = true;
-  return $.ajax(obj);
+  jQuery.support.cors = true;
+  return jQuery.ajax(obj);
 }
 
 /*
@@ -1411,11 +1480,36 @@ ShredHttpClient.prototype.execute = function(obj) {
     return out;
   };
 
+  // Transform an error into a usable response-like object
+  var transformError = function(error) {
+    var out = {
+      // Default to a status of 0 - The client will treat this as a generic permissions sort of error
+      status: 0,
+      data: error.message || error
+    };
+
+    if(error.code) {
+      out.obj = error;
+
+      if(error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED' ) {
+        // We can tell the client that this should be treated as a missing resource and not as a permissions thing
+        out.status = 404;
+      }
+    }
+
+    return out;
+  };
+
   res = {
     error: function(response) {
       if (obj)
         return cb.error(transform(response));
     },
+    // Catch the Shred error raised when the request errors as it is made (i.e. No Response is coming)
+    request_error: function(err) {
+      if(obj)
+        return cb.error(transformError(err));
+    },
     redirect: function(response) {
       if (obj)
         return cb.redirect(transform(response));
@@ -1452,26 +1546,42 @@ SwaggerAuthorizations.prototype.remove = function(name) {
 };
 
 SwaggerAuthorizations.prototype.apply = function(obj, authorizations) {
-  status = null;
+  var status = null;
   var key;
-  for (key in this.authz) {
-    value = this.authz[key];
-    result = value.apply(obj, authorizations);
-    if (result === false)
-      status = false;
-    if (result === true)
-      status = true;
+
+  // if the "authorizations" key is undefined, or has an empty array, add all keys
+  if(typeof authorizations === 'undefined' || Object.keys(authorizations).length == 0) {
+    for (key in this.authz) {
+      value = this.authz[key];
+      result = value.apply(obj, authorizations);
+      if (result === true)
+        status = true;
+    }
   }
+  else {
+    for(name in authorizations) {
+      for (key in this.authz) {
+        if(key == name) {
+          value = this.authz[key];
+          result = value.apply(obj, authorizations);
+          if (result === true)
+            status = true;
+        }
+      }      
+    }
+  }
+
   return status;
 };
 
 /**
  * ApiKeyAuthorization allows a query param or header to be injected
  */
-var ApiKeyAuthorization = function(name, value, type) {
+var ApiKeyAuthorization = function(name, value, type, delimiter) {
   this.name = name;
   this.value = value;
   this.type = type;
+  this.delimiter = delimiter;
 };
 
 ApiKeyAuthorization.prototype.apply = function(obj, authorizations) {
@@ -1482,7 +1592,12 @@ ApiKeyAuthorization.prototype.apply = function(obj, authorizations) {
       obj.url = obj.url + "?" + this.name + "=" + this.value;
     return true;
   } else if (this.type === "header") {
-    obj.headers[this.name] = this.value;
+    if(typeof obj.headers[this.name] !== 'undefined') {
+      if(typeof this.delimiter !== 'undefined')
+        obj.headers[this.name] = obj.headers[this.name] + this.delimiter +  this.value;
+    }
+    else
+      obj.headers[this.name] = this.value;
     return true;
   }
 };

+ 190 - 141
htdocs/includes/restler/framework/Luracast/Restler/explorer/swagger-ui.js

@@ -1,5 +1,5 @@
 // swagger-ui.js
-// version 2.0.17
+// version 2.0.23
 $(function() {
 
 	// Helper function for vertically aligning DOM elements
@@ -68,7 +68,7 @@ log = function(){
   log.history = log.history || [];
   log.history.push(arguments);
   if(this.console){
-    console.log( Array.prototype.slice.call(arguments) );
+    console.log( Array.prototype.slice.call(arguments)[0] );
   }
 };
 
@@ -240,67 +240,74 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 function program1(depth0,data) {
   
   var buffer = "", stack1, stack2;
-  buffer += "\n                ";
+  buffer += "\n    <div class=\"info_title\">"
+    + escapeExpression(((stack1 = ((stack1 = depth0.info),stack1 == null || stack1 === false ? stack1 : stack1.title)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
+    + "</div>\n    <div class=\"info_description\">";
+  stack2 = ((stack1 = ((stack1 = depth0.info),stack1 == null || stack1 === false ? stack1 : stack1.description)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1);
+  if(stack2 || stack2 === 0) { buffer += stack2; }
+  buffer += "</div>\n    ";
   stack2 = helpers['if'].call(depth0, ((stack1 = depth0.info),stack1 == null || stack1 === false ? stack1 : stack1.termsOfServiceUrl), {hash:{},inverse:self.noop,fn:self.program(2, program2, data),data:data});
   if(stack2 || stack2 === 0) { buffer += stack2; }
-  buffer += "\n                ";
+  buffer += "\n    ";
   stack2 = helpers['if'].call(depth0, ((stack1 = depth0.info),stack1 == null || stack1 === false ? stack1 : stack1.contact), {hash:{},inverse:self.noop,fn:self.program(4, program4, data),data:data});
   if(stack2 || stack2 === 0) { buffer += stack2; }
-  buffer += "\n                ";
+  buffer += "\n    ";
   stack2 = helpers['if'].call(depth0, ((stack1 = depth0.info),stack1 == null || stack1 === false ? stack1 : stack1.license), {hash:{},inverse:self.noop,fn:self.program(6, program6, data),data:data});
   if(stack2 || stack2 === 0) { buffer += stack2; }
-  buffer += "\n            ";
+  buffer += "\n  ";
   return buffer;
   }
 function program2(depth0,data) {
   
   var buffer = "", stack1;
-  buffer += "<li><a href=\""
+  buffer += "<div class=\"info_tos\"><a href=\""
     + escapeExpression(((stack1 = ((stack1 = depth0.info),stack1 == null || stack1 === false ? stack1 : stack1.termsOfServiceUrl)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
-    + "\" title=\"Terms of Service\"><i class=\"fa fa-lg fa-bullhorn\"></i> Terms</a></li>";
+    + "\">Terms of service</a></div>";
   return buffer;
   }
 
 function program4(depth0,data) {
   
   var buffer = "", stack1;
-  buffer += "<li><a href=\"mailto:"
+  buffer += "<div class='info_contact'><a href=\"mailto:"
     + escapeExpression(((stack1 = ((stack1 = depth0.info),stack1 == null || stack1 === false ? stack1 : stack1.contact)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
-    + "\"><i class=\"fa fa-lg fa-send\"></i> Contact</a></li>";
+    + "\">Contact the developer</a></div>";
   return buffer;
   }
 
 function program6(depth0,data) {
   
   var buffer = "", stack1;
-  buffer += "<li><a href=\""
+  buffer += "<div class='info_license'><a href='"
     + escapeExpression(((stack1 = ((stack1 = depth0.info),stack1 == null || stack1 === false ? stack1 : stack1.licenseUrl)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
-    + "\" title=\""
+    + "'>"
     + escapeExpression(((stack1 = ((stack1 = depth0.info),stack1 == null || stack1 === false ? stack1 : stack1.license)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
-    + "\"><i class=\"fa fa-lg fa-certificate\"></i> License</a></li>";
+    + "</a></div>";
   return buffer;
   }
 
 function program8(depth0,data) {
   
   var buffer = "", stack1;
-  buffer += " v";
+  buffer += "\n        , <span style=\"font-variant: small-caps\">api version</span>: ";
   if (stack1 = helpers.apiVersion) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.apiVersion; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
-  buffer += escapeExpression(stack1);
+  buffer += escapeExpression(stack1)
+    + "\n        ";
   return buffer;
   }
 
-  buffer += "<div class='info' id='api_info'>\n</div>\n<div class='container' id='resources_container'>\n    <ul id='resources'>\n    </ul>\n\n    <div class=\"footer\">\n        <ul id=\"footer-nav\">\n            ";
+  buffer += "<div class='info' id='api_info'>\n  ";
   stack1 = helpers['if'].call(depth0, depth0.info, {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
   if(stack1 || stack1 === 0) { buffer += stack1; }
-  buffer += "\n            <li>\n                ";
+  buffer += "\n</div>\n<div class='container' id='resources_container'>\n    <ul id='resources'>\n    </ul>\n\n    <div class=\"footer\">\n        <br>\n        <br>\n        <h4 style=\"color: #999\">[ <span style=\"font-variant: small-caps\">base url</span>: ";
   if (stack1 = helpers.basePath) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.basePath; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
-  buffer += escapeExpression(stack1);
+  buffer += escapeExpression(stack1)
+    + "\n        ";
   stack1 = helpers['if'].call(depth0, depth0.apiVersion, {hash:{},inverse:self.noop,fn:self.program(8, program8, data),data:data});
   if(stack1 || stack1 === 0) { buffer += stack1; }
-  buffer += " Powered by Restler & Swagger\n            </li>\n        </ul>\n    </div>\n</div>\n";
+  buffer += "]</h4>\n    </div>\n</div>\n";
   return buffer;
   });
 })();
@@ -389,7 +396,7 @@ function program18(depth0,data) {
 function program20(depth0,data) {
   
   
-  return "\n          <div class='sandbox_header'>\n            <input class='submit' name='commit' type='button' value='Try it out!' />\n            <a href='#' class='response_hider' style='display:none'>Hide Response</a>\n            <img alt='Throbber' class='response_throbber' src='images/throbber.gif' style='display:none' />\n          </div>\n          ";
+  return "\n          <div class='sandbox_header'>\n            <input class='submit' name='commit' type='button' value='Try it out!' />\n            <a href='#' class='response_hider' style='display:none'>Hide Response</a>\n            <span class='response_throbber' style='display:none'></span>\n          </div>\n          ";
   }
 
   buffer += "\n  <ul class='operations' >\n    <li class='";
@@ -555,7 +562,7 @@ function program9(depth0,data) {
   
   var buffer = "", stack1;
   buffer += "\n		";
-  stack1 = helpers['if'].call(depth0, depth0.defaultValue, {hash:{},inverse:self.program(12, program12, data),fn:self.program(10, program10, data),data:data});
+  stack1 = helpers['if'].call(depth0, depth0.isFile, {hash:{},inverse:self.program(10, program10, data),fn:self.program(2, program2, data),data:data});
   if(stack1 || stack1 === 0) { buffer += stack1; }
   buffer += "\n	";
   return buffer;
@@ -563,7 +570,16 @@ function program9(depth0,data) {
 function program10(depth0,data) {
   
   var buffer = "", stack1;
-  buffer += "\n			<input class='parameter' minlength='0' name='";
+  buffer += "\n			";
+  stack1 = helpers['if'].call(depth0, depth0.defaultValue, {hash:{},inverse:self.program(13, program13, data),fn:self.program(11, program11, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n		";
+  return buffer;
+  }
+function program11(depth0,data) {
+  
+  var buffer = "", stack1;
+  buffer += "\n				<input class='parameter' minlength='0' name='";
   if (stack1 = helpers.name) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
@@ -571,18 +587,18 @@ function program10(depth0,data) {
   if (stack1 = helpers.defaultValue) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.defaultValue; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
-    + "'/>\n		";
+    + "'/>\n			";
   return buffer;
   }
 
-function program12(depth0,data) {
+function program13(depth0,data) {
   
   var buffer = "", stack1;
-  buffer += "\n			<input class='parameter' minlength='0' name='";
+  buffer += "\n				<input class='parameter' minlength='0' name='";
   if (stack1 = helpers.name) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
-    + "' placeholder='' type='text' value=''/>\n		";
+    + "' placeholder='' type='text' value=''/>\n			";
   return buffer;
   }
 
@@ -1057,23 +1073,19 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 
 function program1(depth0,data) {
   
-  var buffer = "", stack1;
-  buffer += " : ";
-  if (stack1 = helpers.description) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
-  else { stack1 = depth0.description; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
-  if(stack1 || stack1 === 0) { buffer += stack1; }
-  return buffer;
+  
+  return " : ";
   }
 
   buffer += "<div class='heading'>\n  <h2>\n    <a href='#!/";
   if (stack1 = helpers.id) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
-    + "' onclick=\"Docs.toggleEndpointListForResource('";
+    + "' class=\"toggleEndpointList\" data-id=\"";
   if (stack1 = helpers.id) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
-    + "');\">";
+    + "\">";
   if (stack1 = helpers.name) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
@@ -1083,6 +1095,9 @@ function program1(depth0,data) {
   else { stack1 = depth0.description; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   if (!helpers.description) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
   if(stack1 || stack1 === 0) { buffer += stack1; }
+  if (stack1 = helpers.description) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
+  else { stack1 = depth0.description; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
+  if(stack1 || stack1 === 0) { buffer += stack1; }
   buffer += "\n  </h2>\n  <ul class='options'>\n    <li>\n      <a href='#!/";
   if (stack1 = helpers.id) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
@@ -1091,19 +1106,19 @@ function program1(depth0,data) {
   if (stack1 = helpers.id) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
-    + "'\n         onclick=\"Docs.toggleEndpointListForResource('";
+    + "' class=\"toggleEndpointList\" data-id=\"";
   if (stack1 = helpers.id) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
-    + "');\">Show/Hide</a>\n    </li>\n    <li>\n      <a href='#' onclick=\"Docs.collapseOperationsForResource('";
+    + "\">Show/Hide</a>\n    </li>\n    <li>\n      <a href='#' class=\"collapseResource\" data-id=\"";
   if (stack1 = helpers.id) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
-    + "'); return false;\">\n        List Operations\n      </a>\n    </li>\n    <li>\n      <a href='#' onclick=\"Docs.expandOperationsForResource('";
+    + "\">\n        List Operations\n      </a>\n    </li>\n    <li>\n      <a href='#' class=\"expandResource\" data-id=";
   if (stack1 = helpers.id) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.id; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
-    + "'); return false;\">\n        Expand Operations\n      </a>\n    </li>\n    <li>\n      <a href='";
+    + ">\n        Expand Operations\n      </a>\n    </li>\n    <li>\n      <a href='";
   if (stack1 = helpers.url) { stack1 = stack1.call(depth0, {hash:{},data:data}); }
   else { stack1 = depth0.url; stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1; }
   buffer += escapeExpression(stack1)
@@ -1203,18 +1218,18 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 
 
 
-// Generated by CoffeeScript 1.5.0
+// Generated by CoffeeScript 1.6.3
 (function() {
-  var ContentTypeView, HeaderView, MainView, OperationView, ParameterContentTypeView, ParameterView, ResourceView, ResponseContentTypeView, SignatureView, StatusCodeView, SwaggerUi,
+  var ContentTypeView, HeaderView, MainView, OperationView, ParameterContentTypeView, ParameterView, ResourceView, ResponseContentTypeView, SignatureView, StatusCodeView, SwaggerUi, _ref, _ref1, _ref10, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9,
     __hasProp = {}.hasOwnProperty,
     __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
 
   SwaggerUi = (function(_super) {
-
     __extends(SwaggerUi, _super);
 
     function SwaggerUi() {
-      SwaggerUi.__super__.constructor.apply(this, arguments);
+      _ref = SwaggerUi.__super__.constructor.apply(this, arguments);
+      return _ref;
     }
 
     SwaggerUi.prototype.dom_id = "swagger_ui";
@@ -1263,9 +1278,9 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
     };
 
     SwaggerUi.prototype.load = function() {
-      var url, _ref;
-      if ((_ref = this.mainView) != null) {
-        _ref.clear();
+      var url, _ref1;
+      if ((_ref1 = this.mainView) != null) {
+        _ref1.clear();
       }
       url = this.options.url;
       if (url.indexOf("http") !== 0) {
@@ -1281,10 +1296,10 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
     SwaggerUi.prototype.render = function() {
       var _this = this;
       this.showMessage('Finished Loading Resource Information. Rendering Swagger UI...');
-      this.headerView.updateInfo(this.api.info);
       this.mainView = new MainView({
         model: this.api,
-        el: $('#' + this.dom_id)
+        el: $('#' + this.dom_id),
+        swaggerOptions: this.options
       }).render();
       this.showMessage();
       switch (this.options.docExpansion) {
@@ -1355,11 +1370,11 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
   window.SwaggerUi = SwaggerUi;
 
   HeaderView = (function(_super) {
-
     __extends(HeaderView, _super);
 
     function HeaderView() {
-      HeaderView.__super__.constructor.apply(this, arguments);
+      _ref1 = HeaderView.__super__.constructor.apply(this, arguments);
+      return _ref1;
     }
 
     HeaderView.prototype.events = {
@@ -1412,40 +1427,56 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
       }
     };
 
-    HeaderView.prototype.updateInfo = function(info) {
-      if (info.title != null) {
-        $('#logo').text(info.title);
-      }
-      if (info.description != null) {
-        $('#logo').attr('title', info.description);
-      }
-      if (info.termsOfServiceUrl != null) {
-        return $('#logo').attr('href', info.termsOfServiceUrl);
-      }
-    };
-
     return HeaderView;
 
   })(Backbone.View);
 
   MainView = (function(_super) {
+    var sorters;
 
     __extends(MainView, _super);
 
     function MainView() {
-      MainView.__super__.constructor.apply(this, arguments);
+      _ref2 = MainView.__super__.constructor.apply(this, arguments);
+      return _ref2;
     }
 
-    MainView.prototype.initialize = function() {};
+    sorters = {
+      'alpha': function(a, b) {
+        return a.path.localeCompare(b.path);
+      },
+      'method': function(a, b) {
+        return a.method.localeCompare(b.method);
+      }
+    };
+
+    MainView.prototype.initialize = function(opts) {
+      var route, sorter, sorterName, _i, _len, _ref3;
+      if (opts == null) {
+        opts = {};
+      }
+      if (opts.swaggerOptions.sorter) {
+        sorterName = opts.swaggerOptions.sorter;
+        sorter = sorters[sorterName];
+        _ref3 = this.model.apisArray;
+        for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
+          route = _ref3[_i];
+          route.operationsArray.sort(sorter);
+        }
+        if (sorterName === "alpha") {
+          return this.model.apisArray.sort(sorter);
+        }
+      }
+    };
 
     MainView.prototype.render = function() {
-      var counter, id, resource, resources, _i, _len, _ref;
+      var counter, id, resource, resources, _i, _len, _ref3;
       $(this.el).html(Handlebars.templates.main(this.model));
       resources = {};
       counter = 0;
-      _ref = this.model.apisArray;
-      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-        resource = _ref[_i];
+      _ref3 = this.model.apisArray;
+      for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
+        resource = _ref3[_i];
         id = resource.name;
         while (typeof resources[id] !== 'undefined') {
           id = id + "_" + counter;
@@ -1464,7 +1495,8 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
         model: resource,
         tagName: 'li',
         id: 'resource_' + resource.id,
-        className: 'resource'
+        className: 'resource',
+        swaggerOptions: this.options.swaggerOptions
       });
       return $('#resources').append(resourceView.render().el);
     };
@@ -1478,22 +1510,22 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
   })(Backbone.View);
 
   ResourceView = (function(_super) {
-
     __extends(ResourceView, _super);
 
     function ResourceView() {
-      ResourceView.__super__.constructor.apply(this, arguments);
+      _ref3 = ResourceView.__super__.constructor.apply(this, arguments);
+      return _ref3;
     }
 
     ResourceView.prototype.initialize = function() {};
 
     ResourceView.prototype.render = function() {
-      var counter, id, methods, operation, _i, _len, _ref;
+      var counter, id, methods, operation, _i, _len, _ref4;
       $(this.el).html(Handlebars.templates.resource(this.model));
       methods = {};
-      _ref = this.model.operationsArray;
-      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-        operation = _ref[_i];
+      _ref4 = this.model.operationsArray;
+      for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+        operation = _ref4[_i];
         counter = 0;
         id = operation.nickname;
         while (typeof methods[id] !== 'undefined') {
@@ -1505,6 +1537,10 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
         operation.parentId = this.model.id;
         this.addOperation(operation);
       }
+      $('.toggleEndpointList', this.el).click(this.callDocs.bind(this, 'toggleEndpointListForResource'));
+      $('.collapseResource', this.el).click(this.callDocs.bind(this, 'collapseOperationsForResource'));
+      /* DOL_CHANGE LDR Fix typo error that break expand */
+      $('.expandResource', this.el).click(this.callDocs.bind(this, 'expandOperationsForResource'));
       return this;
     };
 
@@ -1514,22 +1550,28 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
       operationView = new OperationView({
         model: operation,
         tagName: 'li',
-        className: 'endpoint'
+        className: 'endpoint',
+        swaggerOptions: this.options.swaggerOptions
       });
       $('.endpoints', $(this.el)).append(operationView.render().el);
       return this.number++;
     };
 
+    ResourceView.prototype.callDocs = function(fnName, e) {
+      e.preventDefault();
+      return Docs[fnName](e.currentTarget.getAttribute('data-id'));
+    };
+
     return ResourceView;
 
   })(Backbone.View);
 
   OperationView = (function(_super) {
-
     __extends(OperationView, _super);
 
     function OperationView() {
-      OperationView.__super__.constructor.apply(this, arguments);
+      _ref4 = OperationView.__super__.constructor.apply(this, arguments);
+      return _ref4;
     }
 
     OperationView.prototype.invocationUrl = null;
@@ -1548,8 +1590,8 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
     OperationView.prototype.mouseEnter = function(e) {
       var elem, hgh, pos, scMaxX, scMaxY, scX, scY, wd, x, y;
       elem = $(e.currentTarget.parentNode).find('#api_information_panel');
-      x = event.pageX;
-      y = event.pageY;
+      x = e.pageX;
+      y = e.pageY;
       scX = $(window).scrollLeft();
       scY = $(window).scrollTop();
       scMaxX = scX + $(window).width();
@@ -1580,16 +1622,16 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
     };
 
     OperationView.prototype.render = function() {
-      var contentTypeModel, isMethodSubmissionSupported, k, o, param, responseContentTypeView, responseSignatureView, signatureModel, statusCode, type, v, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3;
+      var contentTypeModel, isMethodSubmissionSupported, k, o, param, responseContentTypeView, responseSignatureView, signatureModel, statusCode, type, v, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref5, _ref6, _ref7, _ref8;
       isMethodSubmissionSupported = true;
       if (!isMethodSubmissionSupported) {
         this.model.isReadOnly = true;
       }
       this.model.oauth = null;
       if (this.model.authorizations) {
-        _ref = this.model.authorizations;
-        for (k in _ref) {
-          v = _ref[k];
+        _ref5 = this.model.authorizations;
+        for (k in _ref5) {
+          v = _ref5[k];
           if (k === "oauth2") {
             if (this.model.oauth === null) {
               this.model.oauth = {};
@@ -1624,9 +1666,9 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
       };
       contentTypeModel.consumes = this.model.consumes;
       contentTypeModel.produces = this.model.produces;
-      _ref1 = this.model.parameters;
-      for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
-        param = _ref1[_j];
+      _ref6 = this.model.parameters;
+      for (_j = 0, _len1 = _ref6.length; _j < _len1; _j++) {
+        param = _ref6[_j];
         type = param.type || param.dataType;
         if (type.toLowerCase() === 'file') {
           if (!contentTypeModel.consumes) {
@@ -1639,14 +1681,14 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
         model: contentTypeModel
       });
       $('.response-content-type', $(this.el)).append(responseContentTypeView.render().el);
-      _ref2 = this.model.parameters;
-      for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
-        param = _ref2[_k];
+      _ref7 = this.model.parameters;
+      for (_k = 0, _len2 = _ref7.length; _k < _len2; _k++) {
+        param = _ref7[_k];
         this.addParameter(param, contentTypeModel.consumes);
       }
-      _ref3 = this.model.responseMessages;
-      for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
-        statusCode = _ref3[_l];
+      _ref8 = this.model.responseMessages;
+      for (_l = 0, _len3 = _ref8.length; _l < _len3; _l++) {
+        statusCode = _ref8[_l];
         this.addStatusCode(statusCode);
       }
       return this;
@@ -1673,7 +1715,7 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
     };
 
     OperationView.prototype.submitOperation = function(e) {
-      var error_free, form, isFileUpload, map, o, opts, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
+      var error_free, form, isFileUpload, map, o, opts, val, _i, _j, _k, _len, _len1, _len2, _ref5, _ref6, _ref7;
       if (e != null) {
         e.preventDefault();
       }
@@ -1698,9 +1740,9 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
           parent: this
         };
         isFileUpload = false;
-        _ref = form.find("input");
-        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-          o = _ref[_i];
+        _ref5 = form.find("input");
+        for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
+          o = _ref5[_i];
           if ((o.value != null) && jQuery.trim(o.value).length > 0) {
             map[o.name] = o.value;
           }
@@ -1708,16 +1750,16 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
             isFileUpload = true;
           }
         }
-        _ref1 = form.find("textarea");
-        for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
-          o = _ref1[_j];
+        _ref6 = form.find("textarea");
+        for (_j = 0, _len1 = _ref6.length; _j < _len1; _j++) {
+          o = _ref6[_j];
           if ((o.value != null) && jQuery.trim(o.value).length > 0) {
             map["body"] = o.value;
           }
         }
-        _ref2 = form.find("select");
-        for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
-          o = _ref2[_k];
+        _ref7 = form.find("select");
+        for (_k = 0, _len2 = _ref7.length; _k < _len2; _k++) {
+          o = _ref7[_k];
           val = this.getSelectedValue(o);
           if ((val != null) && jQuery.trim(val).length > 0) {
             map[o.name] = val;
@@ -1739,46 +1781,46 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
     };
 
     OperationView.prototype.handleFileUpload = function(map, form) {
-      var bodyParam, el, headerParams, o, obj, param, params, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3,
+      var bodyParam, el, headerParams, o, obj, param, params, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref5, _ref6, _ref7, _ref8,
         _this = this;
-      _ref = form.serializeArray();
-      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-        o = _ref[_i];
+      _ref5 = form.serializeArray();
+      for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
+        o = _ref5[_i];
         if ((o.value != null) && jQuery.trim(o.value).length > 0) {
           map[o.name] = o.value;
         }
       }
       bodyParam = new FormData();
       params = 0;
-      _ref1 = this.model.parameters;
-      for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
-        param = _ref1[_j];
+      _ref6 = this.model.parameters;
+      for (_j = 0, _len1 = _ref6.length; _j < _len1; _j++) {
+        param = _ref6[_j];
         if (param.paramType === 'form') {
-          if (map[param.name] !== void 0) {
+          if (param.type.toLowerCase() !== 'file' && map[param.name] !== void 0) {
             bodyParam.append(param.name, map[param.name]);
           }
         }
       }
       headerParams = {};
-      _ref2 = this.model.parameters;
-      for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
-        param = _ref2[_k];
+      _ref7 = this.model.parameters;
+      for (_k = 0, _len2 = _ref7.length; _k < _len2; _k++) {
+        param = _ref7[_k];
         if (param.paramType === 'header') {
           headerParams[param.name] = map[param.name];
         }
       }
       log(headerParams);
-      _ref3 = form.find('input[type~="file"]');
-      for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
-        el = _ref3[_l];
+      _ref8 = form.find('input[type~="file"]');
+      for (_l = 0, _len3 = _ref8.length; _l < _len3; _l++) {
+        el = _ref8[_l];
         if (typeof el.files[0] !== 'undefined') {
           bodyParam.append($(el).attr('name'), el.files[0]);
           params += 1;
         }
       }
-      log(bodyParam);
       this.invocationUrl = this.model.supportHeaderParams() ? (headerParams = this.model.getHeaderParams(map), this.model.urlify(map, false)) : this.model.urlify(map, true);
-      $(".request_url", $(this.el)).html("<pre>" + this.invocationUrl + "</pre>");
+      $(".request_url", $(this.el)).html("<pre></pre>");
+      $(".request_url pre", $(this.el)).text(this.invocationUrl);
       obj = {
         type: this.model.method,
         url: this.invocationUrl,
@@ -1829,14 +1871,14 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
     };
 
     OperationView.prototype.getSelectedValue = function(select) {
-      var opt, options, _i, _len, _ref;
+      var opt, options, _i, _len, _ref5;
       if (!select.multiple) {
         return select.value;
       } else {
         options = [];
-        _ref = select.options;
-        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-          opt = _ref[_i];
+        _ref5 = select.options;
+        for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
+          opt = _ref5[_i];
           if (opt.selected) {
             options.push(opt.value);
           }
@@ -1924,9 +1966,9 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
         padding = '';
         indent += transitions[fromTo];
         padding = ((function() {
-          var _j, _ref, _results;
+          var _j, _ref5, _results;
           _results = [];
-          for (j = _j = 0, _ref = indent; 0 <= _ref ? _j < _ref : _j > _ref; j = 0 <= _ref ? ++_j : --_j) {
+          for (j = _j = 0, _ref5 = indent; 0 <= _ref5 ? _j < _ref5 : _j > _ref5; j = 0 <= _ref5 ? ++_j : --_j) {
             _results.push('  ');
           }
           return _results;
@@ -1945,7 +1987,7 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
     };
 
     OperationView.prototype.showStatus = function(response) {
-      var code, content, contentType, headers, pre, response_body, url;
+      var code, content, contentType, headers, opts, pre, response_body, response_body_el, url;
       if (response.content === void 0) {
         content = response.data;
         url = response.url;
@@ -1974,14 +2016,21 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
         pre = $('<pre class="json" />').append(code);
       }
       response_body = pre;
-      $(".request_url", $(this.el)).html("<pre>" + url + "</pre>");
+      $(".request_url", $(this.el)).html("<pre></pre>");
+      $(".request_url pre", $(this.el)).text(url);
       $(".response_code", $(this.el)).html("<pre>" + response.status + "</pre>");
       $(".response_body", $(this.el)).html(response_body);
-      $(".response_headers", $(this.el)).html("<pre>" + JSON.stringify(response.headers, null, "  ").replace(/\n/g, "<br>") + "</pre>");
+      $(".response_headers", $(this.el)).html("<pre>" + _.escape(JSON.stringify(response.headers, null, "  ")).replace(/\n/g, "<br>") + "</pre>");
       $(".response", $(this.el)).slideDown();
       $(".response_hider", $(this.el)).show();
       $(".response_throbber", $(this.el)).hide();
-      return hljs.highlightBlock($('.response_body', $(this.el))[0]);
+      response_body_el = $('.response_body', $(this.el))[0];
+      opts = this.options.swaggerOptions;
+      if (opts.highlightSizeThreshold && response.data.length > opts.highlightSizeThreshold) {
+        return response_body_el;
+      } else {
+        return hljs.highlightBlock(response_body_el);
+      }
     };
 
     OperationView.prototype.toggleOperationContent = function() {
@@ -1999,11 +2048,11 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
   })(Backbone.View);
 
   StatusCodeView = (function(_super) {
-
     __extends(StatusCodeView, _super);
 
     function StatusCodeView() {
-      StatusCodeView.__super__.constructor.apply(this, arguments);
+      _ref5 = StatusCodeView.__super__.constructor.apply(this, arguments);
+      return _ref5;
     }
 
     StatusCodeView.prototype.initialize = function() {};
@@ -2038,11 +2087,11 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
   })(Backbone.View);
 
   ParameterView = (function(_super) {
-
     __extends(ParameterView, _super);
 
     function ParameterView() {
-      ParameterView.__super__.constructor.apply(this, arguments);
+      _ref6 = ParameterView.__super__.constructor.apply(this, arguments);
+      return _ref6;
     }
 
     ParameterView.prototype.initialize = function() {
@@ -2127,11 +2176,11 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
   })(Backbone.View);
 
   SignatureView = (function(_super) {
-
     __extends(SignatureView, _super);
 
     function SignatureView() {
-      SignatureView.__super__.constructor.apply(this, arguments);
+      _ref7 = SignatureView.__super__.constructor.apply(this, arguments);
+      return _ref7;
     }
 
     SignatureView.prototype.events = {
@@ -2146,7 +2195,7 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
       var template;
       template = this.template();
       $(this.el).html(template(this.model));
-      this.switchToDescription();
+      this.switchToSnippet();
       this.isParam = this.model.isParam;
       if (this.isParam) {
         $('.notice', $(this.el)).text('Click to set as parameter value');
@@ -2196,11 +2245,11 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
   })(Backbone.View);
 
   ContentTypeView = (function(_super) {
-
     __extends(ContentTypeView, _super);
 
     function ContentTypeView() {
-      ContentTypeView.__super__.constructor.apply(this, arguments);
+      _ref8 = ContentTypeView.__super__.constructor.apply(this, arguments);
+      return _ref8;
     }
 
     ContentTypeView.prototype.initialize = function() {};
@@ -2222,11 +2271,11 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
   })(Backbone.View);
 
   ResponseContentTypeView = (function(_super) {
-
     __extends(ResponseContentTypeView, _super);
 
     function ResponseContentTypeView() {
-      ResponseContentTypeView.__super__.constructor.apply(this, arguments);
+      _ref9 = ResponseContentTypeView.__super__.constructor.apply(this, arguments);
+      return _ref9;
     }
 
     ResponseContentTypeView.prototype.initialize = function() {};
@@ -2248,11 +2297,11 @@ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
   })(Backbone.View);
 
   ParameterContentTypeView = (function(_super) {
-
     __extends(ParameterContentTypeView, _super);
 
     function ParameterContentTypeView() {
-      ParameterContentTypeView.__super__.constructor.apply(this, arguments);
+      _ref10 = ParameterContentTypeView.__super__.constructor.apply(this, arguments);
+      return _ref10;
     }
 
     ParameterContentTypeView.prototype.initialize = function() {};

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
htdocs/includes/restler/framework/Luracast/Restler/explorer/swagger-ui.min.js


+ 4 - 1
htdocs/install/mysql/migration/5.0.0-6.0.0.sql

@@ -34,6 +34,10 @@
 -- VMYSQL4.3 ALTER TABLE llx_opensurvey_sondage MODIFY COLUMN date_fin DATETIME NULL DEFAULT NULL;
 -- VPGSQL8.2 ALTER TABLE llx_opensurvey_sondage ALTER COLUMN date_fin DROP NOT NULL;
 
+
+ALTER TABLE llx_extrafields ADD COLUMN fieldcomputed text;
+ALTER TABLE llx_extrafields ADD COLUMN fielddefault varchar(255);
+
 ALTER TABLE llx_opensurvey_sondage MODIFY COLUMN tms timestamp DEFAULT CURRENT_TIMESTAMP;
 
 ALTER TABLE llx_opensurvey_sondage ADD COLUMN fk_user_creat integer NOT NULL DEFAULT 0;
@@ -251,7 +255,6 @@ ALTER TABLE llx_product_fournisseur_price_log ADD COLUMN multicurrency_tx	     d
 ALTER TABLE llx_product_fournisseur_price_log ADD COLUMN multicurrency_price	 double(24,8) DEFAULT NULL;
 ALTER TABLE llx_product_fournisseur_price_log ADD COLUMN multicurrency_price_ttc double(24,8) DEFAULT NULL;
 
-
 create table llx_payment_various
 (
   rowid                 integer AUTO_INCREMENT PRIMARY KEY,

+ 4 - 2
htdocs/install/mysql/tables/llx_extrafields.sql

@@ -21,12 +21,14 @@ create table llx_extrafields
 (
 	rowid           integer AUTO_INCREMENT PRIMARY KEY,
 	name            varchar(64) NOT NULL,         				-- name of field into extrafields tables
-	entity          integer DEFAULT 1 NOT NULL,				-- multi company id
-    elementtype     varchar(64) NOT NULL DEFAULT 'member',	-- for which element this extra fields is for
+	entity          integer DEFAULT 1 NOT NULL,					-- multi company id
+    elementtype     varchar(64) NOT NULL DEFAULT 'member',		-- for which element this extra fields is for
 	tms             timestamp,									-- date of last update
 	label           varchar(255) NOT NULL,        				-- label to show for attribute
 	type            varchar(8),
 	size            varchar(8) DEFAULT NULL,
+	fieldcomputed   text,
+	fielddefault    varchar(255),
 	fieldunique     integer DEFAULT 0,
 	fieldrequired   integer DEFAULT 0,
 	perms			varchar(255),								-- not used yet

+ 7 - 5
htdocs/langs/en_US/admin.lang

@@ -375,19 +375,21 @@ Int=Integer
 Float=Float
 DateAndTime=Date and hour
 Unique=Unique
-Boolean=Boolean (Checkbox)
+Boolean=Boolean (one checkbox)
 ExtrafieldPhone = Phone
 ExtrafieldPrice = Price
 ExtrafieldMail = Email
 ExtrafieldUrl = Url
 ExtrafieldSelect = Select list
 ExtrafieldSelectList = Select from table
-ExtrafieldSeparator=Separator
+ExtrafieldSeparator=Separator (not a field)
 ExtrafieldPassword=Password
-ExtrafieldCheckBox=Checkbox
-ExtrafieldRadio=Radio button
-ExtrafieldCheckBoxFromList= Checkbox from table
+ExtrafieldRadio=Radio buttons (on choice only)
+ExtrafieldCheckBox=Checkboxes
+ExtrafieldCheckBoxFromList=Checkboxes from table
 ExtrafieldLink=Link to an object
+ComputedFormula=Computed field
+ComputedFormulaDesc=You can enter here a formula using other properties of object or any PHP coding to get a dynamic computed value. You can use any PHP compatible formulas including the "?" condition operator, and following global object: <strong>$db, $conf, $langs, $mysoc, $user, $object</strong>.<br><strong>WARNING</strong>: Only some properties of $object may be available. If you need a properties not loaded, just fetch yourself the object into your formula like in the second example.<br>Using a computed field means you can't enter yourself any value from interface. Also, if there is a syntax error, the formula may return nothing.<br><br>Example of formula:<br>$object->id < 5 ? round($object->id / 2, 2) : ($object->id + 2*$user->id) * (int) substr($mysoc->zip, 1, 2)<br><br>Example of formula to force load of object and its parent object:<br>(($reloadedobj = new Task($db)) && ($reloadedobj->fetch($object->id) > 0) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetch($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : 'Parent project not found'<br><br>Other example to reload object<br>(($reloadedobj = new Societe($db)) && ($reloadedobj->fetch($obj->id ? $obj->id : ($obj->rowid ? $obj->rowid : $object->id)) > 0)) ? round($reloadedobj->capital / 5) : '-1'
 ExtrafieldParamHelpselect=Parameters list have to be like key,value<br><br> for example : <br>1,value1<br>2,value2<br>3,value3<br>...<br><br>In order to have the list depending on another complementary attribute list :<br>1,value1|options_<i>parent_list_code</i>:parent_key<br>2,value2|options_<i>parent_list_code</i>:parent_key <br><br>In order to have the list depending on another list :<br>1,value1|<i>parent_list_code</i>:parent_key<br>2,value2|<i>parent_list_code</i>:parent_key
 ExtrafieldParamHelpcheckbox=Parameters list have to be like key,value<br><br> for example : <br>1,value1<br>2,value2<br>3,value3<br>...
 ExtrafieldParamHelpradio=Parameters list have to be like key,value<br><br> for example : <br>1,value1<br>2,value2<br>3,value3<br>...

+ 1 - 0
htdocs/langs/en_US/main.lang

@@ -773,6 +773,7 @@ BulkActions=Bulk actions
 ClickToShowHelp=Click to show tooltip help
 HR=HR
 HRAndBank=HR and Bank
+AutomaticallyCalculated=Automatically calculated
 # Week day
 Monday=Monday
 Tuesday=Tuesday

+ 12 - 8
htdocs/modulebuilder/skeletons/skeleton_list.php

@@ -126,13 +126,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
 }
 
 
-// Load object if id or ref is provided as parameter
 $object=new Skeleton_Class($db);
-if (($id > 0 || ! empty($ref)) && $action != 'add')
-{
-	$result=$object->fetch($id,$ref);
-	if ($result < 0) dol_print_error($db);
-}
 
 
 
@@ -354,7 +348,9 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
        if (! empty($arrayfields["ef.".$key]['checked'])) 
        {
 			$align=$extrafields->getAlignFlag($key);
-			print_liste_field_titre($langs->trans($extralabels[$key]),$_SERVER["PHP_SELF"],"ef.".$key,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder);
+			$sortonfield = "ef.".$key;
+			if (! empty($extrafields->attribute_computed[$key])) $sortonfield='';
+			print_liste_field_titre($langs->trans($extralabels[$key]),$_SERVER["PHP_SELF"],$sortonfield,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder);
        }
    }
 }
@@ -383,7 +379,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
             $align=$extrafields->getAlignFlag($key);
             $typeofextrafield=$extrafields->attribute_type[$key];
             print '<td class="liste_titre'.($align?' '.$align:'').'">';
-        	if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select')))
+        	if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select')) && empty($extrafields->attribute_computed[$key]))
 			{
 			    $crit=$val;
 				$tmpkey=preg_replace('/search_options_/','',$key);
@@ -427,6 +423,14 @@ print '</td>';
 print '</tr>'."\n";
     
 
+// Detect if we need a fetch on each output line
+$needToFetchEachLine=0;
+foreach ($extrafields->attribute_computed as $key => $val)
+{
+    if (preg_match('/\$object/',$val)) $needToFetchEachLine++;  // There is at least one compute field that use $object
+}
+
+
 $i=0;
 $totalarray=array();
 while ($i < min($num, $limit))

+ 1 - 1
htdocs/projet/admin/project.php

@@ -318,7 +318,7 @@ print load_fiche_titre($langs->trans("ProjectsSetup"),$linkback,'title_setup');
 
 $head=project_admin_prepare_head();
 
-dol_fiche_head($head, 'project', $langs->trans("Projects"), 0, 'project');
+dol_fiche_head($head, 'project', $langs->trans("Projects"), -1, 'project');
 
 
 

+ 1 - 1
htdocs/projet/admin/project_extrafields.php

@@ -70,7 +70,7 @@ print load_fiche_titre($langs->trans("ProjectsSetup"),$linkback,'title_setup');
 
 $head = project_admin_prepare_head();
 
-dol_fiche_head($head, 'attributes', $langs->trans("Projects"), 0, 'project');
+dol_fiche_head($head, 'attributes', $langs->trans("Projects"), -1, 'project');
 
 require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php';
 

+ 1 - 1
htdocs/projet/admin/project_task_extrafields.php

@@ -69,7 +69,7 @@ print load_fiche_titre($langs->trans("ProjectsSetup"),$linkback,'title_setup');
 
 $head = project_admin_prepare_head();
 
-dol_fiche_head($head, 'attributes_task', $langs->trans("Projects"), 0, 'project');
+dol_fiche_head($head, 'attributes_task', $langs->trans("Projects"), -1, 'project');
 
 require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php';
 

+ 25 - 25
htdocs/projet/list.php

@@ -141,6 +141,8 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
    }
 }
 
+$object = new Project($db);
+
 
 /*
  * Actions
@@ -200,7 +202,6 @@ if (empty($reshook))
  * View
  */
 
-$projectstatic = new Project($db);
 $socstatic = new Societe($db);
 $form = new Form($db);
 $formother = new FormOther($db);
@@ -212,12 +213,12 @@ $title=$langs->trans("Projects");
 
 // Get list of project id allowed to user (in a string list separated by coma)
 $projectsListId='';
-if (! $user->rights->projet->all->lire) $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user,0,1,$socid);
+if (! $user->rights->projet->all->lire) $projectsListId = $object->getProjectsAuthorizedForUser($user,0,1,$socid);
 
 // Get id of types of contacts for projects (This list never contains a lot of elements)
 $listofprojectcontacttype=array();
 $sql = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc";
-$sql.= " WHERE ctc.element = '" . $projectstatic->element . "'";
+$sql.= " WHERE ctc.element = '" . $object->element . "'";
 $sql.= " AND ctc.source = 'internal'";
 $resql = $db->query($sql);
 if ($resql)
@@ -232,7 +233,7 @@ if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0';
 
 
 $distinct='DISTINCT';   // We add distinct until we are added a protection to be sure a contact of a project and task is only once.
-$sql = "SELECT ".$distinct." p.rowid as projectid, p.ref, p.title, p.fk_statut, p.fk_opp_status, p.public, p.fk_user_creat";
+$sql = "SELECT ".$distinct." p.rowid as id, p.ref, p.title, p.fk_statut, p.fk_opp_status, p.public, p.fk_user_creat";
 $sql.= ", p.datec as date_creation, p.dateo as date_start, p.datee as date_end, p.opp_amount, p.opp_percent, p.tms as date_update, p.budget_amount";
 $sql.= ", s.nom as name, s.rowid as socid";
 $sql.= ", cls.code as opp_status_code";
@@ -360,8 +361,7 @@ $arrayofselected=is_array($toselect)?$toselect:array();
 if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $search_all)
 {
     $obj = $db->fetch_object($resql);
-    $id = $obj->projectid;
-    header("Location: ".DOL_URL_ROOT.'/projet/card.php?id='.$id);
+    header("Location: ".DOL_URL_ROOT.'/projet/card.php?id='.$obj->id);
     exit;
 }
 
@@ -566,7 +566,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
             $align=$extrafields->getAlignFlag($key);
             $typeofextrafield=$extrafields->attribute_type[$key];
             print '<td class="liste_titre'.($align?' '.$align:'').'">';
-        	if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select')))
+        	if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select')) && empty($extrafields->attribute_computed[$key]))
 			{
 			    $crit=$val;
 				$tmpkey=preg_replace('/search_options_/','',$key);
@@ -599,7 +599,7 @@ if (! empty($arrayfields['p.fk_statut']['checked']))
 {
 	print '<td class="liste_titre nowrap" align="right">';
     $arrayofstatus = array();
-    foreach($projectstatic->statuts_short as $key => $val) $arrayofstatus[$key]=$langs->trans($val);
+    foreach($object->statuts_short as $key => $val) $arrayofstatus[$key]=$langs->trans($val);
     $arrayofstatus['99']=$langs->trans("NotClosed").' ('.$langs->trans('Draft').'+'.$langs->trans('Opened').')';
 	print $form->selectarray('search_status', $arrayofstatus, $search_status, 1, 0, 0, '', 0, 0, 0, '', 'maxwidth100');
     print '</td>';
@@ -632,7 +632,9 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
         if (! empty($arrayfields["ef.".$key]['checked']))
         {
             $align=$extrafields->getAlignFlag($key);
-            print_liste_field_titre($langs->trans($extralabels[$key]),$_SERVER["PHP_SELF"],"ef.".$key,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder);
+            $sortonfield = "ef.".$key;
+            if (! empty($extrafields->attribute_computed[$key])) $sortonfield='';
+            print_liste_field_titre($langs->trans($extralabels[$key]),$_SERVER["PHP_SELF"],$sortonfield,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder);
         }
     }
 }
@@ -652,15 +654,15 @@ while ($i < min($num,$limit))
 {
 	$obj = $db->fetch_object($resql);
 
-	$projectstatic->id = $obj->projectid;
-	$projectstatic->user_author_id = $obj->fk_user_creat;
-	$projectstatic->public = $obj->public;
-	$projectstatic->ref = $obj->ref;
-	$projectstatic->datee = $db->jdate($obj->date_end);
-	$projectstatic->statut = $obj->fk_statut;
-	$projectstatic->opp_status = $obj->fk_opp_status;
-	 
-	$userAccess = $projectstatic->restrictedProjectArea($user);    // why this ?
+	$object->id = $obj->id;
+	$object->user_author_id = $obj->fk_user_creat;
+	$object->public = $obj->public;
+	$object->ref = $obj->ref;
+	$object->datee = $db->jdate($obj->date_end);
+	$object->statut = $obj->fk_statut;
+	$object->opp_status = $obj->fk_opp_status;
+	
+	$userAccess = $object->restrictedProjectArea($user);    // why this ?
 	if ($userAccess >= 0)
 	{
 		print '<tr class="oddeven">';
@@ -669,8 +671,8 @@ while ($i < min($num,$limit))
     	if (! empty($arrayfields['p.ref']['checked']))
     	{
     		print '<td class="nowrap">';
-    		print $projectstatic->getNomUrl(1);
-    		if ($projectstatic->hasDelay()) print img_warning($langs->trans('Late'));
+    		print $object->getNomUrl(1);
+    		if ($object->hasDelay()) print img_warning($langs->trans('Late'));
     		print '</td>';
 		    if (! $i) $totalarray['nbfield']++;
     	}
@@ -851,8 +853,7 @@ while ($i < min($num,$limit))
 		// Status
 		if (! empty($arrayfields['p.fk_statut']['checked']))
 		{
-    		$projectstatic->statut = $obj->fk_statut;
-    		print '<td align="right">'.$projectstatic->getLibStatut(5).'</td>';
+    		print '<td align="right">'.$object->getLibStatut(5).'</td>';
 			if (! $i) $totalarray['nbfield']++;
 		}
         // Action column
@@ -860,8 +861,8 @@ while ($i < min($num,$limit))
         if ($massactionbutton || $massaction)   // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
         {
             $selected=0;
-    		if (in_array($obj->projectid, $arrayofselected)) $selected=1;
-    		print '<input id="cb'.$obj->projectid.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$obj->projectid.'"'.($selected?' checked="checked"':'').'>';
+    		if (in_array($obj->id, $arrayofselected)) $selected=1;
+    		print '<input id="cb'.$obj->id.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$obj->id.'"'.($selected?' checked="checked"':'').'>';
         }
         print '</td>';
 		if (! $i) $totalarray['nbfield']++;
@@ -871,7 +872,6 @@ while ($i < min($num,$limit))
 	}
 
 	$i++;
-
 }
 
 // Show total line

+ 20 - 17
htdocs/projet/tasks/list.php

@@ -131,6 +131,8 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
     }
 }
 
+$object = new Task($db);
+
 
 /*
  * Actions
@@ -190,11 +192,11 @@ if (empty($search_projectstatus) && $search_projectstatus == '') $search_project
  * View
  */
 
+$now = dol_now();
 $form=new Form($db);
 $formother=new FormOther($db);
 $socstatic=new Societe($db);
 $projectstatic = new Project($db);
-$taskstatic = new Task($db);
 $puser=new User($db);
 $tuser=new User($db);
 if ($search_project_user > 0) $puser->fetch($search_project_user);
@@ -231,7 +233,7 @@ if (count($listofprojectcontacttype) == 0) $listofprojectcontacttype[0]='0';
 // Get id of types of contacts for tasks (This list never contains a lot of elements)
 $listoftaskcontacttype=array();
 $sql = "SELECT ctc.rowid, ctc.code FROM ".MAIN_DB_PREFIX."c_type_contact as ctc";
-$sql.= " WHERE ctc.element = '" . $taskstatic->element . "'";
+$sql.= " WHERE ctc.element = '" . $object->element . "'";
 $sql.= " AND ctc.source = 'internal'";
 $resql = $db->query($sql);
 if ($resql)
@@ -549,7 +551,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
             $align=$extrafields->getAlignFlag($key);
             $typeofextrafield=$extrafields->attribute_type[$key];
             print '<td class="liste_titre'.($align?' '.$align:'').'">';
-        	if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select')))
+        	if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select')) && empty($extrafields->attribute_computed[$key]))
 			{
 			    $crit=$val;
 				$tmpkey=preg_replace('/search_options_/','',$key);
@@ -606,7 +608,9 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
         if (! empty($arrayfields["ef.".$key]['checked']))
         {
             $align=$extrafields->getAlignFlag($key);
-            print_liste_field_titre($langs->trans($extralabels[$key]),$_SERVER["PHP_SELF"],"ef.".$key,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder);
+            $sortonfield = "ef.".$key;
+            if (! empty($extrafields->attribute_computed[$key])) $sortonfield='';
+            print_liste_field_titre($langs->trans($extralabels[$key]),$_SERVER["PHP_SELF"],$sortonfield,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder);
         }
     }
 }
@@ -624,14 +628,21 @@ $plannedworkloadoutputformat='allhourmin';
 $timespentoutputformat='allhourmin';
 if (! empty($conf->global->PROJECT_PLANNED_WORKLOAD_FORMAT)) $plannedworkloadoutputformat=$conf->global->PROJECT_PLANNED_WORKLOAD_FORMAT;
 if (! empty($conf->global->PROJECT_TIMES_SPENT_FORMAT)) $timespentoutputformat=$conf->global->PROJECT_TIME_SPENT_FORMAT;
- 
-$now = dol_now();
+
 $i=0;
 $totalarray=array();
 while ($i < min($num,$limit))
 {
 	$obj = $db->fetch_object($resql);
 
+	$object->id = $obj->id;
+	$object->ref = $obj->ref;
+	$object->label = $obj->label;
+	$object->fk_statut = $obj->fk_statut;
+	$object->progress = $obj->progress;
+	$object->datee = $db->jdate($obj->date_end);	// deprecated
+	$object->date_end = $db->jdate($obj->date_end);
+
 	$projectstatic->id = $obj->projectid;
 	$projectstatic->ref = $obj->projectref;
 	$projectstatic->title = $obj->projecttitle;
@@ -639,14 +650,6 @@ while ($i < min($num,$limit))
 	$projectstatic->statut = $obj->projectstatus;
 	$projectstatic->datee = $db->jdate($obj->projectdatee);
 	
-	$taskstatic->id = $obj->id;
-	$taskstatic->ref = $obj->ref;
-	$taskstatic->label = $obj->label;
-	$taskstatic->fk_statut = $obj->fk_statut;
-	$taskstatic->progress = $obj->progress;
-	$taskstatic->datee = $db->jdate($obj->date_end);	// deprecated
-	$taskstatic->date_end = $db->jdate($obj->date_end);
-	
 	$userAccess = $projectstatic->restrictedProjectArea($user);    // why this ?
 	if ($userAccess >= 0)
 	{
@@ -656,8 +659,8 @@ while ($i < min($num,$limit))
     	if (! empty($arrayfields['t.ref']['checked']))
     	{
     	    print '<td>';
-    	    print $taskstatic->getNomUrl(1,'withproject');
-    		if ($taskstatic->hasDelay()) print img_warning("Late");
+    	    print $object->getNomUrl(1,'withproject');
+    		if ($object->hasDelay()) print img_warning("Late");
     	    print '</td>';
 		    if (! $i) $totalarray['nbfield']++;
     	}        	 
@@ -665,7 +668,7 @@ while ($i < min($num,$limit))
     	if (! empty($arrayfields['t.label']['checked']))
     	{
     	    print '<td>';
-    	    print $taskstatic->label;
+    	    print $object->label;
     	    print '</td>';
 		    if (! $i) $totalarray['nbfield']++;
     	}

+ 2 - 2
htdocs/projet/tasks/time.php

@@ -96,7 +96,7 @@ if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'e
 include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
 
 // Purge search criteria
-if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") ||GETPOST("button_removefilter")) // All test are required to be compatible with all browsers
+if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") ||GETPOST("button_removefilter")) // All tests are required to be compatible with all browsers
 {
     $search_date='';
     $search_datehour='';
@@ -305,7 +305,7 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0)
 			// Tabs for project
 			$tab='tasks';
 			$head=project_prepare_head($projectstatic);
-			dol_fiche_head($head, $tab, $langs->trans("Project"), 0, ($projectstatic->public?'projectpub':'project'));
+			dol_fiche_head($head, $tab, $langs->trans("Project"), -1, ($projectstatic->public?'projectpub':'project'));
 
 			$param=($mode=='mine'?'&mode=mine':'');
 

+ 20 - 1
htdocs/societe/class/api_thirdparties.class.php

@@ -311,7 +311,26 @@ class Thirdparties extends DolibarrApi
       return $this->company;
     }
 
-    /**
+	/**
+	 * Clean sensible object datas
+	 *
+	 * @param   object  $object    Object to clean
+	 * @return    array    Array of cleaned object properties
+	 */
+	function _cleanObjectDatas($object) {
+	
+	    $object = parent::_cleanObjectDatas($object);
+	
+	    unset($object->total_ht);
+	    unset($object->total_tva);
+	    unset($object->total_localtax1);
+	    unset($object->total_localtax2);
+	    unset($object->total_ttc);
+	    
+	    return $object;
+	}	
+	
+	/**
      * Validate fields before create or update object
      * 
      * @param array $data   Datas to validate

+ 0 - 2
htdocs/societe/class/societe.class.php

@@ -389,8 +389,6 @@ class Societe extends CommonObject
         $this->forme_juridique_code  = 0;
         $this->tva_assuj = 1;
         $this->status = 1;
-
-        return 1;
     }
 
 

+ 4 - 2
htdocs/societe/list.php

@@ -831,7 +831,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
             $align=$extrafields->getAlignFlag($key);
             $typeofextrafield=$extrafields->attribute_type[$key];
             print '<td class="liste_titre'.($align?' '.$align:'').'">';
-		    if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select')))
+		    if (in_array($typeofextrafield, array('varchar', 'int', 'double', 'select')) && empty($extrafields->attribute_computed[$key]))
 			{
 			    $crit=$val;
 				$tmpkey=preg_replace('/search_options_/','',$key);
@@ -907,7 +907,9 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab
         if (! empty($arrayfields["ef.".$key]['checked']))
         {
             $align=$extrafields->getAlignFlag($key);
-            print_liste_field_titre($langs->trans($extralabels[$key]),$_SERVER["PHP_SELF"],"ef.".$key,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder);
+            $sortonfield = "ef.".$key;
+            if (! empty($extrafields->attribute_computed[$key])) $sortonfield='';
+            print_liste_field_titre($langs->trans($extralabels[$key]),$_SERVER["PHP_SELF"],$sortonfield,"",$param,($align?'align="'.$align.'"':''),$sortfield,$sortorder);
         }
     }
 }

+ 16 - 4
htdocs/user/class/api_users.class.php

@@ -225,9 +225,9 @@ class Users extends DolibarrApi
     /**
 	 * add user to group
 	 *
-	 * @param   int     $id User ID
-	 * @param   int     $group Group ID
-	 * @return  int
+	 * @param   int     $id        User ID
+	 * @param   int     $group     Group ID
+	 * @return  int                1 if success
      * 
 	 * @url	GET {id}/setGroup/{group}
 	 */
@@ -246,7 +246,13 @@ class Users extends DolibarrApi
           throw new RestException(401, 'Access not allowed for login ' . DolibarrApiAccess::$user->login);
         }
     
-        return $this->useraccount->SetInGroup($group,1);
+        $result = $this->useraccount->SetInGroup($group,1);
+        if (! ($result > 0))
+        {
+            throw new RestException(500, $this->useraccount->error);
+        }
+                
+        return 1;
     }
 
 	/**
@@ -287,6 +293,12 @@ class Users extends DolibarrApi
 	    unset($object->lastsearch_values);
 	    unset($object->lastsearch_values_tmp);
 	     
+	    unset($object->total_ht);
+	    unset($object->total_tva);
+	    unset($object->total_localtax1);
+	    unset($object->total_localtax2);
+	    unset($object->total_ttc);
+	    
 	    return $object;
 	}	
 	

Некоторые файлы не были показаны из-за большого количества измененных файлов