';
+
// Execute hook printTopRightMenu (hooks should output string like '
')
$parameters=array();
$result=$hookmanager->executeHooks('printTopRightMenu',$parameters); // Note that $action and $object may have been modified by some hooks
@@ -1437,7 +1437,17 @@ function top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $a
}
else $toprightmenu.=$result; // For backward compatibility
- // Link to print main content area
+ // Link to module builder
+ if (! empty($conf->modulebuilder->enabled))
+ {
+ $text ='
';
+ //$text.= img_picto(":".$langs->trans("ModuleBuilder"), 'printer_top.png', 'class="printer"');
+ $text.='';
+ $text.='';
+ $toprightmenu.=@Form::textwithtooltip('',$langs->trans("ModuleBuilder"),2,1,$text,'login_block_elem',2);
+ }
+
+ // Link to print main content area
if (empty($conf->global->MAIN_PRINT_DISABLELINK) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && empty($conf->browser->phone))
{
$qs=$_SERVER["QUERY_STRING"];
@@ -1448,7 +1458,8 @@ function top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $a
$qs.=(($qs && $morequerystring)?'&':'').$morequerystring;
$text ='
';
- $text.= img_picto(":".$langs->trans("PrintContentArea"), 'printer_top.png', 'class="printer"');
+ //$text.= img_picto(":".$langs->trans("PrintContentArea"), 'printer_top.png', 'class="printer"');
+ $text.='';
$text.='';
$toprightmenu.=@Form::textwithtooltip('',$langs->trans("PrintContentArea"),2,1,$text,'login_block_elem',2);
}
@@ -1481,7 +1492,8 @@ function top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $a
if ($mode == 'wiki') $text.=sprintf($helpbaseurl,urlencode(html_entity_decode($helppage)));
else $text.=sprintf($helpbaseurl,$helppage);
$text.='">';
- $text.=img_picto('', 'helpdoc_top').' ';
+ //$text.=img_picto('', 'helpdoc_top').' ';
+ $text.='
';
//$toprightmenu.=$langs->trans($mode == 'wiki' ? 'OnlineHelp': 'Help');
//if ($mode == 'wiki') $text.=' ('.dol_trunc(strtr($helppage,'_',' '),8).')';
$text.='';
@@ -1641,11 +1653,11 @@ function left_menu($menu_array_before, $helppagename='', $notused='', $menu_arra
{
$doliurl='https://www.dolibarr.org';
//local communities
- if (preg_match('/fr/i',$langs->defaultlang)) $doliurl='http://www.dolibarr.fr';
- if (preg_match('/es/i',$langs->defaultlang)) $doliurl='http://www.dolibarr.es';
- if (preg_match('/de/i',$langs->defaultlang)) $doliurl='http://www.dolibarr.de';
- if (preg_match('/it/i',$langs->defaultlang)) $doliurl='http://www.dolibarr.it';
- if (preg_match('/gr/i',$langs->defaultlang)) $doliurl='http://www.dolibarr.gr';
+ if (preg_match('/fr/i',$langs->defaultlang)) $doliurl='https://www.dolibarr.fr';
+ if (preg_match('/es/i',$langs->defaultlang)) $doliurl='https://www.dolibarr.es';
+ if (preg_match('/de/i',$langs->defaultlang)) $doliurl='https://www.dolibarr.de';
+ if (preg_match('/it/i',$langs->defaultlang)) $doliurl='https://www.dolibarr.it';
+ if (preg_match('/gr/i',$langs->defaultlang)) $doliurl='https://www.dolibarr.gr';
$appli=constant('DOL_APPLICATION_TITLE');
if (! empty($conf->global->MAIN_APPLICATION_TITLE))
@@ -1676,7 +1688,7 @@ function left_menu($menu_array_before, $helppagename='', $notused='', $menu_arra
$bugbaseurl.= '?title=';
$bugbaseurl.= urlencode("Bug: ");
$bugbaseurl.= '&body=';
- // FIXME: use .github/ISSUE_TEMPLATE.md to generate?
+ // TODO use .github/ISSUE_TEMPLATE.md to generate?
$bugbaseurl .= urlencode("# Bug\n");
$bugbaseurl .= urlencode("\n");
$bugbaseurl.= urlencode("## Environment\n");
diff --git a/htdocs/modulebuilder/index.php b/htdocs/modulebuilder/index.php
new file mode 100644
index 00000000000..182268a5f06
--- /dev/null
+++ b/htdocs/modulebuilder/index.php
@@ -0,0 +1,91 @@
+
+ * Copyright (C) 2004-2010 Laurent Destailleur
+ * Copyright (C) 2005-2010 Regis Houssin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file htdocs/modulebuilder/index.php
+ * \brief Home page for module builder module
+ */
+
+require '../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
+
+$langs->load("admin");
+$langs->load("modulebuilder");
+$langs->load("other");
+
+// Security check
+if (! $user->admin && empty($conf->global->MODULEBUILDER_FOREVERYONE)) accessforbidden('ModuleBuilderNotAllowed');
+
+
+
+/*
+ * View
+ */
+
+$socstatic=new Societe($db);
+
+
+llxHeader("",$langs->trans("ModuleBuilder"),"");
+
+
+$text=$langs->trans("ModuleBuilder");
+
+print load_fiche_titre($text, '', 'title_setup');
+
+$tmp=explode(',', $dolibarr_main_document_root_alt);
+$dircustom = $tmp[0];
+
+// Show description of content
+print $langs->trans("ModuleBuilderDesc", $dircustom).'
';
+
+
+
+/*
+if (!empty($conf->modulebuilder->enabled) && $mainmenu == 'modulebuilder') // Entry for Module builder
+{
+ global $dolibarr_main_document_root_alt;
+ if (! empty($dolibarr_main_document_root_alt) && is_array($dolibarr_main_document_root_alt))
+ {
+ foreach ($dolibarr_main_document_root_alt as $diralt)
+ {*/
+ $dirsincustom=dol_dir_list($dircustom);
+
+ if (is_array($dirsincustom) && count($dirsincustom) > 0)
+ {
+ foreach ($dirsincustom as $dircustom)
+ {
+ $fullname = $dircustom['fullname'];
+ if (dol_is_file($fullname.'/modulebuilder.txt'))
+ {
+ print ''.$module.'
';
+ }
+ }
+ }
+/* }
+ }
+ else
+ {
+ $newmenu->add('', 'NoGeneratedModuleFound', 0, 0);
+ }*/
+
+
+
+llxFooter();
+
+$db->close();
diff --git a/htdocs/modulebuilder/skeletons/.gitignore b/htdocs/modulebuilder/skeletons/.gitignore
new file mode 100644
index 00000000000..2dda6229d24
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/.gitignore
@@ -0,0 +1,2 @@
+out.*
+socpeople*
diff --git a/htdocs/modulebuilder/skeletons/README b/htdocs/modulebuilder/skeletons/README
new file mode 100644
index 00000000000..5b5b992d91e
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/README
@@ -0,0 +1,34 @@
+README (English)
+--------------------------------
+This directory contains PHP script samples that can be used to start a development on Dolibarr.
+
+
+*** build_class_from_table.php:
+
+This is the script to use to generate PHP code of a PHP POJO class
+object, a PHP script that use this POJO. This script use the files
+skeleton_*.php to build its generated code.
+
+
+*** modMyModule.class.php:
+
+Is a sample of module descriptor that you can use if you want to build a new module/plugin for
+Dolibarr.
+
+
+*** skeleton_script.php:
+
+Is a sample you can use as an example if you need to build a script to run on command line
+
+
+*** skeleton_page.php:
+
+Is a sample you can use as an example if you need to build an HTML page to include in Dolibarr GUI.
+
+
+*** skeleton_class.class.php:
+
+Is a sample you can use as an example if you need to build a class file to access a new table required by a Dolibarr development.
+However it is better to run the build_class_from_table.php script that accepts a table name as a parameter and will uses the description table within database and the skeleton_class.class.php file to generate full code for your class file.
+After running this script, the class to access your table (insert a record, update, delete and select) is directly finished and can be used by your module's code.
+No more coding is needed to get access to table with this script because the file is completely generated once.
diff --git a/htdocs/modulebuilder/skeletons/build_api_class.php b/htdocs/modulebuilder/skeletons/build_api_class.php
new file mode 100755
index 00000000000..6263eabcd6e
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/build_api_class.php
@@ -0,0 +1,123 @@
+#!/usr/bin/env php
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file dev/skeletons/build_api_class.php
+ * \ingroup core
+ * \brief Create a complete API class file from existant class file
+ */
+
+$sapi_type = php_sapi_name();
+$script_file = basename(__FILE__);
+$path=dirname(__FILE__).'/';
+
+// Test if batch mode
+if (substr($sapi_type, 0, 3) == 'cgi') {
+ echo "Error: You are using PHP for CGI. To execute ".$script_file." from command line, you must use PHP for CLI mode.\n";
+ exit;
+}
+
+// Include Dolibarr environment
+require_once($path."../../htdocs/master.inc.php");
+// After this $db is a defined handler to database.
+
+// Main
+$version='1';
+@set_time_limit(0);
+$error=0;
+
+$langs->load("main");
+
+
+print "***** $script_file ($version) *****\n";
+
+
+// -------------------- START OF BUILD_API_FROM_CLASS --------------------
+
+// Check parameters
+if (! isset($argv[1]) && ! isset($argv[2]))
+{
+ print "Usage: $script_file phpClassFile phpClassName\n";
+ exit;
+}
+// Show parameters
+print 'Classfile='.$argv[1]."\n";
+print 'Classname='.$argv[2]."\n";
+
+$classfile=$argv[1];
+$classname=$argv[2];
+$classmin=strtolower($classname);
+$classnameApi = $classname.'Api';
+$property=array();
+$targetcontent='';
+
+// Load the class and read properties
+require_once($classfile);
+
+$property=array();
+$class = new $classname($db);
+$values=get_class_vars($classname);
+
+unset($values['db']);
+unset($values['error']);
+unset($values['errors']);
+unset($values['element']);
+unset($values['table_element']);
+unset($values['table_element_line']);
+unset($values['fk_element']);
+unset($values['ismultientitymanaged']);
+
+// Read skeleton_api_class.class.php file
+$skeletonfile=$path.'skeleton_api_class.class.php';
+$sourcecontent=file_get_contents($skeletonfile);
+if (! $sourcecontent)
+{
+ print "\n";
+ print "Error: Failed to read skeleton sample '".$skeletonfile."'\n";
+ print "Try to run script from skeletons directory.\n";
+ exit;
+}
+
+// Define output variables
+$outfile='out.api_'.$classmin.'.class.php';
+$targetcontent=$sourcecontent;
+
+// Substitute class name
+$targetcontent=preg_replace('/skeleton_api_class\.class\.php/', 'api_'.$classmin.'.class.php', $targetcontent);
+$targetcontent=preg_replace('/skeleton/', $classmin, $targetcontent);
+//$targetcontent=preg_replace('/\$table_element=\'skeleton\'/', '\$table_element=\''.$tablenoprefix.'\'', $targetcontent);
+$targetcontent=preg_replace('/SkeletonApi/', $classnameApi, $targetcontent);
+$targetcontent=preg_replace('/Skeleton/', $classname, $targetcontent);
+
+// Build file
+$fp=fopen($outfile,"w");
+if ($fp)
+{
+ fputs($fp, $targetcontent);
+ fclose($fp);
+ print "\n";
+ print "File '".$outfile."' has been built in current directory.\n";
+}
+else $error++;
+
+
+
+// -------------------- END OF BUILD_CLASS_FROM_TABLE SCRIPT --------------------
+
+print "You can now rename generated files by removing the 'out.' prefix in their name and store them into directory /module/class.\n";
+return $error;
diff --git a/htdocs/modulebuilder/skeletons/build_class_from_table.php b/htdocs/modulebuilder/skeletons/build_class_from_table.php
new file mode 100755
index 00000000000..0e5a6d82a82
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/build_class_from_table.php
@@ -0,0 +1,677 @@
+#!/usr/bin/env php
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file dev/skeletons/build_class_from_table.php
+ * \ingroup core
+ * \brief Create a complete class file from a table in database
+ */
+
+$sapi_type = php_sapi_name();
+$script_file = basename(__FILE__);
+$path=dirname(__FILE__).'/';
+
+// Test if batch mode
+if (substr($sapi_type, 0, 3) == 'cgi') {
+ echo "Error: You are using PHP for CGI. To execute ".$script_file." from command line, you must use PHP for CLI mode.\n";
+ exit;
+}
+
+// Include Dolibarr environment
+require_once($path."../../htdocs/master.inc.php");
+// After this $db is a defined handler to database.
+
+// Main
+$version='3.2';
+@set_time_limit(0);
+$error=0;
+
+$langs->load("main");
+
+
+print "***** $script_file ($version) *****\n";
+
+
+// -------------------- START OF BUILD_CLASS_FROM_TABLE SCRIPT --------------------
+
+// Check parameters
+if (! isset($argv[1]) || ! isset($argv[2]) || (isset($argv[3]) && ! isset($argv[7])))
+{
+ print "Usage: $script_file tablename modulename [server port databasename user pass]\n";
+ exit;
+}
+
+if (isset($argv[3]) && isset($argv[4]) && isset($argv[5]) && isset($argv[6]) && isset($argv[7]))
+{
+ print 'Use specific database ids'."\n";
+ $db=getDoliDBInstance('mysqli',$argv[3],$argv[6],$argv[7],$argv[5],$argv[4]);
+}
+
+if ($db->type != 'mysql' && $db->type != 'mysqli')
+{
+ print "Error: This script works with mysql or mysqli driver only\n";
+ exit;
+}
+
+$table=$argv[1];
+$module=$argv[2];
+
+// Show parameters
+print 'Tablename: '.$table."\n";
+print 'Modulename: '.$module."\n";
+print "Current dir: ".getcwd()."\n";
+print "Database name: ".$db->database_name."\n";
+
+
+// Define array with list of properties
+$property=array();
+$foundprimary=0;
+$resql=$db->DDLDescTable($table);
+if ($resql)
+{
+ $i=0;
+ while($obj=$db->fetch_object($resql))
+ {
+ //var_dump($obj);
+ $i++;
+ $property[$i]['field']=$obj->Field;
+ if ($obj->Key == 'PRI')
+ {
+ $property[$i]['primary']=1;
+ $foundprimary=1;
+ }
+ else
+ {
+ $property[$i]['primary']=1;
+ }
+ $property[$i]['type'] =$obj->Type;
+ $property[$i]['null'] =$obj->Null;
+ $property[$i]['extra']=$obj->Extra;
+ if ($property[$i]['type'] == 'date'
+ || $property[$i]['type'] == 'datetime'
+ || $property[$i]['type'] == 'timestamp')
+ {
+ $property[$i]['istime']=true;
+ }
+ else
+ {
+ $property[$i]['istime']=false;
+ }
+ if (preg_match('/varchar/i',$property[$i]['type'])
+ || preg_match('/text/i',$property[$i]['type']))
+ {
+ $property[$i]['ischar']=true;
+ }
+ else
+ {
+ $property[$i]['ischar']=false;
+ }
+ if (preg_match('/int/i',$property[$i]['type']))
+ {
+ $property[$i]['isint']=true;
+ }
+ else
+ {
+ $property[$i]['isint']=false;
+ }
+ }
+}
+else
+{
+ print "Error: Failed to get description for table '".$table."'.\n";
+ return false;
+}
+//var_dump($property);
+
+// Define substitute fetch/select parameters
+$varpropselect="\n";
+$cleanparam='';
+$i=0;
+foreach($property as $key => $prop)
+{
+ $i++;
+ if ($prop['field'] != 'rowid')
+ {
+ $varpropselect.="\t\t\$sql .= \" ";
+ $varpropselect.="t.".$prop['field'];
+ if ($i < count($property)) $varpropselect.=",";
+ $varpropselect.="\";";
+ $varpropselect.="\n";
+ }
+}
+
+
+
+//--------------------------------
+// Build skeleton_class.class.php
+//--------------------------------
+
+// Define working variables
+$table=strtolower($table);
+$tablenoprefix=preg_replace('/'.preg_quote(MAIN_DB_PREFIX,'/').'/i','',$table);
+$classname=preg_replace('/_/','',ucfirst($tablenoprefix));
+$classmin=preg_replace('/_/','',strtolower($classname));
+
+
+// Read skeleton_class.class.php file
+$skeletonfile=$path.'skeleton_class.class.php';
+$sourcecontent=file_get_contents($skeletonfile);
+if (! $sourcecontent)
+{
+ print "\n";
+ print "Error: Failed to read skeleton sample '".$skeletonfile."'\n";
+ print "Try to run script from skeletons directory.\n";
+ exit;
+}
+
+// Define output variables
+$outfile=$classmin.'.class.php';
+$targetcontent=$sourcecontent;
+
+// Substitute module name
+$targetcontent=preg_replace('/dev\/skeletons/', $module, $targetcontent);
+$targetcontent=preg_replace('/mymodule othermodule1 othermodule2/', $module, $targetcontent);
+$targetcontent=preg_replace('/mymodule/', $module, $targetcontent);
+
+// Substitute class name
+$targetcontent=preg_replace('/skeleton_class\.class\.php/', $classmin.'.class.php', $targetcontent);
+$targetcontent=preg_replace('/\$element = \'skeleton\'/', '\$element = \''.$classmin.'\'', $targetcontent);
+$targetcontent=preg_replace('/\$table_element = \'skeleton\'/', '\$table_element = \''.$tablenoprefix.'\'', $targetcontent);
+$targetcontent=preg_replace('/Skeleton_Class/', $classname, $targetcontent);
+$targetcontent=preg_replace('/skeletons/', $classmin, $targetcontent);
+$targetcontent=preg_replace('/skeleton/', $classmin, $targetcontent);
+
+// Substitute comments
+$targetcontent=preg_replace('/This file is an example to create a new class file/', 'Put here description of this class', $targetcontent);
+$targetcontent=preg_replace('/\s*\/\/\.\.\./', '', $targetcontent);
+$targetcontent=preg_replace('/Put here some comments/','Initialy built by build_class_from_table on '.strftime('%Y-%m-%d %H:%M',mktime()), $targetcontent);
+
+// Substitute table name
+$targetcontent=preg_replace('/MAIN_DB_PREFIX."mytable/', 'MAIN_DB_PREFIX."'.$tablenoprefix, $targetcontent);
+
+// Substitute declaration parameters
+$varprop="\n";
+$cleanparam='';
+foreach($property as $key => $prop)
+{
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id')
+ {
+ $varprop.="\tpublic \$".$prop['field'];
+ if ($prop['istime']) $varprop.=" = ''";
+ $varprop.=";";
+ if ($prop['comment']) $varprop.="\t// ".$prop['extra'];
+ $varprop.="\n";
+ }
+}
+$targetcontent=preg_replace('/'.preg_quote('public $prop1;','/').'/', $varprop, $targetcontent);
+$targetcontent=preg_replace('/'.preg_quote('public $prop2;','/').'/', '', $targetcontent);
+
+$targetcontent=preg_replace('/\*((\s|\n|\r|\t)*)\@var mixed Sample property 1((\s|\n|\r|\t)*)/', '', $targetcontent);
+$targetcontent=preg_replace('/\*((\s|\n|\r|\t)*)\@var mixed Sample property 2((\s|\n|\r|\t)*)/', '', $targetcontent);
+
+// Substitute clean parameters
+$varprop="\n";
+$cleanparam='';
+foreach($property as $key => $prop)
+{
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $varprop.="\t\tif (isset(\$this->".$prop['field'].")) {\n\t\t\t \$this->".$prop['field']." = trim(\$this->".$prop['field'].");\n\t\t}";
+ $varprop.="\n";
+ }
+}
+$targetcontent=preg_replace('/if \(isset\(\$this->prop1\)\) {((\n|\r|\t)*)\$this->prop1 = trim\(\$this->prop1\);((\n|\r|\t)*)}/', $varprop, $targetcontent);
+$targetcontent=preg_replace('/if \(isset\(\$this->prop2\)\) {((\n|\r|\t)*)\$this->prop2 = trim\(\$this->prop2\);((\n|\r|\t)*)}/', '', $targetcontent);
+
+
+$no_output_field=0;
+foreach($property as $key => $prop)
+{
+ if ($prop['field'] == 'tms') $no_output_field++; // This is a field of type timestamp edited automatically
+ if ($prop['extra'] == 'auto_increment') $no_output_field++;
+}
+// Substitute insert into parameters
+$varprop="\n";
+$cleanparam='';
+$i=0;
+foreach($property as $key => $prop)
+{
+ $i++;
+ $addfield=1;
+ if ($prop['field'] == 'tms') $addfield=0; // This is a field of type timestamp edited automatically
+ if ($prop['extra'] == 'auto_increment') $addfield=0;
+
+ if ($addfield)
+ {
+ $varprop.="\t\t\$sql.= '".$prop['field'];
+ if ($i < (count($property)-$no_output_field)) $varprop.=",";
+ $varprop.="';";
+ $varprop.="\n";
+ }
+}
+$targetcontent=preg_replace('/\$sql \.= \' field1,\';/', $varprop, $targetcontent);
+$targetcontent=preg_replace('/\$sql \.= \' field2\';/', '', $targetcontent);
+
+// Substitute insert values parameters
+$varprop="\n";
+$cleanparam='';
+$i=0;
+
+//Count nb field to output to manage commat at end SQL instruction
+
+
+foreach($property as $key => $prop)
+{
+
+ $addfield=1;
+ if ($prop['field'] == 'tms') $addfield=0; // This is a field of type timestamp edited automatically
+ if ($prop['extra'] == 'auto_increment') $addfield=0;
+
+ if ($addfield)
+ {
+ $i++;
+
+ $varprop.="\t\t\$sql .= ' ";
+ if ($prop['field']=='datec')
+ {
+ $varprop.='\'."\'".$this->db->idate(dol_now())."\'"';
+ }
+ elseif ($prop['istime'])
+ {
+ $varprop.='\'.(! isset($this->'.$prop['field'].') || dol_strlen($this->'.$prop['field'].')==0?\'NULL\':"\'".$this->db->idate(';
+ $varprop.="\$this->".$prop['field'];
+ $varprop.=").\"'\")";
+ }
+ elseif ($prop['ischar'])
+ {
+ $varprop.="'.(! isset(\$this->".$prop['field'].")?'NULL':\"'\".";
+ $varprop.="\$this->db->escape(\$this->".$prop['field'].")";
+ $varprop.=".\"'\")";
+ }
+ elseif ($prop['field']=='fk_user_mod' || $prop['field']=='fk_user_m' || $prop['field']=='fk_user_author')
+ {
+ $varprop.="'.\$user->id";
+ }
+ elseif ($prop['isint'])
+ {
+ $varprop.='\'.(! isset($this->'.$prop['field'].')?\'NULL\':';
+ $varprop.="\$this->".$prop['field'];
+ $varprop.=')';
+ }
+ else
+ {
+ $varprop.='\'.(! isset($this->'.$prop['field'].')?\'NULL\':"\'".';
+ $varprop.="\$this->".$prop['field'];
+ $varprop.='."\'")';
+ }
+
+ if ($i < (count($property)-$no_output_field)) $varprop.=".','";
+ $varprop.=';';
+ $varprop.="\n";
+ }
+}
+
+$patern1='/\$sql \.= \' (.*)\' \. \$this->prop1 \. \'(.*),\';/';
+$patern2='/\$sql \.= \' (.*)\' \. \$this->prop2 \. \'(.*)\';/';
+$targetcontent=preg_replace($patern1, $varprop, $targetcontent);
+$targetcontent=preg_replace($patern2, '', $targetcontent);
+
+// Substitute update values parameters
+
+//Count nb field to output to manage commat at end SQL instruction
+$no_output_field=0;
+foreach($property as $key => $prop)
+{
+ if ($prop['extra'] == 'auto_increment') $no_output_field++;
+}
+
+$varprop="\n";
+$cleanparam='';
+$i=0;
+foreach($property as $key => $prop)
+{
+
+ $addfield=1;
+ if ($prop['extra'] == 'auto_increment') $addfield=0;
+
+ if ($addfield)
+ {
+ $i++;
+
+ $varprop.="\t\t\$sql .= ' ";
+ $varprop.=$prop['field'].' = ';
+ if ($prop['field']=='tms') {
+ $varprop.='\'.(dol_strlen($this->'.$prop['field'].') != 0 ? "\'".$this->db->idate(';
+ $varprop.='$this->'.$prop['field'];
+ $varprop.=')."\'" : "\'".$this->db->idate(dol_now())."\'")';
+ }
+ elseif ($prop['istime'])
+ {
+ $varprop.='\'.(! isset($this->'.$prop['field'].') || dol_strlen($this->'.$prop['field'].') != 0 ? "\'".$this->db->idate(';
+ $varprop.='$this->'.$prop['field'];
+ $varprop.=')."\'" : \'null\')';
+ }
+
+ elseif ($prop['field']=='fk_user_mod' || $prop['field']=='fk_user_m') {
+ $varprop.="'.\$user->id";
+ }
+ else
+ {
+ $varprop.="'.";
+ if ($prop['ischar']) $varprop.='(isset($this->'.$prop['field'].')?"\'".$this->db->escape($this->'.$prop['field'].')."\'":"null")';
+ elseif ($prop['isint']) $varprop.='(isset($this->'.$prop['field'].')?$this->'.$prop['field'].':"null")';
+ else $varprop.='(isset($this->'.$prop['field'].')?$this->'.$prop['field'].':"null")';
+ }
+
+ if ($i < (count($property)-$no_output_field)) $varprop.=".','";
+ $varprop.=';';
+ $varprop.="\n";
+ }
+}
+$targetcontent=preg_replace('/\$sql \.= " field1=".\(isset\(\$this->field1\)\?"\'".\$this->db->escape\(\$this->field1\)."\'":"null"\).",";/', $varprop, $targetcontent);
+$targetcontent=preg_replace('/\$sql \.= " field2=".\(isset\(\$this->field2\)\?"\'".\$this->db->escape\(\$this->field2\)."\'":"null"\)."";/', '', $targetcontent);
+
+// Substitute fetch/select parameters
+$targetcontent=preg_replace('/\$sql \.= \' t\.field1,\';/', $varpropselect, $targetcontent);
+$targetcontent=preg_replace('/\$sql \.= \' t\.field2\';/', '', $targetcontent);
+
+// Substitute select set parameters
+$varprop="\n";
+$varpropline="\n";
+$cleanparam='';
+$i=0;
+foreach($property as $key => $prop)
+{
+ $i++;
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id')
+ {
+ $varprop.="\t\t\t\t\$this->".$prop['field']." = ";
+ if ($prop['istime']) $varprop.='$this->db->jdate(';
+ $varprop.='$obj->'.$prop['field'];
+ if ($prop['istime']) $varprop.=')';
+ $varprop.=";";
+ $varprop.="\n";
+
+ $varpropline.="\t\t\t\t\$line->".$prop['field']." = ";
+ if ($prop['istime']) $varpropline.='$this->db->jdate(';
+ $varpropline.='$obj->'.$prop['field'];
+ if ($prop['istime']) $varpropline.=')';
+ $varpropline.=";";
+ $varpropline.="\n";
+ }
+}
+$targetcontent=preg_replace('/\$this->prop1 = \$obj->field1;/', $varprop, $targetcontent);
+$targetcontent=preg_replace('/\$this->prop2 = \$obj->field2;/', '', $targetcontent);
+
+//Substirute fetchAll
+$targetcontent=preg_replace('/\$line->prop1 = \$obj->field1;/', $varpropline, $targetcontent);
+$targetcontent=preg_replace('/\$line->prop2 = \$obj->field2;/', '', $targetcontent);
+
+
+// Substitute initasspecimen parameters
+$varprop="\n";
+$cleanparam='';
+foreach($property as $key => $prop)
+{
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id')
+ {
+ $varprop.="\t\t\$this->".$prop['field']." = '';";
+ $varprop.="\n";
+ }
+}
+$targetcontent=preg_replace('/\$this->prop1 = \'prop1\';/', $varprop, $targetcontent);
+$targetcontent=preg_replace('/\$this->prop2 = \'prop2\';/', '', $targetcontent);
+
+// Build file
+$fp=fopen($outfile,"w");
+if ($fp)
+{
+ fputs($fp, $targetcontent);
+ fclose($fp);
+ print "\n";
+ print "File '".$outfile."' has been built in current directory.\n";
+}
+else $error++;
+
+
+
+//--------------------------------------------------------------------
+// Build skeleton_script.php, skeleton_list.php and skeleton_card.php
+//--------------------------------------------------------------------
+
+$skeletonfiles=array(
+ $path.'skeleton_script.php' => $classmin.'_script.php',
+ $path.'skeleton_list.php' => $classmin.'_list.php',
+ $path.'skeleton_card.php' => $classmin.'_card.php'
+ );
+
+foreach ($skeletonfiles as $skeletonfile => $outfile)
+{
+ $sourcecontent=file_get_contents($skeletonfile);
+ if (! $sourcecontent)
+ {
+ print "\n";
+ print "Error: Failed to read skeleton sample '".$skeletonfile."'\n";
+ print "Try to run script from skeletons directory.\n";
+ exit;
+ }
+
+ // Define output variables
+ $targetcontent=$sourcecontent;
+
+ // Substitute module name
+ $targetcontent=preg_replace('/dev\/skeletons/', $module, $targetcontent);
+ $targetcontent=preg_replace('/mymodule othermodule1 othermodule2/', $module, $targetcontent);
+ $targetcontent=preg_replace('/mymodule/', $module, $targetcontent);
+
+ // Substitute class name
+ $targetcontent=preg_replace('/skeleton_class\.class\.php/', $classmin.'.class.php', $targetcontent);
+ $targetcontent=preg_replace('/skeleton_script\.php/', $classmin.'_script.php', $targetcontent);
+ $targetcontent=preg_replace('/\$element = \'skeleton\'/', '\$element=\''.$classmin.'\'', $targetcontent);
+ $targetcontent=preg_replace('/\$table_element = \'skeleton\'/', '\$table_element=\''.$classmin.'\'', $targetcontent);
+ $targetcontent=preg_replace('/Skeleton_Class/', $classname, $targetcontent);
+ $targetcontent=preg_replace('/skeletons/', $classmin, $targetcontent);
+ $targetcontent=preg_replace('/skeleton/', $classmin, $targetcontent);
+
+ // Substitute comments
+ $targetcontent=preg_replace('/This file is an example to create a new class file/', 'Put here description of this class', $targetcontent);
+ $targetcontent=preg_replace('/\s*\/\/\.\.\./', '', $targetcontent);
+ $targetcontent=preg_replace('/Put here some comments/','Initialy built by build_class_from_table on '.strftime('%Y-%m-%d %H:%M',mktime()), $targetcontent);
+
+ // Substitute table name
+ $targetcontent=preg_replace('/MAIN_DB_PREFIX."mytable/', 'MAIN_DB_PREFIX."'.$tablenoprefix, $targetcontent);
+
+ // Substitute GETPOST search_fieldx
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ if ($prop['isint']) $varprop.='$search_'.$prop['field']."=GETPOST('search_".$prop['field']."','int');\n";
+ else $varprop.='$search_'.$prop['field']."=GETPOST('search_".$prop['field']."','alpha');\n";
+ }
+ }
+ $targetcontent=preg_replace('/'.preg_quote('$search_field1=GETPOST("search_field1");','/').'/', $varprop, $targetcontent);
+ $targetcontent=preg_replace('/'.preg_quote('$search_field2=GETPOST("search_field2");','/').'/', '', $targetcontent);
+
+ // Substitute GETPOST fieldx
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ if ($prop['isint']) $varprop.="\t\$object->".$prop['field']."=GETPOST('".$prop['field']."','int');\n";
+ else $varprop.="\t\$object->".$prop['field']."=GETPOST('".$prop['field']."','alpha');\n";
+ }
+ }
+ $targetcontent=preg_replace('/'.preg_quote('$object->prop1=GETPOST("field1");','/').'/', $varprop, $targetcontent);
+ $targetcontent=preg_replace('/'.preg_quote('$object->prop2=GETPOST("field2");','/').'/', '', $targetcontent);
+
+ // Substitute reset search_field = '';
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $varprop.='$search_'.$prop['field']."='';\n";
+ }
+ }
+ $targetcontent=preg_replace('/'.preg_quote('$search_field1=\'\';','/').'/', $varprop, $targetcontent);
+ $targetcontent=preg_replace('/'.preg_quote('$search_field2=\'\';','/').'/', '', $targetcontent);
+
+ // Substitute fetch/select parameters
+ $targetcontent=preg_replace('/\$sql\s*\.= " t\.field1,";/', $varpropselect, $targetcontent);
+ $targetcontent=preg_replace('/\$sql\s*\.= " t\.field2";/', '', $targetcontent);
+
+ // Substitute where for search
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $varprop.='if ($search_'.$prop['field'].') $sql.= natural_search("'.$prop['field'].'",$search_'.$prop['field'].');'."\n";
+ }
+ }
+ $targetcontent=preg_replace('/'.preg_quote('if ($search_field1) $sql.= natural_search("field1",$search_field1);','/').'/', $varprop, $targetcontent);
+ $targetcontent=preg_replace('/'.preg_quote('if ($search_field2) $sql.= natural_search("field2",$search_field2);','/').'/', '', $targetcontent);
+
+ // substitute $params.=
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $varprop.="if (\$search_".$prop['field']." != '') \$params.= '&search_".$prop['field']."='.urlencode(\$search_".$prop['field'].");\n";
+ }
+ }
+ $targetcontent=preg_replace('/'.preg_quote("if (\$search_field1 != '') \$params.= '&search_field1='.urlencode(\$search_field1);",'/').'/', $varprop, $targetcontent);
+ $targetcontent=preg_replace('/'.preg_quote("if (\$search_field2 != '') \$params.= '&search_field2='.urlencode(\$search_field2);",'/').'/', '', $targetcontent);
+
+ // Substitute arrayfields
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $varprop.="'t.".$prop['field']."'=>array('label'=>\$langs->trans(\"Field".$prop['field']."\"), 'checked'=>1),\n";
+ }
+ }
+ $targetcontent=preg_replace('/'.preg_quote("'t.field1'=>array('label'=>\$langs->trans(\"Field1\"), 'checked'=>1),",'/').'/', $varprop, $targetcontent);
+ $targetcontent=preg_replace('/'.preg_quote("'t.field2'=>array('label'=>\$langs->trans(\"Field2\"), 'checked'=>1),",'/').'/', '', $targetcontent);
+
+ // Substitute print_liste_field_titre
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $varprop.="if (! empty(\$arrayfields['t.".$prop['field']."']['checked'])) print_liste_field_titre(\$arrayfields['t.".$prop['field']."']['label'],\$_SERVER['PHP_SELF'],'t.".$prop['field']."','',\$params,'',\$sortfield,\$sortorder);\n";
+ }
+ }
+ $targetcontent=preg_replace('/LIST_OF_TD_TITLE_FIELDS/', $varprop, $targetcontent);
+
+ // Substitute fields title search
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $varprop.="if (! empty(\$arrayfields['t.".$prop['field']."']['checked'])) print ' | ';\n";
+ }
+ }
+ $targetcontent=preg_replace('/LIST_OF_TD_TITLE_SEARCH/', $varprop, $targetcontent);
+
+ // Substitute where for .fieldx. |
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $varprop.="if (! empty(\$arrayfields['t.".$prop['field']."']['checked'])) print ''.\$obj->".$prop['field'].".' | ';\n";
+ }
+ }
+ $targetcontent=preg_replace('/'.preg_quote("if (! empty(\$arrayfields['t.field1']['checked'])) print ''.\$obj->field1.' | ';",'/').'/', $varprop, $targetcontent);
+ $targetcontent=preg_replace('/'.preg_quote("if (! empty(\$arrayfields['t.field2']['checked'])) print ''.\$obj->field2.' | ';",'/').'/', '', $targetcontent);
+
+ // LIST_OF_TD_LABEL_FIELDS_CREATE - List of td for card view
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $varprop.="print '| '.\$langs->trans(\"Field".$prop['field']."\").' | |
';\n";
+ }
+ }
+ $targetcontent=preg_replace('/LIST_OF_TD_LABEL_FIELDS_CREATE/', $varprop, $targetcontent);
+
+ // LIST_OF_TD_LABEL_FIELDS_EDIT - List of td for card view
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $baseString = 'print "| ".\$langs->trans("Field%s")." | %s."\"> |
";';
+ $varprop.= sprintf("\t ".$baseString." \n", $prop['field'], $prop['field'], $prop['field']);
+ }
+ }
+ $targetcontent=preg_replace('/LIST_OF_TD_LABEL_FIELDS_EDIT/', $varprop, $targetcontent);
+
+ // LIST_OF_TD_LABEL_FIELDS_VIEW - List of td for card view
+ $varprop="\n";
+ $cleanparam='';
+ foreach($property as $key => $prop)
+ {
+ if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime'])
+ {
+ $varprop.=sprintf("\t print '| '.\$langs->trans(\"Field%s\").' | '.\$object->%s.' |
';\n",$prop['field'],$prop['field']);
+ }
+ }
+ $targetcontent=preg_replace('/LIST_OF_TD_LABEL_FIELDS_VIEW/', $varprop, $targetcontent);
+
+
+ // LIST_OF_TD_FIELDS_LIST
+
+
+
+ // Build file
+ $fp=fopen($outfile,"w");
+ if ($fp)
+ {
+ fputs($fp, $targetcontent);
+ fclose($fp);
+ print "File '".$outfile."' has been built in current directory.\n";
+ }
+ else $error++;
+}
+
+
+// -------------------- END OF BUILD_CLASS_FROM_TABLE SCRIPT --------------------
+
+print "You can now move generated files to store them into directory /yourmodule/class (for .class.php file) or /yourmodule.\n";
+return $error;
diff --git a/htdocs/modulebuilder/skeletons/build_webservice_from_class.php b/htdocs/modulebuilder/skeletons/build_webservice_from_class.php
new file mode 100755
index 00000000000..c91347a424d
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/build_webservice_from_class.php
@@ -0,0 +1,179 @@
+#!/usr/bin/env php
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file dev/skeletons/build_webservice_from_class.php
+ * \ingroup core
+ * \brief Create a complete webservice file from CRUD functions of a PHP class
+ */
+
+$sapi_type = php_sapi_name();
+$script_file = basename(__FILE__);
+$path=dirname(__FILE__).'/';
+
+// Test if batch mode
+if (substr($sapi_type, 0, 3) == 'cgi') {
+ echo "Error: You are using PHP for CGI. To execute ".$script_file." from command line, you must use PHP for CLI mode.\n";
+ exit;
+}
+
+// Include Dolibarr environment
+require_once($path."../../htdocs/master.inc.php");
+// After this $db is a defined handler to database.
+
+// Main
+$version='1.8';
+@set_time_limit(0);
+$error=0;
+
+$langs->load("main");
+
+
+print "***** $script_file ($version) *****\n";
+
+
+// -------------------- START OF BUILD_CLASS_FROM_TABLE SCRIPT --------------------
+
+// Check parameters
+if (! isset($argv[1]) && ! isset($argv[2]))
+{
+ print "Usage: $script_file phpClassFile phpClassName\n";
+ exit;
+}
+
+// Show parameters
+print 'Classfile='.$argv[1]."\n";
+print 'Classname='.$argv[2]."\n";
+
+$classfile=$argv[1];
+$classname=$argv[2];
+$classmin=strtolower($classname);
+$property=array();
+$targetcontent='';
+
+// Load the class and read properties
+require_once($classfile);
+
+$property=array();
+$class = new $classname($db);
+$values=get_class_vars($classname);
+
+unset($values['db']);
+unset($values['error']);
+unset($values['errors']);
+unset($values['element']);
+unset($values['table_element']);
+unset($values['table_element_line']);
+unset($values['fk_element']);
+unset($values['ismultientitymanaged']);
+
+$properties=array_keys($values);
+
+// Read skeleton_class.class.php file
+$skeletonfile='skeleton_webservice_server.php';
+$sourcecontent=file_get_contents($skeletonfile);
+if (! $sourcecontent)
+{
+ print "\n";
+ print "Error: Failed to read skeleton sample '".$skeletonfile."'\n";
+ print "Try to run script from skeletons directory.\n";
+ exit;
+}
+
+// Define output variables
+$outfile='out.server_'.$classmin.'.php';
+$targetcontent=$sourcecontent;
+
+
+
+// Substitute class name
+$targetcontent=preg_replace('/Skeleton/', $classname, $targetcontent);
+$targetcontent=preg_replace('/skeleton/', $classmin, $targetcontent);
+
+// Substitute declaration parameters
+$varprop="\n";
+$cleanparam='';
+$i=0;
+
+while($i array('name'=>'".$properties[$i]."','type'=>'xsd:string')";
+ $i++;
+
+ if ($i == count($properties))
+ $varprop.="\n";
+ else
+ $varprop.=",\n";
+}
+
+$targetcontent=preg_replace('/\'prop1\'=>\'xxx\',/', $varprop, $targetcontent);
+$targetcontent=preg_replace('/\'prop2\'=>\'xxx\',/', '', $targetcontent);
+// Substitute get method parameters
+$varprop="\n";
+$cleanparam='';
+$i=0;
+
+while($i $".$classmin."->".$properties[$i];
+
+ $i++;
+ if ($i == count($properties))
+ $varprop.="\n";
+ else
+ $varprop.=",\n";
+}
+
+$targetcontent=preg_replace('/\'prop1\'=>\$'.$classmin.'->prop1,/', $varprop, $targetcontent);
+$targetcontent=preg_replace('/\'prop2\'=>\$'.$classmin.'->prop2,/', '', $targetcontent);
+
+// Substitute get method parameters
+$varprop="\n\t\t";
+$cleanparam='';
+$i=0;
+
+while($i'.$properties[$i].'=$'.$classmin.'->'.$properties[$i].';';
+
+ $i++;
+ if ($i == count($properties))
+ $varprop.="\n";
+ else
+ $varprop.="\n\t\t";
+}
+$targetcontent=preg_replace('/\$newobject->prop1=\$'.$classmin.'->prop1;/', $varprop, $targetcontent);
+$targetcontent=preg_replace('/\$newobject->prop2=\$'.$classmin.'->prop2;/', '', $targetcontent);
+
+
+
+// Build file
+$fp=fopen($outfile,"w");
+if ($fp)
+{
+ fputs($fp, $targetcontent);
+ fclose($fp);
+ print "File '".$outfile."' has been built in current directory.\n";
+}
+else $error++;
+
+// -------------------- END OF BUILD_CLASS_FROM_TABLE SCRIPT --------------------
+
+print "You must rename files by removing the 'out.' prefix in their name.\n";
+return $error;
diff --git a/htdocs/modulebuilder/skeletons/modMyModule.class.php b/htdocs/modulebuilder/skeletons/modMyModule.class.php
new file mode 100644
index 00000000000..4f994c7c654
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/modMyModule.class.php
@@ -0,0 +1,291 @@
+
+ * Copyright (C) 2004-2015 Laurent Destailleur
+ * Copyright (C) 2005-2016 Regis Houssin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \defgroup mymodule Module MyModule
+ * \brief Example of a module descriptor.
+ * Such a file must be copied into htdocs/mymodule/core/modules directory.
+ * \file htdocs/mymodule/core/modules/modMyModule.class.php
+ * \ingroup mymodule
+ * \brief Description and activation file for module MyModule
+ */
+include_once DOL_DOCUMENT_ROOT .'/core/modules/DolibarrModules.class.php';
+
+
+/**
+ * Description and activation class for module MyModule
+ */
+class modMyModule extends DolibarrModules
+{
+ /**
+ * Constructor. Define names, constants, directories, boxes, permissions
+ *
+ * @param DoliDB $db Database handler
+ */
+ public function __construct($db)
+ {
+ global $langs,$conf;
+
+ $this->db = $db;
+
+ // Id for module (must be unique).
+ // Use here a free id (See in Home -> System information -> Dolibarr for list of used modules id).
+ $this->numero = 500000; // TODO Go on page http://wiki.dolibarr.org/index.php/List_of_modules_id to reserve id number for your module
+ // Key text used to identify module (for permissions, menus, etc...)
+ $this->rights_class = 'mymodule';
+
+ // Family can be 'crm','financial','hr','projects','products','ecm','technic','interface','other'
+ // It is used to group modules by family in module setup page
+ $this->family = "other";
+ // Module position in the family
+ $this->module_position = 500;
+ // Gives the possibility to the module, to provide his own family info and position of this family (Overwrite $this->family and $this->module_position. Avoid this)
+ //$this->familyinfo = array('myownfamily' => array('position' => '001', 'label' => $langs->trans("MyOwnFamily")));
+
+ // Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
+ $this->name = preg_replace('/^mod/i','',get_class($this));
+ // Module description, used if translation string 'ModuleXXXDesc' not found (where XXX is value of numeric property 'numero' of module)
+ $this->description = "Description of module MyModule";
+ $this->descriptionlong = "A very long description. Can be a full HTML content";
+ $this->editor_name = 'Editor name';
+ $this->editor_url = 'https://www.dolibarr.org';
+
+ // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated' or a version string like 'x.y.z'
+ $this->version = '1.0';
+ // Key used in llx_const table to save module status enabled/disabled (where MYMODULE is value of property name of module in uppercase)
+ $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name);
+ // Name of image file used for this module.
+ // If file is in theme/yourtheme/img directory under name object_pictovalue.png, use this->picto='pictovalue'
+ // If file is in module/img directory under name object_pictovalue.png, use this->picto='pictovalue@module'
+ $this->picto='generic';
+
+ // Defined all module parts (triggers, login, substitutions, menus, css, etc...)
+ // for default path (eg: /mymodule/core/xxxxx) (0=disable, 1=enable)
+ // for specific path of parts (eg: /mymodule/core/modules/barcode)
+ // for specific css file (eg: /mymodule/css/mymodule.css.php)
+ //$this->module_parts = array(
+ // 'triggers' => 0, // Set this to 1 if module has its own trigger directory (core/triggers)
+ // 'login' => 0, // Set this to 1 if module has its own login method directory (core/login)
+ // 'substitutions' => 0, // Set this to 1 if module has its own substitution function file (core/substitutions)
+ // 'menus' => 0, // Set this to 1 if module has its own menus handler directory (core/menus)
+ // 'theme' => 0, // Set this to 1 if module has its own theme directory (theme)
+ // 'tpl' => 0, // Set this to 1 if module overwrite template dir (core/tpl)
+ // 'barcode' => 0, // Set this to 1 if module has its own barcode directory (core/modules/barcode)
+ // 'models' => 0, // Set this to 1 if module has its own models directory (core/modules/xxx)
+ // 'css' => array('/mymodule/css/mymodule.css.php'), // Set this to relative path of css file if module has its own css file
+ // 'js' => array('/mymodule/js/mymodule.js'), // Set this to relative path of js file if module must load a js on all pages
+ // 'hooks' => array('hookcontext1','hookcontext2',...) // Set here all hooks context managed by module. You can also set hook context 'all'
+ // 'dir' => array('output' => 'othermodulename'), // To force the default directories names
+ // 'workflow' => array('WORKFLOW_MODULE1_YOURACTIONTYPE_MODULE2'=>array('enabled'=>'! empty($conf->module1->enabled) && ! empty($conf->module2->enabled)', 'picto'=>'yourpicto@mymodule')) // Set here all workflow context managed by module
+ // );
+ $this->module_parts = array();
+
+ // Data directories to create when module is enabled.
+ // Example: this->dirs = array("/mymodule/temp");
+ $this->dirs = array();
+
+ // Config pages. Put here list of php page, stored into mymodule/admin directory, to use to setup module.
+ $this->config_page_url = array("mysetuppage.php@mymodule");
+
+ // Dependencies
+ $this->hidden = false; // A condition to hide module
+ $this->depends = array(); // List of module class names as string that must be enabled if this module is enabled
+ $this->requiredby = array(); // List of module ids to disable if this one is disabled
+ $this->conflictwith = array(); // List of module class names as string this module is in conflict with
+ $this->phpmin = array(5,0); // Minimum version of PHP required by module
+ $this->need_dolibarr_version = array(3,0); // Minimum version of Dolibarr required by module
+ $this->langfiles = array("mylangfile@mymodule");
+
+ // Constants
+ // List of particular constants to add when module is enabled (key, 'chaine', value, desc, visible, 'current' or 'allentities', deleteonunactive)
+ // Example: $this->const=array(0=>array('MYMODULE_MYNEWCONST1','chaine','myvalue','This is a constant to add',1),
+ // 1=>array('MYMODULE_MYNEWCONST2','chaine','myvalue','This is another constant to add',0, 'current', 1)
+ // );
+ $this->const = array();
+
+ // Array to add new pages in new tabs
+ // Example: $this->tabs = array('objecttype:+tabname1:Title1:mylangfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__', // To add a new tab identified by code tabname1
+ // 'objecttype:+tabname2:SUBSTITUTION_Title2:mylangfile@mymodule:$user->rights->othermodule->read:/mymodule/mynewtab2.php?id=__ID__', // To add another new tab identified by code tabname2. Label will be result of calling all substitution functions on 'Title2' key.
+ // 'objecttype:-tabname:NU:conditiontoremove'); // To remove an existing tab identified by code tabname
+ // where objecttype can be
+ // 'categories_x' to add a tab in category view (replace 'x' by type of category (0=product, 1=supplier, 2=customer, 3=member)
+ // 'contact' to add a tab in contact view
+ // 'contract' to add a tab in contract view
+ // 'group' to add a tab in group view
+ // 'intervention' to add a tab in intervention view
+ // 'invoice' to add a tab in customer invoice view
+ // 'invoice_supplier' to add a tab in supplier invoice view
+ // 'member' to add a tab in fundation member view
+ // 'opensurveypoll' to add a tab in opensurvey poll view
+ // 'order' to add a tab in customer order view
+ // 'order_supplier' to add a tab in supplier order view
+ // 'payment' to add a tab in payment view
+ // 'payment_supplier' to add a tab in supplier payment view
+ // 'product' to add a tab in product view
+ // 'propal' to add a tab in propal view
+ // 'project' to add a tab in project view
+ // 'stock' to add a tab in stock view
+ // 'thirdparty' to add a tab in third party view
+ // 'user' to add a tab in user view
+ $this->tabs = array();
+
+ if (! isset($conf->mymodule) || ! isset($conf->mymodule->enabled))
+ {
+ $conf->mymodule=new stdClass();
+ $conf->mymodule->enabled=0;
+ }
+
+ // Dictionaries
+ $this->dictionaries=array();
+ /* Example:
+ $this->dictionaries=array(
+ 'langs'=>'mylangfile@mymodule',
+ 'tabname'=>array(MAIN_DB_PREFIX."table1",MAIN_DB_PREFIX."table2",MAIN_DB_PREFIX."table3"), // List of tables we want to see into dictonnary editor
+ 'tablib'=>array("Table1","Table2","Table3"), // Label of tables
+ 'tabsql'=>array('SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table1 as f','SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table2 as f','SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table3 as f'), // Request to select fields
+ 'tabsqlsort'=>array("label ASC","label ASC","label ASC"), // Sort order
+ 'tabfield'=>array("code,label","code,label","code,label"), // List of fields (result of select to show dictionary)
+ 'tabfieldvalue'=>array("code,label","code,label","code,label"), // List of fields (list of fields to edit a record)
+ 'tabfieldinsert'=>array("code,label","code,label","code,label"), // List of fields (list of fields for insert)
+ 'tabrowid'=>array("rowid","rowid","rowid"), // Name of columns with primary key (try to always name it 'rowid')
+ 'tabcond'=>array($conf->mymodule->enabled,$conf->mymodule->enabled,$conf->mymodule->enabled) // Condition to show each dictionary
+ );
+ */
+
+ // Boxes
+ // Add here list of php file(s) stored in core/boxes that contains class to show a box.
+ $this->boxes = array(); // List of boxes
+ // Example:
+ //$this->boxes=array(
+ // 0=>array('file'=>'myboxa.php@mymodule','note'=>'','enabledbydefaulton'=>'Home'),
+ // 1=>array('file'=>'myboxb.php@mymodule','note'=>''),
+ // 2=>array('file'=>'myboxc.php@mymodule','note'=>'')
+ //);
+
+ // Cronjobs
+ $this->cronjobs = array(); // List of cron jobs entries to add
+ // Example: $this->cronjobs=array(0=>array('label'=>'My label', 'jobtype'=>'method', 'class'=>'/dir/class/file.class.php', 'objectname'=>'MyClass', 'method'=>'myMethod', 'parameters'=>'', 'comment'=>'Comment', 'frequency'=>2, 'unitfrequency'=>3600, 'test'=>true),
+ // 1=>array('label'=>'My label', 'jobtype'=>'command', 'command'=>'', 'parameters'=>'', 'comment'=>'Comment', 'frequency'=>1, 'unitfrequency'=>3600*24, 'test'=>true)
+ // );
+
+ // Permissions
+ $this->rights = array(); // Permission array used by this module
+ $r=0;
+
+ // Add here list of permission defined by an id, a label, a boolean and two constant strings.
+ // Example:
+ // $this->rights[$r][0] = $this->numero + $r; // Permission id (must not be already used)
+ // $this->rights[$r][1] = 'Permision label'; // Permission label
+ // $this->rights[$r][3] = 1; // Permission by default for new user (0/1)
+ // $this->rights[$r][4] = 'level1'; // In php code, permission will be checked by test if ($user->rights->permkey->level1->level2)
+ // $this->rights[$r][5] = 'level2'; // In php code, permission will be checked by test if ($user->rights->permkey->level1->level2)
+ // $r++;
+
+ // Main menu entries
+ $this->menu = array(); // List of menus to add
+ $r=0;
+
+ // Add here entries to declare new menus
+ //
+ // Example to declare a new Top Menu entry and its Left menu entry:
+ // $this->menu[$r]=array( 'fk_menu'=>'', // '' if this is a top menu. For left menu, use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode
+ // 'type'=>'top', // This is a Top menu entry
+ // 'titre'=>'MyModule top menu',
+ // 'mainmenu'=>'mymodule',
+ // 'leftmenu'=>'mymodule',
+ // 'url'=>'/mymodule/pagetop.php',
+ // 'langs'=>'mylangfile@mymodule', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
+ // 'position'=>100,
+ // 'enabled'=>'$conf->mymodule->enabled', // Define condition to show or hide menu entry. Use '$conf->mymodule->enabled' if entry must be visible if module is enabled.
+ // 'perms'=>'1', // Use 'perms'=>'$user->rights->mymodule->level1->level2' if you want your menu with a permission rules
+ // 'target'=>'',
+ // 'user'=>2); // 0=Menu for internal users, 1=external users, 2=both
+ // $r++;
+ //
+ // Example to declare a Left Menu entry into an existing Top menu entry:
+ // $this->menu[$r]=array( 'fk_menu'=>'fk_mainmenu=xxx', // '' if this is a top menu. For left menu, use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode
+ // 'type'=>'left', // This is a Left menu entry
+ // 'titre'=>'MyModule left menu',
+ // 'mainmenu'=>'xxx',
+ // 'leftmenu'=>'mymodule',
+ // 'url'=>'/mymodule/pagelevel2.php',
+ // 'langs'=>'mylangfile@mymodule', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
+ // 'position'=>100,
+ // 'enabled'=>'$conf->mymodule->enabled', // Define condition to show or hide menu entry. Use '$conf->mymodule->enabled' if entry must be visible if module is enabled. Use '$leftmenu==\'system\'' to show if leftmenu system is selected.
+ // 'perms'=>'1', // Use 'perms'=>'$user->rights->mymodule->level1->level2' if you want your menu with a permission rules
+ // 'target'=>'',
+ // 'user'=>2); // 0=Menu for internal users, 1=external users, 2=both
+ // $r++;
+
+
+ // Exports
+ $r=1;
+
+ // Example:
+ // $this->export_code[$r]=$this->rights_class.'_'.$r;
+ // $this->export_label[$r]='MyModule'; // Translation key (used only if key ExportDataset_xxx_z not found)
+ // $this->export_enabled[$r]='1'; // Condition to show export in list (ie: '$user->id==3'). Set to 1 to always show when module is enabled.
+ // $this->export_icon[$r]='generic:MyModule'; // Put here code of icon then string for translation key of module name
+ // $this->export_permission[$r]=array(array("mymodule","level1","level2"));
+ // $this->export_fields_array[$r]=array('s.rowid'=>"IdCompany",'s.nom'=>'CompanyName','s.address'=>'Address','s.zip'=>'Zip','s.town'=>'Town','s.fk_pays'=>'Country','s.phone'=>'Phone','s.siren'=>'ProfId1','s.siret'=>'ProfId2','s.ape'=>'ProfId3','s.idprof4'=>'ProfId4','s.code_compta'=>'CustomerAccountancyCode','s.code_compta_fournisseur'=>'SupplierAccountancyCode','f.rowid'=>"InvoiceId",'f.facnumber'=>"InvoiceRef",'f.datec'=>"InvoiceDateCreation",'f.datef'=>"DateInvoice",'f.total'=>"TotalHT",'f.total_ttc'=>"TotalTTC",'f.tva'=>"TotalVAT",'f.paye'=>"InvoicePaid",'f.fk_statut'=>'InvoiceStatus','f.note'=>"InvoiceNote",'fd.rowid'=>'LineId','fd.description'=>"LineDescription",'fd.price'=>"LineUnitPrice",'fd.tva_tx'=>"LineVATRate",'fd.qty'=>"LineQty",'fd.total_ht'=>"LineTotalHT",'fd.total_tva'=>"LineTotalTVA",'fd.total_ttc'=>"LineTotalTTC",'fd.date_start'=>"DateStart",'fd.date_end'=>"DateEnd",'fd.fk_product'=>'ProductId','p.ref'=>'ProductRef');
+ // $this->export_TypeFields_array[$r]=array('t.date'=>'Date', 't.qte'=>'Numeric', 't.poids'=>'Numeric', 't.fad'=>'Numeric', 't.paq'=>'Numeric', 't.stockage'=>'Numeric', 't.fadparliv'=>'Numeric', 't.livau100'=>'Numeric', 't.forfait'=>'Numeric', 's.nom'=>'Text','s.address'=>'Text','s.zip'=>'Text','s.town'=>'Text','c.code'=>'Text','s.phone'=>'Text','s.siren'=>'Text','s.siret'=>'Text','s.ape'=>'Text','s.idprof4'=>'Text','s.code_compta'=>'Text','s.code_compta_fournisseur'=>'Text','s.tva_intra'=>'Text','f.facnumber'=>"Text",'f.datec'=>"Date",'f.datef'=>"Date",'f.date_lim_reglement'=>"Date",'f.total'=>"Numeric",'f.total_ttc'=>"Numeric",'f.tva'=>"Numeric",'f.paye'=>"Boolean",'f.fk_statut'=>'Status','f.note_private'=>"Text",'f.note_public'=>"Text",'fd.description'=>"Text",'fd.subprice'=>"Numeric",'fd.tva_tx'=>"Numeric",'fd.qty'=>"Numeric",'fd.total_ht'=>"Numeric",'fd.total_tva'=>"Numeric",'fd.total_ttc'=>"Numeric",'fd.date_start'=>"Date",'fd.date_end'=>"Date",'fd.special_code'=>'Numeric','fd.product_type'=>"Numeric",'fd.fk_product'=>'List:product:label','p.ref'=>'Text','p.label'=>'Text','p.accountancy_code_sell'=>'Text');
+ // $this->export_entities_array[$r]=array('s.rowid'=>"company",'s.nom'=>'company','s.address'=>'company','s.zip'=>'company','s.town'=>'company','s.fk_pays'=>'company','s.phone'=>'company','s.siren'=>'company','s.siret'=>'company','s.ape'=>'company','s.idprof4'=>'company','s.code_compta'=>'company','s.code_compta_fournisseur'=>'company','f.rowid'=>"invoice",'f.facnumber'=>"invoice",'f.datec'=>"invoice",'f.datef'=>"invoice",'f.total'=>"invoice",'f.total_ttc'=>"invoice",'f.tva'=>"invoice",'f.paye'=>"invoice",'f.fk_statut'=>'invoice','f.note'=>"invoice",'fd.rowid'=>'invoice_line','fd.description'=>"invoice_line",'fd.price'=>"invoice_line",'fd.total_ht'=>"invoice_line",'fd.total_tva'=>"invoice_line",'fd.total_ttc'=>"invoice_line",'fd.tva_tx'=>"invoice_line",'fd.qty'=>"invoice_line",'fd.date_start'=>"invoice_line",'fd.date_end'=>"invoice_line",'fd.fk_product'=>'product','p.ref'=>'product');
+ // $this->export_dependencies_array[$r]=array('invoice_line'=>'fd.rowid','product'=>'fd.rowid'); // To add unique key if we ask a field of a child to avoid the DISTINCT to discard them
+ // $this->export_sql_start[$r]='SELECT DISTINCT ';
+ // $this->export_sql_end[$r] =' FROM ('.MAIN_DB_PREFIX.'facture as f, '.MAIN_DB_PREFIX.'facturedet as fd, '.MAIN_DB_PREFIX.'societe as s)';
+ // $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'product as p on (fd.fk_product = p.rowid)';
+ // $this->export_sql_end[$r] .=' WHERE f.fk_soc = s.rowid AND f.rowid = fd.fk_facture';
+ // $this->export_sql_order[$r] .=' ORDER BY s.nom';
+ // $r++;
+ }
+
+ /**
+ * Function called when module is enabled.
+ * The init function add constants, boxes, permissions and menus (defined in constructor) into Dolibarr database.
+ * It also creates data directories
+ *
+ * @param string $options Options when enabling module ('', 'noboxes')
+ * @return int 1 if OK, 0 if KO
+ */
+ public function init($options='')
+ {
+ $sql = array();
+
+ //$this->_load_tables('/mymodule/sql/');
+
+ return $this->_init($sql, $options);
+ }
+
+ /**
+ * Function called when module is disabled.
+ * Remove from database constants, boxes and permissions from Dolibarr database.
+ * Data directories are not deleted
+ *
+ * @param string $options Options when enabling module ('', 'noboxes')
+ * @return int 1 if OK, 0 if KO
+ */
+ public function remove($options = '')
+ {
+ $sql = array();
+
+ return $this->_remove($sql, $options);
+ }
+
+}
+
diff --git a/htdocs/modulebuilder/skeletons/skeleton_api_class.class.php b/htdocs/modulebuilder/skeletons/skeleton_api_class.class.php
new file mode 100644
index 00000000000..a40b00af72c
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/skeleton_api_class.class.php
@@ -0,0 +1,289 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+ use Luracast\Restler\RestException;
+
+
+/**
+ * API class for skeleton object
+ *
+ * @smart-auto-routing false
+ * @access protected
+ * @class DolibarrApiAccess {@requires user,external}
+ *
+ *
+ */
+class SkeletonApi extends DolibarrApi
+{
+ /**
+ * @var array $FIELDS Mandatory fields, checked when create and update object
+ */
+ static $FIELDS = array(
+ 'name'
+ );
+
+ /**
+ * @var Skeleton $skeleton {@type Skeleton}
+ */
+ public $skeleton;
+
+ /**
+ * Constructor
+ *
+ * @url GET skeleton/
+ *
+ */
+ function __construct()
+ {
+ global $db, $conf;
+ $this->db = $db;
+ $this->skeleton = new Skeleton($this->db);
+ }
+
+ /**
+ * Get properties of a skeleton object
+ *
+ * Return an array with skeleton informations
+ *
+ * @param int $id ID of skeleton
+ * @return array|mixed data without useless information
+ *
+ * @url GET skeleton/{id}
+ * @throws RestException
+ */
+ function get($id)
+ {
+ if(! DolibarrApiAccess::$user->rights->skeleton->read) {
+ throw new RestException(401);
+ }
+
+ $result = $this->skeleton->fetch($id);
+ if( ! $result ) {
+ throw new RestException(404, 'Skeleton not found');
+ }
+
+ if( ! DolibarrApi::_checkAccessToResource('skeleton',$this->skeleton->id)) {
+ throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ }
+
+ return $this->_cleanObjectDatas($this->skeleton);
+ }
+
+ /**
+ * List skeletons
+ *
+ * Get a list of skeletons
+ *
+ * @param int $mode Use this param to filter list
+ * @param string $sortfield Sort field
+ * @param string $sortorder Sort order
+ * @param int $limit Limit for list
+ * @param int $page Page number
+ * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101') or (t.import_key:=:'20160101')"
+ * @return array Array of skeleton objects
+ *
+ * @url GET /skeletons/
+ */
+ function index($mode, $sortfield = "t.rowid", $sortorder = 'ASC', $limit = 0, $page = 0, $sqlfilters = '') {
+ global $db, $conf;
+
+ $obj_ret = array();
+
+ $socid = DolibarrApiAccess::$user->societe_id ? DolibarrApiAccess::$user->societe_id : '';
+
+ // If the internal user must only see his customers, force searching by him
+ if (! DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) $search_sale = DolibarrApiAccess::$user->id;
+
+ $sql = "SELECT s.rowid";
+ if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
+ $sql.= " FROM ".MAIN_DB_PREFIX."skeleton as s";
+
+ if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
+ $sql.= ", ".MAIN_DB_PREFIX."c_stcomm as st";
+ $sql.= " WHERE s.fk_stcomm = st.id";
+
+ // Example of use $mode
+ //if ($mode == 1) $sql.= " AND s.client IN (1, 3)";
+ //if ($mode == 2) $sql.= " AND s.client IN (2, 3)";
+
+ $sql.= ' AND s.entity IN ('.getEntity('skeleton', 1).')';
+ if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= " AND s.fk_soc = sc.fk_soc";
+ if ($socid) $sql.= " AND s.fk_soc = ".$socid;
+ if ($search_sale > 0) $sql.= " AND s.rowid = sc.fk_soc"; // Join for the needed table to filter by sale
+ // Insert sale filter
+ if ($search_sale > 0)
+ {
+ $sql .= " AND sc.fk_user = ".$search_sale;
+ }
+ if ($sqlfilters)
+ {
+ if (! DolibarrApi::_checkFilters($sqlfilters))
+ {
+ throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters);
+ }
+ $regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
+ $sql.=" AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
+ }
+
+ $sql.= $db->order($sortfield, $sortorder);
+ if ($limit) {
+ if ($page < 0)
+ {
+ $page = 0;
+ }
+ $offset = $limit * $page;
+
+ $sql.= $db->plimit($limit + 1, $offset);
+ }
+
+ $result = $db->query($sql);
+ if ($result)
+ {
+ $num = $db->num_rows($result);
+ while ($i < $num)
+ {
+ $obj = $db->fetch_object($result);
+ $skeleton_static = new Skeleton($db);
+ if($skeleton_static->fetch($obj->rowid)) {
+ $obj_ret[] = parent::_cleanObjectDatas($skeleton_static);
+ }
+ $i++;
+ }
+ }
+ else {
+ throw new RestException(503, 'Error when retrieve skeleton list');
+ }
+ if( ! count($obj_ret)) {
+ throw new RestException(404, 'No skeleton found');
+ }
+ return $obj_ret;
+ }
+
+ /**
+ * Create skeleton object
+ *
+ * @param array $request_data Request datas
+ * @return int ID of skeleton
+ *
+ * @url POST skeleton/
+ */
+ function post($request_data = NULL)
+ {
+ if(! DolibarrApiAccess::$user->rights->skeleton->create) {
+ throw new RestException(401);
+ }
+ // Check mandatory fields
+ $result = $this->_validate($request_data);
+
+ foreach($request_data as $field => $value) {
+ $this->skeleton->$field = $value;
+ }
+ if( ! $this->skeleton->create(DolibarrApiAccess::$user)) {
+ throw new RestException(500);
+ }
+ return $this->skeleton->id;
+ }
+
+ /**
+ * Update skeleton
+ *
+ * @param int $id Id of skeleton to update
+ * @param array $request_data Datas
+ * @return int
+ *
+ * @url PUT skeleton/{id}
+ */
+ function put($id, $request_data = NULL)
+ {
+ if(! DolibarrApiAccess::$user->rights->skeleton->create) {
+ throw new RestException(401);
+ }
+
+ $result = $this->skeleton->fetch($id);
+ if( ! $result ) {
+ throw new RestException(404, 'Skeleton not found');
+ }
+
+ if( ! DolibarrApi::_checkAccessToResource('skeleton',$this->skeleton->id)) {
+ throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ }
+
+ foreach($request_data as $field => $value) {
+ $this->skeleton->$field = $value;
+ }
+
+ if($this->skeleton->update($id, DolibarrApiAccess::$user))
+ return $this->get ($id);
+
+ return false;
+ }
+
+ /**
+ * Delete skeleton
+ *
+ * @param int $id Skeleton ID
+ * @return array
+ *
+ * @url DELETE skeleton/{id}
+ */
+ function delete($id)
+ {
+ if(! DolibarrApiAccess::$user->rights->skeleton->supprimer) {
+ throw new RestException(401);
+ }
+ $result = $this->skeleton->fetch($id);
+ if( ! $result ) {
+ throw new RestException(404, 'Skeleton not found');
+ }
+
+ if( ! DolibarrApi::_checkAccessToResource('skeleton',$this->skeleton->id)) {
+ throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
+ }
+
+ if( !$this->skeleton->delete($id))
+ {
+ throw new RestException(500);
+ }
+
+ return array(
+ 'success' => array(
+ 'code' => 200,
+ 'message' => 'Skeleton deleted'
+ )
+ );
+
+ }
+
+ /**
+ * Validate fields before create or update object
+ *
+ * @param array $data Data to validate
+ * @return array
+ *
+ * @throws RestException
+ */
+ function _validate($data)
+ {
+ $skeleton = array();
+ foreach (SkeletonApi::$FIELDS as $field) {
+ if (!isset($data[$field]))
+ throw new RestException(400, "$field field missing");
+ $skeleton[$field] = $data[$field];
+ }
+ return $skeleton;
+ }
+}
diff --git a/htdocs/modulebuilder/skeletons/skeleton_card.php b/htdocs/modulebuilder/skeletons/skeleton_card.php
new file mode 100644
index 00000000000..f0a9dd23c26
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/skeleton_card.php
@@ -0,0 +1,351 @@
+
+ * Copyright (C) ---Put here your own copyright and developer email---
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file dev/skeletons/skeleton_card.php
+ * \ingroup mymodule othermodule1 othermodule2
+ * \brief This file is an example of a php page
+ * Put here some comments
+ */
+
+//if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1');
+//if (! defined('NOREQUIREDB')) define('NOREQUIREDB','1');
+//if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1');
+//if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1');
+//if (! defined('NOCSRFCHECK')) define('NOCSRFCHECK','1'); // Do not check anti CSRF attack test
+//if (! defined('NOSTYLECHECK')) define('NOSTYLECHECK','1'); // Do not check style html tag into posted data
+//if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL','1'); // Do not check anti POST attack test
+//if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU','1'); // If there is no need to load and show top and left menu
+//if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML','1'); // If we don't need to load the html.form.class.php
+//if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1');
+//if (! defined("NOLOGIN")) define("NOLOGIN",'1'); // If this page is public (can be called outside logged session)
+
+// Change this following line to use the correct relative path (../, ../../, etc)
+$res=0;
+if (! $res && file_exists("../main.inc.php")) $res=@include '../main.inc.php'; // to work if your module directory is into dolibarr root htdocs directory
+if (! $res && file_exists("../../main.inc.php")) $res=@include '../../main.inc.php'; // to work if your module directory is into a subdir of root htdocs directory
+if (! $res && file_exists("../../../dolibarr/htdocs/main.inc.php")) $res=@include '../../../dolibarr/htdocs/main.inc.php'; // Used on dev env only
+if (! $res && file_exists("../../../../dolibarr/htdocs/main.inc.php")) $res=@include '../../../../dolibarr/htdocs/main.inc.php'; // Used on dev env only
+if (! $res) die("Include of main fails");
+// Change this following line to use the correct relative path from htdocs
+include_once(DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php');
+dol_include_once('/mymodule/class/skeleton_class.class.php');
+
+// Load traductions files requiredby by page
+$langs->load("mymodule");
+$langs->load("other");
+
+// Get parameters
+$id = GETPOST('id','int');
+$action = GETPOST('action','alpha');
+$cancel = GETPOST('cancel');
+$backtopage = GETPOST('backtopage');
+$myparam = GETPOST('myparam','alpha');
+
+$search_field1=GETPOST("search_field1");
+$search_field2=GETPOST("search_field2");
+
+if (empty($action) && empty($id) && empty($ref)) $action='view';
+
+// Protection if external user
+if ($user->societe_id > 0)
+{
+ //accessforbidden();
+}
+//$result = restrictedArea($user, 'mymodule', $id);
+
+
+$object = new Skeleton_Class($db);
+$extrafields = new ExtraFields($db);
+
+// fetch optionals attributes and labels
+$extralabels = $extrafields->fetch_name_optionals_label($object->table_element);
+
+// Load object
+include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals
+
+// Initialize technical object to manage hooks of modules. Note that conf->hooks_modules contains array array
+$hookmanager->initHooks(array('skeleton'));
+
+
+
+/*******************************************************************
+* ACTIONS
+*
+* Put here all code to do according to value of "action" parameter
+********************************************************************/
+
+$parameters=array();
+$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+
+if (empty($reshook))
+{
+ if ($cancel)
+ {
+ if ($action != 'addlink')
+ {
+ $urltogo=$backtopage?$backtopage:dol_buildpath('/mymodule/list.php',1);
+ header("Location: ".$urltogo);
+ exit;
+ }
+ if ($id > 0 || ! empty($ref)) $ret = $object->fetch($id,$ref);
+ $action='';
+ }
+
+ // Action to add record
+ if ($action == 'add')
+ {
+ if (GETPOST('cancel'))
+ {
+ $urltogo=$backtopage?$backtopage:dol_buildpath('/mymodule/list.php',1);
+ header("Location: ".$urltogo);
+ exit;
+ }
+
+ $error=0;
+
+ /* object_prop_getpost_prop */
+ $object->prop1=GETPOST("field1");
+ $object->prop2=GETPOST("field2");
+
+ if (empty($object->ref))
+ {
+ $error++;
+ setEventMessages($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Ref")), null, 'errors');
+ }
+
+ if (! $error)
+ {
+ $result=$object->create($user);
+ if ($result > 0)
+ {
+ // Creation OK
+ $urltogo=$backtopage?$backtopage:dol_buildpath('/mymodule/list.php',1);
+ header("Location: ".$urltogo);
+ exit;
+ }
+ {
+ // Creation KO
+ if (! empty($object->errors)) setEventMessages(null, $object->errors, 'errors');
+ else setEventMessages($object->error, null, 'errors');
+ $action='create';
+ }
+ }
+ else
+ {
+ $action='create';
+ }
+ }
+
+ // Action to update record
+ if ($action == 'update')
+ {
+ $error=0;
+
+ $object->prop1=GETPOST("field1");
+ $object->prop2=GETPOST("field2");
+
+ if (empty($object->ref))
+ {
+ $error++;
+ setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("Ref")), null, 'errors');
+ }
+
+ if (! $error)
+ {
+ $result=$object->update($user);
+ if ($result > 0)
+ {
+ $action='view';
+ }
+ else
+ {
+ // Creation KO
+ if (! empty($object->errors)) setEventMessages(null, $object->errors, 'errors');
+ else setEventMessages($object->error, null, 'errors');
+ $action='edit';
+ }
+ }
+ else
+ {
+ $action='edit';
+ }
+ }
+
+ // Action to delete
+ if ($action == 'confirm_delete')
+ {
+ $result=$object->delete($user);
+ if ($result > 0)
+ {
+ // Delete OK
+ setEventMessages("RecordDeleted", null, 'mesgs');
+ header("Location: ".dol_buildpath('/mymodule/list.php',1));
+ exit;
+ }
+ else
+ {
+ if (! empty($object->errors)) setEventMessages(null, $object->errors, 'errors');
+ else setEventMessages($object->error, null, 'errors');
+ }
+ }
+}
+
+
+
+
+/***************************************************
+* VIEW
+*
+* Put here all code to build page
+****************************************************/
+
+llxHeader('','MyPageName','');
+
+$form=new Form($db);
+
+
+// Put here content of your page
+
+// Example : Adding jquery code
+print '';
+
+
+// Part to create
+if ($action == 'create')
+{
+ print load_fiche_titre($langs->trans("NewMyModule"));
+
+ print '';
+}
+
+
+
+// Part to edit record
+if (($id || $ref) && $action == 'edit')
+{
+ print load_fiche_titre($langs->trans("MyModule"));
+
+ print '';
+}
+
+
+
+// Part to show record
+if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create')))
+{
+ $res = $object->fetch_optionals($object->id, $extralabels);
+
+ $head = commande_prepare_head($object);
+ dol_fiche_head($head, 'order', $langs->trans("CustomerOrder"), 0, 'order');
+
+ print load_fiche_titre($langs->trans("MyModule"));
+
+ dol_fiche_head();
+
+ if ($action == 'delete') {
+ $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('DeleteMyOjbect'), $langs->trans('ConfirmDeleteMyObject'), 'confirm_delete', '', 0, 1);
+ print $formconfirm;
+ }
+
+ print ''."\n";
+ // print '| '.$langs->trans("Label").' | '.$object->label.' |
';
+ // LIST_OF_TD_LABEL_FIELDS_VIEW
+ print '
';
+
+ dol_fiche_end();
+
+
+ // Buttons
+ print ''."\n";
+ $parameters=array();
+ $reshook=$hookmanager->executeHooks('addMoreActionsButtons',$parameters,$object,$action); // Note that $action and $object may have been modified by hook
+ if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+
+ if (empty($reshook))
+ {
+ if ($user->rights->mymodule->write)
+ {
+ print '
'."\n";
+ }
+
+ if ($user->rights->mymodule->delete)
+ {
+ print '
'."\n";
+ }
+ }
+ print '
'."\n";
+
+
+ // Example 2 : Adding links to objects
+ // Show links to link elements
+ //$linktoelem = $form->showLinkToObjectBlock($object, null, array('skeleton'));
+ //$somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
+
+}
+
+
+// End of page
+llxFooter();
+$db->close();
diff --git a/htdocs/modulebuilder/skeletons/skeleton_class.class.php b/htdocs/modulebuilder/skeletons/skeleton_class.class.php
new file mode 100644
index 00000000000..01b48c35f75
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/skeleton_class.class.php
@@ -0,0 +1,593 @@
+
+ * Copyright (C) 2014-2016 Juanjo Menent
+ * Copyright (C) 2015 Florian Henry
+ * Copyright (C) 2015 Raphaël Doursenaud
+ * Copyright (C) ---Put here your own copyright and developer email---
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file dev/skeletons/skeleton_class.class.php
+ * \ingroup mymodule othermodule1 othermodule2
+ * \brief This file is an example for a CRUD class file (Create/Read/Update/Delete)
+ * Put some comments here
+ */
+
+// Put here all includes required by your class file
+require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
+//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
+//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
+
+/**
+ * Class Skeleton_Class
+ *
+ * Put here description of your class
+ *
+ * @see CommonObject
+ */
+class Skeleton_Class extends CommonObject
+{
+ /**
+ * @var string Id to identify managed objects
+ */
+ public $element = 'skeleton';
+ /**
+ * @var string Name of table without prefix where object is stored
+ */
+ public $table_element = 'skeleton';
+
+ /**
+ * @var Skeleton_ClassLine[] Lines
+ */
+ public $lines = array();
+
+ /**
+ * @var mixed Sample property 1
+ */
+ public $prop1;
+ /**
+ * @var mixed Sample property 2
+ */
+ public $prop2;
+ //...
+
+ /**
+ * Constructor
+ *
+ * @param DoliDb $db Database handler
+ */
+ public function __construct(DoliDB $db)
+ {
+ $this->db = $db;
+ }
+
+ /**
+ * Create object into database
+ *
+ * @param User $user User that creates
+ * @param bool $notrigger false=launch triggers after, true=disable triggers
+ *
+ * @return int <0 if KO, Id of created object if OK
+ */
+ public function create(User $user, $notrigger = false)
+ {
+ dol_syslog(__METHOD__, LOG_DEBUG);
+
+ $error = 0;
+
+ // Clean parameters
+ if (isset($this->prop1)) {
+ $this->prop1 = trim($this->prop1);
+ }
+ if (isset($this->prop2)) {
+ $this->prop2 = trim($this->prop2);
+ }
+ //...
+
+ // Check parameters
+ // Put here code to add control on parameters values
+
+ // Insert request
+ $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . '(';
+ $sql .= ' field1,';
+ $sql .= ' field2';
+ //...
+ $sql .= ') VALUES (';
+ $sql .= ' \'' . $this->prop1 . '\',';
+ $sql .= ' \'' . $this->prop2 . '\'';
+ //...
+ $sql .= ')';
+
+ $this->db->begin();
+
+ $resql = $this->db->query($sql);
+ if (!$resql) {
+ $error ++;
+ $this->errors[] = 'Error ' . $this->db->lasterror();
+ dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+ }
+
+ if (!$error) {
+ $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
+
+ if (!$notrigger) {
+ // Uncomment this and change MYOBJECT to your own tag if you
+ // want this action to call a trigger.
+
+ //// Call triggers
+ //$result=$this->call_trigger('MYOBJECT_CREATE',$user);
+ //if ($result < 0) $error++;
+ //// End call triggers
+ }
+ }
+
+ // Commit or rollback
+ if ($error) {
+ $this->db->rollback();
+
+ return - 1 * $error;
+ } else {
+ $this->db->commit();
+
+ return $this->id;
+ }
+ }
+
+ /**
+ * Load object in memory from the database
+ *
+ * @param int $id Id object
+ * @param string $ref Ref
+ *
+ * @return int <0 if KO, 0 if not found, >0 if OK
+ */
+ public function fetch($id, $ref = null)
+ {
+ dol_syslog(__METHOD__, LOG_DEBUG);
+
+ $sql = 'SELECT';
+ $sql .= ' t.rowid,';
+ $sql .= ' t.field1,';
+ $sql .= ' t.field2';
+ //...
+ $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
+ $sql.= ' WHERE 1 = 1';
+ if (! empty($conf->multicompany->enabled)) {
+ $sql .= " AND entity IN (" . getEntity("skeleton", 1) . ")";
+ }
+ if (null !== $ref) {
+ $sql .= ' AND t.ref = ' . '\'' . $ref . '\'';
+ } else {
+ $sql .= ' AND t.rowid = ' . $id;
+ }
+
+ $resql = $this->db->query($sql);
+ if ($resql) {
+ $numrows = $this->db->num_rows($resql);
+ if ($numrows) {
+ $obj = $this->db->fetch_object($resql);
+
+ $this->id = $obj->rowid;
+ $this->prop1 = $obj->field1;
+ $this->prop2 = $obj->field2;
+ //...
+ }
+
+ // Retrieve all extrafields for invoice
+ // fetch optionals attributes and labels
+ /*
+ require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
+ $extrafields=new ExtraFields($this->db);
+ $extralabels=$extrafields->fetch_name_optionals_label($this->table_element,true);
+ $this->fetch_optionals($this->id,$extralabels);
+ */
+
+ // $this->fetch_lines();
+
+ $this->db->free($resql);
+
+ if ($numrows) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ $this->errors[] = 'Error ' . $this->db->lasterror();
+ dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+
+ return - 1;
+ }
+ }
+
+ /**
+ * Load object in memory from the database
+ *
+ * @param string $sortorder Sort Order
+ * @param string $sortfield Sort field
+ * @param int $limit offset limit
+ * @param int $offset offset limit
+ * @param array $filter filter array
+ * @param string $filtermode filter mode (AND or OR)
+ *
+ * @return int <0 if KO, >0 if OK
+ */
+ public function fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter = array(), $filtermode='AND')
+ {
+ dol_syslog(__METHOD__, LOG_DEBUG);
+
+ $sql = 'SELECT';
+ $sql .= ' t.rowid,';
+ $sql .= ' t.field1,';
+ $sql .= ' t.field2';
+ //...
+ $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element. ' as t';
+
+ // Manage filter
+ $sqlwhere = array();
+ if (count($filter) > 0) {
+ foreach ($filter as $key => $value) {
+ $sqlwhere [] = $key . ' LIKE \'%' . $this->db->escape($value) . '%\'';
+ }
+ }
+ $sql.= ' WHERE 1 = 1';
+ if (! empty($conf->multicompany->enabled)) {
+ $sql .= " AND entity IN (" . getEntity("skeleton", 1) . ")";
+ }
+ if (count($sqlwhere) > 0) {
+ $sql .= ' AND ' . implode(' '.$filtermode.' ', $sqlwhere);
+ }
+ if (!empty($sortfield)) {
+ $sql .= $this->db->order($sortfield,$sortorder);
+ }
+ if (!empty($limit)) {
+ $sql .= ' ' . $this->db->plimit($limit, $offset);
+ }
+
+ $resql = $this->db->query($sql);
+ if ($resql) {
+ $num = $this->db->num_rows($resql);
+
+ while ($obj = $this->db->fetch_object($resql)) {
+ $line = new self($this->db);
+
+ $line->id = $obj->rowid;
+ $line->prop1 = $obj->field1;
+ $line->prop2 = $obj->field2;
+ //...
+ }
+ $this->db->free($resql);
+
+ return $num;
+ } else {
+ $this->errors[] = 'Error ' . $this->db->lasterror();
+ dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+
+ return - 1;
+ }
+ }
+
+ /**
+ * Update object into database
+ *
+ * @param User $user User that modifies
+ * @param bool $notrigger false=launch triggers after, true=disable triggers
+ *
+ * @return int <0 if KO, >0 if OK
+ */
+ public function update(User $user, $notrigger = false)
+ {
+ $error = 0;
+
+ dol_syslog(__METHOD__, LOG_DEBUG);
+
+ // Clean parameters
+ if (isset($this->prop1)) {
+ $this->prop1 = trim($this->prop1);
+ }
+ if (isset($this->prop2)) {
+ $this->prop2 = trim($this->prop2);
+ }
+ //...
+
+ // Check parameters
+ // Put here code to add a control on parameters values
+
+ // Update request
+ $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET';
+ $sql .= " field1=".(isset($this->field1)?"'".$this->db->escape($this->field1)."'":"null").",";
+ $sql .= " field2=".(isset($this->field2)?"'".$this->db->escape($this->field2)."'":"null")."";
+ //...
+ $sql .= ' WHERE rowid=' . $this->id;
+
+ $this->db->begin();
+
+ $resql = $this->db->query($sql);
+ if (!$resql) {
+ $error ++;
+ $this->errors[] = 'Error ' . $this->db->lasterror();
+ dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+ }
+
+ if (!$error && !$notrigger) {
+ // Uncomment this and change MYOBJECT to your own tag if you
+ // want this action calls a trigger.
+
+ //// Call triggers
+ //$result=$this->call_trigger('MYOBJECT_MODIFY',$user);
+ //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
+ //// End call triggers
+ }
+
+ // Commit or rollback
+ if ($error) {
+ $this->db->rollback();
+
+ return - 1 * $error;
+ } else {
+ $this->db->commit();
+
+ return 1;
+ }
+ }
+
+ /**
+ * Delete object in database
+ *
+ * @param User $user User that deletes
+ * @param bool $notrigger false=launch triggers after, true=disable triggers
+ *
+ * @return int <0 if KO, >0 if OK
+ */
+ public function delete(User $user, $notrigger = false)
+ {
+ dol_syslog(__METHOD__, LOG_DEBUG);
+
+ $error = 0;
+
+ $this->db->begin();
+
+ if (!$error) {
+ if (!$notrigger) {
+ // Uncomment this and change MYOBJECT to your own tag if you
+ // want this action calls a trigger.
+
+ //// Call triggers
+ //$result=$this->call_trigger('MYOBJECT_DELETE',$user);
+ //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
+ //// End call triggers
+ }
+ }
+
+ // If you need to delete child tables to, you can insert them here
+
+ if (!$error) {
+ $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . $this->table_element;
+ $sql .= ' WHERE rowid=' . $this->id;
+
+ $resql = $this->db->query($sql);
+ if (!$resql) {
+ $error ++;
+ $this->errors[] = 'Error ' . $this->db->lasterror();
+ dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+ }
+ }
+
+ // Commit or rollback
+ if ($error) {
+ $this->db->rollback();
+
+ return - 1 * $error;
+ } else {
+ $this->db->commit();
+
+ return 1;
+ }
+ }
+
+ /**
+ * Load an object from its id and create a new one in database
+ *
+ * @param int $fromid Id of object to clone
+ *
+ * @return int New id of clone
+ */
+ public function createFromClone($fromid)
+ {
+ dol_syslog(__METHOD__, LOG_DEBUG);
+
+ global $user;
+ $error = 0;
+ $object = new Skeleton_Class($this->db);
+
+ $this->db->begin();
+
+ // Load source object
+ $object->fetch($fromid);
+ // Reset object
+ $object->id = 0;
+
+ // Clear fields
+ // ...
+
+ // Create clone
+ $result = $object->create($user);
+
+ // Other options
+ if ($result < 0) {
+ $error ++;
+ $this->errors = $object->errors;
+ dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+ }
+
+ // End
+ if (!$error) {
+ $this->db->commit();
+
+ return $object->id;
+ } else {
+ $this->db->rollback();
+
+ return - 1;
+ }
+ }
+
+ /**
+ * Return a link to the object card (with optionaly the picto)
+ *
+ * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
+ * @param string $option On what the link point to
+ * @param int $notooltip 1=Disable tooltip
+ * @param int $maxlen Max length of visible user name
+ * @param string $morecss Add more css on link
+ * @return string String with URL
+ */
+ function getNomUrl($withpicto=0, $option='', $notooltip=0, $maxlen=24, $morecss='')
+ {
+ global $db, $conf, $langs;
+ global $dolibarr_main_authentication, $dolibarr_main_demo;
+ global $menumanager;
+
+ if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips
+
+ $result = '';
+ $companylink = '';
+
+ $label = '' . $langs->trans("MyModule") . '';
+ $label.= '
';
+ $label.= '' . $langs->trans('Ref') . ': ' . $this->ref;
+
+ $url = DOL_URL_ROOT.'/mymodule/'.$this->table_name.'_card.php?id='.$this->id;
+
+ $linkclose='';
+ if (empty($notooltip))
+ {
+ if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
+ {
+ $label=$langs->trans("ShowProject");
+ $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
+ }
+ $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"';
+ $linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"';
+ }
+ else $linkclose = ($morecss?' class="'.$morecss.'"':'');
+
+ $linkstart = '';
+ $linkend='';
+
+ if ($withpicto)
+ {
+ $result.=($linkstart.img_object(($notooltip?'':$label), 'label', ($notooltip?'':'class="classfortooltip"')).$linkend);
+ if ($withpicto != 2) $result.=' ';
+ }
+ $result.= $linkstart . $this->ref . $linkend;
+ return $result;
+ }
+
+ /**
+ * Retourne le libelle du status d'un user (actif, inactif)
+ *
+ * @param int $mode 0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
+ * @return string Label of status
+ */
+ function getLibStatut($mode=0)
+ {
+ return $this->LibStatut($this->status,$mode);
+ }
+
+ /**
+ * Return the status
+ *
+ * @param int $status Id status
+ * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 5=Long label + Picto
+ * @return string Label of status
+ */
+ static function LibStatut($status,$mode=0)
+ {
+ global $langs;
+
+ if ($mode == 0)
+ {
+ $prefix='';
+ if ($status == 1) return $langs->trans('Enabled');
+ if ($status == 0) return $langs->trans('Disabled');
+ }
+ if ($mode == 1)
+ {
+ if ($status == 1) return $langs->trans('Enabled');
+ if ($status == 0) return $langs->trans('Disabled');
+ }
+ if ($mode == 2)
+ {
+ if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4').' '.$langs->trans('Enabled');
+ if ($status == 0) return img_picto($langs->trans('Disabled'),'statut5').' '.$langs->trans('Disabled');
+ }
+ if ($mode == 3)
+ {
+ if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4');
+ if ($status == 0) return img_picto($langs->trans('Disabled'),'statut5');
+ }
+ if ($mode == 4)
+ {
+ if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4').' '.$langs->trans('Enabled');
+ if ($status == 0) return img_picto($langs->trans('Disabled'),'statut5').' '.$langs->trans('Disabled');
+ }
+ if ($mode == 5)
+ {
+ if ($status == 1) return $langs->trans('Enabled').' '.img_picto($langs->trans('Enabled'),'statut4');
+ if ($status == 0) return $langs->trans('Disabled').' '.img_picto($langs->trans('Disabled'),'statut5');
+ }
+ if ($mode == 6)
+ {
+ if ($status == 1) return $langs->trans('Enabled').' '.img_picto($langs->trans('Enabled'),'statut4');
+ if ($status == 0) return $langs->trans('Disabled').' '.img_picto($langs->trans('Disabled'),'statut5');
+ }
+ }
+
+
+ /**
+ * Initialise object with example values
+ * Id must be 0 if object instance is a specimen
+ *
+ * @return void
+ */
+ public function initAsSpecimen()
+ {
+ $this->id = 0;
+ $this->prop1 = 'prop1';
+ $this->prop2 = 'prop2';
+ }
+
+}
+
+/**
+ * Class Skeleton_ClassLine
+ */
+class Skeleton_ClassLine
+{
+ /**
+ * @var int ID
+ */
+ public $id;
+ /**
+ * @var mixed Sample line property 1
+ */
+ public $prop1;
+ /**
+ * @var mixed Sample line property 2
+ */
+ public $prop2;
+}
diff --git a/htdocs/modulebuilder/skeletons/skeleton_list.php b/htdocs/modulebuilder/skeletons/skeleton_list.php
new file mode 100644
index 00000000000..77485c6d638
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/skeleton_list.php
@@ -0,0 +1,569 @@
+
+ * Copyright (C) 2014-2016 Juanjo Menent
+ * Copyright (C) 2016 Jean-François Ferry
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file dev/skeletons/skeleton_list.php
+ * \ingroup mymodule othermodule1 othermodule2
+ * \brief This file is an example of a php page
+ * Put here some comments
+ */
+
+//if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1');
+//if (! defined('NOREQUIREDB')) define('NOREQUIREDB','1');
+//if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1');
+//if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1');
+//if (! defined('NOCSRFCHECK')) define('NOCSRFCHECK','1'); // Do not check anti CSRF attack test
+//if (! defined('NOSTYLECHECK')) define('NOSTYLECHECK','1'); // Do not check style html tag into posted data
+//if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL','1'); // Do not check anti POST attack test
+//if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU','1'); // If there is no need to load and show top and left menu
+//if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML','1'); // If we don't need to load the html.form.class.php
+//if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1');
+//if (! defined("NOLOGIN")) define("NOLOGIN",'1'); // If this page is public (can be called outside logged session)
+
+// Change this following line to use the correct relative path (../, ../../, etc)
+$res=0;
+if (! $res && file_exists("../main.inc.php")) $res=@include '../main.inc.php'; // to work if your module directory is into dolibarr root htdocs directory
+if (! $res && file_exists("../../main.inc.php")) $res=@include '../../main.inc.php'; // to work if your module directory is into a subdir of root htdocs directory
+if (! $res && file_exists("../../../dolibarr/htdocs/main.inc.php")) $res=@include '../../../dolibarr/htdocs/main.inc.php'; // Used on dev env only
+if (! $res && file_exists("../../../../dolibarr/htdocs/main.inc.php")) $res=@include '../../../../dolibarr/htdocs/main.inc.php'; // Used on dev env only
+if (! $res) die("Include of main fails");
+// Change this following line to use the correct relative path from htdocs
+require_once(DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php');
+require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
+dol_include_once('/mymodule/class/skeleton_class.class.php');
+
+// Load traductions files requiredby by page
+$langs->load("mymodule");
+$langs->load("other");
+
+$action=GETPOST('action','alpha');
+$massaction=GETPOST('massaction','alpha');
+$show_files=GETPOST('show_files','int');
+$confirm=GETPOST('confirm','alpha');
+$toselect = GETPOST('toselect', 'array');
+
+$id = GETPOST('id','int');
+$backtopage = GETPOST('backtopage');
+$myparam = GETPOST('myparam','alpha');
+
+$search_all=trim(GETPOST("sall"));
+$search_field1=GETPOST("search_field1");
+$search_field2=GETPOST("search_field2");
+$search_myfield=GETPOST('search_myfield');
+$optioncss = GETPOST('optioncss','alpha');
+
+// Load variable for pagination
+$limit = GETPOST("limit")?GETPOST("limit","int"):$conf->liste_limit;
+$sortfield = GETPOST('sortfield','alpha');
+$sortorder = GETPOST('sortorder','alpha');
+$page = GETPOST('page','int');
+if ($page == -1) { $page = 0; }
+$offset = $limit * $page;
+$pageprev = $page - 1;
+$pagenext = $page + 1;
+if (! $sortfield) $sortfield="t.rowid"; // Set here default search field
+if (! $sortorder) $sortorder="ASC";
+
+// Protection if external user
+$socid=0;
+if ($user->societe_id > 0)
+{
+ $socid = $user->societe_id;
+ //accessforbidden();
+}
+
+// Initialize technical object to manage context to save list fields
+$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'mymodulelist';
+
+// Initialize technical object to manage hooks. Note that conf->hooks_modules contains array
+$hookmanager->initHooks(array('mymodulelist'));
+$extrafields = new ExtraFields($db);
+
+// fetch optionals attributes and labels
+$extralabels = $extrafields->fetch_name_optionals_label('mymodule');
+$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+
+// List of fields to search into when doing a "search in all"
+$fieldstosearchall = array(
+ 't.ref'=>'Ref',
+ 't.note_public'=>'NotePublic',
+);
+if (empty($user->socid)) $fieldstosearchall["t.note_private"]="NotePrivate";
+
+// Definition of fields for list
+$arrayfields=array(
+ 't.field1'=>array('label'=>$langs->trans("Field1"), 'checked'=>1),
+ 't.field2'=>array('label'=>$langs->trans("Field2"), 'checked'=>1),
+ //'t.entity'=>array('label'=>$langs->trans("Entity"), 'checked'=>1, 'enabled'=>(! empty($conf->multicompany->enabled) && empty($conf->multicompany->transverse_mode))),
+ 't.datec'=>array('label'=>$langs->trans("DateCreationShort"), 'checked'=>0, 'position'=>500),
+ 't.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500),
+ //'t.statut'=>array('label'=>$langs->trans("Status"), 'checked'=>1, 'position'=>1000),
+);
+// Extra fields
+if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
+{
+ foreach($extrafields->attribute_label as $key => $val)
+ {
+ $arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>$extrafields->attribute_list[$key], 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>$extrafields->attribute_perms[$key]);
+ }
+}
+
+
+// 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);
+}
+
+
+
+
+/*******************************************************************
+* ACTIONS
+*
+* Put here all code to do according to value of "action" parameter
+********************************************************************/
+
+if (GETPOST('cancel')) { $action='list'; $massaction=''; }
+if (! GETPOST('confirmmassaction') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction=''; }
+
+$parameters=array();
+$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+
+if (empty($reshook))
+{
+ // Selection of new fields
+ 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 tests are required to be compatible with all browsers
+ {
+ $search_field1='';
+ $search_field2='';
+ $search_date_creation='';
+ $search_date_update='';
+ $toselect='';
+ $search_array_options=array();
+ }
+
+ // Mass actions
+ $objectclass='Skeleton';
+ $objectlabel='Skeleton';
+ $permtoread = $user->rights->skeleton->read;
+ $permtodelete = $user->rights->skeleton->delete;
+ $uploaddir = $conf->skeleton->dir_output;
+ include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php';
+}
+
+
+
+/***************************************************
+* VIEW
+*
+* Put here all code to build page
+****************************************************/
+
+$now=dol_now();
+
+$form=new Form($db);
+
+//$help_url="EN:Module_Customers_Orders|FR:Module_Commandes_Clients|ES:Módulo_Pedidos_de_clientes";
+$help_url='';
+$title = $langs->trans('MyModuleListTitle');
+
+// Put here content of your page
+
+// Example : Adding jquery code
+print '';
+
+
+$sql = "SELECT";
+$sql.= " t.rowid,";
+$sql.= " t.field1,";
+$sql.= " t.field2";
+// Add fields from extrafields
+foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key.' as options_'.$key : '');
+// Add fields from hooks
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldListSelect',$parameters); // Note that $action and $object may have been modified by hook
+$sql.=$hookmanager->resPrint;
+$sql.= " FROM ".MAIN_DB_PREFIX."mytable as t";
+if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."mytable_extrafields as ef on (t.rowid = ef.fk_object)";
+$sql.= " WHERE 1 = 1";
+//$sql.= " WHERE u.entity IN (".getEntity('mytable',1).")";
+if ($search_field1) $sql.= natural_search("field1",$search_field1);
+if ($search_field2) $sql.= natural_search("field2",$search_field2);
+if ($sall) $sql.= natural_search(array_keys($fieldstosearchall), $sall);
+// Add where from extra fields
+foreach ($search_array_options as $key => $val)
+{
+ $crit=$val;
+ $tmpkey=preg_replace('/search_options_/','',$key);
+ $typ=$extrafields->attribute_type[$tmpkey];
+ $mode=0;
+ if (in_array($typ, array('int','double'))) $mode=1; // Search on a numeric
+ if ($val && ( ($crit != '' && ! in_array($typ, array('select'))) || ! empty($crit)))
+ {
+ $sql .= natural_search('ef.'.$tmpkey, $crit, $mode);
+ }
+}
+// Add where from hooks
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldListWhere',$parameters); // Note that $action and $object may have been modified by hook
+$sql.=$hookmanager->resPrint;
+$sql.=$db->order($sortfield,$sortorder);
+//$sql.= $db->plimit($conf->liste_limit+1, $offset);
+
+// Count total nb of records
+$nbtotalofrecords = '';
+if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
+{
+ $result = $db->query($sql);
+ $nbtotalofrecords = $db->num_rows($result);
+}
+
+$sql.= $db->plimit($limit+1, $offset);
+
+dol_syslog($script_file, LOG_DEBUG);
+$resql=$db->query($sql);
+if (! $resql)
+{
+ dol_print_error($db);
+ exit;
+}
+
+$num = $db->num_rows($resql);
+
+// Direct jump if only one record found
+if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $search_all)
+{
+ $obj = $db->fetch_object($resql);
+ $id = $obj->rowid;
+ header("Location: ".DOL_URL_ROOT.'/skeleton/card.php?id='.$id);
+ exit;
+}
+
+llxHeader('', $title, $help_url);
+
+$arrayofselected=is_array($toselect)?$toselect:array();
+
+$param='';
+if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage;
+if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit;
+if ($search_field1 != '') $param.= '&search_field1='.urlencode($search_field1);
+if ($search_field2 != '') $param.= '&search_field2='.urlencode($search_field2);
+if ($optioncss != '') $param.='&optioncss='.$optioncss;
+// Add $param from extra fields
+foreach ($search_array_options as $key => $val)
+{
+ $crit=$val;
+ $tmpkey=preg_replace('/search_options_/','',$key);
+ if ($val != '') $param.='&search_options_'.$tmpkey.'='.urlencode($val);
+}
+
+$arrayofmassactions = array(
+ 'presend'=>$langs->trans("SendByMail"),
+ 'builddoc'=>$langs->trans("PDFMerge"),
+);
+if ($user->rights->mymodule->supprimer) $arrayofmassactions['delete']=$langs->trans("Delete");
+if ($massaction == 'presend') $arrayofmassactions=array();
+$massactionbutton=$form->selectMassAction('', $arrayofmassactions);
+
+print ''."\n";
+
+
+if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files)
+{
+ // Show list of available documents
+ $urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder;
+ $urlsource.=str_replace('&','&',$param);
+
+ $filedir=$diroutputmassaction;
+ $genallowed=$user->rights->facture->lire;
+ $delallowed=$user->rights->facture->lire;
+
+ print $formfile->showdocuments('massfilesarea_mymodule','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'');
+}
+else
+{
+ print '
'.$langs->trans("ShowTempMassFilesArea").'';
+}
+
+
+// End of page
+llxFooter();
+$db->close();
diff --git a/htdocs/modulebuilder/skeletons/skeleton_script.php b/htdocs/modulebuilder/skeletons/skeleton_script.php
new file mode 100644
index 00000000000..5eb1565d4a3
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/skeleton_script.php
@@ -0,0 +1,166 @@
+#!/usr/bin/env php
+
+ * Copyright (C) ---Put here your own copyright and developer email---
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file dev/skeletons/skeleton_script.php
+ * \ingroup mymodule
+ * \brief This file is an example for a command line script
+ * Put here some comments
+ */
+
+$sapi_type = php_sapi_name();
+$script_file = basename(__FILE__);
+$path=dirname(__FILE__).'/';
+
+// Test if batch mode
+if (substr($sapi_type, 0, 3) == 'cgi') {
+ echo "Error: You are using PHP for CGI. To execute ".$script_file." from command line, you must use PHP for CLI mode.\n";
+ exit(-1);
+}
+
+// Global variables
+$version='1.0';
+$error=0;
+
+
+// -------------------- START OF YOUR CODE HERE --------------------
+@set_time_limit(0); // No timeout for this script
+define('EVEN_IF_ONLY_LOGIN_ALLOWED',1); // Set this define to 0 if you want to lock your script when dolibarr setup is "locked to admin user only".
+
+// Include and load Dolibarr environment variables
+require_once($path."../../htdocs/master.inc.php");
+// After this $db, $mysoc, $langs, $conf and $hookmanager are defined (Opened $db handler to database will be closed at end of file).
+// $user is created but empty.
+
+//$langs->setDefaultLang('en_US'); // To change default language of $langs
+$langs->load("main"); // To load language file for default language
+
+// Load user and its permissions
+$result=$user->fetch('','admin'); // Load user for login 'admin'. Comment line to run as anonymous user.
+if (! $result > 0) { dol_print_error('',$user->error); exit; }
+$user->getrights();
+
+
+print "***** ".$script_file." (".$version.") pid=".dol_getmypid()." *****\n";
+if (! isset($argv[1])) { // Check parameters
+ print "Usage: ".$script_file." param1 param2 ...\n";
+ exit(-1);
+}
+print '--- start'."\n";
+print 'Argument 1='.$argv[1]."\n";
+print 'Argument 2='.$argv[2]."\n";
+
+
+// Start of transaction
+$db->begin();
+
+
+// Examples for manipulating class skeleton_class
+require_once(DOL_DOCUMENT_ROOT."/../dev/skeletons/skeleton_class.class.php");
+$myobject=new Skeleton_Class($db);
+
+// Example for inserting creating object in database
+/*
+dol_syslog($script_file." CREATE", LOG_DEBUG);
+$myobject->prop1='value_prop1';
+$myobject->prop2='value_prop2';
+$id=$myobject->create($user);
+if ($id < 0) { $error++; dol_print_error($db,$myobject->error); }
+else print "Object created with id=".$id."\n";
+*/
+
+// Example for reading object from database
+/*
+dol_syslog($script_file." FETCH", LOG_DEBUG);
+$result=$myobject->fetch($id);
+if ($result < 0) { $error; dol_print_error($db,$myobject->error); }
+else print "Object with id=".$id." loaded\n";
+*/
+
+// Example for updating object in database ($myobject must have been loaded by a fetch before)
+/*
+dol_syslog($script_file." UPDATE", LOG_DEBUG);
+$myobject->prop1='newvalue_prop1';
+$myobject->prop2='newvalue_prop2';
+$result=$myobject->update($user);
+if ($result < 0) { $error++; dol_print_error($db,$myobject->error); }
+else print "Object with id ".$myobject->id." updated\n";
+*/
+
+// Example for deleting object in database ($myobject must have been loaded by a fetch before)
+/*
+dol_syslog($script_file." DELETE", LOG_DEBUG);
+$result=$myobject->delete($user);
+if ($result < 0) { $error++; dol_print_error($db,$myobject->error); }
+else print "Object with id ".$myobject->id." deleted\n";
+*/
+
+
+// An example of a direct SQL read without using the fetch method
+/*
+$sql = "SELECT field1, field2";
+$sql.= " FROM ".MAIN_DB_PREFIX."skeleton";
+$sql.= " WHERE field3 = 'xxx'";
+$sql.= " ORDER BY field1 ASC";
+
+dol_syslog($script_file, LOG_DEBUG);
+$resql=$db->query($sql);
+if ($resql)
+{
+ $num = $db->num_rows($resql);
+ $i = 0;
+ if ($num)
+ {
+ while ($i < $num)
+ {
+ $obj = $db->fetch_object($resql);
+ if ($obj)
+ {
+ // You can use here results
+ print $obj->field1;
+ print $obj->field2;
+ }
+ $i++;
+ }
+ }
+}
+else
+{
+ $error++;
+ dol_print_error($db);
+}
+*/
+
+
+// -------------------- END OF YOUR CODE --------------------
+
+if (! $error)
+{
+ $db->commit();
+ print '--- end ok'."\n";
+}
+else
+{
+ print '--- end error code='.$error."\n";
+ $db->rollback();
+}
+
+$db->close(); // Close $db database opened handler
+
+exit($error);
diff --git a/htdocs/modulebuilder/skeletons/skeleton_webservice_server.php b/htdocs/modulebuilder/skeletons/skeleton_webservice_server.php
new file mode 100644
index 00000000000..54a050ff9da
--- /dev/null
+++ b/htdocs/modulebuilder/skeletons/skeleton_webservice_server.php
@@ -0,0 +1,272 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file htdocs/webservices/server_skeleton.php
+ * \brief File that is entry point to call Dolibarr WebServices
+ * \version $Id: server_skeleton.php,v 1.7 2010/12/19 11:49:37 eldy Exp $
+ */
+
+// This is to make Dolibarr working with Plesk
+set_include_path($_SERVER['DOCUMENT_ROOT'].'/htdocs');
+
+require_once("../master.inc.php");
+require_once(NUSOAP_PATH.'/nusoap.php'); // Include SOAP
+require_once(DOL_DOCUMENT_ROOT."/core/lib/ws.lib.php");
+require_once(DOL_DOCUMENT_ROOT."/skeleton/class/skeleton.class.php");
+
+
+dol_syslog("Call Skeleton webservices interfaces");
+
+// Enable and test if module web services is enabled
+if (empty($conf->global->MAIN_MODULE_WEBSERVICES))
+{
+ $langs->load("admin");
+ dol_syslog("Call Dolibarr webservices interfaces with module webservices disabled");
+ print $langs->trans("WarningModuleNotActive",'WebServices').'.
';
+ print $langs->trans("ToActivateModule");
+ exit;
+}
+
+// Create the soap Object
+$server = new nusoap_server();
+$server->soap_defencoding='UTF-8';
+$server->decode_utf8=false;
+$ns='http://www.dolibarr.org/ns/';
+$server->configureWSDL('WebServicesDolibarrSkeleton',$ns);
+$server->wsdl->schemaTargetNamespace=$ns;
+
+
+// Define WSDL Authentication object
+$server->wsdl->addComplexType(
+ 'authentication',
+ 'complexType',
+ 'struct',
+ 'all',
+ '',
+ array(
+ 'dolibarrkey' => array('name'=>'dolibarrkey','type'=>'xsd:string'),
+ 'sourceapplication' => array('name'=>'sourceapplication','type'=>'xsd:string'),
+ 'login' => array('name'=>'login','type'=>'xsd:string'),
+ 'password' => array('name'=>'password','type'=>'xsd:string'),
+ 'entity' => array('name'=>'entity','type'=>'xsd:string'),
+ )
+);
+
+// Define WSDL Return object
+$server->wsdl->addComplexType(
+ 'result',
+ 'complexType',
+ 'struct',
+ 'all',
+ '',
+ array(
+ 'result_code' => array('name'=>'result_code','type'=>'xsd:string'),
+ 'result_label' => array('name'=>'result_label','type'=>'xsd:string'),
+ )
+);
+
+// Define other specific objects
+$server->wsdl->addComplexType(
+ 'skeleton',
+ 'complexType',
+ 'struct',
+ 'all',
+ '',
+ array(
+ 'prop1'=>'xxx',
+ 'prop2'=>'xxx',
+ //...
+ )
+);
+
+
+
+// 5 styles: RPC/encoded, RPC/literal, Document/encoded (not WS-I compliant), Document/literal, Document/literal wrapped
+// Style merely dictates how to translate a WSDL binding to a SOAP message. Nothing more. You can use either style with any programming model.
+// http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/
+$styledoc='rpc'; // rpc/document (document is an extend into SOAP 1.0 to support unstructured messages)
+$styleuse='encoded'; // encoded/literal/literal wrapped
+// Better choice is document/literal wrapped but literal wrapped not supported by nusoap.
+
+
+// Register WSDL
+$server->register(
+ 'getSkeleton',
+ // Entry values
+ array('authentication'=>'tns:authentication','id'=>'xsd:string','ref'=>'xsd:string','ref_ext'=>'xsd:string'),
+ // Exit values
+ array('result'=>'tns:result','skeleton'=>'tns:skeleton'),
+ $ns,
+ $ns.'#getSkeleton',
+ $styledoc,
+ $styleuse,
+ 'WS to get skeleton'
+);
+
+// Register WSDL
+$server->register(
+ 'createSkeleton',
+ // Entry values
+ array('authentication'=>'tns:authentication','skeleton'=>'tns:skeleton'),
+ // Exit values
+ array('result'=>'tns:result','id'=>'xsd:string'),
+ $ns,
+ $ns.'#createSkeleton',
+ $styledoc,
+ $styleuse,
+ 'WS to create a skeleton'
+);
+
+
+
+
+/**
+ * Get Skeleton
+ *
+ * @param array $authentication Array of authentication information
+ * @param int $id Id of object
+ * @param string $ref Ref of object
+ * @param string $ref_ext Ref external of object
+ * @return mixed
+ */
+function getSkeleton($authentication,$id,$ref='',$ref_ext='')
+{
+ global $db,$conf,$langs;
+
+ dol_syslog("Function: getSkeleton login=".$authentication['login']." id=".$id." ref=".$ref." ref_ext=".$ref_ext);
+
+ if ($authentication['entity']) $conf->entity=$authentication['entity'];
+
+ // Init and check authentication
+ $objectresp=array();
+ $errorcode='';$errorlabel='';
+ $error=0;
+ $fuser=check_authentication($authentication,$error,$errorcode,$errorlabel);
+ // Check parameters
+ if (! $error && (($id && $ref) || ($id && $ref_ext) || ($ref && $ref_ext)))
+ {
+ $error++;
+ $errorcode='BAD_PARAMETERS'; $errorlabel="Parameter id, ref and ref_ext can't be both provided. You must choose one or other but not both.";
+ }
+
+ if (! $error)
+ {
+ $fuser->getrights();
+
+ if ($fuser->rights->skeleton->read)
+ {
+ $skeleton=new Skeleton($db);
+ $result=$skeleton->fetch($id,$ref,$ref_ext);
+ if ($result > 0)
+ {
+ // Create
+ $objectresp = array(
+ 'result'=>array('result_code'=>'OK', 'result_label'=>''),
+ 'skeleton'=>array(
+ 'prop1'=>$skeleton->prop1,
+ 'prop2'=>$skeleton->prop2,
+ //...
+ )
+ );
+ }
+ else
+ {
+ $error++;
+ $errorcode='NOT_FOUND'; $errorlabel='Object not found for id='.$id.' nor ref='.$ref.' nor ref_ext='.$ref_ext;
+ }
+ }
+ else
+ {
+ $error++;
+ $errorcode='PERMISSION_DENIED'; $errorlabel='User does not have permission for this request';
+ }
+ }
+
+ if ($error)
+ {
+ $objectresp = array('result'=>array('result_code' => $errorcode, 'result_label' => $errorlabel));
+ }
+
+ return $objectresp;
+}
+
+
+/**
+ * Create Skeleton
+ *
+ * @param array $authentication Array of authentication information
+ * @param Skeleton $skeleton $skeleton
+ * @return array Array result
+ */
+function createSkeleton($authentication,$skeleton)
+{
+ global $db,$conf,$langs;
+
+ $now=dol_now();
+
+ dol_syslog("Function: createSkeleton login=".$authentication['login']);
+
+ if ($authentication['entity']) $conf->entity=$authentication['entity'];
+
+ // Init and check authentication
+ $objectresp=array();
+ $errorcode='';$errorlabel='';
+ $error=0;
+ $fuser=check_authentication($authentication,$error,$errorcode,$errorlabel);
+ // Check parameters
+
+
+ if (! $error)
+ {
+ $newobject=new Skeleton($db);
+ $newobject->prop1=$skeleton->prop1;
+ $newobject->prop2=$skeleton->prop2;
+ //...
+
+ $db->begin();
+
+ $result=$newobject->create($fuser);
+ if ($result <= 0)
+ {
+ $error++;
+ }
+
+ if (! $error)
+ {
+ $db->commit();
+ $objectresp=array('result'=>array('result_code'=>'OK', 'result_label'=>''),'id'=>$newobject->id,'ref'=>$newobject->ref);
+ }
+ else
+ {
+ $db->rollback();
+ $error++;
+ $errorcode='KO';
+ $errorlabel=$newobject->error;
+ }
+ }
+
+ if ($error)
+ {
+ $objectresp = array('result'=>array('result_code' => $errorcode, 'result_label' => $errorlabel));
+ }
+
+ return $objectresp;
+}
+
+// Return the results.
+$server->service(file_get_contents("php://input"));
diff --git a/htdocs/theme/eldy/style.css.php b/htdocs/theme/eldy/style.css.php
index 49c17a749f0..f7cb1c0bb5a 100644
--- a/htdocs/theme/eldy/style.css.php
+++ b/htdocs/theme/eldy/style.css.php
@@ -1642,6 +1642,10 @@ div.login_block_other { padding-top: 3px; text-align: right; }
.alogin:hover, .atoplogin:hover {
text-decoration:underline !important;
}
+span.fa.atoplogin, span.fa.atoplogin:hover {
+ font-size: 16px;
+ text-decoration: none !important;
+}
img.login, img.printer, img.entity {
/* padding: 0px 0px 0px 4px; */
/* margin: 0px 0px 0px 8px; */
diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php
index 1908fcfef4a..f83d5df0d50 100644
--- a/htdocs/theme/md/style.css.php
+++ b/htdocs/theme/md/style.css.php
@@ -1675,6 +1675,10 @@ div.login_block_other { padding-top: 3px; }
.alogin:hover, .atoplogin:hover {
text-decoration:underline !important;
}
+span.fa.atoplogin, span.fa.atoplogin:hover {
+ font-size: 16px;
+ text-decoration: none !important;
+}
img.login, img.printer, img.entity {
/* padding: 0px 0px 0px 4px; */
/* margin: 0px 0px 0px 8px; */
diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php
index 4b9c1c9960c..26af6c659ff 100644
--- a/htdocs/user/class/user.class.php
+++ b/htdocs/user/class/user.class.php
@@ -2118,7 +2118,7 @@ class User extends CommonObject
}
/**
- * Retourne le libelle du statut d'un user (actif, inactif)
+ * Return label of status of user (active, inactive)
*
* @param int $mode 0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
* @return string Label of status