From d9a1be48cf8626fe48667bb66dc6de65521d3b28 Mon Sep 17 00:00:00 2001 From: "geoffrey.girard" Date: Wed, 1 Jun 2016 09:44:43 +0200 Subject: [PATCH 001/170] ADD : new link on weather board for proposals late this link sort on datep asc --- htdocs/comm/propal/class/propal.class.php | 1 + htdocs/index.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php index 56c3839dcb6..2d4fb0a1da0 100644 --- a/htdocs/comm/propal/class/propal.class.php +++ b/htdocs/comm/propal/class/propal.class.php @@ -2571,6 +2571,7 @@ class Propal extends CommonObject $response->warning_delay = $delay_warning/60/60/24; $response->label = $label; $response->url = DOL_URL_ROOT.'/comm/propal/list.php?viewstatut='.$statut; + $response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?viewstatut='.$statut.'&sortfield=p.datep&sortorder=asc'; $response->img = img_object($langs->trans("Propals"),"propal"); // This assignment in condition is not a bug. It allows walking the results. diff --git a/htdocs/index.php b/htdocs/index.php index b28038e63c9..03de65e25e5 100644 --- a/htdocs/index.php +++ b/htdocs/index.php @@ -543,7 +543,7 @@ foreach($valid_dashboardlines as $board) //if ($board->nbtodolate > 0) //{ $textlate = $langs->trans("Late").' = '.$langs->trans("DateReference").' > '.$langs->trans("DateToday").' '.(ceil($board->warning_delay) >= 0 ? '+' : '').ceil($board->warning_delay).' '.$langs->trans("days"); - $boxwork.= ''; + $boxwork.= ''; $boxwork.= $board->nbtodolate; $boxwork.= ''; //} From c082505021aa6b007b25f8da55e93b9b690ba134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Garci=CC=81a=20de=20La=20Fuente?= Date: Sat, 23 Jul 2016 16:37:21 +0200 Subject: [PATCH 002/170] NEW Added product attributes feature --- htdocs/attributes/admin/admin.php | 65 ++ htdocs/attributes/admin/index.html | 0 htdocs/attributes/ajax/getCombinations.php | 50 ++ .../attributes/ajax/get_attribute_values.php | 61 ++ htdocs/attributes/ajax/index.html | 0 htdocs/attributes/ajax/orderAttribute.php | 53 ++ htdocs/attributes/card.php | 245 +++++++ .../class/ProductAttribute.class.php | 317 +++++++++ .../class/ProductAttributeValue.class.php | 221 ++++++ .../class/ProductCombination.class.php | 641 ++++++++++++++++++ .../ProductCombination2ValuePair.class.php | 151 +++++ htdocs/attributes/class/index.html | 0 htdocs/attributes/combinations.php | 641 ++++++++++++++++++ htdocs/attributes/create.php | 73 ++ htdocs/attributes/create_val.php | 102 +++ htdocs/attributes/generator.php | 393 +++++++++++ htdocs/attributes/index.html | 0 htdocs/attributes/lib/index.html | 0 .../attributes/lib/product_attributes.lib.php | 31 + htdocs/attributes/list.php | 140 ++++ htdocs/comm/propal/card.php | 28 +- htdocs/commande/card.php | 29 +- htdocs/compta/facture.php | 31 +- htdocs/contrat/card.php | 4 +- htdocs/core/class/html.form.class.php | 125 +++- htdocs/core/lib/functions2.lib.php | 29 +- htdocs/core/lib/product.lib.php | 19 +- htdocs/core/tpl/objectline_create.tpl.php | 47 +- htdocs/fourn/commande/card.php | 34 +- htdocs/fourn/facture/card.php | 33 +- .../install/mysql/migration/3.9.0-4.0.0.sql | 1 - .../install/mysql/migration/4.0.0-5.0.0.sql | 36 + ....key.sql => llx_product_attribute.key.sql} | 8 +- ...y_events.sql => llx_product_attribute.sql} | 20 +- .../llx_product_attribute_combination.sql | 28 + .../llx_product_attribute_combination2val.sql | 25 + .../llx_product_attribute_value.key.sql | 19 + .../tables/llx_product_attribute_value.sql | 26 + htdocs/langs/en_US/main.lang | 2 + htdocs/langs/en_US/products.lang | 38 +- htdocs/product/card.php | 18 +- htdocs/product/class/product.class.php | 30 + htdocs/product/list.php | 46 +- 43 files changed, 3763 insertions(+), 97 deletions(-) create mode 100644 htdocs/attributes/admin/admin.php create mode 100644 htdocs/attributes/admin/index.html create mode 100644 htdocs/attributes/ajax/getCombinations.php create mode 100644 htdocs/attributes/ajax/get_attribute_values.php create mode 100644 htdocs/attributes/ajax/index.html create mode 100644 htdocs/attributes/ajax/orderAttribute.php create mode 100644 htdocs/attributes/card.php create mode 100644 htdocs/attributes/class/ProductAttribute.class.php create mode 100644 htdocs/attributes/class/ProductAttributeValue.class.php create mode 100644 htdocs/attributes/class/ProductCombination.class.php create mode 100644 htdocs/attributes/class/ProductCombination2ValuePair.class.php create mode 100644 htdocs/attributes/class/index.html create mode 100644 htdocs/attributes/combinations.php create mode 100644 htdocs/attributes/create.php create mode 100644 htdocs/attributes/create_val.php create mode 100644 htdocs/attributes/generator.php create mode 100644 htdocs/attributes/index.html create mode 100644 htdocs/attributes/lib/index.html create mode 100644 htdocs/attributes/lib/product_attributes.lib.php create mode 100644 htdocs/attributes/list.php rename htdocs/install/mysql/tables/{llx_holiday_events.key.sql => llx_product_attribute.key.sql} (81%) rename htdocs/install/mysql/tables/{llx_holiday_events.sql => llx_product_attribute.sql} (70%) create mode 100644 htdocs/install/mysql/tables/llx_product_attribute_combination.sql create mode 100644 htdocs/install/mysql/tables/llx_product_attribute_combination2val.sql create mode 100644 htdocs/install/mysql/tables/llx_product_attribute_value.key.sql create mode 100644 htdocs/install/mysql/tables/llx_product_attribute_value.sql diff --git a/htdocs/attributes/admin/admin.php b/htdocs/attributes/admin/admin.php new file mode 100644 index 00000000000..eedacb086b3 --- /dev/null +++ b/htdocs/attributes/admin/admin.php @@ -0,0 +1,65 @@ + + * + * 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 . + */ + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; + +$langs->load("admin"); +$langs->load("products"); + +// Security check +if (! $user->admin || (empty($conf->product->enabled) && empty($conf->service->enabled))) + accessforbidden(); + +if ($_POST) { + + $value = GETPOST('PRODUIT_ATTRIBUTES_HIDECHILD'); + + if (dolibarr_set_const($db, 'PRODUIT_ATTRIBUTES_HIDECHILD', $value, 'chaine', 0, '', $conf->entity)) { + setEventMessage($langs->trans('RecordSaved')); + } else { + setEventMessage($langs->trans('CoreErrorMessage'), 'errors'); + } + +} + +$title = $langs->trans('ModuleSetup').' '.$langs->trans('ProductAttributes'); +llxHeader('', $title); + +$linkback=''.$langs->trans("BackToModuleList").''; +print load_fiche_titre($title,$linkback,'title_setup'); + +dol_fiche_head(array(), 'general', $tab, 0, 'product'); + +print '
'; +print ''; +print ''; +print ''."\n"; +print ''."\n"; +print ''."\n"; +print ''; +print '
'.$langs->trans("Parameters").''.$langs->trans("Value").' 
'.$langs->trans('HideProductCombinations').''; +print $form->selectyesno("PRODUIT_ATTRIBUTES_HIDECHILD",$conf->global->PRODUIT_ATTRIBUTES_HIDECHILD,1).'
'; +print '
'; +print '
'; + +llxFooter(); + +$db->close(); + diff --git a/htdocs/attributes/admin/index.html b/htdocs/attributes/admin/index.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/htdocs/attributes/ajax/getCombinations.php b/htdocs/attributes/ajax/getCombinations.php new file mode 100644 index 00000000000..5ed3e959ea3 --- /dev/null +++ b/htdocs/attributes/ajax/getCombinations.php @@ -0,0 +1,50 @@ + + * + * 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 . + */ + +define('NOTOKENRENEWAL','1'); +define('NOREQUIREMENU','1'); +define('NOREQUIREHTML','1'); +define('NOREQUIREAJAX','1'); +define('NOREQUIRESOC','1'); + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php'; + +header('Content-Type: application/json'); + +$id = GETPOST('id'); + +if (!$id) { + print json_encode(array( + 'error' => 'ID not set' + )); + die; +} + +$product = new Product($db); + +if ($product->fetch($id) < 0) { + print json_encode(array( + 'error' => 'Product not found' + )); +} + +$prodcomb = new ProductCombination($db); + +echo json_encode($prodcomb->getUniqueAttributesAndValuesByFkProductParent($product->id)); diff --git a/htdocs/attributes/ajax/get_attribute_values.php b/htdocs/attributes/ajax/get_attribute_values.php new file mode 100644 index 00000000000..a08126d997a --- /dev/null +++ b/htdocs/attributes/ajax/get_attribute_values.php @@ -0,0 +1,61 @@ + + * + * 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 . + */ + +define('NOTOKENRENEWAL','1'); +define('NOREQUIREMENU','1'); +define('NOREQUIREHTML','1'); +define('NOREQUIREAJAX','1'); +define('NOREQUIRESOC','1'); + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php'; + +header('Content-Type: application/json'); + +$id = GETPOST('id'); + +if (!$id) { + print json_encode(array( + 'error' => 'ID not set' + )); + die; +} + +$prodattr = new ProductAttribute($db); + +if ($prodattr->fetch($id) < 0) { + print json_encode(array( + 'error' => 'Attribute not found' + )); + die; +} + +$prodattrval = new ProductAttributeValue($db); + +$res = $prodattrval->fetchAllByProductAttribute($id); + +if ($res == -1) { + print json_encode(array( + 'error' => 'Internal error' + )); + die; +} + +print json_encode($res); \ No newline at end of file diff --git a/htdocs/attributes/ajax/index.html b/htdocs/attributes/ajax/index.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/htdocs/attributes/ajax/orderAttribute.php b/htdocs/attributes/ajax/orderAttribute.php new file mode 100644 index 00000000000..6c7eecc9453 --- /dev/null +++ b/htdocs/attributes/ajax/orderAttribute.php @@ -0,0 +1,53 @@ + + * + * 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 . + */ + +if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL','1'); // Disable token renewal +if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU','1'); +if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML','1'); +if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1'); +if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1'); +if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1'); +if (! defined('NOREQUIREHOOK')) define('NOREQUIREHOOK','1'); // Disable "main.inc.php" hooks + +require '../../main.inc.php'; + +/* + * View + */ + +top_httphead(); + +// Registering the location of boxes +if (isset($_POST['roworder'])) { + $roworder=GETPOST('roworder','alpha',2); + + dol_syslog("AjaxOrderAttribute roworder=".$roworder, LOG_DEBUG); + + $rowordertab = explode(',', $roworder); + + foreach ($rowordertab as $value) { + if (!empty($value)) { + $newrowordertab[] = $value; + } + } + + require DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php'; + + ProductAttribute::bulkUpdateOrder($db, $newrowordertab); +} + diff --git a/htdocs/attributes/card.php b/htdocs/attributes/card.php new file mode 100644 index 00000000000..3339a169c89 --- /dev/null +++ b/htdocs/attributes/card.php @@ -0,0 +1,245 @@ + + * + * 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 . + */ + +require '../main.inc.php'; +require 'class/ProductAttribute.class.php'; +require 'class/ProductAttributeValue.class.php'; + +$id = GETPOST('id'); +$valueid = GETPOST('valueid'); +$action = GETPOST('action'); +$label = GETPOST('label'); +$ref = GETPOST('ref'); +$confirm = GETPOST('confirm'); + +$prodattr = new ProductAttribute($db); +$prodattrval = new ProductAttributeValue($db); + +if ($prodattr->fetch($id) < 1) { + dol_print_error($db, $langs->trans('ErrorRecordNotFound')); + die; +} + +if ($_POST) { + + if ($action == 'edit') { + + $prodattr->label = $label; + $prodattr->ref = $ref; + + if ($prodattr->update() < 1) { + setEventMessage($langs->trans('CoreErrorMessage'), 'errors'); + } else { + setEventMessage($langs->trans('RecordSaved')); + header('Location: '.dol_buildpath('/attributes/card.php?id='.$id, 2)); + die; + } + } elseif ($action == 'edit_value') { + + if ($prodattrval->fetch($valueid) > 0) { + + $prodattrval->ref = $ref; + $prodattrval->value = GETPOST('value'); + + if ($prodattrval->update() > 0) { + setEventMessage($langs->trans('RecordSaved')); + } else { + setEventMessage($langs->trans('CoreErrorMessage'), 'errors'); + } + } + + header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2)); + die; + } + +} + +if ($confirm == 'yes') { + if ($action == 'confirm_delete') { + + $db->begin(); + + $res = $prodattrval->deleteByFkAttribute($prodattr->id); + + if ($res < 1 || ($prodattr->delete() < 1)) { + $db->rollback(); + setEventMessage($langs->trans('CoreErrorMessage'), 'errors'); + header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2)); + } else { + $db->commit(); + setEventMessage($langs->trans('RecordSaved')); + header('Location: '.dol_buildpath('/attributes/list.php', 2)); + } + + die; + } elseif ($action == 'confirm_deletevalue') { + + if ($prodattrval->fetch($valueid) > 0) { + + if ($prodattrval->delete() < 1) { + setEventMessage($langs->trans('CoreErrorMessage'), 'errors'); + } else { + setEventMessage($langs->trans('RecordSaved')); + } + + header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2)); + die; + } + } +} + +$langs->load('products'); + +$title = $langs->trans('ProductAttributeName', dol_htmlentities($prodattr->label)); +$var = false; + +llxHeader('', $title); + +print_fiche_titre($title); + +dol_fiche_head(); + +if ($action == 'edit') { + print '
'; +} + +?> + + + + + + + + + + +
trans('Ref') ?> + ref.'">'; + } else { + print dol_htmlentities($prodattr->ref); + } ?> +
trans('Label') ?> + label.'">'; + } else { + print dol_htmlentities($prodattr->label); + } ?> +
+ + +
+ +
+formconfirm( + "card.php?id=".$prodattr->id, + $langs->trans('Delete'), + $langs->trans('ProductAttributeDeleteDialog'), + "confirm_delete", + '', + 0, + 1 + ); + } elseif ($action == 'delete_value') { + + if ($prodattrval->fetch($valueid) > 0) { + + $form = new Form($db); + + print $form->formconfirm( + "card.php?id=".$prodattr->id."&valueid=".$prodattrval->id, + $langs->trans('Delete'), + $langs->trans('ProductAttributeValueDeleteDialog', dol_htmlentities($prodattrval->value), dol_htmlentities($prodattrval->ref)), + "confirm_deletevalue", + '', + 0, + 1 + ); + } + } + + ?> + + + + +
+ + + + + + + + + + fetchAllByProductAttribute($prodattr->id) as $attrval): ?> + > + id)): ?> + + + + + + + + + + +
trans('Ref') ?>trans('Value') ?>
+ + trans('Cancel') ?> + ref) ?>value) ?> + + +
+ + +
+ + + + + + * + * 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 . + */ + +class ProductAttribute +{ + /** + * Database handler + * @var DoliDB + */ + private $db; + + /** + * Id of the product attribute + * @var int + */ + public $id; + + /** + * Ref of the product attribute + * @var + */ + public $ref; + + /** + * Label of the product attribute + * @var string + */ + public $label; + + /** + * Order of attribute. + * Lower ones will be shown first and higher ones last + * @var int + */ + public $rang; + + public function __construct(DoliDB $db) + { + global $conf; + + $this->db = $db; + $this->entity = $conf->entity; + } + + /** + * Fetches the properties of a product attribute + * + * @param int $id Attribute id + * @return int <1 KO, >1 OK + */ + public function fetch($id) + { + if (!$id) { + return -1; + } + + require_once __DIR__.'/../lib/product_attributes.lib.php'; + + $sql = "SELECT rowid, ref, label, rang FROM ".MAIN_DB_PREFIX."product_attribute WHERE rowid = ".(int) $id." AND entity IN (".getProductEntities($this->db).")"; + + $query = $this->db->query($sql); + + if (!$this->db->num_rows($query)) { + return -1; + } + + $result = $this->db->fetch_object($query); + + $this->id = $result->rowid; + $this->ref = $result->ref; + $this->label = $result->label; + $this->rang = $result->rang; + + return 1; + } + + /** + * Returns an array of all product attributes + * + * @return ProductAttribute[] + */ + public function fetchAll() + { + require_once __DIR__.'/../lib/product_attributes.lib.php'; + + $return = array(); + + $sql = 'SELECT rowid, ref, label, rang FROM '.MAIN_DB_PREFIX."product_attribute WHERE entity IN (".getProductEntities($this->db).')'; + $sql .= $this->db->order('rang', 'asc'); + $query = $this->db->query($sql); + + while ($result = $this->db->fetch_object($query)) { + + $tmp = new ProductAttribute($this->db); + $tmp->id = $result->rowid; + $tmp->ref = $result->ref; + $tmp->label = $result->label; + $tmp->rang = $result->rang; + + $return[] = $tmp; + } + + return $return; + } + + /** + * Creates a product attribute + * + * @return int <0 KO, >0 OK + */ + public function create() + { + //Ref must be uppercase + $this->ref = strtoupper($this->ref); + + $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute (ref, label, entity, rang) + VALUES ('".$this->db->escape($this->ref)."', '".$this->db->escape($this->label)."', ".(int) $this->entity.", ".(int) $this->rang.")"; + $query = $this->db->query($sql); + + if ($query) { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute'); + + return 1; + } + + return -1; + } + + /** + * Updates a product attribute + * + * @return int <0 KO, >0 OK + */ + public function update() + { + //Ref must be uppercase + $this->ref = strtoupper($this->ref); + + $sql = "UPDATE ".MAIN_DB_PREFIX."product_attribute SET ref = '".$this->db->escape($this->ref)."', label = '".$this->db->escape($this->label)."', rang = ".(int) $this->rang." WHERE rowid = ".(int) $this->id; + + if ($this->db->query($sql)) { + return 1; + } + + return -1; + } + + /** + * Deletes a product attribute + * + * @return int <0 KO, >0 OK + */ + public function delete() + { + $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute WHERE rowid = ".(int) $this->id; + + if ($this->db->query($sql)) { + return 1; + } + + return -1; + } + + /** + * Returns the number of products that are using this attribute + * + * @return int + */ + public function countChildProducts() + { + require_once __DIR__.'/../lib/product_attributes.lib.php'; + + $sql = "SELECT COUNT(*) count FROM ".MAIN_DB_PREFIX."product_attribute_combination2val pac2v + LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac2v.fk_prod_combination = pac.rowid WHERE pac2v.fk_prod_attr = ".(int) $this->id." AND pac.entity IN (".getProductEntities($this->db).")"; + + $query = $this->db->query($sql); + + $result = $this->db->fetch_object($query); + + return $result->count; + } + + /** + * Reorders the order of the attributes. + * This is an internal function used by moveLine function + * + * @return int <0 KO >0 OK + */ + protected function reorderLines() + { + $tmp_order = array(); + + $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'product_attribute WHERE rang = 0'; + $sql .= $this->db->order('rang, rowid', 'asc'); + + $query = $this->db->query($sql); + + if (!$query) { + return -1; + } + + while ($result = $this->db->fetch_object($query)) { + $tmp_order[] = $result->rowid; + } + + foreach ($tmp_order as $order => $rowid) { + $tmp = new ProductAttribute($this->db); + $tmp->fetch($rowid); + $tmp->rang = $order+1; + + if ($tmp->update() < 0) { + return -1; + } + } + + return 1; + } + + /** + * Internal function to handle moveUp and moveDown functions + * + * @param string $type up/down + * @return int <0 KO >0 OK + */ + private function moveLine($type) + { + if ($this->reorderLines() < 0) { + return -1; + } + + $this->db->begin(); + + if ($type == 'up') { + $newrang = $this->rang - 1; + } else { + $newrang = $this->rang + 1; + } + + $sql = 'UPDATE '.MAIN_DB_PREFIX.'product_attribute SET rang = '.$this->rang.' WHERE rang = '.$newrang; + + if (!$this->db->query($sql)) { + $this->db->rollback(); + return -1; + } + + $this->rang = $newrang; + + if ($this->update() < 0) { + $this->db->rollback(); + return -1; + } + + $this->db->commit(); + return 1; + } + + /** + * Shows this attribute before others + * + * @return int <0 KO >0 OK + */ + public function moveUp() + { + return $this->moveLine('up'); + } + + /** + * Shows this attribute after others + * + * @return int <0 KO >0 OK + */ + public function moveDown() + { + return $this->moveLine('down'); + } + + /** + * Updates the order of all attributes. Used by AJAX page for drag&drop + * + * @param DoliDB $db Database handler + * @param array $order Array with row id ordered in ascendent mode + * @return int <0 KO >0 OK + */ + public static function bulkUpdateOrder(DoliDB $db, array $order) + { + $tmp = new ProductAttribute($db); + + foreach ($order as $key => $attrid) { + if ($tmp->fetch($attrid) < 0) { + return -1; + } + + $tmp->rang = $key; + + if ($tmp->update() < 0) { + return -1; + } + } + + return 1; + } +} \ No newline at end of file diff --git a/htdocs/attributes/class/ProductAttributeValue.class.php b/htdocs/attributes/class/ProductAttributeValue.class.php new file mode 100644 index 00000000000..f500fb251c6 --- /dev/null +++ b/htdocs/attributes/class/ProductAttributeValue.class.php @@ -0,0 +1,221 @@ + + * + * 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 . + */ + +class ProductAttributeValue +{ + /** + * Database handler + * @var DoliDB + */ + private $db; + + /** + * Attribute value id + * @var int + */ + public $id; + + /** + * Product attribute id + * @var int + */ + public $fk_product_attribute; + + /** + * Attribute value ref + * @var string + */ + public $ref; + + /** + * Attribute value value + * @var string + */ + public $value; + + public function __construct(DoliDB $db) + { + global $conf; + + $this->db = $db; + $this->entity = $conf->entity; + } + + /** + * Gets a product attribute value + * + * @param int $valueid Product attribute value id + * @return int <0 KO, >0 OK + */ + public function fetch($valueid) + { + require_once __DIR__.'/../lib/product_attributes.lib.php'; + + $sql = "SELECT rowid, fk_product_attribute, ref, value FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE rowid = ".(int) $valueid." AND entity IN (".getProductEntities($this->db).")"; + + $query = $this->db->query($sql); + + if (!$query) { + return -1; + } + + if (!$this->db->num_rows($query)) { + return -1; + } + + $result = $this->db->fetch_object($query); + + $this->id = $result->rowid; + $this->fk_product_attribute = $result->fk_product_attribute; + $this->ref = $result->ref; + $this->value = $result->value; + + return 1; + } + + /** + * Returns all product attribute values of a product attribute + * + * @param int $prodattr_id Product attribute id + * @param bool $only_used Fetch only used attribute values + * @return ProductAttributeValue[] + */ + public function fetchAllByProductAttribute($prodattr_id, $only_used = false) + { + require_once __DIR__.'/../lib/product_attributes.lib.php'; + + $return = array(); + + $sql = 'SELECT '; + + if ($only_used) { + $sql .= 'DISTINCT '; + } + + $sql .= 'v.fk_product_attribute, v.rowid, v.ref, v.value FROM '.MAIN_DB_PREFIX.'product_attribute_value v '; + + if ($only_used) { + $sql .= 'LEFT JOIN '.MAIN_DB_PREFIX.'product_attribute_combination2val c2v ON c2v.fk_prod_attr_val = v.rowid '; + $sql .= 'LEFT JOIN '.MAIN_DB_PREFIX.'product_attribute_combination c ON c.rowid = c2v.fk_prod_combination '; + $sql .= 'LEFT JOIN '.MAIN_DB_PREFIX.'product p ON p.rowid = c.fk_product_child '; + } + + $sql .= 'WHERE v.fk_product_attribute = '.(int) $prodattr_id; + + if ($only_used) { + $sql .= ' AND c2v.rowid IS NOT NULL AND p.tosell = 1'; + } + + $query = $this->db->query($sql); + + while ($result = $this->db->fetch_object($query)) { + + $tmp = new ProductAttributeValue($this->db); + $tmp->fk_product_attribute = $result->fk_product_attribute; + $tmp->id = $result->rowid; + $tmp->ref = $result->ref; + $tmp->value = $result->value; + + $return[] = $tmp; + } + + return $return; + } + + /** + * Creates a value for a product attribute + * + * @return int <0 KO >0 OK + */ + public function create() + { + if (!$this->fk_product_attribute) { + return -1; + } + + //Ref must be uppercase + $this->ref = strtoupper($this->ref); + + $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_value (fk_product_attribute, ref, value, entity) + VALUES ('".(int) $this->fk_product_attribute."', '".$this->db->escape($this->ref)."', + '".$this->db->escape($this->value)."', ".(int) $this->entity.")"; + + $query = $this->db->query($sql); + + if ($query) { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute_value'); + return 1; + } + + return -1; + } + + /** + * Updates a product attribute value + * + * @return int + */ + public function update() + { + //Ref must be uppercase + $this->ref = strtoupper($this->ref); + + $sql = "UPDATE ".MAIN_DB_PREFIX."product_attribute_value + SET fk_product_attribute = '".(int) $this->fk_product_attribute."', ref = '".$this->db->escape($this->ref)."', + value = '".$this->db->escape($this->value)."' WHERE rowid = ".(int) $this->id; + + if ($this->db->query($sql)) { + return 1; + } + + return -1; + } + + /** + * Deletes a product attribute value + * + * @return int <0 KO, >0 OK + */ + public function delete() + { + $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE rowid = ".(int) $this->id; + + if ($this->db->query($sql)) { + return 1; + } + + return -1; + } + + /** + * Deletes all product attribute values by a product attribute id + * + * @param int $fk_attribute Product attribute id + * @return int <0 KO, >0 OK + */ + public function deleteByFkAttribute($fk_attribute) + { + $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE fk_product_attribute = ".(int) $fk_attribute; + + if ($this->db->query($sql)) { + return 1; + } + + return -1; + } +} \ No newline at end of file diff --git a/htdocs/attributes/class/ProductCombination.class.php b/htdocs/attributes/class/ProductCombination.class.php new file mode 100644 index 00000000000..6722f7f03fc --- /dev/null +++ b/htdocs/attributes/class/ProductCombination.class.php @@ -0,0 +1,641 @@ + + * + * 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 . + */ + +class ProductCombination +{ + /** + * Database handler + * @var DoliDB + */ + private $db; + + /** + * Rowid of combination + * @var int + */ + public $id; + + /** + * Rowid of parent product + * @var int + */ + public $fk_product_parent; + + /** + * Rowid of child product + * @var int + */ + public $fk_product_child; + + /** + * Price variation + * @var float + */ + public $variation_price; + + /** + * Is the price variation a relative variation? + * @var bool + */ + public $variation_price_percentage = false; + + /** + * Weight variation + * @var float + */ + public $variation_weight; + + /** + * Combination entity + * @var int + */ + public $entity; + + public function __construct(DoliDB $db) + { + global $conf; + + $this->db = $db; + $this->entity = $conf->entity; + } + + /** + * Retrieves a combination by its rowid + * + * @param int $rowid Row id + * @return int <0 KO, >0 OK + */ + public function fetch($rowid) + { + require_once __DIR__.'/../lib/product_attributes.lib.php'; + + $sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE rowid = ".(int) $rowid." AND entity IN (".getProductEntities($this->db).")"; + + $query = $this->db->query($sql); + + if (!$query) { + return -1; + } + + if (!$this->db->num_rows($query)) { + return -1; + } + + $result = $this->db->fetch_object($query); + + $this->id = $result->rowid; + $this->fk_product_parent = $result->fk_product_parent; + $this->fk_product_child = $result->fk_product_child; + $this->variation_price = $result->variation_price; + $this->variation_price_percentage = $result->variation_price_percentage; + $this->variation_weight = $result->variation_weight; + + return 1; + } + + /** + * Retrieves a product combination by a child product row id + * + * @param int $fk_child Product row id + * @return int <0 KO, >0 OK + */ + public function fetchByFkProductChild($fk_child) + { + require_once __DIR__.'/../lib/product_attributes.lib.php'; + + $sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE fk_product_child = ".(int) $fk_child." AND entity IN (".getProductEntities($this->db).")"; + + $query = $this->db->query($sql); + + if (!$query) { + return -1; + } + + if (!$this->db->num_rows($query)) { + return -1; + } + + $result = $this->db->fetch_object($query); + + $this->id = $result->rowid; + $this->fk_product_parent = $result->fk_product_parent; + $this->fk_product_child = $result->fk_product_child; + $this->variation_price = $result->variation_price; + $this->variation_price_percentage = $result->variation_price_percentage; + $this->variation_weight = $result->variation_weight; + + return 1; + } + + /** + * Retrieves all product combinations by the product parent row id + * + * @param int $fk_product_parent Rowid of parent product + * @return int|ProductCombination[] <0 KO + */ + public function fetchAllByFkProductParent($fk_product_parent) + { + require_once __DIR__.'/../lib/product_attributes.lib.php'; + + $sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE fk_product_parent = ".(int) $fk_product_parent." AND entity IN (".getProductEntities($this->db).")"; + + $query = $this->db->query($sql); + + if (!$query) { + return -1; + } + + $return = array(); + + while ($result = $this->db->fetch_object($query)) { + + $tmp = new ProductCombination($this->db); + $tmp->id = $result->rowid; + $tmp->fk_product_parent = $result->fk_product_parent; + $tmp->fk_product_child = $result->fk_product_child; + $tmp->variation_price = $result->variation_price; + $tmp->variation_price_percentage = $result->variation_price_percentage; + $tmp->variation_weight = $result->variation_weight; + + $return[] = $tmp; + } + + return $return; + } + + /** + * Creates a product attribute combination + * + * @return int + */ + public function create() + { + $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_combination + (fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight, entity) + VALUES (".(int) $this->fk_product_parent.", ".(int) $this->fk_product_child.", + ".(float) $this->variation_price.", ".(int) $this->variation_price_percentage.", + ".(float) $this->variation_weight.", ".(int) $this->entity.")"; + + $query = $this->db->query($sql); + + if (!$query) { + return -1; + } + + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute_combination'); + + return 1; + } + + /** + * Updates a product combination + * + * @return int <0 KO, >0 OK + */ + public function update() + { + $sql = "UPDATE ".MAIN_DB_PREFIX."product_attribute_combination + SET fk_product_parent = ".(int) $this->fk_product_parent.", fk_product_child = ".(int) $this->fk_product_child.", + variation_price = ".(float) $this->variation_price.", variation_price_percentage = ".(int) $this->variation_price_percentage.", + variation_weight = ".(float) $this->variation_weight." WHERE rowid = ".(int) $this->id; + + $query = $this->db->query($sql); + + if (!$query) { + return -1; + } + + $parent = new Product($this->db); + $parent->fetch($this->fk_product_parent); + + $this->updateProperties($parent); + + return 1; + } + + /** + * Deletes a product combination + * + * @return int <0 KO >0 OK + */ + public function delete() + { + $this->db->begin(); + + $comb2val = new ProductCombination2ValuePair($this->db); + $comb2val->deleteByFkCombination($this->id); + + $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE rowid = ".(int) $this->id; + + if ($this->db->query($sql)) { + $this->db->commit(); + return 1; + } + + $this->db->rollback(); + return -1; + } + + /** + * Deletes all product combinations of a parent product + * + * @param int $fk_product_parent Rowid of parent product + * @return int <0 KO >0 OK + */ + public function deleteByFkProductParent($fk_product_parent) + { + $this->db->begin(); + + foreach ($this->fetchAllByFkProductParent($fk_product_parent) as $prodcomb) { + + $prodstatic = new Product($this->db); + + $res = $prodstatic->fetch($prodcomb->fk_product_child); + + if ($res > 0) { + $res = $prodcomb->delete(); + } + + if ($res > 0 && !$prodstatic->isObjectUsed($prodstatic->id)) { + $res = $prodstatic->delete(); + } + + if ($res < 0) { + $this->db->rollback(); + return -1; + } + } + + $this->db->commit(); + return 1; + } + + /** + * Updates the weight of the child product. The price must be updated using Product::updatePrices + * + * @param Product $parent Parent product + * @return int >0 OK <0 KO + */ + public function updateProperties(Product $parent) + { + global $user, $conf; + + $this->db->begin(); + + $child = new Product($this->db); + $child->fetch($this->fk_product_child); + $child->price_autogen = $parent->price_autogen; + $child->weight = $parent->weight + $this->variation_weight; + $child->weight_units = $parent->weight_units; + + if ($child->update($child->id, $user) > 0) { + + $new_vat = $parent->tva_tx; + $new_npr = $parent->tva_npr; + + // MultiPrix + if (! empty($conf->global->PRODUIT_MULTIPRICES)) { + $new_type = $parent->multiprices_base_type[1]; + $new_min_price = $parent->multiprices_min[1]; + $new_psq = $parent->multiprices_recuperableonly[1]; + + if ($new_type == 'TTC') { + $new_price = $parent->multiprices_ttc[1]; + } else { + $new_price = $parent->multiprices[1]; + } + } else { + $new_type = $parent->price_base_type; + $new_min_price = $parent->price_min; + $new_psq = $parent->price_by_qty; + + if ($new_type == 'TTC') { + $new_price = $parent->price_ttc; + } else { + $new_price = $parent->price; + } + } + + if ($this->variation_price_percentage) { + $new_price *= 1 + ($this->variation_price/100); + } else { + $new_price += $this->variation_price; + } + + $child->updatePrice($new_price, $new_type, $user, $new_vat, $new_min_price, 1, $new_npr, $new_psq); + + $this->db->commit(); + + return 1; + } + + $this->db->rollback(); + return -1; + } + + /** + * Retrieves the combination that matches the given features. + * + * @param int $prodid Id of parent product + * @param array $features Format: [$attr] => $attr_val + * @return false|ProductCombination false if not found + */ + public function fetchByProductCombination2ValuePairs($prodid, array $features) + { + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php'; + + $actual_comp = array(); + + $prodcomb2val = new ProductCombination2ValuePair($this->db); + $prodcomb = new ProductCombination($this->db); + + foreach ($features as $attr => $attr_val) { + $actual_comp[$attr] = $attr_val; + } + + foreach ($prodcomb->fetchAllByFkProductParent($prodid) as $prc) { + + $values = array(); + + foreach ($prodcomb2val->fetchByFkCombination($prc->id) as $value) { + $values[$value->fk_prod_attr] = $value->fk_prod_attr_val; + } + + $check1 = count(array_diff_assoc($values, $actual_comp)); + $check2 = count(array_diff_assoc($actual_comp, $values)); + + if (!$check1 && !$check2) { + return $prc; + } + } + + return false; + } + + /** + * Retrieves all unique attributres for a parent product + * + * @param int $productid Product rowid + * @return ProductAttribute[] + */ + public function getUniqueAttributesAndValuesByFkProductParent($productid) + { + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php'; + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php'; + + $attributes = array(); + + //Attributes + $sql = "SELECT DISTINCT fk_prod_attr, a.rang +FROM ".MAIN_DB_PREFIX."product_attribute_combination2val c2v LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination c + ON c2v.fk_prod_combination = c.rowid + LEFT JOIN ".MAIN_DB_PREFIX."product p ON p.rowid = c.fk_product_child + LEFT JOIN ".MAIN_DB_PREFIX."product_attribute a ON a.rowid = fk_prod_attr +WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1"; + + $sql .= $this->db->order('a.rang', 'asc'); + + $query = $this->db->query($sql); + + //Values + while ($result = $this->db->fetch_object($query)) { + $attr = new ProductAttribute($this->db); + $attr->fetch($result->fk_prod_attr); + + $tmp = new stdClass(); + $tmp->id = $attr->id; + $tmp->ref = $attr->ref; + $tmp->label = $attr->label; + $tmp->values = array(); + + $attrval = new ProductAttributeValue($this->db); + foreach ($res = $attrval->fetchAllByProductAttribute($attr->id, true) as $val) { + $tmp->values[] = $val; + } + + $attributes[] = $tmp; + } + + return $attributes; + } + + /** + * Creates a product combination. Check usages to find more about its use + * + * Format of $combinations array: + * array( + * 0 => array( + * attr => value, + * attr2 => value + * [...] + * ), + * [...] + * ) + * + * @param Product $product Parent product + * @param array $combinations Attribute and value combinations. + * @param array $variations Price and weight variations + * @param bool $price_var_percent Is the price variation a relative variation? + * @param bool|float $forced_pricevar If the price variation is forced + * @param bool|float $forced_weightvar If the weight variation is forced + * @return int <0 KO, >0 OK + */ + public static function createProductCombination(Product $product, array $combinations, array $variations, $price_var_percent = false, $forced_pricevar = false, $forced_weightvar = false) + { + global $db, $user; + + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php'; + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php'; + + $db->begin(); + + $newproduct = clone $product; + + //Final weight impact + $weight_impact = $forced_weightvar; + + if ($forced_weightvar === false) { + $weight_impact = 0; + } + + //Final price impact + $price_impact = $forced_pricevar; + + if ($forced_pricevar === false) { + $price_impact = 0; + } + + $newcomb = new ProductCombination($db); + $existingCombination = $newcomb->fetchByProductCombination2ValuePairs($product->id, $combinations); + + if ($existingCombination) { + $newcomb = $existingCombination; + } else { + $newcomb->fk_product_parent = $product->id; + + if ($newcomb->create() < 0) { + $db->rollback(); + return -1; + } + } + + $prodattr = new ProductAttribute($db); + $prodattrval = new ProductAttributeValue($db); + + foreach ($combinations as $currcombattr => $currcombval) { + + //This was checked earlier, so no need to double check + $prodattr->fetch($currcombattr); + $prodattrval->fetch($currcombval); + + //If there is an existing combination, there is no need to duplicate the valuepair + if (!$existingCombination) { + $tmp = new ProductCombination2ValuePair($db); + $tmp->fk_prod_attr = $currcombattr; + $tmp->fk_prod_attr_val = $currcombval; + $tmp->fk_prod_combination = $newcomb->id; + + if ($tmp->create() < 0) { + $db->rollback(); + return -1; + } + } + + if ($forced_weightvar === false) { + $weight_impact += (float) price2num($variations[$currcombattr][$currcombval]['weight']); + } + if ($forced_pricevar === false) { + $price_impact += (float) price2num($variations[$currcombattr][$currcombval]['price']); + } + + $newproduct->ref .= '_'.$prodattrval->ref; + + //The first one should not contain a linebreak + if ($newproduct->description) { + $newproduct->description .= '
'; + } + $newproduct->description .= ''.$prodattr->label.': '.$prodattrval->value; + } + + $newcomb->variation_price_percentage = $price_var_percent; + $newcomb->variation_price = $price_impact; + $newcomb->variation_weight = $weight_impact; + + $newproduct->weight += $weight_impact; + + //To avoid wrong information in price history log + $newproduct->price = 0; + $newproduct->price_ttc = 0; + $newproduct->price_min = 0; + $newproduct->price_min_ttc = 0; + + if ($newproduct->create($user) < 0) { + + //In case the error is not related with an already existing product + if ($newproduct->error != 'ErrorProductAlreadyExists') { + $db->rollback(); + return -1; + } + + /** + * If there is an existing combination, then we update the prices and weight + * Otherwise, we try adding a random number to the ref + */ + + if ($newcomb->fk_product_child) { + $res = $newproduct->fetch($existingCombination->fk_product_child); + } else { + $orig_prod_ref = $newproduct->ref; + $i = 1; + + do { + $newproduct->ref = $orig_prod_ref.$i; + $res = $newproduct->create($user); + + if ($newproduct->error != 'ErrorProductAlreadyExists') { + break; + } + + $i++; + } while ($res < 0); + } + + if ($res < 0) { + $db->rollback(); + return -1; + } + + $newproduct->weight += $weight_impact; + } + + $newcomb->fk_product_child = $newproduct->id; + + if ($newcomb->update() < 0) { + $db->rollback(); + return -1; + } + + $db->commit(); + return 1; + } + + /** + * Copies all product combinations from the origin product to the destination product + * + * @param int $origProductId Origin product id + * @param Product $destProduct Destination product + * @return int >0 OK <0 KO + */ + public function copyAll($origProductId, Product $destProduct) + { + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php'; + + //To prevent a loop + if ($origProductId == $destProduct->id) { + return -1; + } + + $prodcomb = new ProductCombination($this->db); + $prodcomb2val = new ProductCombination2ValuePair($this->db); + + //Retrieve all product combinations + $combinations = $prodcomb->fetchAllByFkProductParent($origProductId); + + foreach ($combinations as $combination) { + + $variations = array(); + + foreach ($prodcomb2val->fetchByFkCombination($combination->id) as $tmp_pc2v) { + $variations[$tmp_pc2v->fk_prod_attr] = $tmp_pc2v->fk_prod_attr_val; + } + + if (self::createProductCombination( + $destProduct, + $variations, + array(), + $combination->variation_price_percentage, + $combination->variation_price, + $combination->variation_weight + ) < 0) { + return -1; + } + } + + return 1; + } +} \ No newline at end of file diff --git a/htdocs/attributes/class/ProductCombination2ValuePair.class.php b/htdocs/attributes/class/ProductCombination2ValuePair.class.php new file mode 100644 index 00000000000..93521b86bbf --- /dev/null +++ b/htdocs/attributes/class/ProductCombination2ValuePair.class.php @@ -0,0 +1,151 @@ + + * + * 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 . + */ + +class ProductCombination2ValuePair +{ + /** + * Database handler + * @var DoliDB + */ + private $db; + + /** + * Combination 2 value pair id + * @var int + */ + public $id; + + /** + * Product combination id + * @var int + */ + public $fk_prod_combination; + + /** + * Product attribute id + * @var int + */ + public $fk_prod_attr; + + /** + * Product attribute value id + * @var int + */ + public $fk_prod_attr_val; + + public function __construct(DoliDB $db) + { + $this->db = $db; + } + + /** + * Translates this class to a human-readable string + * + * @return string + */ + public function __toString() + { + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php'; + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php'; + + $prodattr = new ProductAttribute($this->db); + $prodattrval = new ProductAttributeValue($this->db); + + $prodattr->fetch($this->fk_prod_attr); + $prodattrval->fetch($this->fk_prod_attr_val); + + return $prodattr->label.': '.$prodattrval->value; + } + + /** + * Creates a product combination 2 value pair + * @return int <0 KO, >0 OK + */ + public function create() + { + $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_combination2val + (fk_prod_combination, fk_prod_attr, fk_prod_attr_val) + VALUES(".(int) $this->fk_prod_combination.", ".(int) $this->fk_prod_attr.", ".(int) $this->fk_prod_attr_val.")"; + + $query = $this->db->query($sql); + + if ($query) { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute_combination2val'); + + return 1; + } + + return -1; + } + + /** + * Retrieves a product combination 2 value pair from its rowid + * + * @param int $fk_combination Fk combination to search + * @return int|ProductCombination2ValuePair[] -1 if KO + */ + public function fetchByFkCombination($fk_combination) + { + $sql = "SELECT + c.rowid, + c2v.fk_prod_attr_val, + c2v.fk_prod_attr, + c2v.fk_prod_combination +FROM ".MAIN_DB_PREFIX."product_attribute c LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination2val c2v ON c.rowid = c2v.fk_prod_attr +WHERE c2v.fk_prod_combination = ".(int) $fk_combination; + + $sql .= $this->db->order('c.rang', 'asc'); + + $query = $this->db->query($sql); + + if (!$query) { + return -1; + } + + $return = array(); + + while ($result = $this->db->fetch_object($query)) { + $tmp = new ProductCombination2ValuePair($this->db); + $tmp->fk_prod_attr_val = $result->fk_prod_attr_val; + $tmp->fk_prod_attr = $result->fk_prod_attr; + $tmp->fk_prod_combination = $result->fk_prod_combination; + $tmp->id = $result->rowid; + + $return[] = $tmp; + } + + return $return; + } + + /** + * Deletes a product combination 2 value pair + * + * @param int $fk_combination Rowid of the combination + * @return int >0 OK <0 KO + */ + public function deleteByFkCombination($fk_combination) + { + $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_combination2val WHERE fk_prod_combination = ".(int) $fk_combination; + + if ($this->db->query($sql)) { + return 1; + } + + return -1; + } +} \ No newline at end of file diff --git a/htdocs/attributes/class/index.html b/htdocs/attributes/class/index.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/htdocs/attributes/combinations.php b/htdocs/attributes/combinations.php new file mode 100644 index 00000000000..b8a758c2341 --- /dev/null +++ b/htdocs/attributes/combinations.php @@ -0,0 +1,641 @@ + + * + * 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 . + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php'; + +$langs->load("products"); +$langs->load("other"); + +$var = false; +$id = GETPOST('id', 'int'); +$valueid = GETPOST('valueid', 'int'); +$ref = GETPOST('ref'); +$weight_impact = (float) GETPOST('weight_impact'); +$price_impact = (float) GETPOST('price_impact'); +$price_impact_percent = (bool) GETPOST('price_impact_percent'); +$form = new Form($db); +$action = GETPOST('action'); + +// Security check +$fieldvalue = (! empty($id) ? $id : $ref); +$fieldtype = (! empty($ref) ? 'ref' : 'rowid'); +$result=restrictedArea($user,'produit|service',$fieldvalue,'product&product','','',$fieldtype); + +$prodstatic = new Product($db); +$prodattr = new ProductAttribute($db); +$prodattr_val = new ProductAttributeValue($db); + +$product = new Product($db); + +$product->fetch($id); + +if (!$product->isProduct()) { + header('Location: '.dol_buildpath('/product/card.php?id='.$product->id, 2)); + die; +} + +$prodcomb = new ProductCombination($db); +$prodcomb2val = new ProductCombination2ValuePair($db); + +$productCombination2ValuePairs1 = array(); + +if ($_POST) { + + if ($action == 'add') { + + $features = GETPOST('features', 'array'); + + if (!$features) { + setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors'); + } else { + $weight_impact = price2num($weight_impact); + $price_impact = price2num($price_impact); + $sanit_features = array(); + + //First, sanitize + foreach ($features as $feature) { + + $explode = explode(':', $feature); + + if ($prodattr->fetch($explode[0]) < 0) { + continue; + } + + if ($prodattr_val->fetch($explode[1]) < 0) { + continue; + } + + //Valuepair + $sanit_features[$explode[0]] = $explode[1]; + + $tmp = new ProductCombination2ValuePair($db); + $tmp->fk_prod_attr = $explode[0]; + $tmp->fk_prod_attr_val = $explode[1]; + + $productCombination2ValuePairs1[] = $tmp; + } + + $db->begin(); + + if (!$prodcomb->fetchByProductCombination2ValuePairs($id, $sanit_features)) { + if (ProductCombination::createProductCombination($product, $sanit_features, array(), $price_impact_percent, $price_impact, $weight_impact)) { + $db->commit(); + setEventMessage($langs->trans('RecordSaved')); + header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$id, 2)); + die; + } else { + setEventMessage($langs->trans('CoreErrorMessage'), 'errors'); + } + } else { + setEventMessage($langs->trans('ErrorRecordAlreadyExists'), 'errors'); + } + + $db->rollback(); + } + } elseif ($action == 'bulk_actions') { + + $prodarray = array_keys(GETPOST('select', 'array')); + $bulkaction = GETPOST('bulk_action'); + $error = 0; + + $prodstatic = new Product($db); + + $db->begin(); + + foreach ($prodarray as $prodid) { + + if ($prodstatic->fetch($prodid) < 0) { + continue; + } + + if ($bulkaction == 'on_sell') { + $prodstatic->status = 1; + $res = $prodstatic->update($prodstatic->id, $user); + } elseif ($bulkaction == 'on_buy') { + $prodstatic->status_buy = 1; + $res = $prodstatic->update($prodstatic->id, $user); + } elseif ($bulkaction == 'not_sell') { + $prodstatic->status = 0; + $res = $prodstatic->update($prodstatic->id, $user); + } elseif ($bulkaction == 'not_buy') { + $prodstatic->status_buy = 0; + $res = $prodstatic->update($prodstatic->id, $user); + } elseif ($bulkaction == 'delete') { + $res = $prodstatic->delete($prodstatic->id); + } else { + break; + } + + if ($res <= 0) { + $error++; + break; + } + } + + if ($error) { + $db->rollback(); + + if ($prodstatic->error) { + setEventMessage($langs->trans($prodstatic->error), 'errors'); + } else { + setEventMessage($langs->trans('CoreErrorMessage'), 'errors'); + } + + } else { + $db->commit(); + setEventMessage($langs->trans('RecordSaved')); + } + + } else { + + if ($prodcomb->fetch($valueid) < 0) { + dol_print_error($db, $langs->trans('ErrorRecordNotFound')); + die; + } + + $prodcomb->variation_price_percentage = $price_impact_percent; + $prodcomb->variation_price = $price_impact; + $prodcomb->variation_weight = $weight_impact; + + if ($prodcomb->update() > 0) { + setEventMessage($langs->trans('RecordSaved')); + header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$id, 2)); + die; + } else { + setEventMessage($langs->trans('CoreErrorMessage'), 'errors'); + } + } +} + +$productCombinations = $prodcomb->fetchAllByFkProductParent($id); + +if ($action === 'confirm_deletecombination') { + + if ($prodcomb->fetch($valueid) > 0) { + + $db->begin(); + + if ($prodcomb->delete() > 0 && $prodstatic->fetch($prodcomb->fk_product_child) > 0 && $prodstatic->delete() > 0) { + $db->commit(); + setEventMessage($langs->trans('RecordSaved')); + header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$product->id, 2)); + die; + } + + $db->rollback(); + setEventMessage($langs->trans('ProductCombinationAlreadyUsed'), 'errors'); + $action = ''; + } +} elseif ($action === 'edit') { + + if ($prodcomb->fetch($valueid) < 0) { + dol_print_error($db, $langs->trans('ErrorRecordNotFound')); + die; + } + + $weight_impact = $prodcomb->variation_weight; + $price_impact = $prodcomb->variation_price; + $price_impact_percent = $prodcomb->variation_price_percentage; + + $productCombination2ValuePairs1 = $prodcomb2val->fetchByFkCombination($valueid); +} elseif ($action === 'confirm_copycombination') { + + //Check destination product + $dest_product = GETPOST('dest_product'); + + if ($prodstatic->fetch('', $dest_product) > 0) { + + //To prevent from copying to the same product + if ($prodstatic->ref != $product->ref) { + if ($prodcomb->copyAll($product->id, $prodstatic) > 0) { + header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$prodstatic->id, 2)); + die; + } else { + setEventMessage($langs->trans('ErrorCopyProductCombinations'), 'errors'); + } + } + + } else { + setEventMessage($langs->trans('ErrorDestinationProductNotFound'), 'errors'); + } + +} + +/* + * View + */ + +if (! empty($id) || ! empty($ref)) { + $object = new Product($db); + $result = $object->fetch($id, $ref); + + llxHeader("", "", $langs->trans("CardProduct".$object->type)); + + if ($result) { + $head = product_prepare_head($object); + $titre = $langs->trans("CardProduct".$object->type); + $picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product'); + + dol_fiche_head($head, 'combinations', $titre, 0, $picto); + + print ''; + + // Reference + print ''; + print ''; + print ''; + + // Label + print ''; + + // Status (to sell) + print ''; + + // Status (to buy) + print ''; + + print '
'.$langs->trans("Ref").''; + print $form->showrefnav($object, 'id', '', 0); + print '
'.$langs->trans("Label").''.$object->label.'
'.$langs->trans("Status").' ('.$langs->trans("Sell").')'; + print $object->getLibStatut(2, 0); + print '
'.$langs->trans("Status").' ('.$langs->trans("Buy").')'; + print $object->getLibStatut(2, 1); + print '
'; + + dol_fiche_end(); + } + + if ($action == 'add' || ($action == 'edit')) { + + if ($action == 'add') { + $title = $langs->trans('NewProductCombination'); + } else { + $title = $langs->trans('EditProductCombination'); + } + + print_fiche_titre($title); + + if ($action == 'add') { + $prodattr_all = $prodattr->fetchAll(); + + if (!$selected) { + $selected = $prodattr_all[key($prodattr_all)]->id; + } + + $prodattr_alljson = array(); + + foreach ($prodattr_all as $each) { + $prodattr_alljson[$each->id] = $each; + } + + ?> + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +

+ + +
+ >
+ +
+
+ + + +
+fetch($valueid) > 0) { + $form = new Form($db); + $prodstatic->fetch($prodcomb->fk_product_child); + + print $form->formconfirm( + "combinations.php?id=".$id."&valueid=".$valueid, + $langs->trans('Delete'), + $langs->trans('ProductCombinationDeleteDialog', $prodstatic->getNomUrl(1)), + "confirm_deletecombination", + '', + 0, + 1 + ); + } + } elseif ($action === 'copy') { + + $form = new Form($db); + + print $form->formconfirm( + 'combinations.php?id='.$id, + $langs->trans('CloneCombinationsProduct'), + $langs->trans('ConfirmCloneProductCombinations'), + 'confirm_copycombination', + array( + array( + 'type' => 'text', + 'label' => $langs->trans('CloneDestinationReference'), + 'name' => 'dest_product' + ) + ), + 0, + 1 + ); + } + + $comb2val = new ProductCombination2ValuePair($db); + + if ($productCombinations): ?> + + + +
+ + + + +
+
+ + + + + + + + + + + + + + fetch($currcomb->fk_product_child); ?> + > + + + + + + + + + + +
+ + + + trans('Product') ?>trans('Combination') ?>trans('PriceImpact') ?>trans('WeightImpact') ?>trans('OnSell') ?>trans('OnBuy') ?>
getNomUrl(1) ?> + fetchByFkCombination($currcomb->id); + $iMax = count($productCombination2ValuePairs); + + for ($i = 0; $i < $iMax; $i++) { + echo dol_htmlentities($productCombination2ValuePairs[$i]); + + if ($i !== ($iMax - 1)) { + echo ', '; + } + } ?> + variation_price >= 0 ? '+' : '').price($currcomb->variation_price).($currcomb->variation_price_percentage ? ' %' : '') ?>variation_weight >= 0 ? '+' : '').price($currcomb->variation_weight).' '.measuring_units_string($prodstatic->weight_units, 'weight') ?>getLibStatut(2, 0) ?>getLibStatut(2, 1) ?> + + +
+ + +
+ + + '; + print ' '; + print ''; + + } +} + +llxFooter(); diff --git a/htdocs/attributes/create.php b/htdocs/attributes/create.php new file mode 100644 index 00000000000..5b68ed28907 --- /dev/null +++ b/htdocs/attributes/create.php @@ -0,0 +1,73 @@ + + * + * 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 . + */ + +require '../main.inc.php'; +require 'class/ProductAttribute.class.php'; + +$ref = GETPOST('ref'); +$label = GETPOST('label'); + +if ($_POST) { + + if (empty($ref) || empty($label)) { + setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors'); + } else { + + $prodattr = new ProductAttribute($db); + $prodattr->label = $label; + $prodattr->ref = $ref; + + if ($prodattr->create()) { + setEventMessage($langs->trans('RecordSaved')); + header('Location: '.dol_buildpath('/attributes/list.php', 2)); + } else { + setEventMessage($langs->trans('ErrorRecordAlreadyExists'), 'errors'); + } + } +} + +$langs->load('products'); + +$title = $langs->trans('NewProductAttribute'); + +llxHeader('', $title); + +print_fiche_titre($title); + +dol_fiche_head(); + +?> +
+ + + + + + + + + + +
+ +
'; + +llxFooter(); \ No newline at end of file diff --git a/htdocs/attributes/create_val.php b/htdocs/attributes/create_val.php new file mode 100644 index 00000000000..b25e24fa64a --- /dev/null +++ b/htdocs/attributes/create_val.php @@ -0,0 +1,102 @@ + + * + * 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 . + */ + +require '../main.inc.php'; +require 'class/ProductAttribute.class.php'; +require 'class/ProductAttributeValue.class.php'; + +$id = GETPOST('id'); +$ref = GETPOST('ref'); +$value = GETPOST('value'); + +$prodattr = new ProductAttribute($db); +$prodattrval = new ProductAttributeValue($db); + +if ($prodattr->fetch($id) < 1) { + dol_print_error($db, $langs->trans('ErrorRecordNotFound')); + die; +} + +if ($_POST) { + + if (empty($ref) || empty($value)) { + setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors'); + } else { + + $prodattrval->fk_product_attribute = $prodattr->id; + $prodattrval->ref = $ref; + $prodattrval->value = $value; + + if ($prodattrval->create() > 0) { + setEventMessage($langs->trans('RecordSaved')); + header('Location: '.dol_buildpath('/attributes/card.php?id='.$prodattr->id, 2)); + die; + } else { + setEventMessage($langs->trans('ErrorCreatingProductAttributeValue'), 'errors'); + } + } + +} + +$langs->load('products'); + +$title = $langs->trans('ProductAttributeName', dol_htmlentities($prodattr->label)); + +llxHeader('', $title); + +print_fiche_titre($title); + +dol_fiche_head(); + +?> + + + + + + + + +
trans('Ref') ?>ref) ?> +
trans('Label') ?>label) ?>
+ +trans('NewProductAttributeValue')); + +dol_fiche_head(); +?> +
+ + + + + + + + + +
+
'; + +llxFooter(); \ No newline at end of file diff --git a/htdocs/attributes/generator.php b/htdocs/attributes/generator.php new file mode 100644 index 00000000000..37f2175cc49 --- /dev/null +++ b/htdocs/attributes/generator.php @@ -0,0 +1,393 @@ + + * + * 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 . + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttribute.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductAttributeValue.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php'; +require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php'; + +$langs->load("products"); +$langs->load('other'); + +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref'); +$form = new Form($db); + +// Security check +$fieldvalue = (! empty($id) ? $id : $ref); +$fieldtype = (! empty($ref) ? 'ref' : 'rowid'); +$result=restrictedArea($user,'produit|service',$fieldvalue,'product&product','','',$fieldtype); + +$prodattr = new ProductAttribute($db); +$prodattrval = new ProductAttributeValue($db); +$product = new Product($db); + +$product->fetch($id); + +if (!$product->isProduct()) { + header('Location: '.dol_buildpath('/product/card.php?id='.$product->id, 2)); + die; +} + +/** + * Posible combinations. Format. + * attrval => array( + * valueid => array( + * price => '' + * weight => '' + * ) + * ) + */ +$combinations = GETPOST('combinations', 'array'); +$price_var_percent = (bool) GETPOST('price_var_percent'); +$donotremove = true; + +if ($_POST) { + + $donotremove = (bool) GETPOST('donotremove'); + + //We must check if all those given combinations actually exist + $sanitized_values = array(); + + foreach ($combinations as $attr => $val) { + if ($prodattr->fetch($attr) > 0) { + foreach ($val as $valueid => $content) { + if ($prodattrval->fetch($valueid) > 0) { + $sanitized_values[$attr][$valueid] = $content; + } + } + } + } + + if ($sanitized_values) { + + require DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; + + $adapted_values = array(); + + //Adapt the array to the cartesian function + foreach ($sanitized_values as $attr => $val) { + $adapted_values[$attr] = array_keys($val); + } + + $db->begin(); + + $combination = new ProductCombination($db); + + $delete_prev_comb_res = 1; + + if (!$donotremove) { + $delete_prev_comb_res = $combination->deleteByFkProductParent($id); + } + + //Current combinations will be deleted + if ($delete_prev_comb_res > 0) { + + $res = 1; + + foreach (cartesianArray($adapted_values) as $currcomb) { + + $res = ProductCombination::createProductCombination($product, $currcomb, $sanitized_values, $price_var_percent); + + if ($res < 0) { + break; + } + } + + if ($res > 0) { + $db->commit(); + setEventMessage($langs->trans('RecordSaved')); + header('Location: '.dol_buildpath('/attributes/combinations.php?id='.$id, 2)); + die; + } else { + setEventMessage($langs->trans('CoreErrorMessage'), 'errors'); + } + } else { + setEventMessage($langs->trans('ErrorDeletingGeneratedProducts'), 'errors'); + } + + $db->rollback(); + + } else { + setEventMessage($langs->trans('ErrorFieldsRequired'), 'errors'); + } +} + +/* + * View + */ + +if (! empty($id) || ! empty($ref)) { + $object = new Product($db); + $result = $object->fetch($id, $ref); + + llxHeader("", "", $langs->trans("CardProduct".$object->type)); + + if ($result) { + $head = product_prepare_head($object); + $titre = $langs->trans("CardProduct".$object->type); + $picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product'); + + dol_fiche_head($head, 'combinations', $titre, 0, $picto); + + print ''; + + // Reference + print ''; + print ''; + print ''; + + // Label + print ''; + + // Status (to sell) + print ''; + + // Status (to buy) + print ''; + + print '
'.$langs->trans("Ref").''; + print $form->showrefnav($object, 'id', '', 0); + print '
'.$langs->trans("Label").''.$object->label.'
'.$langs->trans("Status").' ('.$langs->trans("Sell").')'; + print $object->getLibStatut(2, 0); + print '
'.$langs->trans("Status").' ('.$langs->trans("Buy").')'; + print $object->getLibStatut(2, 1); + print '
'; + + dol_fiche_end(); + } + + print_fiche_titre($langs->trans('ProductCombinationGenerator')); + + $dictionnary_attr = array(); + + foreach ($prodattr->fetchAll() as $attr) { + $dictionnary_attr[$attr->id] = $attr; + foreach ($prodattrval->fetchAllByProductAttribute($attr->id) as $attrval) { + $dictionnary_attr[$attr->id]->values[$attrval->id] = $attrval; + } + } + ?> + + + +
+ +
+ +
+ +

trans('TooMuchCombinationsWarning', $langs->trans('DoNotRemovePreviousCombinations')) ?>

+ > +
+ > + +
+
+ + trans('ProductCombinationGeneratorWarning') ?> +
+
+ +
+ +
+ +
+ +
+
+ + + +

+ + + +
+ +
+ + + + * + * 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 . + */ + +/** + * Returns the entity of the products. + * @param DoliDB $db Database handler + * @return mixed + */ +function getProductEntities(DoliDB $db) +{ + require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + + $product = new Product($db); + + return getEntity($product->element, 1); +} \ No newline at end of file diff --git a/htdocs/attributes/list.php b/htdocs/attributes/list.php new file mode 100644 index 00000000000..3c4cf945524 --- /dev/null +++ b/htdocs/attributes/list.php @@ -0,0 +1,140 @@ + + * + * 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 . + */ + +require '../main.inc.php'; +require 'class/ProductAttribute.class.php'; + +$rowid = GETPOST('rowid'); +$action = GETPOST('action'); +$object = new ProductAttribute($db); + +if ($action == 'up') { + $object->fetch($rowid); + $object->moveUp(); + + header('Location: '.$_SERVER['PHP_SELF']); + die; +} elseif ($action == 'down') { + $object->fetch($rowid); + $object->moveDown(); + + header('Location: '.$_SERVER['PHP_SELF']); + die; +} + +$langs->load('products'); + +$var = false; +$title = $langs->trans($langs->trans('ProductAttributes')); + +$attributes = $object->fetchAll(); + +llxHeader('', $title); + +print_fiche_titre($title); + +$forcereloadpage=empty($conf->global->MAIN_FORCE_RELOAD_PAGE)?0:1; +?> + + + + + + + + + + $attribute): ?> + > + + + + + + + + +
trans('Ref') ?>trans('Label') ?>trans('NbProducts') ?>
ref) ?>label) ?>countChildProducts() ?> + + + + 0): ?> + + + + + +
+ +
+ + + * Copyright (C) 2013-2014 Florian Henry * Copyright (C) 2014 Ferran Marcet + * Copyright (C) 2016 Marcos García * * 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 @@ -48,6 +49,10 @@ if (! empty($conf->projet->enabled)) { require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php'; } +if (!empty($conf->attributes->enabled)) { + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php'; +} + $langs->load('companies'); $langs->load('propal'); $langs->load('compta'); @@ -655,7 +660,8 @@ if (empty($reshook)) $predef=''; $product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):''); $price_ht = GETPOST('price_ht'); - if (GETPOST('prod_entry_mode') == 'free') + $prod_entry_mode = GETPOST('prod_entry_mode'); + if ($prod_entry_mode == 'free') { $idprod=0; $tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0); @@ -681,21 +687,35 @@ if (empty($reshook)) } } - if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && GETPOST('type') < 0) { + if ($prod_entry_mode == 'free' && empty($idprod) && GETPOST('type') < 0) { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors'); $error ++; } - if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && $price_ht == '') // Unit price can be 0 but not ''. Also price can be negative for proposal. + if ($prod_entry_mode == 'free' && empty($idprod) && $price_ht == '') // Unit price can be 0 but not ''. Also price can be negative for proposal. { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors'); $error ++; } - if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && empty($product_desc)) { + if ($prod_entry_mode == 'free' && empty($idprod) && empty($product_desc)) { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Description")), null, 'errors'); $error ++; } + if (!$error && !empty($conf->attributes->enabled) && $prod_entry_mode != 'free') { + if ($combinations = GETPOST('combinations', 'array')) { + //Check if there is a product with the given combination + $prodcomb = new ProductCombination($db); + + if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) { + $idprod = $res->fk_product_child; + } else { + setEventMessage($langs->trans('ErrorProductCombinationNotFound'), 'errors'); + $error ++; + } + } + } + if (! $error && ($qty >= 0) && (! empty($product_desc) || ! empty($idprod))) { $pu_ht = 0; $pu_ttc = 0; diff --git a/htdocs/commande/card.php b/htdocs/commande/card.php index d91ee35fe3e..20def0175ba 100644 --- a/htdocs/commande/card.php +++ b/htdocs/commande/card.php @@ -7,7 +7,7 @@ * Copyright (C) 2010-2013 Juanjo Menent * Copyright (C) 2011-2016 Philippe Grand * Copyright (C) 2012-2013 Christophe Battarel - * Copyright (C) 2012 Marcos García + * Copyright (C) 2012-2016 Marcos García * Copyright (C) 2012 Cedric Salvador * Copyright (C) 2013 Florian Henry * Copyright (C) 2014 Ferran Marcet @@ -51,6 +51,10 @@ if (! empty($conf->projet->enabled)) { } require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; +if (!empty($conf->attributes->enabled)) { + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php'; +} + $langs->load('orders'); $langs->load('sendings'); $langs->load('companies'); @@ -621,7 +625,8 @@ if (empty($reshook)) $predef=''; $product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):''); $price_ht = GETPOST('price_ht'); - if (GETPOST('prod_entry_mode') == 'free') + $prod_entry_mode = GETPOST('prod_entry_mode'); + if ($prod_entry_mode == 'free') { $idprod=0; $tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0); @@ -651,11 +656,11 @@ if (empty($reshook)) setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors'); $error++; } - if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && GETPOST('type') < 0) { + if ($prod_entry_mode == 'free' && empty($idprod) && GETPOST('type') < 0) { setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors'); $error++; } - if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '')) // Unit price can be 0 but not '' + if ($prod_entry_mode == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '')) // Unit price can be 0 but not '' { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors'); $error++; @@ -664,11 +669,25 @@ if (empty($reshook)) setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors'); $error++; } - if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && empty($product_desc)) { + if ($prod_entry_mode == 'free' && empty($idprod) && empty($product_desc)) { setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors'); $error++; } + if (!$error && !empty($conf->attributes->enabled) && $prod_entry_mode != 'free') { + if ($combinations = GETPOST('combinations', 'array')) { + //Check if there is a product with the given combination + $prodcomb = new ProductCombination($db); + + if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) { + $idprod = $res->fk_product_child; + } else { + setEventMessage($langs->trans('ErrorProductCombinationNotFound'), 'errors'); + $error ++; + } + } + } + if (! $error && ($qty >= 0) && (! empty($product_desc) || ! empty($idprod))) { // Clean parameters $date_start=dol_mktime(GETPOST('date_start'.$predef.'hour'), GETPOST('date_start'.$predef.'min'), GETPOST('date_start'.$predef.'sec'), GETPOST('date_start'.$predef.'month'), GETPOST('date_start'.$predef.'day'), GETPOST('date_start'.$predef.'year')); diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index b2b39e375d7..be38e4b48f1 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -13,7 +13,7 @@ * Copyright (C) 2013-2014 Florian Henry * Copyright (C) 2013 Cédric Salvador * Copyright (C) 2014 Ferran Marcet - * Copyright (C) 2015 Marcos García + * Copyright (C) 2015-2016 Marcos García * * 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 @@ -56,6 +56,10 @@ if (! empty($conf->projet->enabled)) { } require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; +if (!empty($conf->attributes->enabled)) { + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php'; +} + $langs->load('bills'); $langs->load('companies'); $langs->load('compta'); @@ -1322,7 +1326,8 @@ if (empty($reshook)) $predef=''; $product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):''); $price_ht = GETPOST('price_ht'); - if (GETPOST('prod_entry_mode') == 'free') + $prod_entry_mode = GETPOST('prod_entry_mode'); + if ($prod_entry_mode == 'free') { $idprod=0; $tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0); @@ -1352,11 +1357,11 @@ if (empty($reshook)) setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors'); $error ++; } - if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && GETPOST('type') < 0) { + if ($prod_entry_mode == 'free' && empty($idprod) && GETPOST('type') < 0) { setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors'); $error ++; } - if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '')) // Unit price can be 0 but not '' + if ($prod_entry_mode == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '')) // Unit price can be 0 but not '' { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors'); $error ++; @@ -1365,7 +1370,7 @@ if (empty($reshook)) setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors'); $error ++; } - if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && empty($product_desc)) { + if ($prod_entry_mode == 'free' && empty($idprod) && empty($product_desc)) { setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors'); $error ++; } @@ -1374,7 +1379,23 @@ if (empty($reshook)) setEventMessages($langs->trans('ErrorQtyForCustomerInvoiceCantBeNegative'), null, 'errors'); $error ++; } + + if (!$error && !empty($conf->attributes->enabled) && $prod_entry_mode != 'free') { + if ($combinations = GETPOST('combinations', 'array')) { + //Check if there is a product with the given combination + $prodcomb = new ProductCombination($db); + + if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) { + $idprod = $res->fk_product_child; + } else { + setEventMessage($langs->trans('ErrorProductCombinationNotFound'), 'errors'); + $error ++; + } + } + } + if (! $error && ($qty >= 0) && (! empty($product_desc) || ! empty($idprod))) { + $ret = $object->fetch($id); if ($ret < 0) { dol_print_error($db, $object->error); diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php index 5a87d3b3d51..4a20f1b8f9e 100644 --- a/htdocs/contrat/card.php +++ b/htdocs/contrat/card.php @@ -7,7 +7,7 @@ * Copyright (C) 2013 Christophe Battarel * Copyright (C) 2013-2014 Florian Henry * Copyright (C) 2014-2016 Ferran Marcet - * Copyright (C) 2014 Marcos García + * Copyright (C) 2014-2016 Marcos García * Copyright (C) 2015 Jean-François Ferry * * This program is free software; you can redistribute it and/or modify @@ -471,7 +471,7 @@ if (empty($reshook)) unset($_POST["options_" . $key]); } } - + if (! $error) { // Clean parameters diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 61953629166..ba60aa5f2c5 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -12,7 +12,7 @@ * Copyright (C) 2010 Juanjo Menent * Copyright (C) 2010-2014 Philippe Grand * Copyright (C) 2011 Herve Prot - * Copyright (C) 2012-2014 Marcos García + * Copyright (C) 2012-2016 Marcos García * Copyright (C) 2012 Cedric Salvador * Copyright (C) 2012-2015 Raphaël Doursenaud * Copyright (C) 2014 Alexandre Spangaro @@ -1605,9 +1605,10 @@ class Form * @param int $hidelabel Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after) * @param array $ajaxoptions Options for ajax_autocompleter * @param int $socid Thirdparty Id (to get also price dedicated to this customer) + * @param array $selected_combinations Selected combinations. Format: array([attrid] => attrval, [...]) * @return void */ - function select_produits($selected='', $htmlname='productid', $filtertype='', $limit=20, $price_level=0, $status=1, $finished=2, $selected_input_value='', $hidelabel=0, $ajaxoptions=array(), $socid=0) + function select_produits($selected='', $htmlname='productid', $filtertype='', $limit=20, $price_level=0, $status=1, $finished=2, $selected_input_value='', $hidelabel=0, $ajaxoptions=array(), $socid=0, $selected_combinations = array()) { global $langs,$conf; @@ -1632,6 +1633,80 @@ class Form $urloption.='&socid='.$socid; } print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions); + + if (!empty($conf->attributes->enabled)) { + ?> + + trans("RefOrLabel").' : '; else if ($hidelabel > 1) { if (! empty($conf->global->MAIN_HTML5_PLACEHOLDER)) $placeholder=' placeholder="'.$langs->trans("RefOrLabel").'"'; @@ -1709,7 +1784,17 @@ class Form { $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid AND pl.lang='". $langs->getDefaultLang() ."'"; } + + if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) { + $sql .= " LEFT JOIN llx_product_attribute_combination pac ON pac.fk_product_child = p.rowid"; + } + $sql.= ' WHERE p.entity IN ('.getEntity('product', 1).')'; + + if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) { + $sql .= " AND pac.rowid IS NULL"; + } + if ($finished == 0) { $sql.= " AND p.finished = ".$finished; @@ -1881,7 +1966,7 @@ class Form $outlabel=$objp->label; $outdesc=$objp->description; $outbarcode=$objp->barcode; - + $outtype=$objp->fk_product_type; $outdurationvalue=$outtype == Product::TYPE_SERVICE?substr($objp->duration,0,dol_strlen($objp->duration)-1):''; $outdurationunit=$outtype == Product::TYPE_SERVICE?substr($objp->duration,-1):''; @@ -1898,13 +1983,13 @@ class Form $opt.= $objp->ref; if ($outbarcode) $opt.=' ('.$outbarcode.')'; $opt.=' - '.dol_trunc($label,$maxlengtharticle).' - '; - + $objRef = $objp->ref; if (! empty($filterkey) && $filterkey != '') $objRef=preg_replace('/('.preg_quote($filterkey).')/i','$1',$objRef,1); $outval.=$objRef; if ($outbarcode) $outval.=' ('.$outbarcode.')'; $outval.=' - '.dol_trunc($label,$maxlengtharticle).' - '; - + $found=0; // Multiprice @@ -2066,7 +2151,7 @@ class Form { global $langs,$conf; global $price_level, $status, $finished; - + $selected_input_value=''; if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) { @@ -3174,7 +3259,7 @@ class Form { dol_syslog(__METHOD__ . ': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING); } - + $cat = new Categorie($this->db); $cate_arbo = $cat->get_full_arbo($type,$excludeafterid); @@ -3837,7 +3922,7 @@ class Form function form_multicurrency_rate($page, $rate='', $htmlname='multicurrency_tx', $currency='') { global $langs, $mysoc, $conf; - + if ($htmlname != "none") { print '
'; @@ -4107,7 +4192,7 @@ class Form $out.= ''; } } - + } $out.= ''; @@ -4204,7 +4289,7 @@ class Form $defaulttx=preg_replace('/\s*\(.*\)/','',$defaulttx); } //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode); - + // Check parameters if (is_object($societe_vendeuse) && ! $societe_vendeuse->country_code) { @@ -4263,7 +4348,7 @@ class Form // Now we get list $num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error - + if ($num > 0) { // Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '') @@ -4304,7 +4389,7 @@ class Form $return.= '"'; if ($defaultcode) // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag { - if ($defaultcode == $rate['code']) $return.= ' selected'; + if ($defaultcode == $rate['code']) $return.= ' selected'; } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) { @@ -5036,7 +5121,7 @@ class Form /* var_dump($val); var_dump(array_key_exists('enabled', $val)); var_dump(!$val['enabled']);*/ - if (array_key_exists('enabled', $val) && isset($val['enabled']) && ! $val['enabled']) + if (array_key_exists('enabled', $val) && isset($val['enabled']) && ! $val['enabled']) { unset($array[$key]); // We don't want this field continue; @@ -5221,17 +5306,17 @@ class Form else if ($objecttype == 'subscription') { $tplpath = 'adherents'; } - + global $linkedObjectBlock; $linkedObjectBlock = $objects; if (empty($numoutput)) { $numoutput++; - + print '
'; print load_fiche_titre($langs->trans('RelatedObjects'), '', ''); - + print ''; print ''; @@ -5244,7 +5329,7 @@ class Form print ''; print ''; } - + // Output template part (modules that overwrite templates must declare this into descriptor) $dirtpls=array_merge($conf->modules_parts['tpl'],array('/'.$tplpath.'/tpl')); foreach($dirtpls as $reldir) @@ -5254,11 +5339,11 @@ class Form } } - if ($numoutput) + if ($numoutput) { print '
'; } - + return $num; } } @@ -5627,7 +5712,7 @@ class Form $ret.=''; if ($morehtmlright) $ret.='
'.$morehtmlright.'
'; - + if ($previous_ref || $next_ref || $morehtml) { $ret.=''; print '
'; diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index f3f03c65ac6..e87b00ee949 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -839,6 +839,17 @@ class Product extends CommonObject if (! $error) { + if ($conf->attributes->enabled) { + + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php'; + + $comb = new ProductCombination($this->db); + + foreach ($comb->fetchAllByFkProductParent($this->id) as $currcomb) { + $currcomb->updateProperties($this); + } + } + $this->db->commit(); return 1; } @@ -944,6 +955,25 @@ class Product extends CommonObject } } + if (!$error) { + + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php'; + + //If it is a parent product, then we remove the association with child products + $prodcomb = new ProductCombination($this->db); + + if ($prodcomb->deleteByFkProductParent($id) < 0) { + $error++; + $this->errors[] = 'Error deleting combinations'; + } + + //We also check if it is a child product + if (!$error && ($prodcomb->fetchByFkProductChild($id) > 0) && ($prodcomb->delete() < 0)) { + $error++; + $this->errors[] = 'Error deleting child combination'; + } + } + // Delete product if (! $error) { diff --git a/htdocs/product/list.php b/htdocs/product/list.php index c95156b4393..29e2216c154 100644 --- a/htdocs/product/list.php +++ b/htdocs/product/list.php @@ -2,7 +2,7 @@ /* Copyright (C) 2001-2006 Rodolphe Quiedeville * Copyright (C) 2004-2016 Laurent Destailleur * Copyright (C) 2005-2012 Regis Houssin - * Copyright (C) 2012-2013 Marcos García + * Copyright (C) 2012-2016 Marcos García * Copyright (C) 2013-2016 Juanjo Menent * Copyright (C) 2013-2015 Raphaël Doursenaud * Copyright (C) 2013 Jean Heimburger @@ -50,7 +50,7 @@ $sref=GETPOST("sref"); $sbarcode=GETPOST("sbarcode"); $snom=GETPOST("snom"); $sall=GETPOST("sall"); -$type=GETPOST("type","int"); +$type= (int) GETPOST("type","int"); $search_sale = GETPOST("search_sale"); $search_categ = GETPOST("search_categ",'int'); $tosell = GETPOST("tosell", 'int'); @@ -62,6 +62,13 @@ $search_accountancy_code_sell = GETPOST("search_accountancy_code_sell",'alpha'); $search_accountancy_code_buy = GETPOST("search_accountancy_code_buy",'alpha'); $optioncss = GETPOST('optioncss','alpha'); +//Show/hide child products. Hidden by default +if (!$_POST) { + $search_hidechildproducts = 'on'; +} else { + $search_hidechildproducts = GETPOST('search_hidechildproducts'); +} + $limit = GETPOST("limit")?GETPOST("limit","int"):$conf->liste_limit; $sortfield = GETPOST("sortfield",'alpha'); $sortorder = GETPOST("sortorder",'alpha'); @@ -170,7 +177,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab } - + /* * Actions */ @@ -230,6 +237,9 @@ else $sql.= ' p.datec as date_creation, p.tms as date_update,'; //$sql.= ' pfp.ref_fourn as ref_supplier, '; $sql.= ' MIN(pfp.unitprice) as minsellprice'; + if (!empty($conf->attributes->enabled) && $search_hidechildproducts && ($type === 0)) { + $sql .= ', pac.rowid prod_comb_id'; + } // 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 @@ -242,6 +252,10 @@ else $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product"; // multilang if (! empty($conf->global->MAIN_MULTILANGS)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid AND pl.lang = '".$langs->getDefaultLang() ."'"; + if (!empty($conf->attributes->enabled) && $search_hidechildproducts && ($type === 0)) { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac.fk_product_child = p.rowid"; + } + $sql.= ' WHERE p.entity IN ('.getEntity('product', 1).')'; if ($sall) $sql .= natural_search(array_keys($fieldstosearchall), $sall); // if the type is not 1, we show all products (type = 0,2,3) @@ -265,6 +279,12 @@ else if ($search_accountancy_code_sell) $sql.= natural_search('p.accountancy_code_sell', $search_accountancy_code_sell); if ($search_accountancy_code_sell) $sql.= natural_search('p.accountancy_code_buy', $search_accountancy_code_buy); // Add where from extra fields + + if (!empty($conf->attributes->enabled) && $search_hidechildproducts && ($type === 0)) { + $sql .= " AND pac.rowid IS NULL"; + } + + // Add where from extra fields foreach ($search_array_options as $key => $val) { $crit=$val; @@ -284,6 +304,9 @@ else $sql.= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type,"; $sql.= " p.fk_product_type, p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock,"; $sql.= ' p.datec, p.tms, p.entity, p.tobatch, p.accountancy_code_sell, p.accountancy_code_buy'; + if (!empty($conf->attributes->enabled) && $search_hidechildproducts && ($type === 0)) { + $sql .= ', pac.rowid'; + } // Add fields from extrafields foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key : ''); // Add fields from hooks @@ -415,6 +438,15 @@ else $moreforfilter.=$htmlother->select_categories(Categorie::TYPE_PRODUCT,$search_categ,'search_categ',1); $moreforfilter.='
'; } + + //Show/hide child products. Hidden by default + if (!empty($conf->attributes->enabled) && $type === 0) { + $moreforfilter.='
'; + $moreforfilter.= ''; + $moreforfilter.= ' '; + $moreforfilter.='
'; + } + if ($moreforfilter) { print '
'; @@ -614,7 +646,7 @@ else $product_static->status_buy = $objp->tobuy; $product_static->status = $objp->tosell; $product_static->entity = $objp->entity; - + if (! empty($conf->stock->enabled) && $user->rights->stock->lire && $type != 1) // To optimize call of load_stock { if ($objp->fk_product_type != 1) // Not a service @@ -622,8 +654,8 @@ else $product_static->load_stock('nobatch'); // Load stock_reel + stock_warehouse. This also call load_virtual_stock() } } - - + + $var=!$var; print ''; @@ -752,7 +784,7 @@ else print ''; print yn($objp->tobatch); print ''; - } + } // Accountancy code sell if (! empty($arrayfields['p.accountancy_code_sell']['checked'])) print ''.$objp->accountancy_code_sell.''; // Accountancy code sell From 8ceb494569c5d30eecc8f8b13265ab08fa4029c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Garci=CC=81a=20de=20La=20Fuente?= Date: Mon, 25 Jul 2016 10:37:39 +0200 Subject: [PATCH 003/170] Added comments on classes to fix travis errors --- htdocs/attributes/class/ProductAttribute.class.php | 4 ++++ htdocs/attributes/class/ProductAttributeValue.class.php | 4 ++++ htdocs/attributes/class/ProductCombination.class.php | 4 ++++ .../attributes/class/ProductCombination2ValuePair.class.php | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/htdocs/attributes/class/ProductAttribute.class.php b/htdocs/attributes/class/ProductAttribute.class.php index 1e0837fcb97..f563b03cd7e 100644 --- a/htdocs/attributes/class/ProductAttribute.class.php +++ b/htdocs/attributes/class/ProductAttribute.class.php @@ -16,6 +16,10 @@ * along with this program. If not, see . */ +/** + * Class ProductAttribute + * Used to represent a product attribute + */ class ProductAttribute { /** diff --git a/htdocs/attributes/class/ProductAttributeValue.class.php b/htdocs/attributes/class/ProductAttributeValue.class.php index f500fb251c6..41d1818ee63 100644 --- a/htdocs/attributes/class/ProductAttributeValue.class.php +++ b/htdocs/attributes/class/ProductAttributeValue.class.php @@ -16,6 +16,10 @@ * along with this program. If not, see . */ +/** + * Class ProductAttributeValue + * Used to represent a product attribute value + */ class ProductAttributeValue { /** diff --git a/htdocs/attributes/class/ProductCombination.class.php b/htdocs/attributes/class/ProductCombination.class.php index 6722f7f03fc..d5d7b6e81af 100644 --- a/htdocs/attributes/class/ProductCombination.class.php +++ b/htdocs/attributes/class/ProductCombination.class.php @@ -16,6 +16,10 @@ * along with this program. If not, see . */ +/** + * Class ProductCombination + * Used to represent a product combination + */ class ProductCombination { /** diff --git a/htdocs/attributes/class/ProductCombination2ValuePair.class.php b/htdocs/attributes/class/ProductCombination2ValuePair.class.php index 93521b86bbf..01bd0952d2d 100644 --- a/htdocs/attributes/class/ProductCombination2ValuePair.class.php +++ b/htdocs/attributes/class/ProductCombination2ValuePair.class.php @@ -16,6 +16,10 @@ * along with this program. If not, see . */ +/** + * Class ProductCombination2ValuePair + * Used to represent the relation between a product combination, a product attribute and a product attribute value + */ class ProductCombination2ValuePair { /** From 1c15cfa7229ee79e47405c670cbab111bb770c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Garci=CC=81a=20de=20La=20Fuente?= Date: Sun, 6 Nov 2016 15:17:41 +0100 Subject: [PATCH 004/170] Adapted code to new changes --- htdocs/core/tpl/objectline_create.tpl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/tpl/objectline_create.tpl.php b/htdocs/core/tpl/objectline_create.tpl.php index 4f0c1e4f479..5a82d1e8c26 100644 --- a/htdocs/core/tpl/objectline_create.tpl.php +++ b/htdocs/core/tpl/objectline_create.tpl.php @@ -185,7 +185,7 @@ else { if (empty($senderissupplier)) { - $form->select_produits(GETPOST('idprod'), 'idprod', $filtertype, $conf->product->limit_size, $buyer->price_level, 1, 2, '', 1, array(),$buyer->id, GETPOST('combinations', 'array')); + $form->select_produits(GETPOST('idprod'), 'idprod', $filtertype, $conf->product->limit_size, $buyer->price_level, 1, 2, '', 1, array(), $buyer->id, 1, 0, '', 0, GETPOST('combinations', 'array')); } else { From 480b08bd0bd46519d4ca2cfaaf8cf0df4580d252 Mon Sep 17 00:00:00 2001 From: gauthier Date: Thu, 24 Nov 2016 15:48:04 +0100 Subject: [PATCH 005/170] NEW : convert exceiss received to reduc --- htdocs/comm/remx.php | 9 +++++ htdocs/compta/facture.php | 66 ++++++++++++++++++++++++++++------- htdocs/langs/en_US/bills.lang | 4 +-- 3 files changed, 64 insertions(+), 15 deletions(-) diff --git a/htdocs/comm/remx.php b/htdocs/comm/remx.php index e358b842d2d..372ee529b8b 100644 --- a/htdocs/comm/remx.php +++ b/htdocs/comm/remx.php @@ -369,6 +369,15 @@ if ($socid > 0) print preg_replace('/\(DEPOSIT\)/',$langs->trans("InvoiceDeposit"),$obj->description).' '.$facturestatic->getNomURl(1); print ''; } + elseif (preg_match('/\(EXCESS RECEIVED\)/',$obj->description)) + { + print ''; + $facturestatic->id=$obj->fk_facture_source; + $facturestatic->ref=$obj->ref; + $facturestatic->type=$obj->type; + print preg_replace('/\(EXCESS RECEIVED\)/',$langs->trans("Invoice"),$obj->description).' '.$facturestatic->getNomURl(1); + print ''; + } else { print ''; diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index 34ed1383e14..8cafa015d92 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -579,7 +579,7 @@ if (empty($reshook)) $canconvert=0; if ($object->type == Facture::TYPE_DEPOSIT && $object->paye == 1 && empty($discountcheck->id)) $canconvert=1; // we can convert deposit into discount if deposit is payed completely and not already converted (see real condition into condition used to show button converttoreduc) - if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->paye == 0 && empty($discountcheck->id)) $canconvert=1; // we can convert credit note into discount if credit note is not payed back and not already converted and amount of payment is 0 (see real condition into condition used to show button converttoreduc) + if (($object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_STANDARD) && $object->paye == 0 && empty($discountcheck->id)) $canconvert=1; // we can convert credit note into discount if credit note is not payed back and not already converted and amount of payment is 0 (see real condition into condition used to show button converttoreduc) if ($canconvert) { $db->begin(); @@ -596,13 +596,15 @@ if (empty($reshook)) $i ++; } } - +//var_dump($amount_ht, $amount_tva, $amount_ttc);exit; // Insert one discount by VAT rate category $discount = new DiscountAbsolute($db); if ($object->type == Facture::TYPE_CREDIT_NOTE) $discount->description = '(CREDIT_NOTE)'; elseif ($object->type == Facture::TYPE_DEPOSIT) $discount->description = '(DEPOSIT)'; + elseif ($object->type == Facture::TYPE_STANDARD) + $discount->description = '(EXCESS RECEIVED)'; else { setEventMessages($langs->trans('CantConvertToReducAnInvoiceOfThisType'), null, 'errors'); } @@ -611,13 +613,22 @@ if (empty($reshook)) $discount->fk_facture_source = $object->id; $error = 0; - - foreach ($amount_ht as $tva_tx => $xxx) - { - $discount->amount_ht = abs($amount_ht[$tva_tx]); - $discount->amount_tva = abs($amount_tva[$tva_tx]); - $discount->amount_ttc = abs($amount_ttc[$tva_tx]); - $discount->tva_tx = abs($tva_tx); + + if ($object->type == Facture::TYPE_STANDARD) { + + // If we're on a standard invoice, we have to get excess received to create it in TTC wuthout VAT + + $sql = 'SELECT SUM(pf.amount) as total_paiements + FROM llx_c_paiement as c, llx_paiement_facture as pf, llx_paiement as p + WHERE pf.fk_facture = 10 AND p.fk_paiement = c.id AND pf.fk_paiement = p.rowid ORDER BY p.datep, p.tms'; + + $resql = $db->query($sql); + $res = $db->fetch_object($resql); + $total_paiements = $res->total_paiements; + + $discount->amount_ht = $discount->amount_ttc = $total_paiements - $object->total_ttc; + $discount->amount_tva = 0; + $discount->tva_tx = 0; $result = $discount->create($user); if ($result < 0) @@ -625,8 +636,25 @@ if (empty($reshook)) $error++; break; } + + } else { + + foreach ($amount_ht as $tva_tx => $xxx) + { + $discount->amount_ht = abs($amount_ht[$tva_tx]); + $discount->amount_tva = abs($amount_tva[$tva_tx]); + $discount->amount_ttc = abs($amount_ttc[$tva_tx]); + $discount->tva_tx = abs($tva_tx); + + $result = $discount->create($user); + if ($result < 0) + { + $error++; + break; + } + } + } - if (empty($error)) { // Classe facture @@ -2587,7 +2615,10 @@ else if ($id > 0 || ! empty($ref)) // Confirmation de la conversion de l'avoir en reduc if ($action == 'converttoreduc') { - $text = $langs->trans('ConfirmConvertToReduc'); + if($object->type == 0) $type_fac = 'ExcessReceived'; + elseif($object->type == 2) $type_fac = 'CreditNote'; + elseif($object->type == 3) $type_fac = 'Deposit'; + $text = $langs->trans('ConfirmConvertToReduc', strtolower($langs->transnoentities($type_fac))); $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?facid=' . $object->id, $langs->trans('ConvertToReduc'), $text, 'confirm_converttoreduc', '', "yes", 2); } @@ -2829,7 +2860,10 @@ else if ($id > 0 || ! empty($ref)) $discount = new DiscountAbsolute($db); $result = $discount->fetch(0, $object->id); if ($result > 0) { - $morehtmlref = ' (' . $langs->trans("CreditNoteConvertedIntoDiscount", $discount->getNomUrl(1, 'discount')) . ')'; + if($object->type == 0) $type_fac = 'ExcessReceived'; + elseif($object->type == 2) $type_fac = 'CreditNote'; + elseif($object->type == 3) $type_fac = 'Deposit'; + $morehtmlref = ' (' . $langs->trans("CreditNoteConvertedIntoDiscount", strtolower($langs->transnoentities($type_fac)), $discount->getNomUrl(1, 'discount')) . ')'; } if ($result < 0) { dol_print_error('', $discount->error); @@ -3205,6 +3239,7 @@ else if ($id > 0 || ! empty($ref)) print $langs->trans('RemainderToPay'); else print $langs->trans('ExcessReceived'); + print ' :'; print '' . price($resteapayeraffiche) . ''; print ' '; @@ -3688,7 +3723,7 @@ else if ($id > 0 || ! empty($ref)) } // Reverse back money or convert to reduction - if ($object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_DEPOSIT) { + if ($object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_DEPOSIT || $object->type == Facture::TYPE_STANDARD) { // For credit note only if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->statut == 1 && $object->paye == 0 && $user->rights->facture->paiement) { @@ -3702,6 +3737,11 @@ else if ($id > 0 || ! empty($ref)) } } + // For standard invoice with excess received + if ($object->type == Facture::TYPE_STANDARD && empty($object->paye) && ($object->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits) < 0 && $user->rights->facture->creer && empty($discount->id)) + { + print ''; + } // For credit note if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->statut == 1 && $object->paye == 0 && $user->rights->facture->creer && $object->getSommePaiement() == 0) { print ''; diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index bfbe32b803e..fb27b364dcf 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -61,7 +61,7 @@ PaymentsBack=Payments back PaidBack=Paid back DeletePayment=Delete payment ConfirmDeletePayment=Are you sure you want to delete this payment ? -ConfirmConvertToReduc=Do you want to convert this credit note or deposit into an absolute discount ?
The amount will so be saved among all discounts and could be used as a discount for a current or a future invoice for this customer. +ConfirmConvertToReduc=Do you want to convert this %s into an absolute discount ?
The amount will so be saved among all discounts and could be used as a discount for a current or a future invoice for this customer. SupplierPayments=Suppliers payments ReceivedPayments=Received payments ReceivedCustomersPayments=Payments received from customers @@ -386,7 +386,7 @@ ChequeDeposits=Checks deposits Cheques=Checks DepositId=Id deposit NbCheque=Number of checks -CreditNoteConvertedIntoDiscount=This credit note or deposit invoice has been converted into %s +CreditNoteConvertedIntoDiscount=This %s has been converted into %s UsBillingContactAsIncoiveRecipientIfExist=Use customer billing contact address instead of third party address as recipient for invoices ShowUnpaidAll=Show all unpaid invoices ShowUnpaidLateOnly=Show late unpaid invoices only From c433829d27420b7a51733c50109de522c29b6704 Mon Sep 17 00:00:00 2001 From: gauthier Date: Fri, 25 Nov 2016 11:04:42 +0100 Subject: [PATCH 006/170] NEW : interface --- htdocs/compta/facture.php | 6 +++--- htdocs/core/class/commonobject.class.php | 6 ++++++ htdocs/core/class/html.form.class.php | 3 ++- htdocs/core/lib/doc.lib.php | 6 ++++++ htdocs/core/lib/pdf.lib.php | 6 ++++++ htdocs/core/tpl/objectline_view.tpl.php | 7 +++++++ htdocs/langs/en_US/bills.lang | 2 ++ htdocs/societe/consumption.php | 7 +++++++ 8 files changed, 39 insertions(+), 4 deletions(-) diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index 8cafa015d92..8ef7f39e0b8 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -2591,8 +2591,8 @@ else if ($id > 0 || ! empty($ref)) $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice } else { - $filterabsolutediscount = "fk_facture_source IS NULL OR (fk_facture_source IS NOT NULL AND description LIKE '(DEPOSIT)%')"; - $filtercreditnote = "fk_facture_source IS NOT NULL AND description NOT LIKE '(DEPOSIT)%'"; + $filterabsolutediscount = "fk_facture_source IS NULL OR (fk_facture_source IS NOT NULL AND (description LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%'))"; + $filtercreditnote = "fk_facture_source IS NOT NULL AND description NOT LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%'"; } $absolute_discount = $soc->getAvailableDiscounts('', $filterabsolutediscount); @@ -3740,7 +3740,7 @@ else if ($id > 0 || ! empty($ref)) // For standard invoice with excess received if ($object->type == Facture::TYPE_STANDARD && empty($object->paye) && ($object->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits) < 0 && $user->rights->facture->creer && empty($discount->id)) { - print ''; + print ''; } // For credit note if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->statut == 1 && $object->paye == 0 && $user->rights->facture->creer && $object->getSommePaiement() == 0) { diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 3279d372a0e..eaf8166e141 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -3411,6 +3411,12 @@ abstract class CommonObject $discount->fetch($line->fk_remise_except); $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit",$discount->getNomUrl(0)); } + elseif ($line->desc == '(EXCESS RECEIVED)') + { + $discount=new DiscountAbsolute($this->db); + $discount->fetch($line->fk_remise_except); + $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0)); + } else { $this->tpl['description'] = dol_trunc($line->desc,60); diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index c4273a779a9..fcd60169470 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -1130,6 +1130,7 @@ class Form $desc=dol_trunc($obj->description,40); if (preg_match('/\(CREDIT_NOTE\)/', $desc)) $desc=preg_replace('/\(CREDIT_NOTE\)/', $langs->trans("CreditNote"), $desc); if (preg_match('/\(DEPOSIT\)/', $desc)) $desc=preg_replace('/\(DEPOSIT\)/', $langs->trans("Deposit"), $desc); + if (preg_match('/\(EXCESS RECEIVED\)/', $desc)) $desc=preg_replace('/\(EXCESS RECEIVED\)/', $langs->trans("ExcessReceived"), $desc); $selectstring=''; if ($selected > 0 && $selected == $obj->rowid) $selectstring=' selected'; @@ -3762,7 +3763,7 @@ class Form } else { - if (! $filter || $filter=="fk_facture_source IS NULL OR (fk_facture_source IS NOT NULL AND description LIKE '(DEPOSIT)%')") print $langs->trans("CompanyHasAbsoluteDiscount",price($amount,0,$langs,0,0,-1,$conf->currency)).': '; + if (! $filter || $filter=="fk_facture_source IS NULL OR (fk_facture_source IS NOT NULL AND (description LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%'))") print $langs->trans("CompanyHasAbsoluteDiscount",price($amount,0,$langs,0,0,-1,$conf->currency)).': '; else print $langs->trans("CompanyHasCreditNote",price($amount,0,$langs,0,0,-1,$conf->currency)).': '; } $newfilter='fk_facture IS NULL AND fk_facture_line IS NULL'; // Remises disponibles diff --git a/htdocs/core/lib/doc.lib.php b/htdocs/core/lib/doc.lib.php index 76337ec2763..99e576ad057 100644 --- a/htdocs/core/lib/doc.lib.php +++ b/htdocs/core/lib/doc.lib.php @@ -83,6 +83,12 @@ function doc_getlinedesc($line,$outputlangs,$hideref=0,$hidedesc=0,$issupplierli // Add date of deposit if (! empty($conf->global->INVOICE_ADD_DEPOSIT_DATE)) $libelleproduitservice.=' ('.dol_print_date($discount->datec,'day','',$outputlangs).')'; } + elseif ($desc == '(EXCESS RECEIVED)' && $line->fk_remise_except) + { + $discount=new DiscountAbsolute($db); + $discount->fetch($line->fk_remise_except); + $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromExcessReceived",$discount->ref_facture_source); + } else { if ($idprod) diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php index 8b21fcdefe9..80f0727bdf4 100644 --- a/htdocs/core/lib/pdf.lib.php +++ b/htdocs/core/lib/pdf.lib.php @@ -1149,6 +1149,12 @@ function pdf_getlinedesc($object,$i,$outputlangs,$hideref=0,$hidedesc=0,$issuppl // Add date of deposit if (! empty($conf->global->INVOICE_ADD_DEPOSIT_DATE)) echo ' ('.dol_print_date($discount->datec,'day','',$outputlangs).')'; } + if ($desc == '(EXCESS RECEIVED)' && $object->lines[$i]->fk_remise_except) + { + $discount=new DiscountAbsolute($db); + $discount->fetch($object->lines[$i]->fk_remise_except); + $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromExcessReceived",$discount->ref_facture_source); + } else { if ($idprod) diff --git a/htdocs/core/tpl/objectline_view.tpl.php b/htdocs/core/tpl/objectline_view.tpl.php index 7162ac66cb9..30bf2eeb05c 100644 --- a/htdocs/core/tpl/objectline_view.tpl.php +++ b/htdocs/core/tpl/objectline_view.tpl.php @@ -61,6 +61,7 @@ if (empty($usemargins)) $usemargins=0; $txt=''; print img_object($langs->trans("ShowReduc"),'reduc').' '; if ($line->description == '(DEPOSIT)') $txt=$langs->trans("Deposit"); + elseif ($line->description == '(EXCESS RECEIVED)') $txt=$langs->trans("ExcessReceived"); //else $txt=$langs->trans("Discount"); print $txt; ?> @@ -82,6 +83,12 @@ if (empty($usemargins)) $usemargins=0; // Add date of deposit if (! empty($conf->global->INVOICE_ADD_DEPOSIT_DATE)) echo ' ('.dol_print_date($discount->datec).')'; } + elseif ($line->description == '(EXCESS RECEIVED)' && $objp->fk_remise_except > 0) + { + $discount=new DiscountAbsolute($this->db); + $discount->fetch($line->fk_remise_except); + echo ($txt?' - ':'').$langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0)); + } else { echo ($txt?' - ':'').dol_htmlentitiesbr($line->description); diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index fb27b364dcf..5a3ab004308 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -99,6 +99,7 @@ SendRemindByMail=Send reminder by EMail DoPayment=Do payment DoPaymentBack=Do payment back ConvertToReduc=Convert into future discount +ConvertExcessReceivedToReduc=Convert excess received into future discount EnterPaymentReceivedFromCustomer=Enter payment received from customer EnterPaymentDueToCustomer=Make payment due to customer DisabledBecauseRemainderToPayIsZero=Disabled because remaining unpaid is zero @@ -255,6 +256,7 @@ Deposit=Deposit Deposits=Deposits DiscountFromCreditNote=Discount from credit note %s DiscountFromDeposit=Payments from deposit invoice %s +DiscountFromExcessReceived=Payments from excess received of invoice %s AbsoluteDiscountUse=This kind of credit can be used on invoice before its validation CreditNoteDepositUse=Invoice must be validated to use this king of credits NewGlobalDiscount=New absolute discount diff --git a/htdocs/societe/consumption.php b/htdocs/societe/consumption.php index ea2cb899893..4bfbe99528a 100644 --- a/htdocs/societe/consumption.php +++ b/htdocs/societe/consumption.php @@ -461,6 +461,7 @@ if ($sql_select) $txt=''; print img_object($langs->trans("ShowReduc"),'reduc').' '; if ($objp->description == '(DEPOSIT)') $txt=$langs->trans("Deposit"); + elseif ($objp->description == '(EXCESS RECEIVED)') $txt=$langs->trans("ExcessReceived"); //else $txt=$langs->trans("Discount"); print $txt; ?> @@ -474,6 +475,12 @@ if ($sql_select) $discount->fetch($objp->fk_remise_except); echo ($txt?' - ':'').$langs->transnoentities("DiscountFromCreditNote",$discount->getNomUrl(0)); } + if ($objp->description == '(EXCESS RECEIVED)' && $objp->fk_remise_except > 0) + { + $discount=new DiscountAbsolute($db); + $discount->fetch($objp->fk_remise_except); + echo ($txt?' - ':'').$langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0)); + } elseif ($objp->description == '(DEPOSIT)' && $objp->fk_remise_except > 0) { $discount=new DiscountAbsolute($db); From 792e12e6c50ae08db3421bce440ac94ac7146704 Mon Sep 17 00:00:00 2001 From: gauthier Date: Fri, 25 Nov 2016 11:09:15 +0100 Subject: [PATCH 007/170] FIX : interface --- htdocs/comm/remx.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/htdocs/comm/remx.php b/htdocs/comm/remx.php index 372ee529b8b..716018dbcca 100644 --- a/htdocs/comm/remx.php +++ b/htdocs/comm/remx.php @@ -537,6 +537,15 @@ if ($socid > 0) print preg_replace('/\(DEPOSIT\)/',$langs->trans("InvoiceDeposit"),$obj->description).' '.$facturestatic->getNomURl(1); print ''; } + elseif (preg_match('/\(EXCESS RECEIVED\)/',$obj->description)) + { + print ''; + $facturestatic->id=$obj->fk_facture_source; + $facturestatic->ref=$obj->ref; + $facturestatic->type=$obj->type; + print preg_replace('/\(EXCESS RECEIVED\)/',$langs->trans("Invoice"),$obj->description).' '.$facturestatic->getNomURl(1); + print ''; + } else { print ''; From 91be05932e4ef150e2d525c598fb503698c019fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Garci=CC=81a=20de=20La=20Fuente?= Date: Sun, 27 Nov 2016 21:50:27 +0100 Subject: [PATCH 008/170] Added missing modAttributes file --- htdocs/core/modules/modAttributes.class.php | 137 ++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 htdocs/core/modules/modAttributes.class.php diff --git a/htdocs/core/modules/modAttributes.class.php b/htdocs/core/modules/modAttributes.class.php new file mode 100644 index 00000000000..a21ae756bf2 --- /dev/null +++ b/htdocs/core/modules/modAttributes.class.php @@ -0,0 +1,137 @@ + + * Copyright (C) 2004-2012 Laurent Destailleur + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2016 Marcos García + * + * 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 produit Module product attributes + * \brief Module to manage product combinations based on product attributes + * \file htdocs/core/modules/modAttributes.class.php + * \ingroup produit + * \brief File to describe module to manage catalog of predefined products + */ +include_once DOL_DOCUMENT_ROOT .'/core/modules/DolibarrModules.class.php'; + + +/** + * Description and activation class for module Product attributes + */ +class modAttributes 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 = 610; + // Key text used to identify module (for permissions, menus, etc...) + $this->rights_class = 'attributes'; + + // Family can be 'crm','financial','hr','projects','products','ecm','technic','other' + // It is used to group modules in module setup page + $this->family = "products"; + // 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 = 'Allows creating product combinations based on product attributes'; + // Possible values for version are: 'development', 'experimental', 'dolibarr' or version + $this->version = 'dolibarr'; + // Key used in llx_const table to save module status enabled/disabled (where MYMODULE is value of property name of module in uppercase) + $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name); + // Where to store the module in setup page (0=common,1=interface,2=others,3=very specific) + $this->special = 0; + // 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='product'; + + // Defined all module parts (triggers, login, substitutions, menus, css, etc...) + $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( + 'admin.php@attributes' + ); + + // Dependencies + $this->hidden = false; // A condition to hide module + $this->depends = array( + 'modProduct' + ); // List of modules id that must be enabled if this module is enabled + $this->requiredby = array(); // List of modules id to disable if this one is disabled + $this->conflictwith = array(); // List of modules id this module is in conflict with + $this->phpmin = array(5,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("products"); + + // Constants + $this->const = array(); + + // Array to add new pages in new tabs + $this->tabs = array( +// 'product:+combinations:Combinaciones:products:1:/attributes/combinations.php?id=__ID__' + ); + + // Dictionaries + if (! isset($conf->mymodule->enabled)) + { + $conf->mymodule=new stdClass(); + $conf->mymodule->enabled=0; + } + $this->dictionaries=array(); + + // 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 + + // Permissions + $this->rights = array(); // Permission array used by this module + + // Main menu entries + $this->menu = array( + array( + 'fk_menu' => 'fk_mainmenu=products,fk_leftmenu=product', + 'type' => 'left', + 'titre' => $langs->trans('Attributes'), + 'mainmenu' => 'products', + 'leftmenu' => 'product', + 'url' => '/attributes/list.php', + 'langs' => 'products', + 'position' => 100, + 'enabled' => '$conf->product->enabled', + 'perms' => 1, + 'target' => '', + 'user' => 0 + ) + ); // List of menus to add + } +} + From 3f6f1ac86fb91b18995becbd58850c9759afac81 Mon Sep 17 00:00:00 2001 From: gauthier Date: Mon, 28 Nov 2016 17:12:17 +0100 Subject: [PATCH 009/170] fix : where fk_facture = object id --- htdocs/compta/facture.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index 8ef7f39e0b8..bc36f16e9e4 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -620,7 +620,7 @@ if (empty($reshook)) $sql = 'SELECT SUM(pf.amount) as total_paiements FROM llx_c_paiement as c, llx_paiement_facture as pf, llx_paiement as p - WHERE pf.fk_facture = 10 AND p.fk_paiement = c.id AND pf.fk_paiement = p.rowid ORDER BY p.datep, p.tms'; + WHERE pf.fk_facture = '.$object->id.' AND p.fk_paiement = c.id AND pf.fk_paiement = p.rowid ORDER BY p.datep, p.tms'; $resql = $db->query($sql); $res = $db->fetch_object($resql); From d8aa572ad79fab306964a1a7e5e615368225cd84 Mon Sep 17 00:00:00 2001 From: gauthier Date: Mon, 28 Nov 2016 17:38:20 +0100 Subject: [PATCH 010/170] FIX : fetch discount desappeared in develop... --- htdocs/compta/facture.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index 7e1baf5395f..a846fb15e00 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -3870,7 +3870,10 @@ else if ($id > 0 || ! empty($ref)) } } } - + + $discount = new DiscountAbsolute($db); + $result = $discount->fetch(0, $object->id); + // Reopen a standard paid invoice if ((($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT) || ($object->type == Facture::TYPE_CREDIT_NOTE && empty($discount->id)) From ef721bebc60933940c2ef0f00fa3dd398518e55f Mon Sep 17 00:00:00 2001 From: gauthier Date: Fri, 9 Dec 2016 17:48:03 +0100 Subject: [PATCH 011/170] FIX : break with no iteration --- htdocs/compta/facture.php | 1 - 1 file changed, 1 deletion(-) diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index a846fb15e00..b5008801645 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -657,7 +657,6 @@ if (empty($reshook)) if ($result < 0) { $error++; - break; } } else { From ac92e23b59f8bb18949af15596c24f43e4e7a1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Doursenaud?= Date: Thu, 22 Dec 2016 17:09:25 +0100 Subject: [PATCH 012/170] [Qual] Optimize language file loading Using fscanf() and the proper format is about twice as fast as using fgets() followed by explode() and trim(). This is a small optimization per se but since this code is called often it yields about 33% rendering time improvement on the homepage where about 50% of the time is spent on loading the translation files. --- htdocs/core/class/translate.class.php | 45 ++++++++++++++------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/htdocs/core/class/translate.class.php b/htdocs/core/class/translate.class.php index 6b596dc3aa5..0b1ed111eb7 100644 --- a/htdocs/core/class/translate.class.php +++ b/htdocs/core/class/translate.class.php @@ -257,32 +257,33 @@ class Translate { if ($usecachekey) $tabtranslatedomain=array(); // To save lang content in cache - while ($line = fgets($fp,4096)) // Ex: Need 225ms for all fgets on all lang file for Third party page. Same speed than file_get_contents - { - if ($line[0] != "\n" && $line[0] != " " && $line[0] != "#") - { - $tab=explode('=',$line,2); - $key=trim($tab[0]); + /** + * Read each lines until a '=' (with any combination of spaces around it) + * and split the rest until a line feed. + * This is more efficient than fgets + explode + trim by a factor of ~2. + */ + while ($line = fscanf($fp, "%[^= ]%*[ =]%[^\n]")) { + if (isset($line[1])) { + list($key, $value) = $line; //if ($domain == 'orders') print "Domain=$domain, found a string for $tab[0] with value $tab[1]. Currently in cache ".$this->tab_translate[$key]."
"; //if ($key == 'Order') print "Domain=$domain, found a string for key=$key=$tab[0] with value $tab[1]. Currently in cache ".$this->tab_translate[$key]."
"; - if (empty($this->tab_translate[$key]) && isset($tab[1])) // If translation was already found, we must not continue, even if MAIN_FORCELANGDIR is set (MAIN_FORCELANGDIR is to replace lang dir, not to overwrite entries) - { - $value=trim(preg_replace('/\\n/',"\n",$tab[1])); - - if ($key == 'DIRECTION') // This is to declare direction of language - { - if ($alt < 2 || empty($this->tab_translate[$key])) // We load direction only for primary files or if not yet loaded - { - $this->tab_translate[$key]=$value; - if ($stopafterdirection) break; // We do not save tab if we stop after DIRECTION - else if ($usecachekey) $tabtranslatedomain[$key]=$value; + if (empty($this->tab_translate[$key])) { // If translation was already found, we must not continue, even if MAIN_FORCELANGDIR is set (MAIN_FORCELANGDIR is to replace lang dir, not to overwrite entries) + $value = preg_replace('/\\n/', "\n", $value); // Parse and render carriage returns + if ($key == 'DIRECTION') { // This is to declare direction of language + if ($alt < 2 || empty($this->tab_translate[$key])) { // We load direction only for primary files or if not yet loaded + $this->tab_translate[$key] = $value; + if ($stopafterdirection) { + break; // We do not save tab if we stop after DIRECTION + } elseif ($usecachekey) { + $tabtranslatedomain[$key] = $value; + } } - } - else - { - $this->tab_translate[$key]=$value; + } else { + $this->tab_translate[$key] = $value; //if ($domain == 'orders') print "$tab[0] value $value
"; - if ($usecachekey) $tabtranslatedomain[$key]=$value; // To save lang content in cache + if ($usecachekey) { + $tabtranslatedomain[$key] = $value; + } // To save lang content in cache } } } From 337897e0a131b21e6bbb36afbb9842f3a646036f Mon Sep 17 00:00:00 2001 From: florian HENRY Date: Fri, 23 Dec 2016 15:01:49 +0100 Subject: [PATCH 013/170] NEW : Allow extrafields list select to be dependands on other standard list and not only other extrafields list --- htdocs/core/class/commonobject.class.php | 36 ++++++++++++------------ htdocs/langs/en_US/admin.lang | 6 ++-- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 5e83f59ea36..7e59685b4b3 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -597,11 +597,11 @@ abstract class CommonObject dol_syslog("CODE_NOT_VALID_FOR_THIS_ELEMENT"); return -3; } - + $datecreate = dol_now(); $this->db->begin(); - + // Insertion dans la base $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact"; $sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) "; @@ -1230,19 +1230,19 @@ abstract class CommonObject function setValueFrom($field, $value, $table='', $id=null, $format='', $id_field='', $user='', $trigkey='') { global $user,$langs,$conf; - + if (empty($table)) $table=$this->table_element; if (empty($id)) $id=$this->id; if (empty($format)) $format='text'; if (empty($id_field)) $id_field='rowid'; $error=0; - + $this->db->begin(); // Special case if ($table == 'product' && $field == 'note_private') $field='note'; - + $sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET "; if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'"; else if ($format == 'int') $sql.= $field." = ".$this->db->escape($value); @@ -1517,7 +1517,7 @@ abstract class CommonObject if($mode == 1) { $line->subprice = 0; } - + switch ($this->element) { case 'propal': $this->updateline($line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice); @@ -2153,7 +2153,7 @@ abstract class CommonObject // Special cas //var_dump($this->table_element);exit; if ($this->table_element == 'product') $suffix=''; - + $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; $sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL"); $sql.= " WHERE rowid =". $this->id; @@ -2163,7 +2163,7 @@ abstract class CommonObject { if ($suffix == '_public') $this->note_public = $note; else if ($suffix == '_private') $this->note_private = $note; - else + else { $this->note = $note; // deprecated $this->note_private = $note; @@ -2412,7 +2412,7 @@ abstract class CommonObject // Special case if ($origin == 'order') $origin='commande'; if ($origin == 'invoice') $origin='facture'; - + $this->db->begin(); $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element ("; @@ -2899,7 +2899,7 @@ abstract class CommonObject function isObjectUsed($id=0) { if (empty($id)) $id=$this->id; - + // Check parameters if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0) { @@ -3341,9 +3341,9 @@ abstract class CommonObject if ($conf->global->MARGIN_TYPE == "1") print ''.$langs->trans('BuyingPrice').''; else - print ''.$langs->trans('CostPrice').''; + print ''.$langs->trans('CostPrice').''; } - + if (! empty($conf->global->DISPLAY_MARGIN_RATES) && $user->rights->margins->liretous) print ''.$langs->trans('MarginRate').''; if (! empty($conf->global->DISPLAY_MARK_RATES) && $user->rights->margins->liretous) @@ -3875,7 +3875,7 @@ abstract class CommonObject if ($filefound) { global $db; // Required to solve a conception default in commonstickergenerator.class.php making an include of code using $db - + require_once $file; $obj = new $classname($this->db); @@ -3934,7 +3934,7 @@ abstract class CommonObject // output format that does not support UTF8. $sav_charset_output=$outputlangs->charset_output; - if (in_array(get_class($this), array('Adherent'))) + if (in_array(get_class($this), array('Adherent'))) { $arrayofrecords = array(); // The write_file of templates of adherent class need this $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams); @@ -4195,7 +4195,7 @@ abstract class CommonObject require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; $extrafields = new ExtraFields($this->db); $target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element); - + //Eliminate copied source object extra_fields that do not exist in target object $new_array_options=array(); foreach ($this->array_options as $key => $value) { @@ -4244,7 +4244,7 @@ abstract class CommonObject { if (is_numeric($value)) $res=$object->fetch($value); else $res=$object->fetch('',$value); - + if ($res > 0) $this->array_options[$key]=$object->id; else { @@ -4503,7 +4503,7 @@ abstract class CommonObject jQuery(document).ready(function() { function showOptions(child_list, parent_list) { - var val = $("select[name=\"options_"+parent_list+"\"]").val(); + var val = $("select[name="+parent_list+"]").val(); var parentVal = parent_list + ":" + val; if(val > 0) { $("select[name=\""+child_list+"\"] option[parent]").hide(); @@ -4518,7 +4518,7 @@ abstract class CommonObject var parent = $(this).find("option[parent]:first").attr("parent"); var infos = parent.split(":"); var parent_list = infos[0]; - $("select[name=\"options_"+parent_list+"\"]").change(function() { + $("select[name="+parent_list+"]").change(function() { showOptions(child_list, parent_list); }); }); diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 82ff69fe88a..44f10c5cf36 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -373,11 +373,11 @@ ExtrafieldCheckBox=Checkbox ExtrafieldRadio=Radio button ExtrafieldCheckBoxFromList= Checkbox from table ExtrafieldLink=Link to an object -ExtrafieldParamHelpselect=Parameters list have to be like key,value

for example :
1,value1
2,value2
3,value3
...

In order to have the list depending on another :
1,value1|parent_list_code:parent_key
2,value2|parent_list_code:parent_key +ExtrafieldParamHelpselect=Parameters list have to be like key,value

for example :
1,value1
2,value2
3,value3
...

In order to have the list depending on another complementary attribute list :
1,value1|options_parent_list_code:parent_key
2,value2|options_parent_list_code:parent_key

In order to have the list depending on another list :
1,value1|parent_list_code:parent_key
2,value2|parent_list_code:parent_key ExtrafieldParamHelpcheckbox=Parameters list have to be like key,value

for example :
1,value1
2,value2
3,value3
... ExtrafieldParamHelpradio=Parameters list have to be like key,value

for example :
1,value1
2,value2
3,value3
... -ExtrafieldParamHelpsellist=Parameters list comes from a table
Syntax : table_name:label_field:id_field::filter
Example : c_typent:libelle:id::filter

filter can be a simple test (eg active=1) to display only active value
You can also use $ID$ in filter witch is the current id of current object
To do a SELECT in filter use $SEL$
if you want to filter on extrafields use syntax extra.fieldcode=... (where field code is the code of extrafield)

In order to have the list depending on another :
c_typent:libelle:id:parent_list_code|parent_column:filter -ExtrafieldParamHelpchkbxlst=Parameters list comes from a table
Syntax : table_name:label_field:id_field::filter
Example : c_typent:libelle:id::filter

filter can be a simple test (eg active=1) to display only active value
You can also use $ID$ in filter witch is the current id of current object
To do a SELECT in filter use $SEL$
if you want to filter on extrafields use syntax extra.fieldcode=... (where field code is the code of extrafield)

In order to have the list depending on another :
c_typent:libelle:id:parent_list_code|parent_column:filter +ExtrafieldParamHelpsellist=Parameters list comes from a table
Syntax : table_name:label_field:id_field::filter
Example : c_typent:libelle:id::filter

filter can be a simple test (eg active=1) to display only active value
You can also use $ID$ in filter witch is the current id of current object
To do a SELECT in filter use $SEL$
if you want to filter on extrafields use syntax extra.fieldcode=... (where field code is the code of extrafield)

In order to have the list depending on another complementary attribute list:
c_typent:libelle:id:options_parent_list_code|parent_column:filter

In order to have the list depending on another list:
c_typent:libelle:id:parent_list_code|parent_column:filter +ExtrafieldParamHelpchkbxlst=Parameters list comes from a table
Syntax : table_name:label_field:id_field::filter
Example : c_typent:libelle:id::filter

filter can be a simple test (eg active=1) to display only active value
You can also use $ID$ in filter witch is the current id of current object
To do a SELECT in filter use $SEL$
if you want to filter on extrafields use syntax extra.fieldcode=... (where field code is the code of extrafield)

In order to have the list depending on another complementary attribute list :
c_typent:libelle:id:options_parent_list_code|parent_column:filter

In order to have the list depending on another list:
c_typent:libelle:id:parent_list_code|parent_column:filter ExtrafieldParamHelplink=Parameters must be ObjectName:Classpath
Syntax : ObjectName:Classpath
Example : Societe:societe/class/societe.class.php LibraryToBuildPDF=Library used for PDF generation WarningUsingFPDF=Warning: Your conf.php contains directive dolibarr_pdf_force_fpdf=1. This means you use the FPDF library to generate PDF files. This library is old and does not support a lot of features (Unicode, image transparency, cyrillic, arab and asiatic languages, ...), so you may experience errors during PDF generation.
To solve this and have a full support of PDF generation, please download TCPDF library, then comment or remove the line $dolibarr_pdf_force_fpdf=1, and add instead $dolibarr_lib_TCPDF_PATH='path_to_TCPDF_dir' From 154425e2bcca8ca241ebf6d6ae7b53eeaa027803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Garci=CC=81a=20de=20La=20Fuente?= Date: Tue, 27 Dec 2016 16:13:39 +0100 Subject: [PATCH 014/170] Fixed PHP fatal error --- htdocs/product/class/product.class.php | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 418b66d4ac7..2f5540cbc8b 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -976,6 +976,7 @@ class Product extends CommonObject if (!$error) { require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination.class.php'; + require_once DOL_DOCUMENT_ROOT.'/attributes/class/ProductCombination2ValuePair.class.php'; //If it is a parent product, then we remove the association with child products $prodcomb = new ProductCombination($this->db); From 7b6488b1c79a9b1fdcd5d1a3cc9b5cadd27c2ace Mon Sep 17 00:00:00 2001 From: Maxime Kohlhaas Date: Fri, 27 Jan 2017 10:05:57 +0100 Subject: [PATCH 015/170] Fix numbering was disaplying technical error instead of error message --- htdocs/commande/class/commande.class.php | 3 ++- htdocs/compta/facture/class/facture.class.php | 3 ++- htdocs/fourn/class/fournisseur.facture.class.php | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php index e554990892c..268459a3c12 100644 --- a/htdocs/commande/class/commande.class.php +++ b/htdocs/commande/class/commande.class.php @@ -239,7 +239,8 @@ class Commande extends CommonOrder } else { - dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error); + $this->error=$obj->error; + //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error); return ""; } } diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index f15526f34ba..623a355023e 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -3003,7 +3003,8 @@ class Facture extends CommonInvoice * set up mask. */ if ($mode != 'last' && !$numref) { - dol_print_error($this->db,"Facture::getNextNumRef ".$obj->error); + $this->error=$obj->error; + //dol_print_error($this->db,"Facture::getNextNumRef ".$obj->error); return ""; } diff --git a/htdocs/fourn/class/fournisseur.facture.class.php b/htdocs/fourn/class/fournisseur.facture.class.php index bc7f5bfdca6..27b7627f450 100644 --- a/htdocs/fourn/class/fournisseur.facture.class.php +++ b/htdocs/fourn/class/fournisseur.facture.class.php @@ -1627,6 +1627,7 @@ class FactureFournisseur extends CommonInvoice } else { + $this->error=$obj->error; //dol_print_error($db,get_class($this)."::getNextNumRef ".$obj->error); return false; } From c0b4667713d2cdadd58bfb6621cc82792b2fd097 Mon Sep 17 00:00:00 2001 From: Charlie Root Date: Fri, 27 Jan 2017 10:50:50 +0100 Subject: [PATCH 016/170] NEW: Use the expense repport author account instead of the default account, if exists --- htdocs/accountancy/journal/expensereportsjournal.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/accountancy/journal/expensereportsjournal.php b/htdocs/accountancy/journal/expensereportsjournal.php index ce1c632e85b..8bc62c27ef6 100644 --- a/htdocs/accountancy/journal/expensereportsjournal.php +++ b/htdocs/accountancy/journal/expensereportsjournal.php @@ -86,14 +86,14 @@ $idpays = $p[0]; $sql = "SELECT er.rowid, er.ref, er.date_debut as de,"; $sql .= " erd.rowid as erdid, erd.comments, erd.total_ttc, erd.tva_tx, erd.total_ht, erd.total_tva, erd.fk_code_ventilation,"; -$sql .= " u.rowid as uid, u.firstname, u.lastname, u.accountancy_code as user_accountancy_code,"; +$sql .= " u.rowid as uid, u.firstname, u.lastname, u.accountancy_code as user_accountancy_account,"; $sql .= " f.accountancy_code, ct.accountancy_code_buy as account_tva, aa.rowid as fk_compte, aa.account_number as compte, aa.label as label_compte"; $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport_det as erd"; $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_tva as ct ON erd.tva_tx = ct.taux AND ct.fk_pays = '" . $idpays . "'"; $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_type_fees as f ON f.id = erd.fk_c_type_fees"; $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "accounting_account as aa ON aa.rowid = erd.fk_code_ventilation"; $sql .= " JOIN " . MAIN_DB_PREFIX . "expensereport as er ON er.rowid = erd.fk_expensereport"; -$sql .= " JOIN " . MAIN_DB_PREFIX . "user as u ON u.rowid = er.fk_user_valid"; +$sql .= " JOIN " . MAIN_DB_PREFIX . "user as u ON u.rowid = er.fk_user_author"; $sql .= " WHERE er.fk_statut > 0 "; $sql .= " AND erd.fk_code_ventilation > 0 "; $sql .= " AND er.entity IN (" . getEntity("expensereport", 0) . ")"; // We don't share object for accountancy From 055a00a33804fd0f41f4f2c7655a72031c482407 Mon Sep 17 00:00:00 2001 From: florian HENRY Date: Fri, 27 Jan 2017 15:18:12 +0100 Subject: [PATCH 017/170] fix travis --- htdocs/core/class/commonobject.class.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 36999f12dc7..4a84ac6a829 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -2151,6 +2151,8 @@ abstract class CommonObject */ function update_note($note,$suffix='') { + global $user; + if (! $this->table_element) { dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR); @@ -2167,6 +2169,7 @@ abstract class CommonObject $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; $sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL"); + $sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id; $sql.= " WHERE rowid =". $this->id; dol_syslog(get_class($this)."::update_note", LOG_DEBUG); From 3e14a89eb0f45a73775f89749548aae7ebd731fb Mon Sep 17 00:00:00 2001 From: Juanjo Menent Date: Fri, 27 Jan 2017 23:05:29 +0100 Subject: [PATCH 018/170] Fix #6314 --- htdocs/compta/bank/bankentries.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/htdocs/compta/bank/bankentries.php b/htdocs/compta/bank/bankentries.php index 54c77ed6e73..ad3817f28cd 100644 --- a/htdocs/compta/bank/bankentries.php +++ b/htdocs/compta/bank/bankentries.php @@ -5,6 +5,7 @@ * Copyright (C) 2012 Vinícius Nogueira * Copyright (C) 2014 Florian Henry * Copyright (C) 2015 Jean-François Ferry + * Copyright (C) 2016 Juanjo Menent * * 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 @@ -773,7 +774,7 @@ if ($resql) if (! empty($arrayfields['ba.ref']['checked'])) print_liste_field_titre($arrayfields['ba.ref']['label'],$_SERVER['PHP_SELF'],'ba.ref','',$param,'align="right"',$sortfield,$sortorder); if (! empty($arrayfields['b.debit']['checked'])) print_liste_field_titre($arrayfields['b.debit']['label'],$_SERVER['PHP_SELF'],'b.amount','',$param,'align="right"',$sortfield,$sortorder); if (! empty($arrayfields['b.credit']['checked'])) print_liste_field_titre($arrayfields['b.credit']['label'],$_SERVER['PHP_SELF'],'b.amount','',$param,'align="right"',$sortfield,$sortorder); - if (! empty($arrayfields['balance']['checked'])) print_liste_field_titre($arrayfields['balance']['label'],$_SERVER['PHP_SELF'],'balance','',$param,'align="right"',$sortfield,$sortorder); + if (! empty($arrayfields['balance']['checked'])) print_liste_field_titre($arrayfields['balance']['label'],$_SERVER['PHP_SELF'],'','',$param,'align="right"',$sortfield,$sortorder); if (! empty($arrayfields['b.num_releve']['checked'])) print_liste_field_titre($arrayfields['b.num_releve']['label'],$_SERVER['PHP_SELF'],'b.num_releve','',$param,'align="center"',$sortfield,$sortorder); // Extra fields if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) From 27caa586c20066fad6a1bdaff5a47f835be6d398 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 28 Jan 2017 12:36:53 +0100 Subject: [PATCH 019/170] Fix auto increment customer code must be done only if error is on duplicate code --- htdocs/societe/class/societe.class.php | 5 ++--- htdocs/societe/soc.php | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index 9248a07469c..b4a9a549ea7 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -503,10 +503,9 @@ class Societe extends CommonObject } else { - if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') + if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') { - - $this->error=$langs->trans("ErrorCompanyNameAlreadyExists",$this->name); + $this->error=$langs->trans("ErrorCompanyNameAlreadyExists",$this->name); // duplicate on a field (code or profid or ...) $result=-1; } else diff --git a/htdocs/societe/soc.php b/htdocs/societe/soc.php index 36fe26447fa..540c3aed883 100644 --- a/htdocs/societe/soc.php +++ b/htdocs/societe/soc.php @@ -514,8 +514,8 @@ if (empty($reshook)) } else { - - if($result == -3) { + if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') // TODO Sometime errors on duplicate on profid and not on code, so + { $duplicate_code_error = true; $object->code_fournisseur = null; $object->code_client = null; From 4bce208035259953b39f580063bf2c9908d36a77 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 28 Jan 2017 12:42:02 +0100 Subject: [PATCH 020/170] FIX detail of deposit and credit not was not visible into final invoice --- htdocs/core/tpl/objectline_view.tpl.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/htdocs/core/tpl/objectline_view.tpl.php b/htdocs/core/tpl/objectline_view.tpl.php index 559cae91464..fabba837e21 100644 --- a/htdocs/core/tpl/objectline_view.tpl.php +++ b/htdocs/core/tpl/objectline_view.tpl.php @@ -69,19 +69,20 @@ if (empty($usemargins)) $usemargins=0; description) { - if ($line->description == '(CREDIT_NOTE)' && $objp->fk_remise_except > 0) + if ($line->description == '(CREDIT_NOTE)' && $line->fk_remise_except > 0) { $discount=new DiscountAbsolute($this->db); $discount->fetch($line->fk_remise_except); echo ($txt?' - ':'').$langs->transnoentities("DiscountFromCreditNote",$discount->getNomUrl(0)); } - elseif ($line->description == '(DEPOSIT)' && $objp->fk_remise_except > 0) + elseif ($line->description == '(DEPOSIT)' && $line->fk_remise_except > 0) { $discount=new DiscountAbsolute($this->db); $discount->fetch($line->fk_remise_except); echo ($txt?' - ':'').$langs->transnoentities("DiscountFromDeposit",$discount->getNomUrl(0)); // Add date of deposit - if (! empty($conf->global->INVOICE_ADD_DEPOSIT_DATE)) echo ' ('.dol_print_date($discount->datec).')'; + if (! empty($conf->global->INVOICE_ADD_DEPOSIT_DATE)) + echo ' ('.dol_print_date($discount->datec).')'; } else { From bc8ccd8ec5f7931e546f3422c6e207e50d82f1d5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 28 Jan 2017 12:59:32 +0100 Subject: [PATCH 021/170] Fix bad var set --- htdocs/core/class/discount.class.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/core/class/discount.class.php b/htdocs/core/class/discount.class.php index 2295a301e03..5ea11cba680 100644 --- a/htdocs/core/class/discount.class.php +++ b/htdocs/core/class/discount.class.php @@ -41,10 +41,10 @@ class DiscountAbsolute public $fk_user; // Id utilisateur qui accorde la remise public $description; // Description libre public $datec; // Date creation - public $fk_facture_line; // Id invoice line when a discount linked to invoice line (for absolute discounts) - public $fk_facture; // Id invoice when a discoutn linked to invoice (for credit note) + public $fk_facture_line; // Id invoice line when a discount is used into an invoice line (for absolute discounts) + public $fk_facture; // Id invoice when a discount line is used into an invoice (for credit note) public $fk_facture_source; // Id facture avoir a l'origine de la remise - public $ref_facture_source; // Ref facture avoir a l'origine de la remise + public $ref_facture_source; // Ref facture avoir a l'origine de la remise /** * Constructor @@ -296,7 +296,7 @@ class DiscountAbsolute $resql = $this->db->query($sql); if ($resql) { - $this->fk_facture_source=$rowidline; + $this->fk_facture_line=$rowidline; $this->fk_facture=$rowidinvoice; return 1; } From 7c638feb3bfa0ae338f9058e7ca5bb4236a9875e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 28 Jan 2017 13:13:57 +0100 Subject: [PATCH 022/170] Fix label of field --- htdocs/margin/agentMargins.php | 7 ++++++- htdocs/margin/checkMargins.php | 11 ++++++----- htdocs/margin/customerMargins.php | 7 ++++++- htdocs/margin/productMargins.php | 9 ++++++++- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/htdocs/margin/agentMargins.php b/htdocs/margin/agentMargins.php index 61cee328b41..26a5813c8be 100644 --- a/htdocs/margin/agentMargins.php +++ b/htdocs/margin/agentMargins.php @@ -171,6 +171,11 @@ if ($result) print '
'; print_barre_liste($langs->trans("MarginDetails"),$page,$_SERVER["PHP_SELF"],"",$sortfield,$sortorder,'',$num,$num,''); + if ($conf->global->MARGIN_TYPE == "1") + $labelcostprice=$langs->trans('BuyingPrice'); + else // value is 'costprice' or 'pmp' + $labelcostprice=$langs->trans('CostPrice'); + $i = 0; print ""; @@ -181,7 +186,7 @@ if ($result) print_liste_field_titre($langs->trans("SalesRepresentative"),$_SERVER["PHP_SELF"],"u.lastname","","&agentid=".$agentid,'',$sortfield,$sortorder); print_liste_field_titre($langs->trans("SellingPrice"),$_SERVER["PHP_SELF"],"selling_price","","&agentid=".$agentid,'align="right"',$sortfield,$sortorder); - print_liste_field_titre($langs->trans("BuyingPrice"),$_SERVER["PHP_SELF"],"buying_price","","&agentid=".$agentid,'align="right"',$sortfield,$sortorder); + print_liste_field_titre($labelcostprice,$_SERVER["PHP_SELF"],"buying_price","","&agentid=".$agentid,'align="right"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("Margin"),$_SERVER["PHP_SELF"],"marge","","&agentid=".$agentid,'align="right"',$sortfield,$sortorder); if (! empty($conf->global->DISPLAY_MARGIN_RATES)) print_liste_field_titre($langs->trans("MarginRate"),$_SERVER["PHP_SELF"],"","","&agentid=".$agentid,'align="right"',$sortfield,$sortorder); diff --git a/htdocs/margin/checkMargins.php b/htdocs/margin/checkMargins.php index 93f6e6915f9..b3c663ad886 100644 --- a/htdocs/margin/checkMargins.php +++ b/htdocs/margin/checkMargins.php @@ -179,6 +179,11 @@ if ($result) { print '
'; print_barre_liste($langs->trans("MarginDetails"), $page, $_SERVER["PHP_SELF"], "", $sortfield, $sortorder, '', $num, $nbtotalofrecords, ''); + if ($conf->global->MARGIN_TYPE == "1") + $labelcostprice=$langs->trans('BuyingPrice'); + else // value is 'costprice' or 'pmp' + $labelcostprice=$langs->trans('CostPrice'); + $moreforfilter=''; print '
'; @@ -189,11 +194,7 @@ if ($result) { print_liste_field_titre($langs->trans("Ref"), $_SERVER["PHP_SELF"], "f.ref", "", $options, '', $sortfield, $sortorder); print_liste_field_titre($langs->trans("Description"), $_SERVER["PHP_SELF"], "", "", $options, 'width=20%', $sortfield, $sortorder); print_liste_field_titre($langs->trans("UnitPriceHT"), $_SERVER["PHP_SELF"], "d.subprice", "", $options, 'align="right"', $sortfield, $sortorder); - if ($conf->global->MARGIN_TYPE == "1") { - print_liste_field_titre($langs->trans("BuyingPrice"), $_SERVER["PHP_SELF"], "d.buy_price_ht", "", $options, 'align="right"', $sortfield, $sortorder); - } else { - print_liste_field_titre($langs->trans("CostPrice"), $_SERVER["PHP_SELF"], "d.buy_price_ht", "", $options, 'align="right"', $sortfield, $sortorder); - } + print_liste_field_titre($labelcostprice, $_SERVER["PHP_SELF"], "d.buy_price_ht", "", $options, 'align="right"', $sortfield, $sortorder); print_liste_field_titre($langs->trans("Qty"), $_SERVER["PHP_SELF"], "d.qty", "", $options, 'align="right"', $sortfield, $sortorder); print_liste_field_titre($langs->trans("AmountTTC"), $_SERVER["PHP_SELF"], "d.total_ht", "", $options, 'align="right"', $sortfield, $sortorder); diff --git a/htdocs/margin/customerMargins.php b/htdocs/margin/customerMargins.php index 8b960a150a4..8bdff263ebf 100644 --- a/htdocs/margin/customerMargins.php +++ b/htdocs/margin/customerMargins.php @@ -207,6 +207,11 @@ if ($result) print '
'; print_barre_liste($langs->trans("MarginDetails"),$page,$_SERVER["PHP_SELF"],"",$sortfield,$sortorder,'',$num,$num,''); + if ($conf->global->MARGIN_TYPE == "1") + $labelcostprice=$langs->trans('BuyingPrice'); + else // value is 'costprice' or 'pmp' + $labelcostprice=$langs->trans('CostPrice'); + $i = 0; print "
"; @@ -218,7 +223,7 @@ if ($result) else print_liste_field_titre($langs->trans("Customer"),$_SERVER["PHP_SELF"],"s.nom","","&socid=".$socid,'',$sortfield,$sortorder); print_liste_field_titre($langs->trans("SellingPrice"),$_SERVER["PHP_SELF"],"selling_price","","&socid=".$socid,'align="right"',$sortfield,$sortorder); - print_liste_field_titre($langs->trans("BuyingPrice"),$_SERVER["PHP_SELF"],"buying_price","","&socid=".$socid,'align="right"',$sortfield,$sortorder); + print_liste_field_titre($labelcostprice,$_SERVER["PHP_SELF"],"buying_price","","&socid=".$socid,'align="right"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("Margin"),$_SERVER["PHP_SELF"],"marge","","&socid=".$socid,'align="right"',$sortfield,$sortorder); if (! empty($conf->global->DISPLAY_MARGIN_RATES)) print_liste_field_titre($langs->trans("MarginRate"),$_SERVER["PHP_SELF"],"","","&socid=".$socid,'align="right"',$sortfield,$sortorder); diff --git a/htdocs/margin/productMargins.php b/htdocs/margin/productMargins.php index dcf80e9a49c..26d5accf710 100644 --- a/htdocs/margin/productMargins.php +++ b/htdocs/margin/productMargins.php @@ -203,6 +203,11 @@ if ($result) print '
'; print_barre_liste($langs->trans("MarginDetails"),$page,$_SERVER["PHP_SELF"],"&id=".$id,$sortfield,$sortorder,'',$num,$num,''); + if ($conf->global->MARGIN_TYPE == "1") + $labelcostprice=$langs->trans('BuyingPrice'); + else // value is 'costprice' or 'pmp' + $labelcostprice=$langs->trans('CostPrice'); + $moreforfilter=''; $i = 0; @@ -215,9 +220,11 @@ if ($result) print_liste_field_titre($langs->trans("DateInvoice"),$_SERVER["PHP_SELF"],"f.datef","","&id=".$id,'align="center"',$sortfield,$sortorder); } else + { print_liste_field_titre($langs->trans("ProductService"),$_SERVER["PHP_SELF"],"p.ref","","&id=".$id,'',$sortfield,$sortorder); + } print_liste_field_titre($langs->trans("SellingPrice"),$_SERVER["PHP_SELF"],"selling_price","","&id=".$id,'align="right"',$sortfield,$sortorder); - print_liste_field_titre($langs->trans("BuyingPrice"),$_SERVER["PHP_SELF"],"buying_price","","&id=".$id,'align="right"',$sortfield,$sortorder); + print_liste_field_titre($labelcostprice,$_SERVER["PHP_SELF"],"buying_price","","&id=".$id,'align="right"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("Margin"),$_SERVER["PHP_SELF"],"marge","","&id=".$id,'align="right"',$sortfield,$sortorder); if (! empty($conf->global->DISPLAY_MARGIN_RATES)) print_liste_field_titre($langs->trans("MarginRate"),$_SERVER["PHP_SELF"],"","","&id=".$id,'align="right"',$sortfield,$sortorder); From 1e78c4c4182906599b464360aa40517d8324b776 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 28 Jan 2017 14:54:06 +0100 Subject: [PATCH 023/170] FIX Change the customer code only if error on duplicate --- htdocs/societe/soc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/societe/soc.php b/htdocs/societe/soc.php index f3f87ba6f79..d3c8240417f 100644 --- a/htdocs/societe/soc.php +++ b/htdocs/societe/soc.php @@ -509,8 +509,8 @@ if (empty($reshook)) } else { - - if($result == -3) { + if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') // TODO Sometime errors on duplicate on profid and not on code, so + { $duplicate_code_error = true; $object->code_fournisseur = null; $object->code_client = null; From e2ce31e1a7ed7dd9023825dd7566c2642c92415f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 28 Jan 2017 14:54:51 +0100 Subject: [PATCH 024/170] Add code comment --- htdocs/margin/lib/margins.lib.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/margin/lib/margins.lib.php b/htdocs/margin/lib/margins.lib.php index 84d7945ec0d..d7dbd5d81eb 100644 --- a/htdocs/margin/lib/margins.lib.php +++ b/htdocs/margin/lib/margins.lib.php @@ -109,7 +109,7 @@ function marges_prepare_head() * @param float $localtax2_tx Vat rate special 2 (not used) * @param int $fk_pa Id of buying price (prefer set this to 0 and provide $paht instead. With id, buying price may have change) * @param float $paht Buying price without tax - * @return array Array of margin info + * @return array Array of margin info (buying price, marge rate, marque rate) */ function getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht) { @@ -134,7 +134,7 @@ function getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localta } else { - $paht_ret = $paht; + $paht_ret = $paht; } // Calculate selling unit price including line discount From c88e63ecc972eafaf70752144c4f5735529ca47e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 28 Jan 2017 15:01:17 +0100 Subject: [PATCH 025/170] FIX #6259 --- htdocs/compta/facture/class/facture.class.php | 15 +++++- htdocs/core/class/html.formmargin.class.php | 49 ++++++++++--------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index f15526f34ba..2d76242ccc4 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -1164,7 +1164,7 @@ class Facture extends CommonInvoice { $this->lines=array(); - $sql = 'SELECT l.rowid, l.fk_product, l.fk_parent_line, l.label as custom_label, l.description, l.product_type, l.price, l.qty, l.tva_tx, '; + $sql = 'SELECT l.rowid, l.fk_facture, l.fk_product, l.fk_parent_line, l.label as custom_label, l.description, l.product_type, l.price, l.qty, l.tva_tx, '; $sql.= ' l.situation_percent, l.fk_prev_id,'; $sql.= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise_percent, l.fk_remise_except, l.subprice,'; $sql.= ' l.rang, l.special_code,'; @@ -1191,6 +1191,7 @@ class Facture extends CommonInvoice $line->id = $objp->rowid; $line->rowid = $objp->rowid; // deprecated + $line->fk_facture = $objp->fk_facture; $line->label = $objp->custom_label; // deprecated $line->desc = $objp->description; // Description line $line->description = $objp->description; // Description line @@ -1443,6 +1444,18 @@ class Facture extends CommonInvoice $facligne->rang=-1; $facligne->info_bits=2; + // Get buy/cost price of invoice that is source of discount + if ($remise->fk_facture_source > 0) + { + $srcinvoice=new Facture($this->db); + $srcinvoice->fetch($remise->fk_facture_source); + $totalcostpriceofinvoice=0; + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject + $formmargin=new FormMargin($this->db); + $arraytmp=$formmargin->getMarginInfosArray($srcinvoice, false); + $facligne->pa_ht = $arraytmp['pa_total']; + } + $facligne->total_ht = -$remise->amount_ht; $facligne->total_tva = -$remise->amount_tva; $facligne->total_ttc = -$remise->amount_ttc; diff --git a/htdocs/core/class/html.formmargin.class.php b/htdocs/core/class/html.formmargin.class.php index 6678d87ca60..6d29125df6c 100644 --- a/htdocs/core/class/html.formmargin.class.php +++ b/htdocs/core/class/html.formmargin.class.php @@ -48,6 +48,7 @@ class FormMargin /** * get array with margin information from lines of object + * TODO Move this in common class. * * @param CommonObject $object Object we want to get margin information for * @param boolean $force_price True of not @@ -92,19 +93,23 @@ class FormMargin $line->pa_ht = $line->subprice * (1 - ($line->remise_percent / 100)); } + $pv = $line->qty * $line->subprice * (1 - $line->remise_percent / 100); + $pa_ht = ($pv < 0 ? - $line->pa_ht : $line->pa_ht); // We choosed to have line->pa_ht always positive in database, so we guess the correct sign + $pa = $line->qty * $pa_ht; + // calcul des marges if (isset($line->fk_remise_except) && isset($conf->global->MARGIN_METHODE_FOR_DISCOUNT)) { // remise - $pa = $line->qty * $line->pa_ht; - $pv = $line->qty * $line->subprice * (1 - $line->remise_percent / 100); if ($conf->global->MARGIN_METHODE_FOR_DISCOUNT == '1') { // remise globale considérée comme produit $marginInfos['pa_products'] += $pa; $marginInfos['pv_products'] += $pv; $marginInfos['pa_total'] += $pa; $marginInfos['pv_total'] += $pv; // if credit note, margin = -1 * (abs(selling_price) - buying_price) - if ($pv < 0) - $marginInfos['margin_on_products'] += -1 * (abs($pv) - $pa); - else + //if ($pv < 0) + //{ + // $marginInfos['margin_on_products'] += -1 * (abs($pv) - $pa); + //} + //else $marginInfos['margin_on_products'] += $pv - $pa; } elseif ($conf->global->MARGIN_METHODE_FOR_DISCOUNT == '2') { // remise globale considérée comme service @@ -113,9 +118,9 @@ class FormMargin $marginInfos['pa_total'] += $pa; $marginInfos['pv_total'] += $pv; // if credit note, margin = -1 * (abs(selling_price) - buying_price) - if ($pv < 0) - $marginInfos['margin_on_services'] += -1 * (abs($pv) - $pa); - else + //if ($pv < 0) + // $marginInfos['margin_on_services'] += -1 * (abs($pv) - $pa); + //else $marginInfos['margin_on_services'] += $pv - $pa; } elseif ($conf->global->MARGIN_METHODE_FOR_DISCOUNT == '3') { // remise globale prise en compte uniqt sur total @@ -126,29 +131,29 @@ class FormMargin else { $type=$line->product_type?$line->product_type:$line->fk_product_type; if ($type == 0) { // product - $pa = $line->qty * $line->pa_ht; - $pv = $line->qty * $line->subprice * (1 - $line->remise_percent / 100); $marginInfos['pa_products'] += $pa; $marginInfos['pv_products'] += $pv; $marginInfos['pa_total'] += $pa; $marginInfos['pv_total'] += $pv; // if credit note, margin = -1 * (abs(selling_price) - buying_price) - if ($pv < 0) - $marginInfos['margin_on_products'] += -1 * (abs($pv) - $pa); - else - $marginInfos['margin_on_products'] += $pv - $pa; + //if ($pv < 0) + //{ + // $marginInfos['margin_on_products'] += -1 * (abs($pv) - $pa); + //} + //else + //{ + $marginInfos['margin_on_products'] += $pv - $pa; + //} } elseif ($type == 1) { // service - $pa = $line->qty * $line->pa_ht; - $pv = $line->qty * $line->subprice * (1 - $line->remise_percent / 100); $marginInfos['pa_services'] += $pa; $marginInfos['pv_services'] += $pv; $marginInfos['pa_total'] += $pa; $marginInfos['pv_total'] += $pv; // if credit note, margin = -1 * (abs(selling_price) - buying_price) - if ($pv < 0) - $marginInfos['margin_on_services'] += -1 * (abs($pv) - $pa); - else + //if ($pv < 0) + // $marginInfos['margin_on_services'] += -1 * (abs($pv) - $pa); + //else $marginInfos['margin_on_services'] += $pv - $pa; } } @@ -164,9 +169,9 @@ class FormMargin $marginInfos['mark_rate_services'] = 100 * $marginInfos['margin_on_services'] / $marginInfos['pv_services']; // if credit note, margin = -1 * (abs(selling_price) - buying_price) - if ($marginInfos['pv_total'] < 0) - $marginInfos['total_margin'] = -1 * (abs($marginInfos['pv_total']) - $marginInfos['pa_total']); - else + //if ($marginInfos['pv_total'] < 0) + // $marginInfos['total_margin'] = -1 * (abs($marginInfos['pv_total']) - $marginInfos['pa_total']); + //else $marginInfos['total_margin'] = $marginInfos['pv_total'] - $marginInfos['pa_total']; if ($marginInfos['pa_total'] > 0) $marginInfos['total_margin_rate'] = 100 * $marginInfos['total_margin'] / $marginInfos['pa_total']; From 2c07d2df22d08cf7a866323cf667d5ce6278eaaf Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 28 Jan 2017 15:08:46 +0100 Subject: [PATCH 026/170] Add comments --- htdocs/margin/agentMargins.php | 1 + htdocs/margin/customerMargins.php | 1 + htdocs/margin/productMargins.php | 1 + 3 files changed, 3 insertions(+) diff --git a/htdocs/margin/agentMargins.php b/htdocs/margin/agentMargins.php index 66bdbb26f8e..d5f50d7529c 100644 --- a/htdocs/margin/agentMargins.php +++ b/htdocs/margin/agentMargins.php @@ -115,6 +115,7 @@ $sql = "SELECT"; if ($agentid > 0) $sql.= " s.rowid as socid, s.nom as name, s.code_client, s.client,"; $sql.= " u.rowid as agent, u.login, u.lastname, u.firstname,"; $sql.= " sum(d.total_ht) as selling_price,"; +// Note: qty and buy_price_ht is always positive (if not your database may be corrupted, you can update this) $sql.= " sum(".$db->ifsql('d.total_ht < 0','d.qty * d.buy_price_ht * -1','d.qty * d.buy_price_ht').") as buying_price,"; $sql.= " sum(".$db->ifsql('d.total_ht < 0','-1 * (abs(d.total_ht) - (d.buy_price_ht * d.qty))','d.total_ht - (d.buy_price_ht * d.qty)').") as marge" ; $sql.= " FROM ".MAIN_DB_PREFIX."societe as s"; diff --git a/htdocs/margin/customerMargins.php b/htdocs/margin/customerMargins.php index 7f6d5a951c2..3d95d2ae091 100644 --- a/htdocs/margin/customerMargins.php +++ b/htdocs/margin/customerMargins.php @@ -164,6 +164,7 @@ $sql = "SELECT"; $sql.= " s.rowid as socid, s.nom as name, s.code_client, s.client,"; if ($client) $sql.= " f.rowid as facid, f.facnumber, f.total as total_ht, f.datef, f.paye, f.fk_statut as statut,"; $sql.= " sum(d.total_ht) as selling_price,"; +// Note: qty and buy_price_ht is always positive (if not your database may be corrupted, you can update this) $sql.= " sum(".$db->ifsql('d.total_ht < 0','d.qty * d.buy_price_ht * -1','d.qty * d.buy_price_ht').") as buying_price,"; $sql.= " sum(".$db->ifsql('d.total_ht < 0','-1 * (abs(d.total_ht) - (d.buy_price_ht * d.qty))','d.total_ht - (d.buy_price_ht * d.qty)').") as marge"; $sql.= " FROM ".MAIN_DB_PREFIX."societe as s"; diff --git a/htdocs/margin/productMargins.php b/htdocs/margin/productMargins.php index b4609633447..ac9d61fb814 100644 --- a/htdocs/margin/productMargins.php +++ b/htdocs/margin/productMargins.php @@ -166,6 +166,7 @@ $sql = "SELECT p.label, p.rowid, p.fk_product_type, p.ref, p.entity as pentity," if ($id > 0) $sql.= " d.fk_product,"; if ($id > 0) $sql.= " f.rowid as facid, f.facnumber, f.total as total_ht, f.datef, f.paye, f.fk_statut as statut,"; $sql.= " SUM(d.total_ht) as selling_price,"; +// Note: qty and buy_price_ht is always positive (if not your database may be corrupted, you can update this) $sql.= " SUM(".$db->ifsql('d.total_ht < 0','d.qty * d.buy_price_ht * -1','d.qty * d.buy_price_ht').") as buying_price,"; $sql.= " SUM(".$db->ifsql('d.total_ht < 0','-1 * (abs(d.total_ht) - (d.buy_price_ht * d.qty))','d.total_ht - (d.buy_price_ht * d.qty)').") as marge"; $sql.= " FROM ".MAIN_DB_PREFIX."societe as s"; From 4f76626f51adf5e4e07569c19d74e4d4a6030c6e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 28 Jan 2017 15:55:44 +0100 Subject: [PATCH 027/170] FIX Creation of credit note on invoice with deposit stole the discount. --- htdocs/compta/facture.php | 4 ++-- htdocs/compta/facture/class/facture.class.php | 22 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index abce93d9d90..f900e2889a3 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -812,14 +812,14 @@ if (empty($reshook)) $line->fk_parent_line = $fk_parent_line; $line->subprice =-$line->subprice; // invert price for object - $line->pa_ht = -$line->pa_ht; + $line->pa_ht = $line->pa_ht; // we choosed to have buy/cost price always positive, so no revert of sign here $line->total_ht=-$line->total_ht; $line->total_tva=-$line->total_tva; $line->total_ttc=-$line->total_ttc; $line->total_localtax1=-$line->total_localtax1; $line->total_localtax2=-$line->total_localtax2; - $result = $line->insert(); + $result = $line->insert(0, 1); // When creating credit note with same lines than source, we must ignore error if discount alreayd linked $object->lines[] = $line; // insert new line in current object diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index 2d76242ccc4..36046fa8010 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -4096,10 +4096,11 @@ class FactureLigne extends CommonInvoiceLine /** * Insert line into database * - * @param int $notrigger 1 no triggers - * @return int <0 if KO, >0 if OK + * @param int $notrigger 1 no triggers + * @param int $noerrorifdiscountalreadylinked 1=Do not make error if lines is linked to a discount and discount already linked to another + * @return int <0 if KO, >0 if OK */ - function insert($notrigger=0) + function insert($notrigger=0, $noerrorifdiscountalreadylinked=0) { global $langs,$user,$conf; @@ -4243,13 +4244,16 @@ class FactureLigne extends CommonInvoiceLine // Check if discount was found if ($result > 0) { - // Check if discount not already affected to another invoice - if ($discount->fk_facture) + // Check if discount not already affected to another invoice + if ($discount->fk_facture_line > 0) { - $this->error=$langs->trans("ErrorDiscountAlreadyUsed",$discount->id); - dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR); - $this->db->rollback(); - return -3; + if (empty($noerrorifdiscountalreadylinked)) + { + $this->error=$langs->trans("ErrorDiscountAlreadyUsed",$discount->id); + dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR); + $this->db->rollback(); + return -3; + } } else { From 56d75c552d5e25634effcd8b2ca12b1ed7631136 Mon Sep 17 00:00:00 2001 From: atm-ph Date: Sun, 29 Jan 2017 13:04:06 +0100 Subject: [PATCH 028/170] New movement stock can be linked to project --- htdocs/product/class/product.class.php | 16 +++++++++-- .../stock/class/mouvementstock.class.php | 27 +++++++++++++++++++ htdocs/product/stock/mouvement.php | 23 ++++++++++++++-- htdocs/product/stock/product.php | 24 ++++++++++++++--- .../product/stock/tpl/stockcorrection.tpl.php | 9 ++++++- 5 files changed, 91 insertions(+), 8 deletions(-) diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 34faaddfe4e..71213bd923e 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -3518,20 +3518,26 @@ class Product extends CommonObject * @param string $label Label of stock movement * @param double $price Unit price HT of product, used to calculate average weighted price (PMP in french). If 0, average weighted price is not changed. * @param string $inventorycode Inventory code + * @param string $origin_element Origin element type + * @param int $origin_id Origin id of element * @return int <0 if KO, >0 if OK */ - function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $inventorycode='') + function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $inventorycode='', $origin_element='', $origin_id=null) { if ($id_entrepot) { $this->db->begin(); require_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php'; + + // If remove stock then save it the actual PMP as price (the new calculation of PMP is triggered only with $movement = 0 || 3 ) + if ($movement == 1) $price = $this->pmp; $op[0] = "+".trim($nbpiece); $op[1] = "-".trim($nbpiece); $movementstock=new MouvementStock($this->db); + $movementstock->setOrigin($origin_element, $origin_id); $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],$movement,$price,$label,$inventorycode); if ($result >= 0) @@ -3563,9 +3569,11 @@ class Product extends CommonObject * @param date $dluo sell-by date * @param string $lot Lot number * @param string $inventorycode Inventory code + * @param string $origin_element Origin element type + * @param int $origin_id Origin id of element * @return int <0 if KO, >0 if OK */ - function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $dlc='', $dluo='',$lot='', $inventorycode='') + function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $dlc='', $dluo='',$lot='', $inventorycode='', $origin_element='', $origin_id=null) { if ($id_entrepot) { @@ -3573,10 +3581,14 @@ class Product extends CommonObject require_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php'; + // If remove stock then save it the actual PMP as price (the new calculation of PMP is triggered only with $movement = 0 || 3 ) + if ($movement == 1) $price = $this->pmp; + $op[0] = "+".trim($nbpiece); $op[1] = "-".trim($nbpiece); $movementstock=new MouvementStock($this->db); + $movementstock->setOrigin($origin_element, $origin_id); $result=$movementstock->_create($user,$this->id,$id_entrepot,$op[$movement],$movement,$price,$label,$inventorycode,'',$dlc,$dluo,$lot); if ($result >= 0) diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index dee344b6a5b..6a09dde054c 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -893,6 +893,33 @@ class MouvementStock extends CommonObject return ''; } + + /** + * Set attribute origin to object + * + * @param string $origin_element + * @param int $origin_id + * + * @return void + */ + function setOrigin($origin_element, $origin_id) + { + if (!empty($origin_element) && $origin_id > 0) + { + $origin=''; + if ($origin_element == 'project') + { + if (!class_exists('Project')) require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; + $origin = new Project($this->db); + } + + if (!empty($origin)) + { + $this->origin = $origin; + $this->origin->id = $origin_id; + } + } + } /** diff --git a/htdocs/product/stock/mouvement.php b/htdocs/product/stock/mouvement.php index 8e6775e2e6e..f5d9cbab8e2 100644 --- a/htdocs/product/stock/mouvement.php +++ b/htdocs/product/stock/mouvement.php @@ -34,6 +34,11 @@ require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/stock.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; +if (! empty($conf->projet->enabled)) +{ + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php'; + require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; +} $langs->load("products"); $langs->load("stocks"); @@ -156,6 +161,15 @@ if ($action == "correct_stock") if (! $error) { + $origin_element = ''; + $origin_id = null; + + if (GETPOST('projectid', 'int')) + { + $origin_element = 'project'; + $origin_id = GETPOST('projectid', 'int'); + } + if ($product->hasbatch()) { $batch=GETPOST('batch_number'); @@ -173,7 +187,9 @@ if ($action == "correct_stock") GETPOST("label",'san_alpha'), GETPOST('unitprice'), $eatby,$sellby,$batch, - GETPOST('inventorycode') + GETPOST('inventorycode'), + $origin_element, + $origin_id ); // We do not change value of stock for a correction } else @@ -185,7 +201,9 @@ if ($action == "correct_stock") GETPOST("mouvement"), GETPOST("label",'san_alpha'), GETPOST('unitprice'), - GETPOST('inventorycode') + GETPOST('inventorycode'), + $origin_element, + $origin_id ); // We do not change value of stock for a correction } @@ -392,6 +410,7 @@ $userstatic=new User($db); $form=new Form($db); $formother=new FormOther($db); $formproduct=new FormProduct($db); +if (!empty($conf->projet->enabled)) $formproject=new FormProjets($db); $sql = "SELECT p.rowid, p.ref as product_ref, p.label as produit, p.fk_product_type as type, p.entity,"; $sql.= " e.label as stock, e.rowid as entrepot_id, e.lieu,"; diff --git a/htdocs/product/stock/product.php b/htdocs/product/stock/product.php index 6bcf98e96c1..158440c5855 100644 --- a/htdocs/product/stock/product.php +++ b/htdocs/product/stock/product.php @@ -37,6 +37,11 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productstockentrepot.class.php'; if (! empty($conf->productbatch->enabled)) require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php'; +if (! empty($conf->projet->enabled)) +{ + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php'; + require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; +} $langs->load("products"); $langs->load("orders"); @@ -220,6 +225,15 @@ if ($action == "correct_stock" && ! $cancel) $priceunit=price2num(GETPOST("unitprice")); if (is_numeric(GETPOST("nbpiece")) && $id) { + $origin_element = ''; + $origin_id = null; + + if (GETPOST('projectid', 'int')) + { + $origin_element = 'project'; + $origin_id = GETPOST('projectid', 'int'); + } + if (empty($object)) { $object = new Product($db); $result=$object->fetch($id); @@ -236,7 +250,9 @@ if ($action == "correct_stock" && ! $cancel) $d_eatby, $d_sellby, $batchnumber, - GETPOST('inventorycode') + GETPOST('inventorycode'), + $origin_element, + $origin_id ); // We do not change value of stock for a correction } else @@ -248,7 +264,9 @@ if ($action == "correct_stock" && ! $cancel) GETPOST("mouvement"), GETPOST("label"), $priceunit, - GETPOST('inventorycode') + GETPOST('inventorycode'), + $origin_element, + $origin_id ); // We do not change value of stock for a correction } @@ -490,7 +508,7 @@ if ($action == 'updateline' && GETPOST('save') == $langs->trans('Save')) $form = new Form($db); $formproduct=new FormProduct($db); - +if (! empty($conf->projet->enabled)) $formproject=new FormProjets($db); if ($id > 0 || $ref) { diff --git a/htdocs/product/stock/tpl/stockcorrection.tpl.php b/htdocs/product/stock/tpl/stockcorrection.tpl.php index ac37566457f..d08cf334671 100644 --- a/htdocs/product/stock/tpl/stockcorrection.tpl.php +++ b/htdocs/product/stock/tpl/stockcorrection.tpl.php @@ -77,7 +77,14 @@ // Purchase price print ''; print ''; - print ''; + print ''; + if (! empty($conf->projet->enabled)) + { + print ''; + print ''; + } print ''; // Serial / Eat-by date From ccad6d6ddee596a9aa58ac38083eece4edb664ea Mon Sep 17 00:00:00 2001 From: atm-ph Date: Sun, 29 Jan 2017 13:36:33 +0100 Subject: [PATCH 029/170] New show on project "overview" tab the informations about movements stock --- htdocs/core/class/html.formprojet.class.php | 8 +- htdocs/langs/en_US/stocks.lang | 2 + .../stock/class/mouvementstock.class.php | 86 +++++++++++++++++++ htdocs/product/stock/mouvement.php | 10 ++- htdocs/projet/class/project.class.php | 4 + htdocs/projet/element.php | 29 ++++++- 6 files changed, 132 insertions(+), 7 deletions(-) diff --git a/htdocs/core/class/html.formprojet.class.php b/htdocs/core/class/html.formprojet.class.php index 0129f1c1382..064c8bd1d8d 100644 --- a/htdocs/core/class/html.formprojet.class.php +++ b/htdocs/core/class/html.formprojet.class.php @@ -460,7 +460,7 @@ class FormProjets if ($table_element == 'projet_task') return ''; // Special cas of element we never link to a project (already always done) $linkedtothirdparty=false; - if (! in_array($table_element, array('don','expensereport_det','expensereport','loan'))) $linkedtothirdparty=true; + if (! in_array($table_element, array('don','expensereport_det','expensereport','loan','stock_mouvement'))) $linkedtothirdparty=true; $sqlfilter=''; $projectkey="fk_projet"; @@ -499,6 +499,10 @@ class FormProjets case "fichinter": $sql = "SELECT t.rowid, t.ref"; break; + case 'stock_mouvement': + $sql = 'SELECT t.rowid, t.label as ref'; + $projectkey='fk_origin'; + break; default: $sql = "SELECT t.rowid, t.ref"; break; @@ -512,7 +516,7 @@ class FormProjets if (is_numeric($socid)) $sql.= " AND t.fk_soc=".$socid; else $sql.= " AND t.fk_soc IN (".$socid.")"; } - if (! in_array($table_element, array('expensereport_det'))) $sql.= ' AND t.entity IN ('.getEntity('project',1).')'; + if (! in_array($table_element, array('expensereport_det','stock_mouvement'))) $sql.= ' AND t.entity IN ('.getEntity('project',1).')'; if ($linkedtothirdparty) $sql.=" AND s.rowid = t.fk_soc"; if ($sqlfilter) $sql.= " AND ".$sqlfilter; $sql.= " ORDER BY ref DESC"; diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index f7a77db4821..cfced089c3a 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -22,6 +22,8 @@ Movements=Movements ErrorWarehouseRefRequired=Warehouse reference name is required ListOfWarehouses=List of warehouses ListOfStockMovements=List of stock movements +StockMovementForId=Movement ID %d +ListMouvementStockProject=List of stock movements associated to project StocksArea=Warehouses area Location=Location LocationSummary=Short name location diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index 6a09dde054c..53ab3163566 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -938,4 +938,90 @@ class MouvementStock extends CommonObject // There is no specific properties. All data into insert are provided as method parameter. } + + /** + * Return a link (with optionaly the picto) + * Use this->id,this->lastname, this->firstname + * + * @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 integer $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 $langs, $conf, $db; + + $result = ''; + $companylink = ''; + + $label = '' . $langs->trans("Movement") . ' '.$this->id.''; + $label.= '
'; + $label.= '' . $langs->trans('Label') . ': ' . $this->label; + $label.= '
' . $langs->trans('Qty') . ': ' .$this->qty; + $label.= '
'; + + $link = 'id . $linkend; + return $result; + } + + /** + * Return label statut + * + * @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($mode); + } + + /** + * Renvoi le libelle d'un status donne + * + * @param int $status Id status + * @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 LibStatut($mode=0) + { + global $langs; + + if ($mode == 0) + { + return $langs->trans('StatusNotApplicable'); + } + if ($mode == 1) + { + return $langs->trans('StatusNotApplicable'); + } + if ($mode == 2) + { + return img_picto($langs->trans('StatusNotApplicable'),'statut9').' '.$langs->trans('StatusNotApplicable'); + } + if ($mode == 3) + { + return img_picto($langs->trans('StatusNotApplicable'),'statut9'); + } + if ($mode == 4) + { + return img_picto($langs->trans('StatusNotApplicable'),'statut9').' '.$langs->trans('StatusNotApplicable'); + } + if ($mode == 5) + { + return $langs->trans('StatusNotApplicable').' '.img_picto($langs->trans('StatusNotApplicable'),'statut9'); + } + } } diff --git a/htdocs/product/stock/mouvement.php b/htdocs/product/stock/mouvement.php index f5d9cbab8e2..a5daa87a510 100644 --- a/htdocs/product/stock/mouvement.php +++ b/htdocs/product/stock/mouvement.php @@ -48,6 +48,7 @@ if (! empty($conf->productbatch->enabled)) $langs->load("productbatch"); $result=restrictedArea($user,'stock'); $id=GETPOST('id','int'); +$msid=GETPOST('msid','int'); $product_id=GETPOST("product_id"); $action=GETPOST('action'); $cancel=GETPOST('cancel'); @@ -431,6 +432,7 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."user as u ON m.fk_user_author = u.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_lot as pl ON m.batch = pl.batch AND m.fk_product = pl.fk_product"; $sql.= " WHERE m.fk_product = p.rowid"; +if ($msid > 0) $sql .= " AND m.rowid = ".$msid; $sql.= " AND m.fk_entrepot = e.rowid"; $sql.= " AND e.entity IN (".getEntity('stock', 1).")"; if (empty($conf->global->STOCK_SUPPORTS_SERVICES)) $sql.= " AND p.fk_product_type = 0"; @@ -511,8 +513,12 @@ if ($resql) $i = 0; $help_url='EN:Module_Stocks_En|FR:Module_Stock|ES:Módulo_Stocks'; - $texte = $langs->trans("ListOfStockMovements"); - if ($id) $texte.=' ('.$langs->trans("ForThisWarehouse").')'; + if ($msid) $texte = $langs->trans('StockMovementForId', $msid); + else + { + $texte = $langs->trans("ListOfStockMovements"); + if ($id) $texte.=' ('.$langs->trans("ForThisWarehouse").')'; + } llxHeader("",$texte,$help_url); /* diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 65c4e6bc94c..c7afb489634 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -496,6 +496,10 @@ class Project extends CommonObject { $sql = "SELECT DISTINCT pt.rowid, ptt.fk_user FROM " . MAIN_DB_PREFIX . "projet_task as pt, " . MAIN_DB_PREFIX . "projet_task_time as ptt WHERE pt.rowid = ptt.fk_task AND pt.fk_projet=" . $this->id; } + elseif ($type == 'stock_mouvement') + { + $sql = 'SELECT ms.rowid, ms.fk_user_author as fk_user FROM ' . MAIN_DB_PREFIX . 'stock_mouvement as ms WHERE ms.origintype = "project" AND ms.fk_origin = ' . $this->id . ' AND ms.type_mouvement = 1'; + } else { $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . $tablename." WHERE fk_projet=" . $this->id; diff --git a/htdocs/projet/element.php b/htdocs/projet/element.php index 908acd89b3f..2853b9031da 100644 --- a/htdocs/projet/element.php +++ b/htdocs/projet/element.php @@ -48,6 +48,7 @@ if (! empty($conf->expensereport->enabled)) require_once DOL_DOCUMENT_ROOT.'/exp if (! empty($conf->agenda->enabled)) require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php'; if (! empty($conf->don->enabled)) require_once DOL_DOCUMENT_ROOT.'/don/class/don.class.php'; if (! empty($conf->loan->enabled)) require_once DOL_DOCUMENT_ROOT.'/loan/class/loan.class.php'; +if (! empty($conf->stock->enabled)) require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php'; $langs->load("projects"); $langs->load("companies"); @@ -397,6 +398,15 @@ $listofreferent=array( 'buttonnew'=>'AddTimeSpent', 'testnew'=>$user->rights->projet->creer, 'test'=>($conf->projet->enabled && $user->rights->projet->lire && empty($conf->global->PROJECT_HIDE_TASKS))), +'stock_mouvement'=>array( + 'name'=>"MouvementStockAssociated", + 'title'=>"ListMouvementStockProject", + 'class'=>'MouvementStock', + 'margin'=>'minus', + 'table'=>'stock_mouvement', + 'datefieldname'=>'datem', + 'disableamount'=>0, + 'test'=>$conf->stock->enabled && $user->rights->stock->mouvement->lire), /* No need for this, available on dedicated tab "Agenda/Events" 'agenda'=>array( 'name'=>"Agenda", @@ -483,6 +493,7 @@ $langs->load("bills"); $langs->load("orders"); $langs->load("proposals"); $langs->load("margins"); +if (!empty($conf->stock->enabled)) $langs->load('stocks'); print load_fiche_titre($langs->trans("Profit"), '', 'title_accountancy'); @@ -529,6 +540,7 @@ foreach ($listofreferent as $key => $value) // Special cases if ($tablename != 'expensereport_det' && method_exists($element, 'fetch_thirdparty')) $element->fetch_thirdparty(); if ($tablename == 'don') $total_ht_by_line=$element->amount; + elseif ($tablename == 'stock_mouvement') $total_ht_by_line=$element->price*abs($element->qty); elseif ($tablename == 'projet_task') { if ($idofelementuser) @@ -553,6 +565,7 @@ foreach ($listofreferent as $key => $value) if ($qualifiedfortotal) $total_ht = $total_ht + $total_ht_by_line; if ($tablename == 'don') $total_ttc_by_line=$element->amount; + elseif ($tablename == 'stock_mouvement') $total_ttc_by_line=$element->price*abs($element->qty); elseif ($tablename == 'projet_task') { $defaultvat = get_default_tva($mysoc, $mysoc); @@ -601,6 +614,9 @@ foreach ($listofreferent as $key => $value) case 'Contrat': $newclassname = 'Contract'; break; + case 'MouvementStock': + $newclassname = 'StockMovement'; + break; default: $newclassname = $classname; } @@ -717,7 +733,7 @@ foreach ($listofreferent as $key => $value) // Thirdparty or user print '
'; // Amount HT @@ -800,7 +816,7 @@ foreach ($listofreferent as $key => $value) print ""; // Remove link print ''; } // Last send From 5e4509953d79c8714bdb0a5df2ad97801456aecd Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 29 Jan 2017 21:25:29 +0100 Subject: [PATCH 037/170] Associated product must be on another export profile --- htdocs/core/modules/modProduct.class.php | 34 +++++++++++++++++++++--- htdocs/exports/export.php | 4 +++ htdocs/langs/en_US/products.lang | 2 +- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/htdocs/core/modules/modProduct.class.php b/htdocs/core/modules/modProduct.class.php index 330349b0842..6927c7c7fef 100644 --- a/htdocs/core/modules/modProduct.class.php +++ b/htdocs/core/modules/modProduct.class.php @@ -165,11 +165,11 @@ class modProduct extends DolibarrModules if (! empty($conf->fournisseur->enabled)) $this->export_TypeFields_array[$r]=array_merge($this->export_TypeFields_array[$r],array('s.nom'=>'Text','pf.ref_fourn'=>'Text','pf.unitprice'=>'Numeric')); if (! empty($conf->global->MAIN_MULTILANGS)) $this->export_TypeFields_array[$r]=array_merge($this->export_TypeFields_array[$r],array('l.lang'=>'Text', 'l.label'=>'Text','l.description'=>'Text','l.note'=>'Text')); if (! empty($conf->global->EXPORTTOOL_CATEGORIES)) $this->export_TypeFields_array[$r]=array_merge($this->export_TypeFields_array[$r],array("group_concat(cat.label)"=>'Text')); - $this->export_entities_array[$r]=array('p.rowid'=>"product",'p.ref'=>"product",'p.label'=>"product",'p.description'=>"product",'p.url'=>"product",'p.accountancy_code_sell'=>'product','p.accountancy_code_sell'=>'product','p.note'=>"product",'p.length'=>"product",'p.surface'=>"product",'p.volume'=>"product",'p.weight'=>"product",'p.customcode'=>'product','p.price_base_type'=>"product",'p.price'=>"product",'p.price_ttc'=>"product",'p.tva_tx'=>"product",'p.tosell'=>"product",'p.tobuy'=>"product",'p.datec'=>"product",'p.tms'=>"product"); + $this->export_entities_array[$r]=array('p.rowid'=>"product",'p.ref'=>"product",'p.label'=>"product",'p.description'=>"product",'p.url'=>"product",'p.accountancy_code_sell'=>'product','p.accountancy_code_buy'=>'product','p.note'=>"product",'p.length'=>"product",'p.surface'=>"product",'p.volume'=>"product",'p.weight'=>"product",'p.customcode'=>'product','p.price_base_type'=>"product",'p.price'=>"product",'p.price_ttc'=>"product",'p.tva_tx'=>"product",'p.tosell'=>"product",'p.tobuy'=>"product",'p.datec'=>"product",'p.tms'=>"product"); if (! empty($conf->global->EXPORTTOOL_CATEGORIES)) $this->export_entities_array[$r]=array_merge($this->export_entities_array[$r],array("group_concat(cat.label)"=>'category')); if (! empty($conf->stock->enabled)) $this->export_entities_array[$r]=array_merge($this->export_entities_array[$r],array('p.stock'=>'product','p.pmp'=>'product')); if (! empty($conf->barcode->enabled)) $this->export_entities_array[$r]=array_merge($this->export_entities_array[$r],array('p.barcode'=>'product')); - if (! empty($conf->fournisseur->enabled)) $this->export_entities_array[$r]=array_merge($this->export_entities_array[$r],array('s.nom'=>'product','pf.ref_fourn'=>'product','pf.unitprice'=>'product')); + if (! empty($conf->fournisseur->enabled)) $this->export_entities_array[$r]=array_merge($this->export_entities_array[$r],array('s.nom'=>'company','pf.ref_fourn'=>'product','pf.unitprice'=>'product')); if (! empty($conf->global->MAIN_MULTILANGS)) $this->export_entities_array[$r]=array_merge($this->export_entities_array[$r],array('l.lang'=>'translation', 'l.label'=>'translation','l.description'=>'translation','l.note'=>'translation')); $keyforselect='product'; $keyforelement='product'; $keyforaliasextra='extra'; include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; @@ -211,7 +211,35 @@ class modProduct extends DolibarrModules $this->export_sql_end[$r] .=' WHERE p.fk_product_type = 0 AND p.entity IN ('.getEntity("product", 1).')'; } - + if (! empty($conf->global->PRODUIT_SOUSPRODUITS)) + { + $r++; + $this->export_code[$r]=$this->rights_class.'_'.$r; + $this->export_label[$r]="AssociatedProducts"; // Translation key (used only if key ExportDataset_xxx_z not found) + $this->export_permission[$r]=array(array("produit","export")); + $this->export_fields_array[$r]=array('p.rowid'=>"Id",'p.ref'=>"Ref",'p.label'=>"Label",'p.description'=>"Description",'p.url'=>"PublicUrl",'p.accountancy_code_sell'=>"ProductAccountancySellCode",'p.accountancy_code_buy'=>"ProductAccountancyBuyCode",'p.note'=>"Note",'p.length'=>"Length",'p.surface'=>"Surface",'p.volume'=>"Volume",'p.weight'=>"Weight",'p.customcode'=>'CustomCode','p.price_base_type'=>"PriceBase",'p.price'=>"UnitPriceHT",'p.price_ttc'=>"UnitPriceTTC",'p.tva_tx'=>'VATRate','p.tosell'=>"OnSell",'p.tobuy'=>"OnBuy",'p.datec'=>'DateCreation','p.tms'=>'DateModification'); + if (! empty($conf->stock->enabled)) $this->export_fields_array[$r]=array_merge($this->export_fields_array[$r],array('p.stock'=>'Stock','p.seuil_stock_alerte'=>'StockLimit','p.desiredstock'=>'DesiredStock','p.pmp'=>'PMPValue')); + if (! empty($conf->barcode->enabled)) $this->export_fields_array[$r]=array_merge($this->export_fields_array[$r],array('p.barcode'=>'BarCode')); + $this->export_fields_array[$r]=array_merge($this->export_fields_array[$r],array('pa.qty'=>'Qty','pa.incdec'=>'ComposedProductIncDecStock')); + $this->export_TypeFields_array[$r]=array('p.ref'=>"Text",'p.label'=>"Text",'p.description'=>"Text",'p.url'=>"Text",'p.accountancy_code_sell'=>"Text",'p.accountancy_code_buy'=>"Text",'p.note'=>"Text",'p.length'=>"Numeric",'p.surface'=>"Numeric",'p.volume'=>"Numeric",'p.weight'=>"Numeric",'p.customcode'=>'Text','p.price_base_type'=>"Text",'p.price'=>"Numeric",'p.price_ttc'=>"Numeric",'p.tva_tx'=>'Numeric','p.tosell'=>"Boolean",'p.tobuy'=>"Boolean",'p.datec'=>'Date','p.tms'=>'Date'); + if (! empty($conf->stock->enabled)) $this->export_TypeFields_array[$r]=array_merge($this->export_TypeFields_array[$r],array('p.stock'=>'Numeric','p.seuil_stock_alerte'=>'Numeric','p.desiredstock'=>'Numeric','p.pmp'=>'Numeric','p.cost_price'=>'Numeric')); + if (! empty($conf->barcode->enabled)) $this->export_TypeFields_array[$r]=array_merge($this->export_TypeFields_array[$r],array('p.barcode'=>'Text')); + $this->export_TypeFields_array[$r]=array_merge($this->export_TypeFields_array[$r],array('pa.qty'=>'Numeric')); + $this->export_entities_array[$r]=array('p.rowid'=>"virtualproduct",'p.ref'=>"virtualproduct",'p.label'=>"virtualproduct",'p.description'=>"virtualproduct",'p.url'=>"virtualproduct",'p.accountancy_code_sell'=>'virtualproduct','p.accountancy_code_buy'=>'virtualproduct','p.note'=>"virtualproduct",'p.length'=>"virtualproduct",'p.surface'=>"virtualproduct",'p.volume'=>"virtualproduct",'p.weight'=>"virtualproduct",'p.customcode'=>'virtualproduct','p.price_base_type'=>"virtualproduct",'p.price'=>"virtualproduct",'p.price_ttc'=>"virtualproduct",'p.tva_tx'=>"virtualproduct",'p.tosell'=>"virtualproduct",'p.tobuy'=>"virtualproduct",'p.datec'=>"virtualproduct",'p.tms'=>"virtualproduct"); + if (! empty($conf->stock->enabled)) $this->export_entities_array[$r]=array_merge($this->export_entities_array[$r],array('p.stock'=>'virtualproduct','p.seuil_stock_alerte'=>'virtualproduct','p.desiredstock'=>'virtualproduct','p.pmp'=>'virtualproduct')); + if (! empty($conf->barcode->enabled)) $this->export_entities_array[$r]=array_merge($this->export_entities_array[$r],array('p.barcode'=>'virtualproduct')); + $this->export_entities_array[$r]=array_merge($this->export_entities_array[$r],array('pa.qty'=>"subproduct",'pa.incdec'=>'subproduct')); + $keyforselect='product'; $keyforelement='product'; $keyforaliasextra='extra'; + include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; + $this->export_fields_array[$r]=array_merge($this->export_fields_array[$r],array('p2.rowid'=>"Id",'p2.ref'=>"Ref",'p2.label'=>"Label",'p2.description'=>"Description")); + $this->export_entities_array[$r]=array_merge($this->export_entities_array[$r],array('p2.rowid'=>"subproduct",'p2.ref'=>"subproduct",'p2.label'=>"subproduct",'p2.description'=>"subproduct")); + $this->export_sql_start[$r]='SELECT DISTINCT '; + $this->export_sql_end[$r] =' FROM '.MAIN_DB_PREFIX.'product as p'; + $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'product_extrafields as extra ON p.rowid = extra.fk_object,'; + $this->export_sql_end[$r] .=' '.MAIN_DB_PREFIX.'product_association as pa, '.MAIN_DB_PREFIX.'product as p2'; + $this->export_sql_end[$r] .=' WHERE p.fk_product_type = 0 AND p.entity IN ('.getEntity("product", 1).')'; + $this->export_sql_end[$r] .=' AND p.rowid = pa.fk_product_pere AND p2.rowid = pa.fk_product_fils'; + } // Imports //-------- diff --git a/htdocs/exports/export.php b/htdocs/exports/export.php index 604af215581..01e963585ec 100644 --- a/htdocs/exports/export.php +++ b/htdocs/exports/export.php @@ -61,6 +61,8 @@ $entitytoicon = array( 'other' => 'generic', 'account' => 'account', 'product' => 'product', + 'virtualproduct'=>'product', + 'subproduct' => 'product', 'warehouse' => 'stock', 'batch' => 'stock', 'category' => 'category', @@ -93,6 +95,8 @@ $entitytolang = array( 'account' => 'BankTransactions', 'payment' => 'Payment', 'product' => 'Product', + 'virtualproduct' => 'AssociatedProducts', + 'subproduct' => 'SubProduct', 'service' => 'Service', 'stock' => 'Stock', 'batch' => 'Batch', diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang index 89aa8ff296e..1bf032de891 100644 --- a/htdocs/langs/en_US/products.lang +++ b/htdocs/langs/en_US/products.lang @@ -256,4 +256,4 @@ VolumeUnits=Volume unit SizeUnits=Size unit DeleteProductBuyPrice=Delete buying price ConfirmDeleteProductBuyPrice=Are you sure you want to delete this buying price? - +SubProduct=Sub product From 58d77a14af7118a7ae9060c34c52488de668367e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Garci=CC=81a=20de=20La=20Fuente?= Date: Sun, 29 Jan 2017 23:05:20 +0100 Subject: [PATCH 038/170] FIX #4758 PHP warning when installing to PostgreSQL with incorrect credentials Closes #4758 --- htdocs/core/db/pgsql.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php index 3b9ad9ac3e4..89f2a66bdea 100644 --- a/htdocs/core/db/pgsql.class.php +++ b/htdocs/core/db/pgsql.class.php @@ -412,8 +412,8 @@ class DoliDBPgsql extends DoliDB { $this->database_name = $name; pg_set_error_verbosity($this->db, PGSQL_ERRORS_VERBOSE); // Set verbosity to max + pg_query($this->db, "set datestyle = 'ISO, YMD';"); } - pg_query($this->db, "set datestyle = 'ISO, YMD';"); return $this->db; } From f16e65c6b11bbfe38ef0921689389a670c9c9810 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 30 Jan 2017 12:22:58 +0100 Subject: [PATCH 039/170] Fix css --- htdocs/projet/activity/perday.php | 2 +- htdocs/projet/activity/perweek.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/projet/activity/perday.php b/htdocs/projet/activity/perday.php index e694d6630a9..195ad3dbd89 100644 --- a/htdocs/projet/activity/perday.php +++ b/htdocs/projet/activity/perday.php @@ -427,7 +427,7 @@ print ''; print ''; print ''; print ''; -if (! empty($conf->global->PROJECT_LINES_PERDAY_SHOW_THIRDPARTY)) print ''; +if (! empty($conf->global->PROJECT_LINES_PERDAY_SHOW_THIRDPARTY)) print ''; print ''; print ''; print ''; diff --git a/htdocs/projet/activity/perweek.php b/htdocs/projet/activity/perweek.php index 584923636f0..9a34913b2f5 100644 --- a/htdocs/projet/activity/perweek.php +++ b/htdocs/projet/activity/perweek.php @@ -427,7 +427,7 @@ print ''; print ''; print ''; print ''; -if (! empty($conf->global->PROJECT_LINES_PERWEEK_SHOW_THIRDPARTY)) print ''; +if (! empty($conf->global->PROJECT_LINES_PERWEEK_SHOW_THIRDPARTY)) print ''; print ''; print ''; print ''; From c79137d14cae26a33711b0fc689c2172d547db00 Mon Sep 17 00:00:00 2001 From: arnaud Date: Mon, 30 Jan 2017 17:29:01 +0100 Subject: [PATCH 040/170] FIX Supplier Order list filter by project --- htdocs/fourn/commande/list.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/htdocs/fourn/commande/list.php b/htdocs/fourn/commande/list.php index c8d333f578d..33b9c81fd0c 100644 --- a/htdocs/fourn/commande/list.php +++ b/htdocs/fourn/commande/list.php @@ -77,6 +77,7 @@ $search_total_vat=GETPOST('search_total_vat','alpha'); $search_total_ttc=GETPOST('search_total_ttc','alpha'); $optioncss = GETPOST('optioncss','alpha'); $billed = GETPOST('billed','int'); +$search_project_ref=GETPOST('search_project_ref','alpha'); $page = GETPOST('page','int'); $sortorder = GETPOST('sortorder','alpha'); @@ -204,6 +205,7 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP $deliverymonth=''; $deliveryyear=''; $billed=''; + $search_project_ref=''; $search_array_options=array(); } @@ -350,6 +352,7 @@ if ($search_user > 0) $sql.= " AND ec.fk_c_type_contact = tc.rowid AND tc.elemen if ($search_total_ht != '') $sql.= natural_search('cf.total_ht', $search_total_ht, 1); if ($search_total_vat != '') $sql.= natural_search('cf.tva', $search_total_vat, 1); if ($search_total_ttc != '') $sql.= natural_search('cf.total_ttc', $search_total_ttc, 1); +if ($search_project_ref != '') $sql.= natural_search("p.ref",$search_project_ref); // Add where from extra fields foreach ($search_array_options as $key => $val) From 8e9a9637a0ccaf3a7b9117b6f9529018b3c008b0 Mon Sep 17 00:00:00 2001 From: atm-ph Date: Mon, 30 Jan 2017 21:52:52 +0100 Subject: [PATCH 041/170] Fix travis --- htdocs/product/stock/class/mouvementstock.class.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index 53ab3163566..e6631d4b716 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -897,8 +897,8 @@ class MouvementStock extends CommonObject /** * Set attribute origin to object * - * @param string $origin_element - * @param int $origin_id + * @param string $origin_element type of element + * @param int $origin_id id of element * * @return void */ @@ -991,7 +991,6 @@ class MouvementStock extends CommonObject /** * Renvoi le libelle d'un status donne * - * @param int $status Id status * @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 */ From e1e2c49d93b77017473638ec1ff478b33ce9d91b Mon Sep 17 00:00:00 2001 From: aspangaro Date: Tue, 31 Jan 2017 06:08:40 +0100 Subject: [PATCH 042/170] NEW : [FP17] Accountancy - Add select field in list of accounts --- htdocs/accountancy/admin/account.php | 216 ++++++++++++++++++--------- 1 file changed, 142 insertions(+), 74 deletions(-) diff --git a/htdocs/accountancy/admin/account.php b/htdocs/accountancy/admin/account.php index e82522df26f..a7ad5d14e90 100644 --- a/htdocs/accountancy/admin/account.php +++ b/htdocs/accountancy/admin/account.php @@ -1,6 +1,6 @@ - * Copyright (C) 2013-2016 Alexandre Spangaro + * Copyright (C) 2013-2017 Alexandre Spangaro * Copyright (C) 2016 Laurent Destailleur * * This program is free software; you can redistribute it and/or modify @@ -41,6 +41,7 @@ $action = GETPOST('action'); $cancel = GETPOST('cancel'); $id = GETPOST('id', 'int'); $rowid = GETPOST('rowid', 'int'); + $search_account = GETPOST("search_account"); $search_label = GETPOST("search_label"); $search_accountparent = GETPOST("search_accountparent"); @@ -68,6 +69,15 @@ if (! $sortfield) if (! $sortorder) $sortorder = "ASC"; +$arrayfields=array( + 'aa.account_number'=>array('label'=>$langs->trans("AccountNumber"), 'checked'=>1), + 'aa.label'=>array('label'=>$langs->trans("Label"), 'checked'=>1), + 'aa.account_parent'=>array('label'=>$langs->trans("Accountparent"), 'checked'=>0), + 'aa.pcg_type'=>array('label'=>$langs->trans("Pcgtype"), 'checked'=>0), + 'aa.pcg_subtype'=>array('label'=>$langs->trans("Pcgsubtype"), 'checked'=>0), + 'aa.active'=>array('label'=>$langs->trans("Activated"), 'checked'=>1) +); + $accounting = new AccountingAccount($db); @@ -95,6 +105,7 @@ if (empty($reshook)) $search_accountparent = ""; $search_pcgtype = ""; $search_pcgsubtype = ""; + $search_array_options=array(); } if (GETPOST('change_chart')) @@ -135,6 +146,7 @@ if (empty($reshook)) /* * View */ +$form=new Form($db); llxHeader('', $langs->trans("ListAccounts")); @@ -145,8 +157,6 @@ if ($action == 'delete') { $pcgver = $conf->global->CHARTOFACCOUNTS; - - $sql = "SELECT aa.rowid, aa.fk_pcg_version, aa.pcg_type, aa.pcg_subtype, aa.account_number, aa.account_parent , aa.label, aa.active, "; $sql .= " a2.rowid as rowid2, a2.label as label2, a2.account_number as account_number2"; $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_account as aa"; @@ -154,21 +164,12 @@ $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_system as asy ON aa.fk_pcg_vers $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as a2 ON aa.account_parent = a2.rowid"; $sql .= " WHERE asy.rowid = " . $pcgver; -if (strlen(trim($search_account))) { - $sql .= natural_search("aa.account_number", $search_account); -} -if (strlen(trim($search_label))) { - $sql .= natural_search("aa.label", $search_label); -} -if (strlen(trim($search_accountparent))) { - $sql .= natural_search("aa.account_parent", $search_accountparent); -} -if (strlen(trim($search_pcgtype))) { - $sql .= natural_search("aa.pcg_type", $search_pcgtype); -} -if (strlen(trim($search_pcgsubtype))) { - $sql .= natural_search("aa.pcg_subtype", $search_pcgsubtype); -} +if (strlen(trim($search_account))) $sql .= natural_search("aa.account_number", $search_account); +if (strlen(trim($search_label))) $sql .= natural_search("aa.label", $search_label); +if (strlen(trim($search_accountparent))) $sql .= natural_search("aa.account_parent", $search_accountparent); +if (strlen(trim($search_pcgtype))) $sql .= natural_search("aa.pcg_type", $search_pcgtype); +if (strlen(trim($search_pcgsubtype))) $sql .= natural_search("aa.pcg_subtype", $search_pcgsubtype); + $sql .= $db->order($sortfield, $sortorder); // Count total nb of records @@ -184,18 +185,19 @@ $sql .= $db->plimit($limit + 1, $offset); dol_syslog('accountancy/admin/account.php:: $sql=' . $sql); $resql = $db->query($sql); -if ($resql) { - +if ($resql) +{ $num = $db->num_rows($resql); - + $params=''; - if ($search_account != "") $params.= '&search_account='.urlencode($search_account); - if ($search_label != "") $params.= '&search_label='.urlencode($search_label); - if ($search_accountparent != "") $params.= '&search_accountparent='.urlencode($search_accountparent); - if ($search_pcgtype != "") $params.= '&search_pcgtype='.urlencode($search_pcgtype); - if ($search_pcgsubtype != "") $params.= '&search_pcgsubtype='.urlencode($search_pcgsubtype); - if ($optioncss != '') $param.='&optioncss='.$optioncss; - + if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage; + if ($search_account) $params.= '&search_account='.urlencode($search_account); + if ($search_label) $params.= '&search_label='.urlencode($search_label); + if ($search_accountparent) $params.= '&search_accountparent='.urlencode($search_accountparent); + if ($search_pcgtype) $params.= '&search_pcgtype='.urlencode($search_pcgtype); + if ($search_pcgsubtype) $params.= '&search_pcgsubtype='.urlencode($search_pcgsubtype); + if ($optioncss) $param.='&optioncss='.$optioncss; + print_barre_liste($langs->trans('ListAccounts'), $page, $_SERVER["PHP_SELF"], $params, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_accountancy'); print ''; @@ -207,7 +209,7 @@ if ($resql) { $sql = "SELECT rowid, pcg_version, label, active"; $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_system"; $sql .= " WHERE active = 1"; - dol_syslog('accountancy/admin/index.php:: $sql=' . $sql); + dol_syslog('accountancy/admin/account.php:: $sql=' . $sql); $resqlchart = $db->query($sql); $var = true; if ($resqlchart) { @@ -228,34 +230,53 @@ if ($resql) { print ''; print '
'; print "
\n"; + + print ''; + $i = 0; + print '
'."\n"; + if ($optioncss != '') print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print '
' . $langs->trans("Addanaccount") . ''; print '' . $langs->trans("ApplyMassCategories") . ''; // print '' . $langs->trans("ImportAccount") . ''; // print '' . $langs->trans("CheckProductAccountancyCode") . ''; print '

'; - print '
'.$langs->trans("UnitPurchaseValue").''.$langs->trans('Project').''; + $formproject->select_projects(); + print '
'; if (in_array($tablename, array('projet_task')) && $key == 'project_task') print ''; // if $key == 'project_task', we don't want details per user - elseif (in_array($tablename, array('expensereport_det','don','projet_task'))) print $langs->trans("User"); + elseif (in_array($tablename, array('expensereport_det','don','projet_task','stock_mouvement'))) print $langs->trans("User"); else print $langs->trans("ThirdParty"); print '
'; - if ($tablename != 'projet_task') + if ($tablename != 'projet_task' && $tablename != 'stock_mouvement') { print '' . img_picto($langs->trans('Unlink'), 'editdelete') . ''; } @@ -848,6 +864,7 @@ foreach ($listofreferent as $key => $value) // Date or TimeSpent $date=''; $total_time_by_line = null; if ($tablename == 'expensereport_det') $date = $element->date; // No draft status on lines + elseif ($tablename == 'stock_mouvement') $date = $element->datem; elseif (! empty($element->status) || ! empty($element->statut) || ! empty($element->fk_status)) { if ($tablename=='don') $date = $element->datedon; @@ -889,7 +906,7 @@ foreach ($listofreferent as $key => $value) $tmpuser->fetch($expensereport->fk_user_author); print $tmpuser->getNomUrl(1,'',48); } - else if ($tablename == 'don') + else if ($tablename == 'don' || $tablename == 'stock_mouvement') { if ($element->fk_user_author > 0) { @@ -911,6 +928,7 @@ foreach ($listofreferent as $key => $value) $total_ht_by_line=null; $othermessage=''; if ($tablename == 'don') $total_ht_by_line=$element->amount; + elseif ($tablename == 'stock_mouvement') $total_ht_by_line=$element->price*abs($element->qty); elseif (in_array($tablename, array('projet_task'))) { if (! empty($conf->salaries->enabled)) @@ -950,6 +968,7 @@ foreach ($listofreferent as $key => $value) { $total_ttc_by_line=null; if ($tablename == 'don') $total_ttc_by_line=$element->amount; + elseif ($tablename == 'stock_mouvement') $total_ttc_by_line=$element->price*abs($element->qty); elseif ($tablename == 'projet_task') { if (! empty($conf->salaries->enabled)) @@ -998,6 +1017,10 @@ foreach ($listofreferent as $key => $value) print $element->progress.' %'; } } + else if ($tablename == 'stock_mouvement') + { + print $element->getLibStatut(3); + } else { print $element->getLibStatut(5); From b4b81d8dcb83f84f45292c73ff3820cc141d2d78 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 29 Jan 2017 14:53:05 +0100 Subject: [PATCH 030/170] Fix css and missing translation key --- htdocs/comm/mailing/cibles.php | 2 +- htdocs/core/modules/mailings/advthirdparties.modules.php | 1 + htdocs/core/modules/mailings/contacts1.modules.php | 6 +++--- htdocs/core/modules/mailings/contacts2.modules.php | 2 +- htdocs/core/modules/mailings/contacts3.modules.php | 4 ++-- htdocs/core/modules/mailings/contacts4.modules.php | 4 ++-- htdocs/core/modules/mailings/example.modules.php | 4 +++- htdocs/core/modules/mailings/fraise.modules.php | 6 ++---- htdocs/core/modules/mailings/framboise.modules.php | 6 ++---- htdocs/core/modules/mailings/modules_mailings.php | 6 ++++-- htdocs/core/modules/mailings/pomme.modules.php | 1 + htdocs/core/modules/mailings/thirdparties.modules.php | 1 + .../mailings/thirdparties_services_expired.modules.php | 1 + htdocs/core/modules/mailings/xinputfile.modules.php | 1 + htdocs/core/modules/mailings/xinputuser.modules.php | 1 + htdocs/langs/en_US/mails.lang | 5 ++++- htdocs/theme/eldy/style.css.php | 3 +++ htdocs/theme/md/style.css.php | 3 +++ 18 files changed, 36 insertions(+), 21 deletions(-) diff --git a/htdocs/comm/mailing/cibles.php b/htdocs/comm/mailing/cibles.php index e104db4dcda..0d096f1ecef 100644 --- a/htdocs/comm/mailing/cibles.php +++ b/htdocs/comm/mailing/cibles.php @@ -231,7 +231,7 @@ if ($object->fetch($id) >= 0) $var=!$var; - $allowaddtarget=($object->statut == 0); + $allowaddtarget=($object->statut == 0 || $object->statut == 1); // Show email selectors if ($allowaddtarget && $user->rights->mailing->creer) diff --git a/htdocs/core/modules/mailings/advthirdparties.modules.php b/htdocs/core/modules/mailings/advthirdparties.modules.php index cd0f229983d..737b239498f 100644 --- a/htdocs/core/modules/mailings/advthirdparties.modules.php +++ b/htdocs/core/modules/mailings/advthirdparties.modules.php @@ -26,6 +26,7 @@ include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; class mailing_advthirdparties extends MailingTargets { var $name='ThirdPartyAdvancedTargeting'; + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found var $desc="Third parties"; var $require_admin=0; diff --git a/htdocs/core/modules/mailings/contacts1.modules.php b/htdocs/core/modules/mailings/contacts1.modules.php index 99de75c6d26..02bcc17ec30 100644 --- a/htdocs/core/modules/mailings/contacts1.modules.php +++ b/htdocs/core/modules/mailings/contacts1.modules.php @@ -34,12 +34,12 @@ include_once DOL_DOCUMENT_ROOT.'/core/modules/mailings/modules_mailings.php'; class mailing_contacts1 extends MailingTargets { var $name='ContactCompanies'; // Identifiant du module mailing - // This label is used if no translation is found for key MailingModuleDescXXX where XXX=name is found - var $desc='Contacts des tiers (prospects, clients, fournisseurs...)'; + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found + var $desc='Contacts of thirdparties (prospects, customers, suppliers...)'; var $require_module=array("societe"); // Module mailing actif si modules require_module actifs var $require_admin=0; // Module mailing actif pour user admin ou non var $picto='contact'; - + var $db; diff --git a/htdocs/core/modules/mailings/contacts2.modules.php b/htdocs/core/modules/mailings/contacts2.modules.php index 08e92af45e8..778b2182e1a 100644 --- a/htdocs/core/modules/mailings/contacts2.modules.php +++ b/htdocs/core/modules/mailings/contacts2.modules.php @@ -33,7 +33,7 @@ include_once DOL_DOCUMENT_ROOT.'/core/modules/mailings/modules_mailings.php'; class mailing_contacts2 extends MailingTargets { var $name='ContactsByFunction'; - // This label is used if no translation is found for key MailingModuleDescXXX where XXX=name is found + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found var $desc='Add contacts by function'; var $require_admin=0; diff --git a/htdocs/core/modules/mailings/contacts3.modules.php b/htdocs/core/modules/mailings/contacts3.modules.php index b9878fd1c83..2c142541023 100644 --- a/htdocs/core/modules/mailings/contacts3.modules.php +++ b/htdocs/core/modules/mailings/contacts3.modules.php @@ -32,8 +32,8 @@ include_once DOL_DOCUMENT_ROOT.'/core/modules/mailings/modules_mailings.php'; class mailing_contacts3 extends MailingTargets { var $name='ContactsByCompanyCategory'; - // This label is used if no translation is found for key MailingModuleDescXXX where XXX=name is found - var $desc='Add contacts by company category'; + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found + var $desc='Add contacts by company category'; var $require_admin=0; var $require_module=array(); diff --git a/htdocs/core/modules/mailings/contacts4.modules.php b/htdocs/core/modules/mailings/contacts4.modules.php index 421fd2676c8..a79d48a03e4 100644 --- a/htdocs/core/modules/mailings/contacts4.modules.php +++ b/htdocs/core/modules/mailings/contacts4.modules.php @@ -32,8 +32,8 @@ include_once DOL_DOCUMENT_ROOT.'/core/modules/mailings/modules_mailings.php'; class mailing_contacts4 extends MailingTargets { var $name='ContactsByCategory'; - // This label is used if no translation is found for key MailingModuleDescXXX where XXX=name is found - var $desc='Add contacts by category'; + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found + var $desc='Add contacts by category'; var $require_admin=0; var $require_module=array(); diff --git a/htdocs/core/modules/mailings/example.modules.php b/htdocs/core/modules/mailings/example.modules.php index f0f8b99e3c5..548510f81a0 100644 --- a/htdocs/core/modules/mailings/example.modules.php +++ b/htdocs/core/modules/mailings/example.modules.php @@ -32,7 +32,9 @@ class mailing_example extends MailingTargets var $desc='Put here a description'; // CHANGE THIS: Set to 1 if selector is available for admin users only var $require_admin=0; - + // CHANGE THIS: Add a tooltip language key to add a tooltip help icon after the email target selector + var $tooltip='MyTooltipLangKey'; + var $require_module=array(); var $picto=''; var $db; diff --git a/htdocs/core/modules/mailings/fraise.modules.php b/htdocs/core/modules/mailings/fraise.modules.php index 089c42d4950..05d9e525652 100644 --- a/htdocs/core/modules/mailings/fraise.modules.php +++ b/htdocs/core/modules/mailings/fraise.modules.php @@ -32,12 +32,10 @@ include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; */ class mailing_fraise extends MailingTargets { - // CHANGE THIS: Put here a name not already used var $name='FundationMembers'; // Identifiant du module mailing - // CHANGE THIS: Put here a description of your selector module. - // This label is used if no translation found for key MailingModuleDescXXX where XXX=name is found + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found var $desc='Foundation members with emails (by status)'; - // CHANGE THIS: Set to 1 if selector is available for admin users only + // Set to 1 if selector is available for admin users only var $require_admin=0; var $require_module=array('adherent'); diff --git a/htdocs/core/modules/mailings/framboise.modules.php b/htdocs/core/modules/mailings/framboise.modules.php index a30a9ba3c17..82f7e51e0ef 100644 --- a/htdocs/core/modules/mailings/framboise.modules.php +++ b/htdocs/core/modules/mailings/framboise.modules.php @@ -23,12 +23,10 @@ include_once DOL_DOCUMENT_ROOT.'/core/modules/mailings/modules_mailings.php'; */ class mailing_framboise extends MailingTargets { - // CHANGE THIS: Put here a name not already used var $name='MembersCategories'; - // CHANGE THIS: Put here a description of your selector module. - // This label is used if no translation found for key MailingModuleDescXXX where XXX=name is found + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found var $desc="Foundation members with emails (by categories)"; - // CHANGE THIS: Set to 1 if selector is available for admin users only + // Set to 1 if selector is available for admin users only var $require_admin=0; var $require_module=array("adherent","categorie"); diff --git a/htdocs/core/modules/mailings/modules_mailings.php b/htdocs/core/modules/mailings/modules_mailings.php index 16b930e8907..1a97f3502b7 100644 --- a/htdocs/core/modules/mailings/modules_mailings.php +++ b/htdocs/core/modules/mailings/modules_mailings.php @@ -49,7 +49,7 @@ class MailingTargets // This can't be abstract as it is used for some method /** * Return description of email selector * - * @return string Retourne la traduction de la cle MailingModuleDescXXX ou XXX nom du module, ou $this->desc si non trouve + * @return string Return translation of module label. Try translation of $this->name then translation of 'MailingModuleDesc'.$this->name, or $this->desc if not found */ function getDesc() { @@ -58,7 +58,9 @@ class MailingTargets // This can't be abstract as it is used for some method $langs->load("mails"); $transstring="MailingModuleDesc".$this->name; $s=''; - if ($langs->trans($transstring) != $transstring) $s=$langs->trans($transstring); + + if ($langs->trans($this->name) != $this->name) $s=$langs->trans($this->name); + elseif ($langs->trans($transstring) != $transstring) $s=$langs->trans($transstring); else $s=$this->desc; if ($this->tooltip && is_object($form)) $s .= ' '.$form->textwithpicto('', $langs->trans($this->tooltip), 1, 1); diff --git a/htdocs/core/modules/mailings/pomme.modules.php b/htdocs/core/modules/mailings/pomme.modules.php index 3db13ac824c..5824f456365 100644 --- a/htdocs/core/modules/mailings/pomme.modules.php +++ b/htdocs/core/modules/mailings/pomme.modules.php @@ -31,6 +31,7 @@ include_once DOL_DOCUMENT_ROOT.'/core/modules/mailings/modules_mailings.php'; class mailing_pomme extends MailingTargets { var $name='DolibarrUsers'; // Identifiant du module mailing + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found var $desc='Dolibarr users with emails'; // Libelle utilise si aucune traduction pour MailingModuleDescXXX ou XXX=name trouv�e var $require_module=array(); // Module mailing actif si modules require_module actifs var $require_admin=1; // Module mailing actif pour user admin ou non diff --git a/htdocs/core/modules/mailings/thirdparties.modules.php b/htdocs/core/modules/mailings/thirdparties.modules.php index cfccc1ff673..55cce13fbcf 100644 --- a/htdocs/core/modules/mailings/thirdparties.modules.php +++ b/htdocs/core/modules/mailings/thirdparties.modules.php @@ -24,6 +24,7 @@ include_once DOL_DOCUMENT_ROOT.'/core/modules/mailings/modules_mailings.php'; class mailing_thirdparties extends MailingTargets { var $name='ContactsCategories'; + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found var $desc="Third parties (by categories)"; var $require_admin=0; diff --git a/htdocs/core/modules/mailings/thirdparties_services_expired.modules.php b/htdocs/core/modules/mailings/thirdparties_services_expired.modules.php index 39790959b97..de92354a964 100644 --- a/htdocs/core/modules/mailings/thirdparties_services_expired.modules.php +++ b/htdocs/core/modules/mailings/thirdparties_services_expired.modules.php @@ -23,6 +23,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; class mailing_thirdparties_services_expired extends MailingTargets { var $name='DolibarrContractsLinesExpired'; + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found var $desc='Third parties with expired contract\'s lines'; var $require_admin=0; diff --git a/htdocs/core/modules/mailings/xinputfile.modules.php b/htdocs/core/modules/mailings/xinputfile.modules.php index 04c4be3ce2e..152322c2899 100644 --- a/htdocs/core/modules/mailings/xinputfile.modules.php +++ b/htdocs/core/modules/mailings/xinputfile.modules.php @@ -31,6 +31,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; class mailing_xinputfile extends MailingTargets { var $name='EmailsFromFile'; // Identifiant du module mailing + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found var $desc='EMails from a file'; // Libelle utilise si aucune traduction pour MailingModuleDescXXX ou XXX=name trouv�e var $require_module=array(); // Module mailing actif si modules require_module actifs var $require_admin=0; // Module mailing actif pour user admin ou non diff --git a/htdocs/core/modules/mailings/xinputuser.modules.php b/htdocs/core/modules/mailings/xinputuser.modules.php index 4c5567f574d..794de3d9294 100644 --- a/htdocs/core/modules/mailings/xinputuser.modules.php +++ b/htdocs/core/modules/mailings/xinputuser.modules.php @@ -31,6 +31,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; class mailing_xinputuser extends MailingTargets { var $name='EmailsFromUser'; // Identifiant du module mailing + // This label is used if no translation is found for key XXX neither MailingModuleDescXXX where XXX=name is found var $desc='EMails input by user'; // Libelle utilise si aucune traduction pour MailingModuleDescXXX ou XXX=name trouv�e var $require_module=array(); // Module mailing actif si modules require_module actifs var $require_admin=0; // Module mailing actif pour user admin ou non diff --git a/htdocs/langs/en_US/mails.lang b/htdocs/langs/en_US/mails.lang index 0c894cc7b19..879f4469478 100644 --- a/htdocs/langs/en_US/mails.lang +++ b/htdocs/langs/en_US/mails.lang @@ -74,7 +74,10 @@ ResultOfMailSending=Result of mass EMail sending NbSelected=Nb selected NbIgnored=Nb ignored NbSent=Nb sent -ContactsWithThirdpartyFilter=Contact with customer filters +MailingModuleDescContactsWithThirdpartyFilter=Contact with customer filters +MailingModuleDescContactsByCompanyCategory=Contacts by third party category +MailingModuleDescContactsByCategory=Contacts by categories +MailingModuleDescContactsByFunction=Contacts by position # Libelle des modules de liste de destinataires mailing LineInFile=Line %s in file diff --git a/htdocs/theme/eldy/style.css.php b/htdocs/theme/eldy/style.css.php index ce5b7f3761e..6731b541423 100644 --- a/htdocs/theme/eldy/style.css.php +++ b/htdocs/theme/eldy/style.css.php @@ -2778,6 +2778,9 @@ tr.even td, tr.pair td, tr.odd td, tr.impair td, form.odd div.tagtd, form.impair padding: 5px 2px 5px 3px; border-bottom: 1px solid #ddd; } +form.pair, form.impair { + font-weight: normal; +} form.tagtr:last-of-type div.tagtd, tr.even:last-of-type td, tr.pair:last-of-type td, tr.odd:last-of-type td, tr.impair:last-of-type td { border-bottom: 0px !important; } diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index f1caa8e13a1..5fe8597b04c 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -2651,6 +2651,9 @@ tr.even td, tr.pair td, tr.odd td, tr.impair td, form.odd div.tagtd, form.impair padding: 5px 2px 5px 3px; border-bottom: 1px solid #eee; } +form.pair, form.impair { + font-weight: normal; +} tr.even:last-of-type td, tr.pair:last-of-type td, tr.odd:last-of-type td, tr.impair:last-of-type td { border-bottom: 0px !important; } From 756919de2a0a62ee86fa328a93415fbe3764c366 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 29 Jan 2017 15:31:22 +0100 Subject: [PATCH 031/170] Fix deadlock feature: could not solve a situation where an emailing was validated by error. --- htdocs/comm/mailing/card.php | 39 ++++++++++++++++++++++-- htdocs/comm/mailing/cibles.php | 2 +- htdocs/core/class/commonobject.class.php | 1 + htdocs/langs/en_US/mails.lang | 1 + 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/htdocs/comm/mailing/card.php b/htdocs/comm/mailing/card.php index aeaf19f8471..21e26814119 100644 --- a/htdocs/comm/mailing/card.php +++ b/htdocs/comm/mailing/card.php @@ -602,6 +602,29 @@ if (empty($reshook)) } } + // Action confirmation validation + if ($action == 'confirm_settodraft' && $confirm == 'yes') + { + if ($object->id > 0) + { + $result = $object->setStatut(0); + if ($result > 0) + { + //setEventMessages($langs->trans("MailingSuccessfullyValidated"), null, 'mesgs'); + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + else + { + setEventMessages($object->error, $object->errors, 'errors'); + } + } + else + { + dol_print_error($db); + } + } + // Resend if ($action == 'confirm_reset' && $confirm == 'yes') { @@ -727,7 +750,12 @@ else dol_fiche_head($head, 'card', $langs->trans("Mailing"), 0, 'email'); - // Confirmation de la validation du mailing + // Confirmation back to draft + if ($action == 'settodraft') + { + print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("SetToDraft"),$langs->trans("ConfirmUnvalidateEmailing"),"confirm_settodraft",'','',1); + } + // Confirmation validation of mailing if ($action == 'valid') { print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("ValidMailing"),$langs->trans("ConfirmValidMailing"),"confirm_valid",'','',1); @@ -900,10 +928,15 @@ else * Boutons d'action */ - if (GETPOST("cancel") || $confirm=='no' || $action == '' || in_array($action,array('valid','delete','sendall','clone'))) + if (GETPOST("cancel") || $confirm=='no' || $action == '' || in_array($action,array('settodraft', 'valid','delete','sendall','clone'))) { print "\n\n
\n"; + if (($object->statut == 1) && ($user->rights->mailing->valider || $object->fk_user_valid == $user->id)) + { + print ''.$langs->trans("SetToDraft").''; + } + if (($object->statut == 0 || $object->statut == 1) && $user->rights->mailing->creer) { print ''.$langs->trans("EditMailing").''; @@ -1212,7 +1245,7 @@ else print '
'; // Editeur wysiwyg require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; - $doleditor=new DolEditor('body',$object->body,'',320,'dolibarr_mailings','',true,true,$conf->global->FCKEDITOR_ENABLE_MAILING,20,'90%'); + $doleditor=new DolEditor('body',$object->body,'',600,'dolibarr_mailings','',true,true,$conf->global->FCKEDITOR_ENABLE_MAILING,20,'90%'); $doleditor->Create(); print '
'; diff --git a/htdocs/comm/mailing/cibles.php b/htdocs/comm/mailing/cibles.php index 0d096f1ecef..e104db4dcda 100644 --- a/htdocs/comm/mailing/cibles.php +++ b/htdocs/comm/mailing/cibles.php @@ -231,7 +231,7 @@ if ($object->fetch($id) >= 0) $var=!$var; - $allowaddtarget=($object->statut == 0 || $object->statut == 1); + $allowaddtarget=($object->statut == 0); // Show email selectors if ($allowaddtarget && $user->rights->mailing->creer) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 7b48240a72b..6340facf31a 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -2771,6 +2771,7 @@ abstract class CommonObject $this->db->begin(); $fieldstatus="fk_statut"; + if ($elementTable == 'mailing') $fieldstatus="statut"; if ($elementTable == 'user') $fieldstatus="statut"; if ($elementTable == 'expensereport') $fieldstatus="fk_statut"; if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status"; diff --git a/htdocs/langs/en_US/mails.lang b/htdocs/langs/en_US/mails.lang index 879f4469478..c337b6e1119 100644 --- a/htdocs/langs/en_US/mails.lang +++ b/htdocs/langs/en_US/mails.lang @@ -74,6 +74,7 @@ ResultOfMailSending=Result of mass EMail sending NbSelected=Nb selected NbIgnored=Nb ignored NbSent=Nb sent +ConfirmUnvalidateEmailing=Are you sure you want to change email %s to draft status? MailingModuleDescContactsWithThirdpartyFilter=Contact with customer filters MailingModuleDescContactsByCompanyCategory=Contacts by third party category MailingModuleDescContactsByCategory=Contacts by categories From d7c2bdba384fbe2627664a99198a26ec742afbab Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 29 Jan 2017 16:42:59 +0100 Subject: [PATCH 032/170] Fix: link was useless with a nofollow tag. --- htdocs/core/lib/security.lib.php | 5 +++-- htdocs/core/tpl/login.tpl.php | 15 +++++++++++++-- htdocs/main.inc.php | 5 +++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/htdocs/core/lib/security.lib.php b/htdocs/core/lib/security.lib.php index 4cfc077f0be..ced20a79ea6 100644 --- a/htdocs/core/lib/security.lib.php +++ b/htdocs/core/lib/security.lib.php @@ -339,7 +339,8 @@ function restrictedArea($user, $features, $objectid=0, $tableandshare='', $featu } /** - * Check access by user to object + * Check access by user to object. + * This function is also called by restrictedArea * * @param User $user User to check * @param array $featuresarray Features/modules to check @@ -348,8 +349,8 @@ function restrictedArea($user, $features, $objectid=0, $tableandshare='', $featu * @param string $feature2 Feature to check, second level of permission (optional). Can be or check with 'level1|level2'. * @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional) * @param string $dbt_select Field name for select if not rowid. Not used if objectid is null (optional) - * * @return bool True if user has access, False otherwise + * @see restrictedArea */ function checkUserAccessToObject($user, $featuresarray, $objectid=0, $tableandshare='', $feature2='', $dbt_keyfield='', $dbt_select='rowid') { diff --git a/htdocs/core/tpl/login.tpl.php b/htdocs/core/tpl/login.tpl.php index a9d7c73ae1a..b3a2ced1023 100644 --- a/htdocs/core/tpl/login.tpl.php +++ b/htdocs/core/tpl/login.tpl.php @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +// Need global variable $title to be defined + header('Cache-Control: Public, must-revalidate'); header("Content-type: text/html; charset=".$conf->file->character_set_client); @@ -35,7 +37,10 @@ $arrayofjs=array( ); $titleofloginpage=$langs->trans('Login').' @ '.$titletruedolibarrversion; // $titletruedolibarrversion is defined by dol_loginfunction in security2.lib.php. We must keep the @, some tools use it to know it is login page and find true dolibarr version. -print top_htmlhead('',$titleofloginpage,0,0,$arrayofjs); +$disablenofollow=1; +if (! preg_match('/'.constant('DOL_APPLICATION_TITLE').'/', $title)) $disablenofollow=0; + +print top_htmlhead('', $titleofloginpage, 0, 0, $arrayofjs, array(), 0, $disablenofollow); ?> @@ -71,7 +76,13 @@ $(document).ready(function () { - +
diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index 8aba2e428f2..a18840f131c 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -993,9 +993,10 @@ function top_httphead() * @param array $arrayofjs Array of complementary js files * @param array $arrayofcss Array of complementary css files * @param int $disablejmobile Disable jmobile + * @param int $disablenofollow Disable no follow tag * @return void */ -function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $disablejmobile=0) +function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $disablejmobile=0, $disablenofollow=0) { global $user, $conf, $langs, $db; @@ -1017,7 +1018,7 @@ function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs print "\n"; if (GETPOST('dol_basehref')) print ''."\n"; // Displays meta - print ''."\n"; // Do not index + print ''."\n"; // Do not index print ''; // Scale for mobile device print ''."\n"; $favicon=dol_buildpath('/theme/'.$conf->theme.'/img/favicon.ico',1); From 45c99d9d24efae791f8c75ab0c3790346c77e8c3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 29 Jan 2017 16:46:59 +0100 Subject: [PATCH 033/170] Fix HTML5 error --- htdocs/main.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index a18840f131c..7c22308f201 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -1024,7 +1024,7 @@ function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs $favicon=dol_buildpath('/theme/'.$conf->theme.'/img/favicon.ico',1); if (! empty($conf->global->MAIN_FAVICON_URL)) $favicon=$conf->global->MAIN_FAVICON_URL; print ''."\n"; - if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && ! GETPOST('textbrowser')) print ''."\n"; + //if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && ! GETPOST('textbrowser')) print ''."\n"; if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && ! GETPOST('textbrowser')) print ''."\n"; if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && ! GETPOST('textbrowser')) print ''."\n"; From 92e61e67070d8030cdd172feb7ebbadaa6204d81 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 29 Jan 2017 18:29:37 +0100 Subject: [PATCH 034/170] Fix hide url if not defined --- htdocs/comm/mailing/cibles.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/comm/mailing/cibles.php b/htdocs/comm/mailing/cibles.php index e104db4dcda..470ee2a4ec1 100644 --- a/htdocs/comm/mailing/cibles.php +++ b/htdocs/comm/mailing/cibles.php @@ -533,7 +533,7 @@ if ($object->fetch($id) >= 0) print '
'; if (empty($obj->source_id) || empty($obj->source_type)) { - print $obj->source_url; // For backward compatibility + print empty($obj->source_url)?'':$obj->source_url; // For backward compatibility } else { From ff8b7e6e4d3259ac4121a89c7d5dc05fd61e8616 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 29 Jan 2017 18:35:56 +0100 Subject: [PATCH 035/170] Fix size of email --- htdocs/comm/mailing/card.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/comm/mailing/card.php b/htdocs/comm/mailing/card.php index 21e26814119..b7f2a002105 100644 --- a/htdocs/comm/mailing/card.php +++ b/htdocs/comm/mailing/card.php @@ -730,7 +730,7 @@ if ($action == 'create') print '
'; // Editeur wysiwyg require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; - $doleditor=new DolEditor('body',$_POST['body'],'',320,'dolibarr_mailings','',true,true,$conf->global->FCKEDITOR_ENABLE_MAILING,20,'90%'); + $doleditor=new DolEditor('body',$_POST['body'],'',600,'dolibarr_mailings','',true,true,$conf->global->FCKEDITOR_ENABLE_MAILING,20,'90%'); $doleditor->Create(); print '
'; @@ -1098,7 +1098,7 @@ else $readonly=1; // Editeur wysiwyg require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; - $doleditor=new DolEditor('body',$object->body,'',320,'dolibarr_mailings','',false,true,empty($conf->global->FCKEDITOR_ENABLE_MAILING)?0:1,20,120,$readonly); + $doleditor=new DolEditor('body',$object->body,'',600,'dolibarr_mailings','',false,true,empty($conf->global->FCKEDITOR_ENABLE_MAILING)?0:1,20,120,$readonly); $doleditor->Create(); } else print dol_htmlentitiesbr($object->body); From 45263c85ecc3b3a0cf521ee04d8443fcc5c9bfed Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 29 Jan 2017 20:33:08 +0100 Subject: [PATCH 036/170] Fix in emailing --- htdocs/comm/mailing/class/mailing.class.php | 37 +++++++++++---------- htdocs/comm/mailing/list.php | 5 +-- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/htdocs/comm/mailing/class/mailing.class.php b/htdocs/comm/mailing/class/mailing.class.php index dc39c2e9399..b0dff81332c 100644 --- a/htdocs/comm/mailing/class/mailing.class.php +++ b/htdocs/comm/mailing/class/mailing.class.php @@ -601,35 +601,38 @@ class Mailing extends CommonObject if ($mode == 2) { if ($statut==-1) return $langs->trans("MailingStatusError").' '.img_error($desc); - if ($statut==1) return $langs->trans("MailingStatusSent").' '.img_picto($langs->trans("MailingStatusSent"),'statut4'); - if ($statut==2) return $langs->trans("MailingStatusRead").' '.img_picto($langs->trans("MailingStatusRead"),'statut6'); - if ($statut==3) return $langs->trans("MailingStatusNotContact").' '.img_picto($langs->trans("MailingStatusNotContact"),'statut8'); + if ($statut==1) return $langs->trans("MailingStatusSent").' '.img_picto($langs->trans("MailingStatusSent"),'statut6'); + if ($statut==2) return $langs->trans("MailingStatusRead").' '.img_picto($langs->trans("MailingStatusRead"),'statut4'); + if ($statut==3) return $langs->trans("MailingStatusNotContact").' '.img_picto($langs->trans("MailingStatusNotContact"),'statut3'); } if ($mode == 3) { if ($statut==-1) return $langs->trans("MailingStatusError").' '.img_error($desc); - if ($statut==1) return $langs->trans("MailingStatusSent").' '.img_picto($langs->trans("MailingStatusSent"),'statut4'); - if ($statut==2) return $langs->trans("MailingStatusRead").' '.img_picto($langs->trans("MailingStatusRead"),'statut6'); - if ($statut==3) return $langs->trans("MailingStatusNotContact").' '.img_picto($langs->trans("MailingStatusNotContact"),'statut8'); + if ($statut==1) return $langs->trans("MailingStatusSent").' '.img_picto($langs->trans("MailingStatusSent"),'statut6'); + if ($statut==2) return $langs->trans("MailingStatusRead").' '.img_picto($langs->trans("MailingStatusRead"),'statut4'); + if ($statut==3) return $langs->trans("MailingStatusNotContact").' '.img_picto($langs->trans("MailingStatusNotContact"),'statut3'); } if ($mode == 4) { if ($statut==-1) return $langs->trans("MailingStatusError").' '.img_error($desc); - if ($statut==1) return $langs->trans("MailingStatusSent").' '.img_picto($langs->trans("MailingStatusSent"),'statut4'); - if ($statut==2) return $langs->trans("MailingStatusRead").' '.img_picto($langs->trans("MailingStatusRead"),'statut6'); - if ($statut==3) return $langs->trans("MailingStatusNotContact").' '.img_picto($langs->trans("MailingStatusNotContact"),'statut8'); + if ($statut==1) return $langs->trans("MailingStatusSent").' '.img_picto($langs->trans("MailingStatusSent"),'statut6'); + if ($statut==2) return $langs->trans("MailingStatusRead").' '.img_picto($langs->trans("MailingStatusRead"),'statut4'); + if ($statut==3) return $langs->trans("MailingStatusNotContact").' '.img_picto($langs->trans("MailingStatusNotContact"),'statut3'); } if ($mode == 5) { - if ($statut==-1) return $langs->trans("MailingStatusError").' '.img_error($desc); - if ($statut==1) return $langs->trans("MailingStatusSent").' '.img_picto($langs->trans("MailingStatusSent"),'statut4'); - if ($statut==2) return $langs->trans("MailingStatusRead").' '.img_picto($langs->trans("MailingStatusRead"),'statut6'); - if ($statut==3) return $langs->trans("MailingStatusNotContact").' '.img_picto($langs->trans("MailingStatusNotContact"),'statut8'); + if ($statut==-1) return $langs->trans("MailingStatusError").' '.img_error($desc); + if ($statut==1) return $langs->trans("MailingStatusSent").' '.img_picto($langs->trans("MailingStatusSent"),'statut6'); + if ($statut==2) return $langs->trans("MailingStatusRead").' '.img_picto($langs->trans("MailingStatusRead"),'statut4'); + if ($statut==3) return $langs->trans("MailingStatusNotContact").' '.img_picto($langs->trans("MailingStatusNotContact"),'statut3'); + } + if ($mode == 6) + { + if ($statut==-1) return $langs->trans("MailingStatusError").' '.img_error($desc); + if ($statut==1) return $langs->trans("MailingStatusSent").' '.img_picto($langs->trans("MailingStatusSent"),'statut6'); + if ($statut==2) return $langs->trans("MailingStatusRead").' '.img_picto($langs->trans("MailingStatusRead"),'statut4'); + if ($statut==3) return $langs->trans("MailingStatusNotContact").' '.img_picto($langs->trans("MailingStatusNotContact"),'statut3'); } - - - - } } diff --git a/htdocs/comm/mailing/list.php b/htdocs/comm/mailing/list.php index b0801c817aa..2d6fae37b98 100644 --- a/htdocs/comm/mailing/list.php +++ b/htdocs/comm/mailing/list.php @@ -174,7 +174,7 @@ if ($result) { print '
'; $nbemail = $obj->nbemail; - if ($obj->statut != 3 && !empty($conf->global->MAILING_LIMIT_SENDBYWEB) && $conf->global->MAILING_LIMIT_SENDBYWEB < $nbemail) + /*if ($obj->statut != 3 && !empty($conf->global->MAILING_LIMIT_SENDBYWEB) && $conf->global->MAILING_LIMIT_SENDBYWEB < $nbemail) { $text=$langs->trans('LimitSendingEmailing',$conf->global->MAILING_LIMIT_SENDBYWEB); print $form->textwithpicto($nbemail,$text,1,'warning'); @@ -182,7 +182,8 @@ if ($result) else { print $nbemail; - } + }*/ + print $nbemail; print '
'; - print ''; - print_liste_field_titre($langs->trans("AccountNumber"), $_SERVER["PHP_SELF"], "aa.account_number", "", $params, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Label"), $_SERVER["PHP_SELF"], "aa.label", "", $params, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Accountparent"), $_SERVER["PHP_SELF"], "aa.account_parent", "", $params, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Pcgtype"), $_SERVER["PHP_SELF"], "aa.pcg_type", "", $params, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Pcgsubtype"), $_SERVER["PHP_SELF"], "aa.pcg_subtype", "", $params, "", $sortfield, $sortorder); - print_liste_field_titre($langs->trans("Activated"), $_SERVER["PHP_SELF"], "aa.active", "", $params, "", $sortfield, $sortorder); - print_liste_field_titre('', $_SERVER["PHP_SELF"], "", $params, "", 'width="60" align="center"', $sortfield, $sortorder); - print ''; + $varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage; + $selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields + print '
'; + print '
'."\n"; + print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; + + if (! empty($arrayfields['aa.account_number']['checked'])) print_liste_field_titre($arrayfields['aa.account_number']['label'], $_SERVER["PHP_SELF"],"aa.account_number","",$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['aa.label']['checked'])) print_liste_field_titre($arrayfields['aa.label']['label'], $_SERVER["PHP_SELF"],"aa.label","",$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['aa.account_parent']['checked'])) print_liste_field_titre($arrayfields['aa.account_parent']['label'], $_SERVER["PHP_SELF"],"aa.account_parent", "", $param,'align="left"',$sortfield,$sortorder); + if (! empty($arrayfields['aa.pcg_type']['checked'])) print_liste_field_titre($arrayfields['aa.pcg_type']['label'],$_SERVER["PHP_SELF"],'aa.pcg_type','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['aa.pcg_subtype']['checked'])) print_liste_field_titre($arrayfields['aa.pcg_subtype']['label'],$_SERVER["PHP_SELF"],'aa.pcg_subtype','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['aa.active']['checked'])) print_liste_field_titre($arrayfields['aa.active']['label'],$_SERVER["PHP_SELF"],'aa.active','',$param,'',$sortfield,$sortorder); + + print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="right"',$sortfield,$sortorder,'maxwidthsearch '); + print "\n"; + + // Line for search fields + print ''; + if (! empty($arrayfields['aa.account_number']['checked'])) print ''; + if (! empty($arrayfields['aa.label']['checked'])) print ''; + if (! empty($arrayfields['aa.account_parent']['checked'])) print ''; + if (! empty($arrayfields['aa.pcg_type']['checked'])) print ''; + if (! empty($arrayfields['aa.pcg_subtype']['checked'])) print ''; + if (! empty($arrayfields['aa.active']['checked'])) print ''; print ''; print ''; @@ -263,8 +284,7 @@ if ($resql) { $accountstatic = new AccountingAccount($db); $accountparent = new AccountingAccount($db); - - $i = 0; + while ( $i < min($num, $limit) ) { $obj = $db->fetch_object($resql); @@ -274,35 +294,81 @@ if ($resql) { $accountstatic->account_number = $obj->account_number; print ''; - print ''; - print ''; - if (! empty($obj->account_parent)) - { - $accountparent->id = $obj->rowid2; - $accountparent->label = $obj->label2; - $accountparent->account_number = $obj->account_number2; - - print ''; - } - else + // Account number + if (! empty($arrayfields['aa.account_number']['checked'])) { - print ''; + print "\n"; + if (! $i) $totalarray['nbfield']++; } - print ''; - print ''; - print '\n"; + if (! $i) $totalarray['nbfield']++; } - print ''; - + + // Account parent + if (! empty($arrayfields['aa.account_parent']['checked'])) + { + if (! empty($obj->account_parent)) + { + $accountparent->id = $obj->rowid2; + $accountparent->label = $obj->label2; + $accountparent->account_number = $obj->account_number2; + + print "\n"; + if (! $i) $totalarray['nbfield']++; + } + else + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + } + + // Chart of accounts type + if (! empty($arrayfields['aa.pcg_type']['checked'])) + { + print "\n"; + if (! $i) $totalarray['nbfield']++; + } + + // Chart of accounts subtype + if (! empty($arrayfields['aa.pcg_subtype']['checked'])) + { + print "\n"; + if (! $i) $totalarray['nbfield']++; + } + + // Activated or not + if (! empty($arrayfields['aa.active']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } + // Action print '' . "\n"; + if (! $i) $totalarray['nbfield']++; print "\n"; $var = ! $var; @@ -322,6 +389,7 @@ if ($resql) { } print "
 
 '; - $searchpitco=$form->showFilterAndCheckAddButtons($massactionbutton?1:0, 'checkforselect', 1); - print $searchpitco; + $searchpicto=$form->showFilterAndCheckAddButtons($massactionbutton?1:0, 'checkforselect', 1); + print $searchpicto; print '
' . $accountstatic->getNomUrl(1) . '' . $obj->label . '' . $accountparent->getNomUrl(1) . ' "; + print $accountstatic->getNomUrl(1); + print "' . $obj->pcg_type . '' . $obj->pcg_subtype . ''; - if (empty($obj->active)) { - print ''; - print img_picto($langs->trans("Disabled"), 'switch_off'); - print ''; - } else { - print ''; - print img_picto($langs->trans("Activated"), 'switch_on'); - print ''; + + // Account label + if (! empty($arrayfields['aa.label']['checked'])) + { + print ""; + print $obj->label; + print ""; + print $accountparent->getNomUrl(1); + print " "; + print $obj->pcg_type; + print ""; + print $obj->pcg_subtype; + print "'; + if (empty($obj->active)) { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + print ''; + } else { + print ''; + print img_picto($langs->trans("Activated"), 'switch_on'); + print ''; + } + print ''; if ($user->admin) { @@ -315,6 +381,7 @@ if ($resql) { print ''; } print '
"; + print "
"; print ''; } else { dol_print_error($db); From 399aea179f08131379535525a712e880ed72d6d4 Mon Sep 17 00:00:00 2001 From: philippe-opendsi Date: Tue, 31 Jan 2017 10:15:04 +0100 Subject: [PATCH 043/170] New : Option to reload supplier order and invoice to retrieve payment terms & type Add RELOAD_PAGE_ON_SUPPLIER_CHANGE --- htdocs/fourn/commande/card.php | 13 +++++++++++++ htdocs/fourn/facture/card.php | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index d54a477aeee..d1100326f42 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -1445,6 +1445,19 @@ if ($action=='create') else { print $form->select_company((empty($socid)?'':$socid), 'socid', 's.fournisseur = 1', 'SelectThirdParty'); + // reload page to retrieve customer informations + if (!empty($conf->global->RELOAD_PAGE_ON_SUPPLIER_CHANGE)) + { + print ''; + } } print ''; diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index d756bc2b72a..1fce3965c6b 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -1446,6 +1446,19 @@ if ($action == 'create') else { print $form->select_company($societe->id, 'socid', 's.fournisseur = 1', 'SelectThirdParty'); + // reload page to retrieve supplier informations + if (!empty($conf->global->RELOAD_PAGE_ON_SUPPLIER_CHANGE)) + { + print ''; + } } print ''; From 90e8f062e93c6ef1cdc294272fa158183d82cd6f Mon Sep 17 00:00:00 2001 From: Maxime Kohlhaas Date: Tue, 31 Jan 2017 10:23:03 +0100 Subject: [PATCH 044/170] Fix : link to user in bank was wrong regarding expensereport payment --- .../expensereport/class/paymentexpensereport.class.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/htdocs/expensereport/class/paymentexpensereport.class.php b/htdocs/expensereport/class/paymentexpensereport.class.php index 4c9c5843a49..9b05313a581 100644 --- a/htdocs/expensereport/class/paymentexpensereport.class.php +++ b/htdocs/expensereport/class/paymentexpensereport.class.php @@ -527,13 +527,14 @@ class PaymentExpenseReport extends CommonObject { if ($mode == 'payment_expensereport') { - $euser = new User($this->db); - $euser->fetch($key); + $er = new ExpenseReport($this->db); + $er->fetch($key); + $er->fetch_user($er->fk_user_author); $result=$acc->add_url_line( $bank_line_id, - $euser->id, + $er->user->id, DOL_URL_ROOT.'/user/card.php?id=', - $euser->getFullName($langs), + $er->user->getFullName($langs), 'user' ); if ($result <= 0) From e73c60b526b45e92e56a5633a8efefc6c2d1dc05 Mon Sep 17 00:00:00 2001 From: phf Date: Tue, 31 Jan 2017 12:20:45 +0100 Subject: [PATCH 045/170] New htmlid can be use instead of htmlname for select projects --- htdocs/core/class/html.formprojet.class.php | 12 +++++++----- htdocs/core/lib/ajax.lib.php | 16 +++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/htdocs/core/class/html.formprojet.class.php b/htdocs/core/class/html.formprojet.class.php index 0129f1c1382..d81be84d044 100644 --- a/htdocs/core/class/html.formprojet.class.php +++ b/htdocs/core/class/html.formprojet.class.php @@ -60,9 +60,10 @@ class FormProjets * @param int $nooutput No print output. Return it only. * @param int $forceaddid Force to add project id in list, event if not qualified * @param string $morecss More css + * @param int $htmlid Html id to use instead of htmlname * @return string Return html content */ - function select_projects($socid=-1, $selected='', $htmlname='projectid', $maxlength=16, $option_only=0, $show_empty=1, $discard_closed=0, $forcefocus=0, $disabled=0, $mode = 0, $filterkey = '', $nooutput=0, $forceaddid=0, $morecss='') + function select_projects($socid=-1, $selected='', $htmlname='projectid', $maxlength=16, $option_only=0, $show_empty=1, $discard_closed=0, $forcefocus=0, $disabled=0, $mode = 0, $filterkey = '', $nooutput=0, $forceaddid=0, $morecss='', $htmlid='') { global $langs,$conf,$form; @@ -90,7 +91,7 @@ class FormProjets } else { - $out.=$this->select_projects_list($socid, $selected, $htmlname, $maxlength, $option_only, $show_empty, $discard_closed, $forcefocus, $disabled, 0, $filterkey, 1); + $out.=$this->select_projects_list($socid, $selected, $htmlname, $maxlength, $option_only, $show_empty, $discard_closed, $forcefocus, $disabled, 0, $filterkey, 1, $forceaddid, $htmlid); if ($discard_closed) { if (class_exists('Form')) @@ -125,9 +126,10 @@ class FormProjets * @param string $filterkey Key to filter * @param int $nooutput No print output. Return it only. * @param int $forceaddid Force to add project id in list, event if not qualified + * @param int $htmlid Html id to use instead of htmlname * @return int Nb of project if OK, <0 if KO */ - function select_projects_list($socid=-1, $selected='', $htmlname='projectid', $maxlength=24, $option_only=0, $show_empty=1, $discard_closed=0, $forcefocus=0, $disabled=0, $mode=0, $filterkey = '', $nooutput=0, $forceaddid=0) + function select_projects_list($socid=-1, $selected='', $htmlname='projectid', $maxlength=24, $option_only=0, $show_empty=1, $discard_closed=0, $forcefocus=0, $disabled=0, $mode=0, $filterkey = '', $nooutput=0, $forceaddid=0, $htmlid='') { global $user,$conf,$langs; @@ -173,14 +175,14 @@ class FormProjets if (! empty($conf->use_javascript_ajax)) { include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php'; - $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus); + $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus,'resolve',$htmlid); $out.=$comboenhancement; $nodatarole=($comboenhancement?' data-role="none"':''); $minmax='minwidth100 maxwidth300'; } if (empty($option_only)) { - $out.= ''; } if (!empty($show_empty)) { $out.= ''; diff --git a/htdocs/core/lib/ajax.lib.php b/htdocs/core/lib/ajax.lib.php index 0193a33b305..d593e279178 100644 --- a/htdocs/core/lib/ajax.lib.php +++ b/htdocs/core/lib/ajax.lib.php @@ -357,12 +357,14 @@ function ajax_dialog($title,$message,$w=350,$h=150) * @param int $minLengthToAutocomplete Minimum length of input string to start autocomplete * @param int $forcefocus Force focus on field * @param string $widthTypeOfAutocomplete 'resolve' or 'off' + * @param int $htmlid Html id to use instead of htmlname * @return string Return html string to convert a select field into a combo, or '' if feature has been disabled for some reason. */ -function ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve') +function ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve',$htmlid='') { global $conf; + if (empty($htmlid)) $htmlid = $htmlname; if (! empty($conf->browser->phone)) return ''; // select2 disabled for smartphones with standard browser (does not works, popup appears outside screen) if (! empty($conf->dol_use_jmobile)) return ''; // select2 works with jmobile but it breaks the autosize feature of jmobile. if (! empty($conf->global->MAIN_DISABLE_AJAX_COMBOX)) return ''; @@ -371,10 +373,10 @@ function ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $ if (empty($minLengthToAutocomplete)) $minLengthToAutocomplete=0; $tmpplugin='select2'; - $msg=' + $msg=' '; } diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 460e698e432..b242f7d6b06 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -773,7 +773,7 @@ class Project extends CommonObject * Close a project * * @param User $user User that close project - * @return int <0 if KO, >0 if OK + * @return int <0 if KO, 0 if already closed, >0 if OK */ function setClose($user) { @@ -828,6 +828,8 @@ class Project extends CommonObject return -1; } } + + return 0; } /** diff --git a/htdocs/societe/soc.php b/htdocs/societe/soc.php index 374a87c72ef..3da099390ac 100644 --- a/htdocs/societe/soc.php +++ b/htdocs/societe/soc.php @@ -1308,11 +1308,16 @@ else print '
'; print ''; - if ($backtopage) + if (! empty($backtopage)) { - print '   '; + print '     '; print ''; } + else + { + print '     '; + print ''; + } print '
'."\n"; print ''."\n"; @@ -1898,7 +1903,7 @@ else print '
'; print ''; - print '     '; + print '     '; print ''; print '
'; From d945a0de927b396708a97950759ce19d2d9bfaf7 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 1 Feb 2017 14:59:51 +0100 Subject: [PATCH 058/170] NEW Uniformize behaviour: Action to make order is an action button. --- htdocs/fourn/commande/card.php | 197 +++++++++++++++++---------------- 1 file changed, 104 insertions(+), 93 deletions(-) diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index d54a477aeee..50b481f4b14 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -2613,6 +2613,18 @@ elseif (! empty($object->id)) } } + if ($object->statut == 2) + { + if ($user->rights->fournisseur->commande->commander) + { + print ''; + } + else + { + print ''; + } + } + // Create bill if (! empty($conf->facture->enabled)) { @@ -2664,103 +2676,102 @@ elseif (! empty($object->id)) } - print '
'; - - /* - * Documents generes - */ - $comfournref = dol_sanitizeFileName($object->ref); - $file = $conf->fournisseur->dir_output . '/commande/' . $comfournref . '/' . $comfournref . '.pdf'; - $relativepath = $comfournref.'/'.$comfournref.'.pdf'; - $filedir = $conf->fournisseur->dir_output . '/commande/' . $comfournref; - $urlsource=$_SERVER["PHP_SELF"]."?id=".$object->id; - $genallowed=$user->rights->fournisseur->commande->creer; - $delallowed=$user->rights->fournisseur->commande->supprimer; - - print $formfile->showdocuments('commande_fournisseur',$comfournref,$filedir,$urlsource,$genallowed,$delallowed,$object->modelpdf,1,0,0,0,0,'','','',$object->thirdparty->default_lang); - $somethingshown=$formfile->numoffiles; - - // Show links to link elements - $linktoelem = $form->showLinkToObjectBlock($object, null, array('supplier_order','order_supplier')); - $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem); - - print '
'; - - - if ($user->rights->fournisseur->commande->commander && $object->statut == 2) + if ($user->rights->fournisseur->commande->commander && $object->statut == 2 && $action == 'makeorder') { - // Set status to ordered (action=commande) - print ''."\n"; - print '
'; - print ''; - print ''; - print load_fiche_titre($langs->trans("ToOrder"),'',''); - print ''; - //print ''; - print ''; - - print ''; - - print ''; - print ''; - print '
'.$langs->trans("ToOrder").'
'.$langs->trans("OrderDate").''; - $date_com = dol_mktime(0, 0, 0, GETPOST('remonth'), GETPOST('reday'), GETPOST('reyear')); - print $form->select_date($date_com,'',1,1,'',"commande",1,0,1); - print '
'.$langs->trans("OrderMode").''; - $formorder->selectInputMethod(GETPOST('methodecommande'), "methodecommande", 1); - print '
'.$langs->trans("Comment").'
'; - print '
'; - print "
"; + // Set status to ordered (action=commande) + print ''."\n"; + print '
'; + print ''; + print ''; + print load_fiche_titre($langs->trans("ToOrder"),'',''); + print ''; + //print ''; + print ''; + + print ''; + + print ''; + print ''; + print '
'.$langs->trans("ToOrder").'
'.$langs->trans("OrderDate").''; + $date_com = dol_mktime(0, 0, 0, GETPOST('remonth'), GETPOST('reday'), GETPOST('reyear')); + print $form->select_date($date_com,'',1,1,'',"commande",1,0,1); + print '
'.$langs->trans("OrderMode").''; + $formorder->selectInputMethod(GETPOST('methodecommande'), "methodecommande", 1); + print '
'.$langs->trans("Comment").'
'; + print ''; + print '     '; + print ''; + print '
'; + print '
'; + print "
"; } - - if ($user->rights->fournisseur->commande->receptionner && ($object->statut == 3 || $object->statut == 4)) + + + if ($action != 'makeorder') { - // Set status to received (action=livraison) - print ''."\n"; - print '
'; - print ''; - print ''; - print load_fiche_titre($langs->trans("Receive"),'',''); - - print ''; - //print ''; - print '\n"; - - print "'; - print ''; - print ''; - print "
'.$langs->trans("Receive").'
'.$langs->trans("DeliveryDate").''; - print $form->select_date('','',1,1,'',"commande",1,0,1); - print "
".$langs->trans("Delivery")."\n"; - $liv = array(); - $liv[''] = ' '; - $liv['tot'] = $langs->trans("CompleteOrNoMoreReceptionExpected"); - $liv['par'] = $langs->trans("PartialWoman"); - $liv['nev'] = $langs->trans("NeverReceived"); - $liv['can'] = $langs->trans("Canceled"); - - print $form->selectarray("type",$liv); - - print '
'.$langs->trans("Comment").'
\n"; - print "
\n"; - print "
"; + print '
'; + + /* + * Documents generes + */ + $comfournref = dol_sanitizeFileName($object->ref); + $file = $conf->fournisseur->dir_output . '/commande/' . $comfournref . '/' . $comfournref . '.pdf'; + $relativepath = $comfournref.'/'.$comfournref.'.pdf'; + $filedir = $conf->fournisseur->dir_output . '/commande/' . $comfournref; + $urlsource=$_SERVER["PHP_SELF"]."?id=".$object->id; + $genallowed=$user->rights->fournisseur->commande->creer; + $delallowed=$user->rights->fournisseur->commande->supprimer; + + print $formfile->showdocuments('commande_fournisseur',$comfournref,$filedir,$urlsource,$genallowed,$delallowed,$object->modelpdf,1,0,0,0,0,'','','',$object->thirdparty->default_lang); + $somethingshown=$formfile->numoffiles; + + // Show links to link elements + $linktoelem = $form->showLinkToObjectBlock($object, null, array('supplier_order','order_supplier')); + $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem); + + print '
'; + + + if ($user->rights->fournisseur->commande->receptionner && ($object->statut == 3 || $object->statut == 4)) + { + // Set status to received (action=livraison) + print ''."\n"; + print '
'; + print ''; + print ''; + print load_fiche_titre($langs->trans("Receive"),'',''); + + print ''; + //print ''; + print '\n"; + + print "'; + print ''; + print ''; + print "
'.$langs->trans("Receive").'
'.$langs->trans("DeliveryDate").''; + print $form->select_date('','',1,1,'',"commande",1,0,1); + print "
".$langs->trans("Delivery")."\n"; + $liv = array(); + $liv[''] = ' '; + $liv['tot'] = $langs->trans("CompleteOrNoMoreReceptionExpected"); + $liv['par'] = $langs->trans("PartialWoman"); + $liv['nev'] = $langs->trans("NeverReceived"); + $liv['can'] = $langs->trans("Canceled"); + + print $form->selectarray("type",$liv); + + print '
'.$langs->trans("Comment").'
\n"; + print "
\n"; + print "
"; + } + + // List of actions on element + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; + $formactions=new FormActions($db); + $somethingshown=$formactions->showactions($object,'order_supplier',$socid,0,'listaction'.($genallowed?'largetitle':'')); + + print '
'; } - - // List of actions on element - include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; - $formactions=new FormActions($db); - $somethingshown=$formactions->showactions($object,'order_supplier',$socid,0,'listaction'.($genallowed?'largetitle':'')); - - - // List of actions on element - /* Hidden because" available into "Log" tab - print '
'; - include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; - $formactions=new FormActions($db); - $somethingshown=$formactions->showactions($object,'order_supplier',$socid); - */ - - print '
'; } print ''; From fbbf8cd8a4227f94e8831aed0eabd02f73c96d0f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 1 Feb 2017 15:47:08 +0100 Subject: [PATCH 059/170] Fix bad side effect of uniformizing a constant name. --- htdocs/contact/agenda.php | 2 +- htdocs/fourn/commande/info.php | 2 +- htdocs/projet/info.php | 2 +- htdocs/societe/agenda.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/contact/agenda.php b/htdocs/contact/agenda.php index 3882d2ebca3..e97263ebf00 100644 --- a/htdocs/contact/agenda.php +++ b/htdocs/contact/agenda.php @@ -81,7 +81,7 @@ if (GETPOST('actioncode','array')) } else { - $actioncode=GETPOST("actioncode","alpha",3)?GETPOST("actioncode","alpha",3):(GETPOST("actioncode")=='0'?'0':(empty($conf->global->AGENDA_DEFAULT_FILTER_TYPE)?'':$conf->global->AGENDA_DEFAULT_FILTER_TYPE)); + $actioncode=GETPOST("actioncode","alpha",3)?GETPOST("actioncode","alpha",3):(GETPOST("actioncode")=='0'?'0':(empty($conf->global->AGENDA_DEFAULT_FILTER_TYPE_FOR_OBJECT)?'':$conf->global->AGENDA_DEFAULT_FILTER_TYPE_FOR_OBJECT)); } $search_agenda_label=GETPOST('search_agenda_label'); diff --git a/htdocs/fourn/commande/info.php b/htdocs/fourn/commande/info.php index d858dc7172c..50b310a55e5 100644 --- a/htdocs/fourn/commande/info.php +++ b/htdocs/fourn/commande/info.php @@ -45,7 +45,7 @@ if (GETPOST('actioncode','array')) } else { - $actioncode=GETPOST("actioncode","alpha",3)?GETPOST("actioncode","alpha",3):(GETPOST("actioncode")=='0'?'0':(empty($conf->global->AGENDA_DEFAULT_FILTER_TYPE)?'':$conf->global->AGENDA_DEFAULT_FILTER_TYPE)); + $actioncode=GETPOST("actioncode","alpha",3)?GETPOST("actioncode","alpha",3):(GETPOST("actioncode")=='0'?'0':(empty($conf->global->AGENDA_DEFAULT_FILTER_TYPE_FOR_OBJECTS)?'':$conf->global->AGENDA_DEFAULT_FILTER_TYPE_FOR_OBJECTS)); } $search_agenda_label=GETPOST('search_agenda_label'); diff --git a/htdocs/projet/info.php b/htdocs/projet/info.php index de27a363533..65a95e232ff 100644 --- a/htdocs/projet/info.php +++ b/htdocs/projet/info.php @@ -54,7 +54,7 @@ if (GETPOST('actioncode','array')) } else { - $actioncode=GETPOST("actioncode","alpha",3)?GETPOST("actioncode","alpha",3):(GETPOST("actioncode")=='0'?'0':(empty($conf->global->AGENDA_DEFAULT_FILTER_TYPE)?'':$conf->global->AGENDA_DEFAULT_FILTER_TYPE)); + $actioncode=GETPOST("actioncode","alpha",3)?GETPOST("actioncode","alpha",3):(GETPOST("actioncode")=='0'?'0':(empty($conf->global->AGENDA_DEFAULT_FILTER_TYPE_FOR_OBJECT)?'':$conf->global->AGENDA_DEFAULT_FILTER_TYPE_FOR_OBJECT)); } $search_agenda_label=GETPOST('search_agenda_label'); diff --git a/htdocs/societe/agenda.php b/htdocs/societe/agenda.php index 25eb78cfe24..644cda92bc4 100644 --- a/htdocs/societe/agenda.php +++ b/htdocs/societe/agenda.php @@ -41,7 +41,7 @@ if (GETPOST('actioncode','array')) } else { - $actioncode=GETPOST("actioncode","alpha",3)?GETPOST("actioncode","alpha",3):(GETPOST("actioncode")=='0'?'0':(empty($conf->global->AGENDA_DEFAULT_FILTER_TYPE)?'':$conf->global->AGENDA_DEFAULT_FILTER_TYPE)); + $actioncode=GETPOST("actioncode","alpha",3)?GETPOST("actioncode","alpha",3):(GETPOST("actioncode")=='0'?'0':(empty($conf->global->AGENDA_DEFAULT_FILTER_TYPE_FOR_OBJECT)?'':$conf->global->AGENDA_DEFAULT_FILTER_TYPE_FOR_OBJECT)); } $search_agenda_label=GETPOST('search_agenda_label'); From 924a08a0785dd86502014b1864f28fc5bd1307c0 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 1 Feb 2017 16:24:40 +0100 Subject: [PATCH 060/170] Fix code should use 5.0 framework --- htdocs/product/stock/massstockmove.php | 2 +- htdocs/product/stock/replenishorders.php | 90 ++++++++++++------------ 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/htdocs/product/stock/massstockmove.php b/htdocs/product/stock/massstockmove.php index 944cc27dffb..03c0ebd78ba 100644 --- a/htdocs/product/stock/massstockmove.php +++ b/htdocs/product/stock/massstockmove.php @@ -342,7 +342,7 @@ print ''; print ''; -print '
'; +print '
'; print ''; //print '
'; diff --git a/htdocs/product/stock/replenishorders.php b/htdocs/product/stock/replenishorders.php index 785760ef511..4986d7c524d 100644 --- a/htdocs/product/stock/replenishorders.php +++ b/htdocs/product/stock/replenishorders.php @@ -39,6 +39,41 @@ $langs->load("orders"); if ($user->societe_id) $socid=$user->societe_id; $result=restrictedArea($user,'produit|service'); +$sref = GETPOST('search_ref', 'alpha'); +$snom = GETPOST('search_nom', 'alpha'); +$suser = GETPOST('search_user', 'alpha'); +$sttc = GETPOST('search_ttc', 'alpha'); +$sall = GETPOST('search_all', 'alpha'); +$sdate = GETPOST('search_date', 'alpha'); +$page = GETPOST('page', 'int'); +$sproduct = GETPOST('sproduct', 'int'); + +$limit = GETPOST('limit')?GETPOST('limit','int'):$conf->liste_limit; +$sortfield = GETPOST("sortfield"); +$sortorder = GETPOST("sortorder"); +if (!$sortorder) $sortorder = 'DESC'; +if (!$sortfield) $sortfield = 'cf.date_creation'; +$page = GETPOST("page"); +if ($page < 0) $page = 0; +$offset = $limit * $page; + + + +/* + * Actions + */ + +if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETPOST("button_removefilter")) // Both test are required to be compatible with all browsers +{ + $sall=""; + $sref=""; + $snom=""; + $suser=""; + $sttc=""; + $sdate=''; + $sproduct=0; +} + /* @@ -65,21 +100,6 @@ $head[1][2] = 'replenishorders'; dol_fiche_head($head, 'replenishorders', '', 0, ''); $commandestatic = new CommandeFournisseur($db); -$sref = GETPOST('search_ref', 'alpha'); -$snom = GETPOST('search_nom', 'alpha'); -$suser = GETPOST('search_user', 'alpha'); -$sttc = GETPOST('search_ttc', 'int'); -$sall = GETPOST('search_all', 'alpha'); -$sdate = GETPOST('search_date', 'alpha'); -$page = GETPOST('page', 'int'); -$sproduct = GETPOST('sproduct', 'int'); -$sortorder = GETPOST('sortorder', 'alpha'); -$sortfield = GETPOST('sortfield', 'alpha'); - -if (!$sortorder) $sortorder = 'DESC'; -if (!$sortfield) $sortfield = 'cf.date_creation'; - -$offset = $conf->liste_limit * $page ; $sql = 'SELECT s.rowid as socid, s.nom as name, cf.date_creation as dc,'; $sql.= ' cf.rowid, cf.ref, cf.fk_statut, cf.total_ttc, cf.fk_user_author,'; @@ -101,25 +121,10 @@ if ($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER) { if (!$user->rights->societe->client->voir && !$socid) { $sql .= ' AND s.rowid = sc.fk_soc AND sc.fk_user = ' . $user->id; } -if ($sref) { - //natural search - $scrit = explode(' ', $sref); - foreach ($scrit as $crit) { - $sql .= ' AND cf.ref LIKE "%' . $db->escape($crit) . '%"'; - } -} -if ($snom) { - $scrit = explode(' ', $snom); - foreach ($scrit as $crit) { - $sql .= ' AND s.nom LIKE "%' . $db->escape($crit) . '%"'; - } -} -if ($suser) { - $sql .= ' AND u.login LIKE "%' . $db->escape($suser) . '%"'; -} -if ($sttc) { - $sql .= ' AND cf.total_ttc = ' . price2num($sttc); -} +if ($sref) $sql .= natural_search('cf.ref', $sref); +if ($snom) $sql .= natural_search('s.nom', $snom); +if ($suser) $sql .= natural_search('u.login', $suser); +if ($sttc) $sql .= natural_search('cf.total_ttc', $sttc, 1); if ($sdate) { if (GETPOST('search_datemonth', 'int') && GETPOST('search_dateday', 'int') && GETPOST('search_dateyear', 'int')) @@ -132,21 +137,15 @@ if ($sdate) } $sql .= " AND cf.date_creation = '" . $db->idate($date) . "'"; } -if ($sall) { - $sql .= ' AND (cf.ref LIKE "%' . $db->escape($sall) . '%" '; - $sql .= 'OR cf.note LIKE "%' . $db->escape($sall) . '%")'; -} -if (!empty($socid)) { - $sql .= ' AND s.rowid = ' . $socid; -} - +if ($sall) $sql .= natural_search(array('cf.ref','cf.note'), $sall); +if (!empty($socid)) $sql .= ' AND s.rowid = ' . $socid; if (GETPOST('statut', 'int')) { $sql .= ' AND fk_statut = ' . GETPOST('statut', 'int'); } $sql .= ' GROUP BY cf.rowid, cf.ref, cf.date_creation, cf.fk_statut'; $sql .= ', cf.total_ttc, cf.fk_user_author, u.login, s.rowid, s.nom'; $sql .= $db->order($sortfield, $sortorder); -$sql .= $db->plimit($conf->liste_limit+1, $offset); +$sql .= $db->plimit($limit+1, $offset); //print $sql; $resql = $db->query($sql); @@ -242,9 +241,8 @@ if ($resql) $form->select_date('', 'search_date', 0, 0, 1, '', 1, 0, 1, 0, ''). ''. '
'. ''; From 8b2d0064a37bfca8872bb74d62a5ad6a3fdfe942 Mon Sep 17 00:00:00 2001 From: fappels Date: Wed, 1 Feb 2017 16:51:42 +0100 Subject: [PATCH 061/170] Update changelog for developer --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 9b561820629..4abc3d9b932 100644 --- a/ChangeLog +++ b/ChangeLog @@ -150,6 +150,7 @@ Dolibarr better: - A new paramater sqlfilters was introduced to allow filter on any fields int the REST API. Few old parameters, no more required, were also removed. Use this new one if you were using one of them. - The trigger that activate or close a contract line is run on a contract line, not on contract. +- Method commande->set_availability(user, availability_id) removed from commande class. Dolibarr 5.0 was frozen before PHP 7.1 was released. Unit tests are successful on PHP 7.1 but we don't have enough feedback to confirm all application is compatible. Current officiel supported PHP versions are PHP 5.3 to 7.0. From 52b92995ccf9f29f3b503317f8b8d96df7dd3cfd Mon Sep 17 00:00:00 2001 From: fappels Date: Wed, 1 Feb 2017 19:49:52 +0100 Subject: [PATCH 062/170] use method commande->availability --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 4abc3d9b932..d8387c6a0e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -150,7 +150,7 @@ Dolibarr better: - A new paramater sqlfilters was introduced to allow filter on any fields int the REST API. Few old parameters, no more required, were also removed. Use this new one if you were using one of them. - The trigger that activate or close a contract line is run on a contract line, not on contract. -- Method commande->set_availability(user, availability_id) removed from commande class. +- Method commande->set_availability(user, availability_id) removed from commande class, use method commande->availability(availability_id, notrigger). Dolibarr 5.0 was frozen before PHP 7.1 was released. Unit tests are successful on PHP 7.1 but we don't have enough feedback to confirm all application is compatible. Current officiel supported PHP versions are PHP 5.3 to 7.0. From 9b37b52f45c3e4c4ef776c282d0e4685d60155bc Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 1 Feb 2017 20:55:30 +0100 Subject: [PATCH 063/170] Fix css --- htdocs/public/demo/index.php | 16 ++++++---------- htdocs/theme/eldy/style.css.php | 8 ++++++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/htdocs/public/demo/index.php b/htdocs/public/demo/index.php index e50193d8d51..4c7c8cbebc8 100644 --- a/htdocs/public/demo/index.php +++ b/htdocs/public/demo/index.php @@ -228,8 +228,8 @@ if (GETPOST("action") == 'gotodemo') */ $head=''; -$head.=''."\n"; -$head.=''."\n"; +$head.=''."\n"; +$head.=''."\n"; $head.=' @@ -309,20 +333,23 @@ llxHeaderVierge($langs->trans("DolibarrDemo"), $head); print "\n"; -print '
'; - $src = DOL_URL_ROOT . '/theme/' . $conf->theme . '/img/search.png'; - $value = dol_escape_htmltag($langs->trans('Search')); - print ''. + $searchpitco=$form->showFilterAndCheckAddButtons(0); + print $searchpitco; '
'; - -print ''; -print ''; -print ''; +print ''; -// Description -print ''; - -print '
'; -print '

'; -print '
'; +print '
'; +print '
'; +print ''; +print '
'; +print '
'; +print '
'; +print '
'; print '
'.$langs->trans("DemoDesc").'

'; -print '
'; print ''.$langs->trans("ChooseYourDemoProfil").''; +print '
'; +print '
'; -print '
'; -//print ''."\n"; +print '
'; +print '
'; + $i=0; foreach ($demoprofiles as $profilearray) { @@ -339,11 +366,12 @@ foreach ($demoprofiles as $profilearray) //print $urlfrom; if (! empty($profilearray['url'])) $urlwithmod=$profilearray['url']; - //if ($i % $NBOFCOLS == 0) print '
'; - //print ''; - //print ''; - //if ($i % $NBOFCOLS == ($NBOFCOLS-1)) print ''."\n"; - //print ''."\n"; $i++; } } -//print '
'."\n"; - - print '
'."\n"; + if (empty($profilearray['url'])) + { + print '
'; + } + + print ''."\n"; print ''."\n"; print ''."\n"; print ''."\n"; @@ -354,20 +382,35 @@ foreach ($demoprofiles as $profilearray) print ''."\n"; print ''."\n"; - print ''."\n"; - // Title - print ''; - print ''; - print ''; - print ''."\n"; + print ''; - print ''; - print ''."\n"; - print ''; + print ''; + + print '
'; + print ''; + print '
'; + + print ''; } - print '
Demo '.$profilearray['label'].''.$langs->trans($profilearray['label']).'
'; + print '
'; + + print ''; print '
'."\n"; - //print '
'; -print '
'; - - -print '
'; // TODO Replace this with a hook // Google Adsense (need Google module) @@ -477,7 +512,7 @@ function llxHeaderVierge($title, $head = "") top_htmlhead($head,$title); - print '
'."\n"; + print '
'."\n"; } /** diff --git a/htdocs/theme/eldy/style.css.php b/htdocs/theme/eldy/style.css.php index 34536323ec6..c42ea93cfce 100644 --- a/htdocs/theme/eldy/style.css.php +++ b/htdocs/theme/eldy/style.css.php @@ -4549,20 +4549,36 @@ border-top-right-radius: 6px; /* For demo pages */ +.demobody { + line-height: 130%; + font-size: 150%; + text-align: center; +} +.demobantext { + max-width: 1024px; + text-align: center; + display: inline-block; +} +.demobanbox { + font-size: 90%; +} img.demothumb { - box-shadow: 2px 2px 8px #BBB; + /* box-shadow: 2px 2px 8px #BBB; */ margin-right: 20px; margin-left: 10px; + width: 110px; } - .demobackground { - /* - background-image: url(/dolibarr_5.0/htdocs/theme/image-demo.jpg); + background-image: url(); background-size: cover; object-fit: contain; - */ + height: 140px; + background-position-y: bottom; +} +div#tr1profdemoall { + margin-left: -40px; + margin-right: -40px; } - /* ============================================================================== */ @@ -4601,12 +4617,19 @@ img.demothumb { width: px; } + .demobody { + line-height: 150% !important; + font-size: 100% !important; + } img.demothumb { box-shadow: 1px 1px 4px #BBB; margin-right: 6px; margin-left: 4px; width: 80px; } + .demobanbox { + font-size: 100%; + } div.tabBar { padding-left: 0px; From 5381eadc75f93403fe2aba0a2592532a3ae3ae27 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 3 Feb 2017 15:33:44 +0100 Subject: [PATCH 081/170] Fix translation --- htdocs/langs/en_US/other.lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index 1ea1f9da1db..7f33114abd7 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -67,7 +67,7 @@ PredefinedMailContentSendSupplierInvoice=__CONTACTCIVNAME__\n\nYou will find her PredefinedMailContentSendShipping=__CONTACTCIVNAME__\n\nYou will find here the shipping __SHIPPINGREF__\n\n__PERSONALIZED__Sincerely\n\n__SIGNATURE__ PredefinedMailContentSendFichInter=__CONTACTCIVNAME__\n\nYou will find here the intervention __FICHINTERREF__\n\n__PERSONALIZED__Sincerely\n\n__SIGNATURE__ PredefinedMailContentThirdparty=__CONTACTCIVNAME__\n\n__PERSONALIZED__\n\n__SIGNATURE__ -DemoDesc=Dolibarr is a compact ERP/CRM supporting several functional modules. A demo showcasing all modules makes no sense as this scenario never occurs. So, several demo profiles are available. +DemoDesc=Dolibarr is a compact ERP/CRM supporting several business modules. A demo showcasing all modules makes no sense as this scenario never occurs (several hundred available). So, several demo profiles are available. ChooseYourDemoProfil=Choose the demo profile that best suits your needs... DemoFundation=Manage members of a foundation DemoFundation2=Manage members and bank account of a foundation From a0251a262a3fa3ecf68360d72e52450b8b0c2d73 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 3 Feb 2017 15:34:09 +0100 Subject: [PATCH 082/170] The demo has its own CSS file. --- htdocs/public/demo/demo.css | 113 ++++++++++++++++++++++++++++++++ htdocs/public/demo/index.php | 58 +--------------- htdocs/theme/eldy/style.css.php | 51 -------------- htdocs/theme/md/style.css.php | 20 ------ 4 files changed, 115 insertions(+), 127 deletions(-) create mode 100644 htdocs/public/demo/demo.css diff --git a/htdocs/public/demo/demo.css b/htdocs/public/demo/demo.css new file mode 100644 index 00000000000..486096ccdfa --- /dev/null +++ b/htdocs/public/demo/demo.css @@ -0,0 +1,113 @@ + +/* ============================================================================== */ +/* Demo */ +/* ============================================================================== */ + + +.CTable { + padding: 6px; + font-weight: normal; + color: #444444 !important; + + margin: 8px 0px 8px 2px; + + /*border: 1px solid #bbb; + border-radius: 8px; + -moz-border-radius: 8px;*/ + + background: -webkit-linear-gradient(bottom, rgb(255,255,255) 85%, rgb(255,255,255) 100%); +} +.csscolumns { + margin-top: 6px; + -webkit-column-count: 4; /* Chrome, Safari, Opera */ + -moz-column-count: 4; /* Firefox */ + column-count: 3; + text-align: left; +} +.demomaxoveflow { + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; +} +@media only screen and (max-width: 840px) +{ + .csscolumns { + -webkit-column-count: 3; /* Chrome, Safari, Opera */ + -moz-column-count: 3; /* Firefox */ + column-count: 3; + text-align: left; + } +} +@media only screen and (max-width: 640px) +{ + .csscolumns { + -webkit-column-count: 2; /* Chrome, Safari, Opera */ + -moz-column-count: 2; /* Firefox */ + column-count: 2; + text-align: left; + } +} +@media only screen and (max-width: 420px) +{ + .csscolumns { + -webkit-column-count: 1; /* Chrome, Safari, Opera */ + -moz-column-count: 1; /* Firefox */ + column-count: 1; + text-align: left; + } +} + + +/* For demo pages */ +.demobody { + line-height: 130%; + font-size: 150%; + text-align: center; +} +.demobantext { + max-width: 1024px; + text-align: center; + display: inline-block; +} +.demobanbox { + font-size: 90%; +} +img.demothumb { + /* box-shadow: 2px 2px 8px #BBB; */ + margin-right: 20px; + margin-left: 10px; + width: 110px; +} +.demobackground { + background-image: url('dolibarr_demo_ban.jpg'); + background-size: cover; + object-fit: contain; + height: 140px; + background-position-y: bottom; +} +div#tr1profdemoall { + margin-left: -40px; + margin-right: -40px; +} + + +@media only screen and (max-width: 767px) +{ + .demobody { + line-height: 150% !important; + font-size: 100% !important; + } + img.demothumb { + box-shadow: 1px 1px 4px #BBB; + margin-right: 6px; + margin-left: 4px; + width: 80px; + } + .demobanbox { + font-size: 100%; + } + div#tr1profdemoall { + margin-left: 0px; + margin-right: 0px; + } +} \ No newline at end of file diff --git a/htdocs/public/demo/index.php b/htdocs/public/demo/index.php index a83ef87b65b..800639be251 100644 --- a/htdocs/public/demo/index.php +++ b/htdocs/public/demo/index.php @@ -247,62 +247,8 @@ if (GETPOST("action") == 'gotodemo') $head=''; $head.=''."\n"; $head.=''."\n"; -$head.=' +$head.=' From 5a7c20e897a47acdd3417c84c87ef1e7f5a8cd8b Mon Sep 17 00:00:00 2001 From: Gustavo Novaro Date: Wed, 8 Feb 2017 17:13:54 -0300 Subject: [PATCH 137/170] Code style var to public --- htdocs/contact/class/contact.class.php | 68 +++++++++++++------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/htdocs/contact/class/contact.class.php b/htdocs/contact/class/contact.class.php index 3b4fa942f81..40051753252 100644 --- a/htdocs/contact/class/contact.class.php +++ b/htdocs/contact/class/contact.class.php @@ -41,61 +41,61 @@ class Contact extends CommonObject public $table_element='socpeople'; protected $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe - var $civility_id; // In fact we store civility_code - var $civility_code; - var $address; - var $zip; - var $town; + public $civility_id; // In fact we store civility_code + public $civility_code; + public $address; + public $zip; + public $town; /** * @deprecated * @see state_id */ - var $fk_departement; + public $fk_departement; /** * @deprecated * @see state_code */ - var $departement_code; + public $departement_code; /** * @deprecated * @see state */ - var $departement; - var $state_id; // Id of department - var $state_code; // Code of department - var $state; // Label of department + public $departement; + public $state_id; // Id of department + public $state_code; // Code of department + public $state; // Label of department - var $poste; // Position + public $poste; // Position - var $socid; // fk_soc - var $statut; // 0=inactif, 1=actif + public $socid; // fk_soc + public $statut; // 0=inactif, 1=actif - var $code; - var $email; - var $skype; - var $photo; - var $jabberid; - var $phone_pro; - var $phone_perso; - var $phone_mobile; - var $fax; + public $code; + public $email; + public $skype; + public $photo; + public $jabberid; + public $phone_pro; + public $phone_perso; + public $phone_mobile; + public $fax; - var $priv; + public $priv; - var $birthday; - var $default_lang; - var $no_email; // 1=Don't send e-mail to this contact, 0=do + public $birthday; + public $default_lang; + public $no_email; // 1=Don't send e-mail to this contact, 0=do - var $ref_facturation; // Nb de reference facture pour lequel il est contact - var $ref_contrat; // Nb de reference contrat pour lequel il est contact - var $ref_commande; // Nb de reference commande pour lequel il est contact - var $ref_propal; // Nb de reference propal pour lequel il est contact + public $ref_facturation; // Reference number of invoice for which it is contact + public $ref_contrat; // Nb de reference contrat pour lequel il est contact + public $ref_commande; // Nb de reference commande pour lequel il est contact + public $ref_propal; // Nb de reference propal pour lequel il est contact - var $user_id; - var $user_login; + public $user_id; + public $user_login; - var $oldcopy; // To contains a clone of this when we need to save old properties of object + public $oldcopy; // To contains a clone of this when we need to save old properties of object /** From e053308c6fc5ae9da3a2aca9eba7f7fec06880b7 Mon Sep 17 00:00:00 2001 From: Gustavo Novaro Date: Wed, 8 Feb 2017 17:24:11 -0300 Subject: [PATCH 138/170] Code style var to public --- htdocs/societe/class/address.class.php | 58 +++++++++++++------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/htdocs/societe/class/address.class.php b/htdocs/societe/class/address.class.php index d8a1687cf9d..a3a20dfbd3d 100644 --- a/htdocs/societe/class/address.class.php +++ b/htdocs/societe/class/address.class.php @@ -31,21 +31,21 @@ */ class Address { - var $db; + private $db; - var $id; - var $type; - var $label; - var $socid; - var $name; - var $address; - var $zip; - var $town; - var $country_id; - var $country_code; - var $phone; - var $fax; - var $note; + public $id; + public $type; + public $label; + public $socid; + public $name; + public $address; + public $zip; + public $town; + public $country_id; + public $country_code; + public $phone; + public $fax; + public $note; /** * Adresses liees a la societe @@ -495,21 +495,21 @@ class Address */ class AddressLine { - - var $id; - var $date_creation; - var $date_modification; - var $label; - var $name; - var $address; - var $zip; - var $town; - var $country_id; - var $country_code; - var $country; - var $phone; - var $fax; - var $note; + private $db; + public $id; + public $date_creation; + public $date_modification; + public $label; + public $name; + public $address; + public $zip; + public $town; + public $country_id; + public $country_code; + public $country; + public $phone; + public $fax; + public $note; /** From a8c5661cb70f5f3314274e94f5f16f977336e5fe Mon Sep 17 00:00:00 2001 From: Gustavo Novaro Date: Wed, 8 Feb 2017 17:36:04 -0300 Subject: [PATCH 139/170] Code style Set public and private --- htdocs/cashdesk/class/Auth.class.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/htdocs/cashdesk/class/Auth.class.php b/htdocs/cashdesk/class/Auth.class.php index 33bbd094050..0de4254cf29 100644 --- a/htdocs/cashdesk/class/Auth.class.php +++ b/htdocs/cashdesk/class/Auth.class.php @@ -22,14 +22,14 @@ */ class Auth { - var $db; + private $db; - var $login; - var $passwd; + private $login; + private $passwd; - var $reponse; + private $reponse; - var $sqlQuery; + public $sqlQuery; /** * Enter description here ... @@ -37,7 +37,7 @@ class Auth * @param DoliDB $db Database handler * @return void */ - function __construct($db) + public function __construct($db) { $this->db = $db; $this->reponse(null); @@ -49,7 +49,7 @@ class Auth * @param string $aLogin Login * @return void */ - function login($aLogin) + public function login($aLogin) { $this->login = $aLogin; } @@ -71,7 +71,7 @@ class Auth * @param string $aReponse Response * @return void */ - function reponse($aReponse) + public function reponse($aReponse) { $this->reponse = $aReponse; } @@ -83,7 +83,7 @@ class Auth * @param string $aPasswd Password * @return int 0 or 1 */ - function verif($aLogin, $aPasswd) + public function verif($aLogin, $aPasswd) { global $conf,$langs; global $dolibarr_main_authentication,$dolibarr_auto_user; From 2418754d71811bcec2d0abf3fc79fd8d69c43ddd Mon Sep 17 00:00:00 2001 From: Ion Date: Wed, 8 Feb 2017 23:27:58 +0100 Subject: [PATCH 140/170] Fill oldline in supplier order updateline --- htdocs/fourn/class/fournisseur.commande.class.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/htdocs/fourn/class/fournisseur.commande.class.php b/htdocs/fourn/class/fournisseur.commande.class.php index f656e7a41e7..053e5b314d6 100644 --- a/htdocs/fourn/class/fournisseur.commande.class.php +++ b/htdocs/fourn/class/fournisseur.commande.class.php @@ -2346,8 +2346,11 @@ class CommandeFournisseur extends CommonOrder $subprice = price2num($pu_ht,'MU'); + //Fetch current line from the database and then clone the object and set it in $oldline property $this->line=new CommandeFournisseurLigne($this->db); $this->line->fetch($rowid); + $oldline = clone $this->line; + $this->line->oldline = $oldline; $this->line->context = $this->context; From f8a6d60fe53b2966ed4707a2c741931fdde79095 Mon Sep 17 00:00:00 2001 From: Gustavo Novaro Date: Wed, 8 Feb 2017 20:24:20 -0300 Subject: [PATCH 141/170] $db attibute now be protected. --- htdocs/cashdesk/class/Auth.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/cashdesk/class/Auth.class.php b/htdocs/cashdesk/class/Auth.class.php index 0de4254cf29..5e431514d1c 100644 --- a/htdocs/cashdesk/class/Auth.class.php +++ b/htdocs/cashdesk/class/Auth.class.php @@ -22,7 +22,7 @@ */ class Auth { - private $db; + protected $db; private $login; private $passwd; From febe95cd2eaa45a044c354f6a9590637d77db15a Mon Sep 17 00:00:00 2001 From: Gustavo Novaro Date: Wed, 8 Feb 2017 20:27:14 -0300 Subject: [PATCH 142/170] $db attributte now be protected --- htdocs/societe/class/address.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/societe/class/address.class.php b/htdocs/societe/class/address.class.php index a3a20dfbd3d..43e46008910 100644 --- a/htdocs/societe/class/address.class.php +++ b/htdocs/societe/class/address.class.php @@ -31,7 +31,7 @@ */ class Address { - private $db; + protected $db; public $id; public $type; @@ -495,7 +495,7 @@ class Address */ class AddressLine { - private $db; + protected $db; public $id; public $date_creation; public $date_modification; From fbe0b13706522db47429339204a17f4487d6775f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 9 Feb 2017 01:07:54 +0100 Subject: [PATCH 143/170] Add parameter to set the url to use for the file integrity checker --- htdocs/admin/system/filecheck.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/htdocs/admin/system/filecheck.php b/htdocs/admin/system/filecheck.php index 7536c6df793..3a78fb6e9ea 100644 --- a/htdocs/admin/system/filecheck.php +++ b/htdocs/admin/system/filecheck.php @@ -73,11 +73,15 @@ print '
'; // Modified or missing files $file_list = array('missing' => array(), 'updated' => array()); -// File to analyze -//$xmlfile = DOL_DOCUMENT_ROOT.'/install/filelist-'.DOL_VERSION.'.xml'; +// Local file to compare to $xmlshortfile = GETPOST('xmlshortfile')?GETPOST('xmlshortfile'):'/install/filelist-'.DOL_VERSION.'.xml'; $xmlfile = DOL_DOCUMENT_ROOT.$xmlshortfile; -$xmlremote = GETPOST('xmlremote')?GETPOST('xmlremote'):'https://www.dolibarr.org/files/stable/signatures/filelist-'.DOL_VERSION.'.xml'; +// Remote file to compare to +$xmlremote = GETPOST('xmlremote'); +if (empty($xmlremote) && ! empty($conf->global->MAIN_FILECHECK_URL)) $xmlremote = $conf->global->MAIN_FILECHECK_URL; +$param='MAIN_FILECHECK_URL_'.DOL_VERSION; +if (empty($xmlremote) && ! empty($conf->global->$param)) $xmlremote = $conf->global->$param; +if (empty($xmlremote)) $xmlremote = 'https://www.dolibarr.org/files/stable/signatures/filelist-'.DOL_VERSION.'.xml'; // Test if remote test is ok From 5c9c7619112e905292cd0a3512ec8264555fe632 Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Thu, 9 Feb 2017 05:58:39 +0100 Subject: [PATCH 144/170] Fix : Bank account label not shown in dol_banner_tab --- htdocs/core/lib/functions.lib.php | 45 ++++++++++++++++--------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index acd74643a38..4d9f7c46e17 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -1,18 +1,18 @@ - * Copyright (C) 2003 Jean-Louis Bergamo - * Copyright (C) 2004-2013 Laurent Destailleur - * Copyright (C) 2004 Sebastien Di Cintio - * Copyright (C) 2004 Benoit Mortier - * Copyright (C) 2004 Christophe Combelles - * Copyright (C) 2005-2012 Regis Houssin - * Copyright (C) 2008 Raphael Bertrand (Resultic) - * Copyright (C) 2010-2016 Juanjo Menent - * Copyright (C) 2013 Cédric Salvador - * Copyright (C) 2013 Alexandre Spangaro - * Copyright (C) 2014 Cédric GROSS - * Copyright (C) 2014-2015 Marcos García - * Copyright (C) 2015 Jean-François Ferry +/* Copyright (C) 2000-2007 Rodolphe Quiedeville + * Copyright (C) 2003 Jean-Louis Bergamo + * Copyright (C) 2004-2013 Laurent Destailleur + * Copyright (C) 2004 Sebastien Di Cintio + * Copyright (C) 2004 Benoit Mortier + * Copyright (C) 2004 Christophe Combelles + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2008 Raphael Bertrand (Resultic) + * Copyright (C) 2010-2016 Juanjo Menent + * Copyright (C) 2013 Cédric Salvador + * Copyright (C) 2013-2017 Alexandre Spangaro + * Copyright (C) 2014 Cédric GROSS + * Copyright (C) 2014-2015 Marcos García + * Copyright (C) 2015 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 @@ -980,11 +980,11 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r $showbarcode=empty($conf->barcode->enabled)?0:($object->barcode?1:0); if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode=0; $modulepart='unknown'; - if ($object->element == 'societe') $modulepart='societe'; - if ($object->element == 'contact') $modulepart='contact'; - if ($object->element == 'member') $modulepart='memberphoto'; - if ($object->element == 'user') $modulepart='userphoto'; - if ($object->element == 'product') $modulepart='product'; + if ($object->element == 'societe') $modulepart='societe'; + if ($object->element == 'contact') $modulepart='contact'; + if ($object->element == 'member') $modulepart='memberphoto'; + if ($object->element == 'user') $modulepart='userphoto'; + if ($object->element == 'product') $modulepart='product'; if ($object->element == 'product') { @@ -1003,7 +1003,6 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r $nophoto='/public/theme/common/nophoto.png'; $morehtmlleft.='
No photo
'; } - } } else @@ -1088,7 +1087,11 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r $morehtmlstatus.=$tmptxt; } if (! empty($object->name_alias)) $morehtmlref.='
'.$object->name_alias.'
'; // For thirdparty - if ($object->element == 'product' && ! empty($object->label)) $morehtmlref.='
'.$object->label.'
'; + + if ($object->element == 'product' || $object->element == 'bank_account') + { + if(! empty($object->label)) $morehtmlref.='
'.$object->label.'
'; + } if ($object->element != 'product' && $object->element != 'bookmark') { From 56d7fe1bf6481d28f32287a9fa4a95c859a87979 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 9 Feb 2017 10:23:44 +0100 Subject: [PATCH 145/170] Pad files with https --- build/pad/pad_dolibarr_s.xml | 232 +++++++++++++++++++++++++++++++++++ build/pad/pad_doliwamp.xml | 8 +- build/pad/pad_doliwamp_s.xml | 225 +++++++++++++++++++++++++++++++++ 3 files changed, 461 insertions(+), 4 deletions(-) create mode 100644 build/pad/pad_dolibarr_s.xml create mode 100644 build/pad/pad_doliwamp_s.xml diff --git a/build/pad/pad_dolibarr_s.xml b/build/pad/pad_dolibarr_s.xml new file mode 100644 index 00000000000..8effe4b771a --- /dev/null +++ b/build/pad/pad_dolibarr_s.xml @@ -0,0 +1,232 @@ + + + + 3.11 + PADGen 3.1.1.47 http://www.padgen.org + Portable Application Description, or PAD for short, is a data set that is used by shareware authors to disseminate information to anyone interested in their software products. To find out more go to http://pad.asp-software.org + + + Dolibarr team + 20 rue camponac + + Pessac + + 33600 + FRANCE + https://www.dolibarr.org + + Dolibarr team + Dolibarr team + contact@dolibarr.org + Dolibarr team + Dolibarr team + contact@dolibarr.org + + + dolibarr-dev@nongnu.org + dolibarr-dev@nongnu.org + dolibarr-dev@nongnu.org + + + + + + + + Dolibarr + 5.0 + 02 + 01 + 2017 + + + + Freeware + Major Update + No Install Support + Linux,Mac OS X,Mac Other,Unix,Win2000,Win7 x32,Win7 x64,Win98,WinOther,WinServer,WinVista,WinVista x64,WinXP,Other + English,Arabic,Catalan,Chinese,Danish,Dutch,Finnish,French,German,Greek,Icelandic,Italian,Norwegian,Polish,Portuguese,Romanian,Russian,Slovenian,Spanish,Swedish,Turkish + Increase performances, Setup process is easier, Reduce number of clicks required to use software + Business + Business::Accounting & Finance + None + + 18037439 + 18037 + 18.03 + + + N + + Days + + + + + + + + + dolibarr, erp, crm, invoices, commercial proposals, orders, accounting, stock, products, agenda, bank, business, company, foundation, management, sme, doliwamp + Dolibarr ERP & CRM + Dolibarr ERP & CRM, the easy to use open source software to manage your activity + Dolibarr ERP & CRM, the easy to use open source software to manage your activity (invoices, customers, suppliers, contracts, agenda, emailings...) and any other things a small or mid-sized business or a foundation needs to manage. + Dolibarr ERP & CRM is a software built by modules addition (you enable only features you need), to manage small or medium companies, freelancers or foundations. We can say Dolibarr is an ERP or CRM. Dolibarr is also available with an auto-installer for Windows users with no technical knowledge to install Dolibarr and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. See DoliWamp software for this. + Dolibarr ERP & CRM is a software built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations. We can say Dolibarr is an ERP or CRM (or both depending on activated modules). It's an OpenSource project base on a WAMP, MAMP or LAMP server (Apache, Mysql, PHP for all Operating Systems). Dolibarr differs from other ERP or CRM softwares (like OpenAguila, OpenBravo, OpenERP, Neogia, Compiere, etc) because everything was made to be more simple: +Simple to install +Simple to use +Simple to develop +Note that Dolibarr is also available with an auto-installer for Windows or Ubuntu users with no technical knowledge to install Dolibarr and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. This version is called DoliWamp (for Windows) or DoliBuntu (for Ubuntu/Debian). + + + dolibarr, erp, crm, invoices, commercial proposals, orders, accounting, stock, products, agenda, bank, business, company, foundation, management, sme, doliwamp + Dolibarr ERP & CRM + Dolibarr ERP & CRM, le gestionnaire simple pour gérer votre activité + Dolibarr ERP & CRM, le logiciel simple et OpenSource pour gérer votre activité (factures, devis, facturation, commandes, compta, trésorerie, stocks, produits, agenda, comptes bancaires, associations) + Dolibarr ERP & CRM est un logiciel modulaire (on n'active que les fonctions que l'on désire) de gestions de TPE/PME, d'indépendants, d'entrepreneurs ou d'associations. En terme plus techniques, c'est un ERP et CRM. C'est un projet OpenSource qui s'exécute au sein d'un serveur Web et peut donc être accessible depuis n'importe quel lieu disposant d'une connexion Internet (Projet basé sur un serveur WAMP, MAMP ou LAMP: Apache, MySQL, PHP). + Dolibarr ERP & CRM est un logiciel modulaire (on n'active que les fonctions que l'on désire) de gestions de TPE/PME, d'indépendants, d'entrepreneurs ou d'associations. En terme plus techniques, c'est un ERP et CRM. C'est un projet OpenSource qui s'exécute au sein d'un serveur Web et peut donc être accessible depuis n'importe quel lieu disposant d'une connexion Internet (Projet basé sur un serveur WAMP, MAMP ou LAMP: Apache, MySQL, PHP). Dolibarr vient compléter les offres déjà nombreuses de logiciels de cette catégorie (comme OpenBravo, OpenERP, SugarCRM, Neogia, Compiere, etc.) mais se démarque par le fait qu'ici tout est fait pour offrir de la simplicité (règle des 3 S): +Simple pour l'installation (avec au choix des installeurs clé en main pour ceux qui ignorent comment installer un serveur Web, ou une installation manuelle) +Simple pour l'utilisation (fonctions modulaires pour ne pas surcharger les menus, informations claires à la saisie) +Simple pour le développement (pas de frameworks lourds). +Dolibarr intègre en effet sa propre architecture (design patterns) permettant à tout développeur d'être tout de suite opérationnel sans connaissances particulières autre que le PHP. + + + erp, crm, gestionale, medie imprese, fondazioni + Gestionale open source e gratuito + Gestionale open source e gratuito per piccole e medie imprese, fondazioni + Dolibarr è un a gestionale open source e gratuito per piccole e medie imprese, fondazioni e liberi professionisti. Include varie funzionalità per Enterprise Resource Planning e gestione dei clienti (CRM), ma anche ulteriori attività. + Dolibarr è un programma gestionale open source e gratuito per piccole e medie imprese, fondazioni e liberi professionisti. Include varie funzionalità per Enterprise Resource Planning e gestione dei clienti (CRM), ma anche ulteriori attività. Dolibar è progettato per poter fornire solo ciò di cui hai bisogno ed essere facile da usare. + Dolibarr è un programma gestionale open source e gratuito per piccole e medie imprese, fondazioni e liberi professionisti. Include varie funzionalità per Enterprise Resource Planning e gestione dei clienti (CRM), ma anche ulteriori attività. Dolibar è progettato per poter fornire solo ciò di cui hai bisogno ed essere facile da usare. Dolibar è completamente web-based, progettato per poter fornire solo ciò di cui hai bisogno ed essere facile da usare. + + + + + https://www.dolibarr.org + https://www.dolibarr.org + http://www.dolibarr.org/images/dolibarr_screenshot1.png + http://www.dolibarr.org/images/dolibarr.gif + https://www.dolibarr.org/files/pad_dolibarr.xml + + + https://www.dolibarr.org/files/dolibarr.zip + http://www.dolibarr.org/files/dolibarr.zip + + + + + + GNU GPL + GNU GPL + + + Y + 1.4 + http://pad.asp-software.org/extensions/Affiliates.htm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Y + N + + + diff --git a/build/pad/pad_doliwamp.xml b/build/pad/pad_doliwamp.xml index 158327977d2..4f7981a4314 100644 --- a/build/pad/pad_doliwamp.xml +++ b/build/pad/pad_doliwamp.xml @@ -13,7 +13,7 @@ 33600 FRANCE - http://www.nltechno.com + https://www.dolibarr.org NLTechno NLTechno @@ -53,7 +53,7 @@ 26048004 25437 - 24.84 + 45.84 N @@ -96,8 +96,8 @@ DoliWamp is the auto-installer for Windows users with no technical knowledge to http://www.nltechno.com/doliwamp/ http://www.nltechno.com/doliwamp/ - https://www.dolibarr.org/images/dolibarr_screenshot1.png - https://www.dolibarr.org/images/dolibarr.gif + http://www.dolibarr.org/images/dolibarr_screenshot1.png + http://www.dolibarr.org/images/dolibarr.gif https://www.dolibarr.org/files/pad_doliwamp.xml diff --git a/build/pad/pad_doliwamp_s.xml b/build/pad/pad_doliwamp_s.xml new file mode 100644 index 00000000000..06eaec3af98 --- /dev/null +++ b/build/pad/pad_doliwamp_s.xml @@ -0,0 +1,225 @@ + + + + 3.11 + PADGen 3.1.1.47 http://www.padgen.org + Portable Application Description, or PAD for short, is a data set that is used by shareware authors to disseminate information to anyone interested in their software products. To find out more go to http://pad.asp-software.org + + + NLTechno + 20 Rue Camponac + + Pessac + + 33600 + FRANCE + https://www.dolibarr.org + + NLTechno + NLTechno + contact@nltechno.com + NLTechno + NLTechno + contact@nltechno.com + + + support@nltechno.com + support@nltechno.com + support@nltechno.com + + + + + + + + DoliWamp + 5.0 + 02 + 01 + 2017 + + + + Freeware + Major Update + Install and Uninstall + Win2000,Win7 x32,Win7 x64,Win98,WinOther,WinServer,WinVista,WinVista x64,WinXP,Other + English,Arabic,Catalan,Chinese,Dutch,Finnish,French,German,Icelandic,Italian,Norwegian,Polish,Portuguese,Romanian,Russian,Slovenian,Spanish,Swedish,Turkish + Increase performances, Setup process is easier, Reduce number of clicks required to use software + Business + Business::Accounting & Finance + None + + 26048004 + 25437 + 45.84 + + + N + + Days + + + + + + + + + doliwamp, dolibarr, erp, crm, invoices, commercial proposals, orders, accounting, stock, products, agenda, bank, business, company, foundation, management + DoliWamp, Dolibarr ERP/CRM for Windows + DoliWamp, the easy to use Dolibarr for Windows to manage your company,foundation + DoliWamp is the Dolibarr ERP/CRM for Windows, the easy to use open source software to manage your activity (invoices, customers, suppliers, contracts, agenda, emailings...) and any other things a small or mid-sized business or a foundation needs. + DoliWamp is the Dolibarr ERP/CRM autoinstaller for Windows users with no technical knowledge to install Dolibarr and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. Dolibarr ERP/CRM is a software package built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations. + DoliWamp is the Dolibarr ERP/CRM for Windows. Dolibarr ERP & CRM is a software built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations (You can manage or follow contacts, invoices, orders, commercial proposals, products, stock management, agenda, mass emailings, members of a foundation, bank accounts...). Based on a WAMP, MAMP or LAMP server (Apache, Mysql, PHP for all Operating Systems), you can install it as a standalone program or use it from anywhere with any web browser. Dolibarr is an OpenSource project. It differs from other ERP or CRM softwares (like OpenAguila, OpenBravo, OpenERP, Neogia, Compiere, etc) because everything was made to be more simple: Simple to install, Simple to use, Simple to develop. +DoliWamp is the auto-installer for Windows users with no technical knowledge to install Dolibarr ERP/CRM and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. + + + doliwamp, dolibarr, erp, crm, factures, devis, facturation, commandes, compta, trésorerie, stocks, produits, agenda, comptes bancaires, associations, entreprises, PME, TPE + DoliWamp, Dolibarr ERP/CRM pour Windows + DoliWamp, la distribution de Dolibarr pour gérer votre entreprise ou association + DoliWamp est la version spécialisée pour Windows de Dolibarr ERP-CRM, le logiciel simple et OpenSource pour gérer votre activité (factures, devis, facturation, commandes, compta, trésorerie, stocks, produits, agenda, comptes bancaires, associations) + DoliWamp est la version spécialisée pour Windows de Dolibarr ERP-CRM, le logiciel simple et OpenSource pour gérer votre activité (factures, devis, facturation, commandes, compta, trésorerie, stocks, produits, agenda, comptes bancaires, associations) + DoliWamp est la version spécialisée pour Windows de Dolibarr ERP-CRM, le logiciel simple et OpenSource pour gérer votre activité (factures, devis, facturation, commandes, compta, trésorerie, stocks, produits, agenda, comptes bancaires, associations) + + + doliwamp, dolibarr, erp, crm, gestionale, medie imprese, fondazioni + DoliWamp, Dolibarr ERP/CRM per Windows + Gestionale open source e gratuito per piccole e medie imprese, fondazioni + Dolibarr è un a gestionale open source e gratuito per piccole e medie imprese, fondazioni e liberi professionisti. Include varie funzionalità per Enterprise Resource Planning e gestione dei clienti (CRM), ma anche ulteriori attività. + Dolibarr è un programma gestionale open source e gratuito per piccole e medie imprese, fondazioni e liberi professionisti. Include varie funzionalità per Enterprise Resource Planning e gestione dei clienti (CRM), ma anche ulteriori attività. Dolibar è progettato per poter fornire solo ciò di cui hai bisogno ed essere facile da usare. + Dolibarr è un programma gestionale open source e gratuito per piccole e medie imprese, fondazioni e liberi professionisti. Include varie funzionalità per Enterprise Resource Planning e gestione dei clienti (CRM), ma anche ulteriori attività. Dolibar è progettato per poter fornire solo ciò di cui hai bisogno ed essere facile da usare. Dolibar è completamente web-based, progettato per poter fornire solo ciò di cui hai bisogno ed essere facile da usare. + + + + + http://www.nltechno.com/doliwamp/ + http://www.nltechno.com/doliwamp/ + https://www.dolibarr.org/images/dolibarr_screenshot1.png + https://www.dolibarr.org/images/dolibarr.gif + https://www.dolibarr.org/files/pad_doliwamp.xml + + + https://www.dolibarr.org/files/doliwamp.exe + http://www.dolibarr.org/files/doliwamp.exe + + + + + + GNU GPL + GNU GPL + + + Y + 1.4 + http://pad.asp-software.org/extensions/Affiliates.htm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Y + N + + + From 6f3f0a826f8bb96ed13012ddaed043010703dc23 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 9 Feb 2017 11:24:01 +0100 Subject: [PATCH 146/170] Fix pad files --- build/pad/{pad_dolibarr_s.xml => pad_dolibarr_nos.xml} | 0 build/pad/pad_doliwamp.xml | 4 ++-- build/pad/{pad_doliwamp_s.xml => pad_doliwamp_nos.xml} | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename build/pad/{pad_dolibarr_s.xml => pad_dolibarr_nos.xml} (100%) rename build/pad/{pad_doliwamp_s.xml => pad_doliwamp_nos.xml} (98%) diff --git a/build/pad/pad_dolibarr_s.xml b/build/pad/pad_dolibarr_nos.xml similarity index 100% rename from build/pad/pad_dolibarr_s.xml rename to build/pad/pad_dolibarr_nos.xml diff --git a/build/pad/pad_doliwamp.xml b/build/pad/pad_doliwamp.xml index 4f7981a4314..06eaec3af98 100644 --- a/build/pad/pad_doliwamp.xml +++ b/build/pad/pad_doliwamp.xml @@ -96,8 +96,8 @@ DoliWamp is the auto-installer for Windows users with no technical knowledge to http://www.nltechno.com/doliwamp/ http://www.nltechno.com/doliwamp/ - http://www.dolibarr.org/images/dolibarr_screenshot1.png - http://www.dolibarr.org/images/dolibarr.gif + https://www.dolibarr.org/images/dolibarr_screenshot1.png + https://www.dolibarr.org/images/dolibarr.gif https://www.dolibarr.org/files/pad_doliwamp.xml diff --git a/build/pad/pad_doliwamp_s.xml b/build/pad/pad_doliwamp_nos.xml similarity index 98% rename from build/pad/pad_doliwamp_s.xml rename to build/pad/pad_doliwamp_nos.xml index 06eaec3af98..4f7981a4314 100644 --- a/build/pad/pad_doliwamp_s.xml +++ b/build/pad/pad_doliwamp_nos.xml @@ -96,8 +96,8 @@ DoliWamp is the auto-installer for Windows users with no technical knowledge to http://www.nltechno.com/doliwamp/ http://www.nltechno.com/doliwamp/ - https://www.dolibarr.org/images/dolibarr_screenshot1.png - https://www.dolibarr.org/images/dolibarr.gif + http://www.dolibarr.org/images/dolibarr_screenshot1.png + http://www.dolibarr.org/images/dolibarr.gif https://www.dolibarr.org/files/pad_doliwamp.xml From 488d68113d02452af35344592d50264749c38b70 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 9 Feb 2017 12:56:39 +0100 Subject: [PATCH 147/170] Add total in the file integrity checker --- htdocs/admin/system/filecheck.php | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/htdocs/admin/system/filecheck.php b/htdocs/admin/system/filecheck.php index 3a78fb6e9ea..6499f16f76c 100644 --- a/htdocs/admin/system/filecheck.php +++ b/htdocs/admin/system/filecheck.php @@ -184,6 +184,7 @@ if ($xml) } } + // Files missings $out.=load_fiche_titre($langs->trans("FilesMissing")); $out.=''; @@ -216,8 +217,10 @@ if ($xml) $out.='
'; + // Files modified $out.=load_fiche_titre($langs->trans("FilesModified")); + $totalsize=0; $out.='
'; $out.=''; $out.=''; @@ -241,10 +244,20 @@ if ($xml) $out.='' . "\n"; $out.='' . "\n"; $out.='' . "\n"; - $out.='' . "\n"; + $size = dol_filesize(DOL_DOCUMENT_ROOT.'/'.$file['filename']); + $totalsize += $size; + $out.='' . "\n"; $out.='' . "\n"; $out.="\n"; } + $out.=''; + $out.='' . "\n"; + $out.='' . "\n"; + $out.='' . "\n"; + $out.='' . "\n"; + $out.='' . "\n"; + $out.='' . "\n"; + $out.="\n"; } else { @@ -254,8 +267,10 @@ if ($xml) $out.='
'; + // Files added $out.=load_fiche_titre($langs->trans("FilesAdded")); + $totalsize = 0; $out.='
#'.$file['filename'].''.$file['expectedmd5'].''.$file['md5'].''.dol_print_size(dol_filesize(DOL_DOCUMENT_ROOT.'/'.$file['filename'])).''.dol_print_size($size).''.dol_print_date(dol_filemtime(DOL_DOCUMENT_ROOT.'/'.$file['filename']),'dayhour').'
'.$langs->trans("Total").''.dol_print_size($totalsize).'
'; $out.=''; $out.=''; @@ -279,10 +294,20 @@ if ($xml) $out.='' . "\n"; $out.='' . "\n"; $out.='' . "\n"; - $out.='' . "\n"; + $size = dol_filesize(DOL_DOCUMENT_ROOT.'/'.$file['filename']); + $totalsize += $size; + $out.='' . "\n"; $out.='' . "\n"; $out.="\n"; } + $out.=''; + $out.='' . "\n"; + $out.='' . "\n"; + $out.='' . "\n"; + $out.='' . "\n"; + $out.='' . "\n"; + $out.='' . "\n"; + $out.="\n"; } else { From fa527ff4eb436003a4bb5a4764f14807f12183b4 Mon Sep 17 00:00:00 2001 From: De Coninck Laurent Date: Thu, 9 Feb 2017 13:10:32 +0100 Subject: [PATCH 148/170] fix the card page generator --- dev/skeletons/build_class_from_table.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev/skeletons/build_class_from_table.php b/dev/skeletons/build_class_from_table.php index d9e30b23501..0e5a6d82a82 100755 --- a/dev/skeletons/build_class_from_table.php +++ b/dev/skeletons/build_class_from_table.php @@ -636,7 +636,8 @@ foreach ($skeletonfiles as $skeletonfile => $outfile) { if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime']) { - $varprop.="print '';\n"; + $baseString = 'print "";'; + $varprop.= sprintf("\t ".$baseString." \n", $prop['field'], $prop['field'], $prop['field']); } } $targetcontent=preg_replace('/LIST_OF_TD_LABEL_FIELDS_EDIT/', $varprop, $targetcontent); @@ -648,7 +649,7 @@ foreach ($skeletonfiles as $skeletonfile => $outfile) { if ($prop['field'] != 'rowid' && $prop['field'] != 'id' && ! $prop['istime']) { - $varprop.="print '';\n"; + $varprop.=sprintf("\t print '';\n",$prop['field'],$prop['field']); } } $targetcontent=preg_replace('/LIST_OF_TD_LABEL_FIELDS_VIEW/', $varprop, $targetcontent); From 7dddba3e39423636c0ed605e4c8caad1a9d33d99 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 9 Feb 2017 14:42:15 +0100 Subject: [PATCH 149/170] Add octicons --- htdocs/theme/common/octicons/.gitignore | 4 + htdocs/theme/common/octicons/.npmignore | 5 + htdocs/theme/common/octicons/.travis.yml | 3 + htdocs/theme/common/octicons/CHANGELOG.md | 363 +++++ htdocs/theme/common/octicons/Gruntfile.js | 137 ++ htdocs/theme/common/octicons/LICENSE | 21 + htdocs/theme/common/octicons/README.md | 194 +++ htdocs/theme/common/octicons/index.js | 67 + htdocs/theme/common/octicons/lib/data.json | 1174 +++++++++++++++++ .../octicons/lib/octicons-master.sketch | Bin 0 -> 1957888 bytes htdocs/theme/common/octicons/lib/octicons.css | 5 + .../theme/common/octicons/lib/svg/alert.svg | 12 + .../common/octicons/lib/svg/arrow-down.svg | 12 + .../common/octicons/lib/svg/arrow-left.svg | 12 + .../common/octicons/lib/svg/arrow-right.svg | 12 + .../octicons/lib/svg/arrow-small-down.svg | 12 + .../octicons/lib/svg/arrow-small-left.svg | 12 + .../octicons/lib/svg/arrow-small-right.svg | 12 + .../octicons/lib/svg/arrow-small-up.svg | 12 + .../common/octicons/lib/svg/arrow-up.svg | 12 + .../theme/common/octicons/lib/svg/beaker.svg | 12 + htdocs/theme/common/octicons/lib/svg/bell.svg | 12 + htdocs/theme/common/octicons/lib/svg/bold.svg | 12 + htdocs/theme/common/octicons/lib/svg/book.svg | 12 + .../common/octicons/lib/svg/bookmark.svg | 12 + .../common/octicons/lib/svg/briefcase.svg | 12 + .../common/octicons/lib/svg/broadcast.svg | 12 + .../theme/common/octicons/lib/svg/browser.svg | 12 + htdocs/theme/common/octicons/lib/svg/bug.svg | 12 + .../common/octicons/lib/svg/calendar.svg | 12 + .../theme/common/octicons/lib/svg/check.svg | 12 + .../common/octicons/lib/svg/checklist.svg | 12 + .../common/octicons/lib/svg/chevron-down.svg | 12 + .../common/octicons/lib/svg/chevron-left.svg | 12 + .../common/octicons/lib/svg/chevron-right.svg | 12 + .../common/octicons/lib/svg/chevron-up.svg | 12 + .../common/octicons/lib/svg/circle-slash.svg | 12 + .../common/octicons/lib/svg/circuit-board.svg | 12 + .../theme/common/octicons/lib/svg/clippy.svg | 12 + .../theme/common/octicons/lib/svg/clock.svg | 12 + .../octicons/lib/svg/cloud-download.svg | 12 + .../common/octicons/lib/svg/cloud-upload.svg | 12 + htdocs/theme/common/octicons/lib/svg/code.svg | 12 + .../octicons/lib/svg/comment-discussion.svg | 12 + .../theme/common/octicons/lib/svg/comment.svg | 12 + .../common/octicons/lib/svg/credit-card.svg | 12 + htdocs/theme/common/octicons/lib/svg/dash.svg | 12 + .../common/octicons/lib/svg/dashboard.svg | 12 + .../common/octicons/lib/svg/database.svg | 12 + .../octicons/lib/svg/desktop-download.svg | 12 + .../octicons/lib/svg/device-camera-video.svg | 12 + .../common/octicons/lib/svg/device-camera.svg | 12 + .../octicons/lib/svg/device-desktop.svg | 12 + .../common/octicons/lib/svg/device-mobile.svg | 12 + .../common/octicons/lib/svg/diff-added.svg | 12 + .../common/octicons/lib/svg/diff-ignored.svg | 12 + .../common/octicons/lib/svg/diff-modified.svg | 12 + .../common/octicons/lib/svg/diff-removed.svg | 12 + .../common/octicons/lib/svg/diff-renamed.svg | 12 + htdocs/theme/common/octicons/lib/svg/diff.svg | 12 + .../common/octicons/lib/svg/ellipses.svg | 12 + .../common/octicons/lib/svg/ellipsis.svg | 3 + htdocs/theme/common/octicons/lib/svg/eye.svg | 12 + .../common/octicons/lib/svg/file-binary.svg | 12 + .../common/octicons/lib/svg/file-code.svg | 12 + .../octicons/lib/svg/file-directory.svg | 12 + .../common/octicons/lib/svg/file-media.svg | 12 + .../common/octicons/lib/svg/file-pdf.svg | 12 + .../octicons/lib/svg/file-submodule.svg | 12 + .../lib/svg/file-symlink-directory.svg | 12 + .../octicons/lib/svg/file-symlink-file.svg | 12 + .../common/octicons/lib/svg/file-text.svg | 3 + .../common/octicons/lib/svg/file-zip.svg | 12 + htdocs/theme/common/octicons/lib/svg/file.svg | 12 + .../theme/common/octicons/lib/svg/flame.svg | 12 + htdocs/theme/common/octicons/lib/svg/fold.svg | 12 + htdocs/theme/common/octicons/lib/svg/gear.svg | 12 + htdocs/theme/common/octicons/lib/svg/gift.svg | 12 + .../common/octicons/lib/svg/gist-secret.svg | 12 + htdocs/theme/common/octicons/lib/svg/gist.svg | 12 + .../common/octicons/lib/svg/git-branch.svg | 12 + .../common/octicons/lib/svg/git-commit.svg | 12 + .../common/octicons/lib/svg/git-compare.svg | 12 + .../common/octicons/lib/svg/git-merge.svg | 12 + .../octicons/lib/svg/git-pull-request.svg | 12 + .../theme/common/octicons/lib/svg/globe.svg | 12 + .../theme/common/octicons/lib/svg/grabber.svg | 12 + .../theme/common/octicons/lib/svg/graph.svg | 12 + .../theme/common/octicons/lib/svg/heart.svg | 12 + .../theme/common/octicons/lib/svg/history.svg | 12 + htdocs/theme/common/octicons/lib/svg/home.svg | 12 + .../octicons/lib/svg/horizontal-rule.svg | 12 + .../theme/common/octicons/lib/svg/hubot.svg | 12 + .../theme/common/octicons/lib/svg/inbox.svg | 12 + htdocs/theme/common/octicons/lib/svg/info.svg | 12 + .../common/octicons/lib/svg/issue-closed.svg | 12 + .../common/octicons/lib/svg/issue-opened.svg | 12 + .../octicons/lib/svg/issue-reopened.svg | 12 + .../theme/common/octicons/lib/svg/italic.svg | 12 + .../theme/common/octicons/lib/svg/jersey.svg | 12 + htdocs/theme/common/octicons/lib/svg/key.svg | 12 + .../common/octicons/lib/svg/keyboard.svg | 12 + htdocs/theme/common/octicons/lib/svg/law.svg | 12 + .../common/octicons/lib/svg/light-bulb.svg | 12 + .../common/octicons/lib/svg/link-external.svg | 12 + htdocs/theme/common/octicons/lib/svg/link.svg | 12 + .../common/octicons/lib/svg/list-ordered.svg | 12 + .../octicons/lib/svg/list-unordered.svg | 12 + .../common/octicons/lib/svg/location.svg | 12 + htdocs/theme/common/octicons/lib/svg/lock.svg | 12 + .../common/octicons/lib/svg/logo-gist.svg | 12 + .../common/octicons/lib/svg/logo-github.svg | 12 + .../common/octicons/lib/svg/mail-read.svg | 12 + .../common/octicons/lib/svg/mail-reply.svg | 12 + htdocs/theme/common/octicons/lib/svg/mail.svg | 12 + .../common/octicons/lib/svg/mark-github.svg | 12 + .../common/octicons/lib/svg/markdown.svg | 12 + .../common/octicons/lib/svg/megaphone.svg | 12 + .../theme/common/octicons/lib/svg/mention.svg | 12 + .../common/octicons/lib/svg/milestone.svg | 12 + .../theme/common/octicons/lib/svg/mirror.svg | 12 + .../common/octicons/lib/svg/mortar-board.svg | 12 + htdocs/theme/common/octicons/lib/svg/mute.svg | 12 + .../common/octicons/lib/svg/no-newline.svg | 12 + htdocs/theme/common/octicons/lib/svg/note.svg | 12 + .../common/octicons/lib/svg/octoface.svg | 12 + .../common/octicons/lib/svg/organization.svg | 12 + .../theme/common/octicons/lib/svg/package.svg | 12 + .../common/octicons/lib/svg/paintcan.svg | 12 + .../theme/common/octicons/lib/svg/pencil.svg | 12 + .../theme/common/octicons/lib/svg/person.svg | 12 + htdocs/theme/common/octicons/lib/svg/pin.svg | 12 + htdocs/theme/common/octicons/lib/svg/plug.svg | 12 + .../common/octicons/lib/svg/plus-small.svg | 12 + htdocs/theme/common/octicons/lib/svg/plus.svg | 12 + .../common/octicons/lib/svg/primitive-dot.svg | 12 + .../octicons/lib/svg/primitive-square.svg | 12 + .../theme/common/octicons/lib/svg/project.svg | 12 + .../theme/common/octicons/lib/svg/pulse.svg | 12 + .../common/octicons/lib/svg/question.svg | 12 + .../theme/common/octicons/lib/svg/quote.svg | 12 + .../common/octicons/lib/svg/radio-tower.svg | 12 + .../theme/common/octicons/lib/svg/reply.svg | 12 + .../common/octicons/lib/svg/repo-clone.svg | 12 + .../octicons/lib/svg/repo-force-push.svg | 12 + .../common/octicons/lib/svg/repo-forked.svg | 12 + .../common/octicons/lib/svg/repo-pull.svg | 12 + .../common/octicons/lib/svg/repo-push.svg | 12 + htdocs/theme/common/octicons/lib/svg/repo.svg | 12 + .../theme/common/octicons/lib/svg/rocket.svg | 12 + htdocs/theme/common/octicons/lib/svg/rss.svg | 12 + htdocs/theme/common/octicons/lib/svg/ruby.svg | 12 + .../common/octicons/lib/svg/screen-full.svg | 12 + .../common/octicons/lib/svg/screen-normal.svg | 12 + .../theme/common/octicons/lib/svg/search.svg | 12 + .../theme/common/octicons/lib/svg/server.svg | 12 + .../common/octicons/lib/svg/settings.svg | 12 + .../theme/common/octicons/lib/svg/shield.svg | 12 + .../theme/common/octicons/lib/svg/sign-in.svg | 12 + .../common/octicons/lib/svg/sign-out.svg | 12 + .../theme/common/octicons/lib/svg/smiley.svg | 12 + .../common/octicons/lib/svg/squirrel.svg | 12 + htdocs/theme/common/octicons/lib/svg/star.svg | 12 + htdocs/theme/common/octicons/lib/svg/stop.svg | 12 + htdocs/theme/common/octicons/lib/svg/sync.svg | 12 + htdocs/theme/common/octicons/lib/svg/tag.svg | 12 + .../common/octicons/lib/svg/tasklist.svg | 12 + .../common/octicons/lib/svg/telescope.svg | 12 + .../common/octicons/lib/svg/terminal.svg | 12 + .../common/octicons/lib/svg/text-size.svg | 12 + .../common/octicons/lib/svg/three-bars.svg | 12 + .../common/octicons/lib/svg/thumbsdown.svg | 12 + .../common/octicons/lib/svg/thumbsup.svg | 12 + .../theme/common/octicons/lib/svg/tools.svg | 12 + .../common/octicons/lib/svg/trashcan.svg | 12 + .../common/octicons/lib/svg/triangle-down.svg | 12 + .../common/octicons/lib/svg/triangle-left.svg | 12 + .../octicons/lib/svg/triangle-right.svg | 12 + .../common/octicons/lib/svg/triangle-up.svg | 12 + .../theme/common/octicons/lib/svg/unfold.svg | 12 + .../theme/common/octicons/lib/svg/unmute.svg | 12 + .../common/octicons/lib/svg/unverified.svg | 12 + .../common/octicons/lib/svg/verified.svg | 12 + .../common/octicons/lib/svg/versions.svg | 12 + .../theme/common/octicons/lib/svg/watch.svg | 12 + htdocs/theme/common/octicons/lib/svg/x.svg | 12 + htdocs/theme/common/octicons/lib/svg/zap.svg | 12 + htdocs/theme/common/octicons/package.json | 39 + 188 files changed, 4106 insertions(+) create mode 100644 htdocs/theme/common/octicons/.gitignore create mode 100644 htdocs/theme/common/octicons/.npmignore create mode 100644 htdocs/theme/common/octicons/.travis.yml create mode 100644 htdocs/theme/common/octicons/CHANGELOG.md create mode 100644 htdocs/theme/common/octicons/Gruntfile.js create mode 100644 htdocs/theme/common/octicons/LICENSE create mode 100644 htdocs/theme/common/octicons/README.md create mode 100644 htdocs/theme/common/octicons/index.js create mode 100644 htdocs/theme/common/octicons/lib/data.json create mode 100644 htdocs/theme/common/octicons/lib/octicons-master.sketch create mode 100644 htdocs/theme/common/octicons/lib/octicons.css create mode 100644 htdocs/theme/common/octicons/lib/svg/alert.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/arrow-down.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/arrow-left.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/arrow-right.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/arrow-small-down.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/arrow-small-left.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/arrow-small-right.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/arrow-small-up.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/arrow-up.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/beaker.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/bell.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/bold.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/book.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/bookmark.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/briefcase.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/broadcast.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/browser.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/bug.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/calendar.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/check.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/checklist.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/chevron-down.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/chevron-left.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/chevron-right.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/chevron-up.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/circle-slash.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/circuit-board.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/clippy.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/clock.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/cloud-download.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/cloud-upload.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/code.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/comment-discussion.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/comment.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/credit-card.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/dash.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/dashboard.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/database.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/desktop-download.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/device-camera-video.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/device-camera.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/device-desktop.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/device-mobile.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/diff-added.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/diff-ignored.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/diff-modified.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/diff-removed.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/diff-renamed.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/diff.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/ellipses.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/ellipsis.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/eye.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file-binary.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file-code.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file-directory.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file-media.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file-pdf.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file-submodule.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file-symlink-directory.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file-symlink-file.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file-text.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file-zip.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/file.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/flame.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/fold.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/gear.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/gift.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/gist-secret.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/gist.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/git-branch.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/git-commit.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/git-compare.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/git-merge.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/git-pull-request.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/globe.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/grabber.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/graph.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/heart.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/history.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/home.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/horizontal-rule.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/hubot.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/inbox.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/info.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/issue-closed.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/issue-opened.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/issue-reopened.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/italic.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/jersey.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/key.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/keyboard.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/law.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/light-bulb.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/link-external.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/link.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/list-ordered.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/list-unordered.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/location.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/lock.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/logo-gist.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/logo-github.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/mail-read.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/mail-reply.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/mail.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/mark-github.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/markdown.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/megaphone.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/mention.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/milestone.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/mirror.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/mortar-board.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/mute.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/no-newline.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/note.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/octoface.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/organization.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/package.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/paintcan.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/pencil.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/person.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/pin.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/plug.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/plus-small.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/plus.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/primitive-dot.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/primitive-square.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/project.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/pulse.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/question.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/quote.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/radio-tower.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/reply.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/repo-clone.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/repo-force-push.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/repo-forked.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/repo-pull.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/repo-push.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/repo.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/rocket.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/rss.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/ruby.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/screen-full.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/screen-normal.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/search.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/server.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/settings.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/shield.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/sign-in.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/sign-out.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/smiley.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/squirrel.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/star.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/stop.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/sync.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/tag.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/tasklist.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/telescope.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/terminal.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/text-size.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/three-bars.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/thumbsdown.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/thumbsup.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/tools.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/trashcan.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/triangle-down.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/triangle-left.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/triangle-right.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/triangle-up.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/unfold.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/unmute.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/unverified.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/verified.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/versions.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/watch.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/x.svg create mode 100644 htdocs/theme/common/octicons/lib/svg/zap.svg create mode 100644 htdocs/theme/common/octicons/package.json diff --git a/htdocs/theme/common/octicons/.gitignore b/htdocs/theme/common/octicons/.gitignore new file mode 100644 index 00000000000..b0e34801fac --- /dev/null +++ b/htdocs/theme/common/octicons/.gitignore @@ -0,0 +1,4 @@ +node_modules +.DS_Store +build +*.log diff --git a/htdocs/theme/common/octicons/.npmignore b/htdocs/theme/common/octicons/.npmignore new file mode 100644 index 00000000000..f50769fa751 --- /dev/null +++ b/htdocs/theme/common/octicons/.npmignore @@ -0,0 +1,5 @@ +.github +.travis.yml +Gruntfile.js +lib +test diff --git a/htdocs/theme/common/octicons/.travis.yml b/htdocs/theme/common/octicons/.travis.yml new file mode 100644 index 00000000000..435bc15d7af --- /dev/null +++ b/htdocs/theme/common/octicons/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - '5' diff --git a/htdocs/theme/common/octicons/CHANGELOG.md b/htdocs/theme/common/octicons/CHANGELOG.md new file mode 100644 index 00000000000..2085dcb0169 --- /dev/null +++ b/htdocs/theme/common/octicons/CHANGELOG.md @@ -0,0 +1,363 @@ +### HEAD + +### 5.0.1 + +Fixes: + +- projects icon renders as a block, using `fill-rule` fixes it + +### 5.0.0 + +Adds: + +- `project` +- `note` +- `screen-full` +- `screen-normal` +- More node.js api endpoints for accessing icons https://github.com/primer/octicons/pull/120 +- Creating a spritesheet demo https://github.com/primer/octicons/pull/121 + +Removes: + +- Deprecating support for the webfont https://github.com/primer/octicons/pull/117 +- Stop checking `/build/` directory into repository https://github.com/primer/octicons/pull/118 +- Removing sass as a dependency https://github.com/primer/octicons/pull/119 + +### 4.4.0 + +Adds: + +- svg.json file that is accessible from node require + +### 4.3.0 + +Fixes: + +- Vertical alignment on `italic` + +Modifies: + +- `person` +- `organization` + +### 4.2.1 + +Fixes: + +- Removing inline sourcemap from `min` versions of css. + +### 4.2.0 + +Adds: + +- Keywords.json file that has an index of all octicons with alias names + +### 4.1.1 (June 16, 2016) + +Fixes: + +- Putting the `$octicons-font-path` back in the scss file. + +### 4.1.0 (June 6, 2016) + +Adds: + +- Installation docs https://github.com/primer/octicons/pull/94 +- `grabber` +- `plus-small` + +Modifies: + +- `smiley` + +Refines: + +- Renames `mail-reply` to `reply` and refines its shape. + +Fixes: + +- Revert license back to SPDX standard + +### 4.0.0 (June 6, 2016) + +Adds: + +- Whole new grunt build system including svg sprite sheet. +- adding css min https://github.com/primer/octicons/pull/60 +- adding woff2 format https://github.com/primer/octicons/issues/3 +- creates spritesheet of svg files https://github.com/primer/octicons/issues/88 + +Removes: + +- Bower support + +Fixes: + +- all svg icons include viewBox https://github.com/primer/octicons/issues/87 +- license in package.json https://github.com/primer/octicons/issues/85 + +### 3.5.0 (February 12, 2016) + +Adds: + +- `unverified` + +Refines: + +- `verified` + +### 3.4.1 (January 24, 2016) + +This includes various SVG viewport refinements. + +Refines: + +- `thumbs-down` +- `logo-github` + +### 3.4.0 (January 22, 2016) + +Adds: + +- `verified` +- `smiley` + +Removes: + +- `color-mode` + +Refines: + +- `primitive-dot` +- `horizontal-rule` +- `triangle-down` +- `triangle-up` +- `triangle-left` +- `triangle-right` +- `globe` +- `flame` +- `comment-discussion` + +### 3.3.0 (November 12, 2015) + +Adds: + +- `logo-gist` + +Resizes all our SVG to be 16x16 instead of 1024x1024 + +### 3.2.0 (November 6, 2015) + +Adds: + +- `bold` +- `text-size` +- `italic` +- `tasklist` + +It also normalizes some styling in: + +- `list-unordered` +- `list-ordered` +- `quote` +- `mention` +- `bookmark` +- `threebars` + +Removes + +- `screen-normal` +- `screen-full` + + +### 3.1.0 (August 13, 2015) + +Adds + +- `shield` + +This thickens stroke widths slightly on the following icons: + +- `circle-slash` +- `clock` +- `cloud-upload` +- `cloud-download` +- `dashboard` +- `info` +- `issue-closed` +- `issue` +- `issue-reopened` +- `history` +- `question` +- `search` + +Fills `comment-discussion` + +Thickens `x` to match `checkmark` + +### 3.0.1 (August 10, 2015) + +Some files were missing in `3.0.0` + +### 3.0.0 (August 10, 2015) + +Removes + +- `microscope` +- `beer` +- `split` +- `puzzle` +- `steps` +- `podium` +- `timer` +- all `alignment` icons +- all `move` icons +- all `playback` icons +- all `jump` icons + +Adds + +- `beaker` +- `bell` +- `desktop-download` +- `watch` + +Line-weight changes, sizing normalization, and new drawings + +- `circle-slash` +- `lock` +- `cloud-upload` +- `cloud-download` +- `plus` +- `✕` +- `broadcast` +- `lock` +- all `repo` icons +- organization +- person +- all `chevrons` & `triangles` +- all `diff` icons +- `clippy` +- all `issue` and circular icons +- `rss` +- `ruby` +- `cancel` +- `settings` +- `mirror` +- `external-link` +- `history` +- `gear` +- `settings` +- `info` +- `history` +- `package` +- `gist-secret` +- `rocket` +- `law` +- `telescope` +- `search` +- `tag` +- `normal-screen` +- `iphone` +- `no-new-line` +- `desktop` +- all `git` icons +- `circuit-board` +- `heart` +- `home` +- `briefcase` +- `wiki` +- `bookmark` +- `briefcase` +- `calendar` +- `color-mode` +- `comment` +- `discussions` +- `credit-card` +- `dashboard` +- `camera` +- `video` +- `bug` +- `desktop` +- `ellipses` +- `eye` +- all `files` & `folders` +- `fold` +- `unfold` +- `gift` +- `graph` +- `hubot` +- `inbox` +- `jersey` +- `keyboard` +- `light-bulb` +- `link` +- `location` +- `mail` +- `mail-read` +- `marker` +- `plug` +- `mute` +- `pencil` +- `push-pin` +- `fullscreen` +- `unfullscreen` +- `server` +- `sign-in` +- `sign-out` +- `tag` +- `terminal` +- `thumbs-up` +- `thumbs-down` +- `trash` +- `unmute` +- `versions` +- `gist` +- `key` +- `megaphone` +- `checklist` + +## 2.4.1 (June 2, 2015) + +- Add the scss file I forgot to include + +## 2.4.0 (June 2, 2015) + +- Add `octicons.scss` +- Revert path changes to `sprockets-octicons.scss`, as they broke octicons in sprockets. + +## 2.3.0 (May 28, 2015) + +- Add a path variable to `sprockets-octicons.scss` to be consistent with octicons.less` + +## 2.2.3 (May 21, 2015) + +- Use SPDX license identifiers in package.json + +## 2.2.2 (April 1, 2015) + +Fixes file icons for + +- `file-binary` +- `file-code` +- `file-media` +- `file-pdf` +- `file-symlink-file` +- `file-text` +- `file-zip` + +## 2.2.1 (March 30, 2015) + +- Fix vector artifact and smooth curves in `mark-github` + +## 2.2.0 (Feb 18, 2015) + +- Add two new icons: `thumbsup` and `thumbsdown` + +## 2.0.1 (June 16, 2014) + +- Add mention of github.com/logos to the license + +## 2.0.0 (June 16, 2014) + +- Hello world diff --git a/htdocs/theme/common/octicons/Gruntfile.js b/htdocs/theme/common/octicons/Gruntfile.js new file mode 100644 index 00000000000..48e0c300427 --- /dev/null +++ b/htdocs/theme/common/octicons/Gruntfile.js @@ -0,0 +1,137 @@ +var fs = require("fs") +var path = require("path") + +module.exports = function(grunt) { + + grunt.initConfig({ + + pkg: grunt.file.readJSON('package.json'), + + cssnano: { + options: {}, + dist: { + files: { + 'build/octicons.min.css': 'build/octicons.css' + } + } + }, + + svgmin: { + dist: { + options: { + plugins: [ + {removeTitle: true}, + {removeStyleElement: true}, + {removeAttrs: { attrs: ['id', 'class', 'data-name', 'fill'] }}, + {removeEmptyContainers: true}, + {sortAttrs: true}, + {removeUselessDefs: true}, + {removeEmptyText: true}, + {removeEditorsNSData: true}, + {removeEmptyAttrs: true}, + {removeHiddenElems: true} + ] + }, + files: [{ + expand: true, + cwd: 'lib/svg', + src: ['*.svg'], + dest: 'build/svg' + }] + } + }, + + svgstore: { + options: { + includeTitleElement: false, + inheritviewbox: true, + includedemo: function(arg) { + var octicons = require("./index.js") + + var icons = function() { + var result = [] + Object.keys(octicons).forEach(function(key){ + result.push("
" + octicons[key].toSVGUse({ height: 32 }) + "
" + key + "
") + }) + return result.join("\n") + } + + return ` + + + + + Octicons Spritesheet test + + + + + ${arg.svg} +
Octicons SVG Spritesheet demo
+
All the icons rendered below use the svg spriteheet located in the /build/ directory.
+
+ ${icons()} +
+ + +` + } + }, + default: { + files: { + "build/sprite.octicons.svg": ['build/svg/*.svg'] + } + }, + }, + + clean: { + build: [ + 'build/*' + ] + }, + + copy: { + css: { + src: "lib/octicons.css", + dest: "build/octicons.css" + } + } + }); + + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-svgstore'); + grunt.loadNpmTasks('grunt-svgmin'); + grunt.loadNpmTasks('grunt-cssnano'); + + // build tasks + grunt.registerTask('css', ['copy', 'cssnano']); + grunt.registerTask('svg', ['clean', 'svgmin']); + + // default task, build /dist/ + grunt.registerTask('default', [ 'svg', 'css', 'json:svg', 'svgstore']); + + grunt.registerTask('json:svg', 'add svg string to data.json build', function() { + var files = fs.readdirSync("./build/svg/") + var data = JSON.parse(fs.readFileSync("./lib/data.json")) + + files.forEach(function(file) { + var svg = fs.readFileSync(path.resolve("./build/svg", file)) + var key = path.basename(file, ".svg") + if (data[key]) { + var raw = svg.toString() + data[key].path = //g.exec(raw)[0] + data[key].height = /height="(\d+)"/g.exec(raw)[1] + data[key].width = /width="(\d+)"/g.exec(raw)[1] + } + }) + + fs.writeFileSync("build/data.json", JSON.stringify(data)); + }) +}; diff --git a/htdocs/theme/common/octicons/LICENSE b/htdocs/theme/common/octicons/LICENSE new file mode 100644 index 00000000000..4cf2020ce77 --- /dev/null +++ b/htdocs/theme/common/octicons/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012-2016 GitHub, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/htdocs/theme/common/octicons/README.md b/htdocs/theme/common/octicons/README.md new file mode 100644 index 00000000000..84edd84dce5 --- /dev/null +++ b/htdocs/theme/common/octicons/README.md @@ -0,0 +1,194 @@ +# GitHub Octicons + +[![NPM version](https://img.shields.io/npm/v/octicons.svg)](https://www.npmjs.org/package/octicons) +[![Build Status](https://travis-ci.org/primer/octicons.svg?branch=master)](https://travis-ci.org/primer/octicons) + +> Octicons are a scalable set of icons handcrafted with <3 by GitHub. + +## Install + +**NOTE:** The compiled files are located in `/build/`. This directory is located in the published npm package. Which means you can access it when you `npm install octicons`. You can also build this directory by following the [building octicons directions](#building-octicons). The files in the `/lib/` directory are the raw source files and are not compiled or optimized. + +#### NPM + +This repository is distributed with [npm][npm]. After [installing npm][install-npm], you can install `octicons` with this command. + +``` +$ npm install --save octicons +``` + +## Usage + +For all the usages, we recommend using the CSS located in `./build/octicons.css`. This is some simple CSS to normalize the icons and inherit colors. + +### Spritesheet + +With a [SVG sprite icon system](https://css-tricks.com/svg-sprites-use-better-icon-fonts/) you can include the sprite sheet located `./build/sprite.octicons.svg` after you [build the icons](#building-octicons) or from the npm package. There is a demo of how to use the spritesheet in the build directory also. + +### Node + +After installing `npm install octicons` you can access the icons like this. + +```js +var octicons = require("octicons") +octicons.alert +// { keywords: [ 'warning', 'triangle', 'exclamation', 'point' ], +// path: '', +// height: '16', +// width: '16', +// symbol: 'alert', +// options: +// { version: '1.1', +// width: '16', +// height: '16', +// viewBox: '0 0 16 16', +// class: 'octicon octicon-alert', +// 'aria-hidden': 'true' }, +// toSVG: [Function] } +``` + +There will be a key for every icon, with `keywords` and `svg`. + +#### `octicons.alert.symbol` + +Returns the string of the symbol name + +```js +octicons.x.symbol +// "x" +``` + +#### `octicons.person.path` + +Path returns the string representation of the path of the icon. + +```js +octicons.x.path +// +``` + +#### `octicons.issue.options` + +This is a json object of all the `options` that will be added to the output tag. + +```js +octicons.x.options +// { version: '1.1', width: '12', height: '16', viewBox: '0 0 12 16', class: 'octicon octicon-x', 'aria-hidden': 'true' } +``` + +#### `octicons.alert.width` + +Width is the icon's true width. Based on the svg view box width. _Note, this doesn't change if you scale it up with size options, it only is the natural width of the icon_ + +#### `octicons.alert.height` + +Height is the icon's true height. Based on the svg view box height. _Note, this doesn't change if you scale it up with size options, it only is the natural height of the icon_ + +#### `keywords` + +Returns an array of keywords for the icon. The data [comes from the octicons repository](https://github.com/primer/octicons/blob/master/lib/data.json). Consider contributing more aliases for the icons. + +```js +octicons.x.keywords +// ["remove", "close", "delete"] +``` + +#### `octicons.alert.toSVG()` + +Returns a string of the svg tag + +```js +octicons.x.toSVG() +// +``` + +The `.toSVG()` method accepts an optional `options` object. This is used to add CSS classnames, a11y options, and sizing. + +##### class + +Add more CSS classes to the `` tag. + +```js +octicons.x.toSVG({ "class": "close" }) +// +``` + +##### aria-label + +Add accessibility `aria-label` to the icon. + +```js +octicons.x.toSVG({ "aria-label": "Close the window" }) +// +``` + +##### width & height + +Size the SVG icon larger using `width` & `height` independently or together. + +```js +octicons.x.toSVG({ "width": 45 }) +// +``` + +#### `octicons.alert.toSVGUse()` + +Returns a string of the svg tag with the `` tag, for use with the spritesheet located in the /build/ directory. + +```js +octicons.x.toSVGUse() +// +``` + +### Ruby + +If your environment is Ruby on Rails, we have a [octicons_helper](https://github.com/primer/octicons_helper) gem available that renders SVG in your page. The octicons_helper uses the [octicons_gem](https://github.com/primer/octicons_gem) to do the computing and reading of the SVG files. + +### Jekyll + +For jekyll, there's a [jekyll-octicons](https://github.com/primer/jekyll-octicons) plugin available. This works exactly like the octicons_helper. + +## Changing, adding, or deleting icons + +1. Open the [Sketch document][sketch-document] in `/lib/`. Each icon exists as an artboard within our master Sketch document. If you’re adding an icon, duplicate one of the artboards and add your shapes to it. Be sure to give your artboard a name that makes sense. +2. Once you’re happy with your icon set, choose File > Export… +3. Choose all the artboards you’d like to export and then press “Export” +4. Export to `/lib/svg/` + +You’ll next need to build your Octicons. + +## Building Octicons + +All the files you need will be in the `/build/` directory already, but if you’ve made changes to the `/lib/` directory and need to regenerate, follow these steps: + +1. Open the Octicons directory in Terminal +2. `npm install` to install all dependencies for the project. +3. Run the command `npm run build`. This will run the grunt task to build the SVGs, placing them in the `/build/` directory. + +## Publishing + +If you have access to publish this repository, these are the steps to publishing. If you need access, contact [#design-systems](https://github.slack.com/archives/design-systems). + +1. Update the [CHANGELOG.md](./CHANGELOG.md) with relevant version number and any updates made to the repository. +2. `npm version ` Run [npm version](https://docs.npmjs.com/cli/version) inputing the relevant version type. The versioning is [semver](http://semver.org/), so version appropriately based on what has changed. +3. `npm publish` This will publish the new version to npmjs.org +4. `git push && git push --tags` Push all these changes to origin. + +## License + +(c) 2012-2016 GitHub, Inc. + +When using the GitHub logos, be sure to follow the [GitHub logo guidelines](https://github.com/logos). + +_SVG License:_ [SIL OFL 1.1](http://scripts.sil.org/OFL) +Applies to all SVG files + +_Code License:_ [MIT](./LICENSE) +Applies to all other files + +[primer]: https://github.com/primer/primer +[docs]: http://primercss.io/ +[npm]: https://www.npmjs.com/ +[install-npm]: https://docs.npmjs.com/getting-started/installing-node +[sass]: http://sass-lang.com/ +[sketch-document]: https://github.com/primer/octicons/blob/master/lib/octicons-master.sketch diff --git a/htdocs/theme/common/octicons/index.js b/htdocs/theme/common/octicons/index.js new file mode 100644 index 00000000000..186af5cb46c --- /dev/null +++ b/htdocs/theme/common/octicons/index.js @@ -0,0 +1,67 @@ +var data = require('./build/data.json') + +Object.keys(data).forEach(function(key) { + + // Returns a string representation of html attributes + var htmlAttributes = function(icon, options) { + var attributes = [] + var attrObj = Object.assign({}, data[key].options, options) + + // If the user passed in options + if (options) { + + // If any of the width or height is passed in + if(options["width"] || options["height"]) { + attrObj["width"] = options["width"] ? options["width"] : (parseInt(options["height"]) * data[key].options["width"] / data[key].options["height"]) + attrObj["height"] = options["height"] ? options["height"] : (parseInt(options["width"]) * data[key].options["height"] / data[key].options["width"]) + } + + // If the user passed in class + if (options["class"]) { + attrObj["class"] = "octicon octicon-" + key + " " + options["class"] + attrObj["class"].trim() + } + + // If the user passed in aria-label + if (options["aria-label"]) { + attrObj["aria-label"] = options["aria-label"] + attrObj["role"] = "img" + + // Un-hide the icon + delete attrObj["aria-hidden"] + } + } + + Object.keys(attrObj).forEach(function(option) { + attributes.push(option + "=\"" + attrObj[option] + "\"") + }) + + return attributes.join(" ").trim() + } + + // Set the symbol for easy access + data[key].symbol = key + + // Set all the default options + data[key].options = { + "version": "1.1", + "width": data[key].width, + "height": data[key].height, + "viewBox": "0 0 " + data[key].width + " " + data[key].height, + "class": "octicon octicon-" + key, + "aria-hidden": "true" + } + + // Function to return an SVG object + data[key].toSVG = function(options) { + return "" + data[key].path + "" + } + + // Function to return an SVG object with a use, assuming you use the svg sprite + data[key].toSVGUse = function(options) { + return "" + } +}) + +// Import data into exports +module.exports = data diff --git a/htdocs/theme/common/octicons/lib/data.json b/htdocs/theme/common/octicons/lib/data.json new file mode 100644 index 00000000000..af6b94aa6b1 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/data.json @@ -0,0 +1,1174 @@ +{ + "alert": { + "keywords": [ + "warning", + "triangle", + "exclamation", + "point" + ] + }, + "arrow-down": { + "keywords": [ + "point", + "direction" + ] + }, + "arrow-left": { + "keywords": [ + "point", + "direction" + ] + }, + "arrow-right": { + "keywords": [ + "point", + "direction" + ] + }, + "arrow-small-down": { + "keywords": [ + "point", + "direction" + ] + }, + "arrow-small-left": { + "keywords": [ + "point", + "direction", + "little", + "tiny" + ] + }, + "arrow-small-right": { + "keywords": [ + "point", + "direction", + "little", + "tiny" + ] + }, + "arrow-small-up": { + "keywords": [ + "point", + "direction", + "little", + "tiny" + ] + }, + "arrow-up": { + "keywords": [ + "point", + "direction" + ] + }, + "beaker": { + "keywords": [ + "experiment", + "labs", + "experimental", + "feature", + "test", + "science", + "education", + "study", + "development", + "testing" + ] + }, + "bell": { + "keywords": [ + "notification" + ] + }, + "bold": { + "keywords": [ + "bold" + ] + }, + "book": { + "keywords": [ + "book", + "journal", + "wiki", + "readme" + ] + }, + "bookmark": { + "keywords": [ + "tabbard" + ] + }, + "briefcase": { + "keywords": [ + "suitcase", + "business" + ] + }, + "broadcast": { + "keywords": [ + "rss", + "radio", + "signal" + ] + }, + "browser": { + "keywords": [ + "window", + "web" + ] + }, + "bug": { + "keywords": [ + "insect" + ] + }, + "calendar": { + "keywords": [ + "time", + "day", + "month", + "year" + ] + }, + "check": { + "keywords": [ + "mark", + "yes", + "confirm", + "accept", + "ok", + "success" + ] + }, + "checklist": { + "keywords": [ + "todo" + ] + }, + "chevron-down": { + "keywords": [ + "triangle", + "arrow" + ] + }, + "chevron-left": { + "keywords": [ + "triangle", + "arrow" + ] + }, + "chevron-right": { + "keywords": [ + "triangle", + "arrow" + ] + }, + "chevron-up": { + "keywords": [ + "triangle", + "arrow" + ] + }, + "circle-slash": { + "keywords": [ + "no", + "deny", + "fail", + "failure", + "error", + "bad" + ] + }, + "circuit-board": { + "keywords": [ + "developer", + "hardware", + "electricity" + ] + }, + "clippy": { + "keywords": [ + "copy", + "paste", + "save", + "capture" + ] + }, + "clock": { + "keywords": [ + "time", + "hour", + "minute", + "second" + ] + }, + "cloud-download": { + "keywords": [ + "save", + "install", + "get" + ] + }, + "cloud-upload": { + "keywords": [ + "put", + "export" + ] + }, + "code": { + "keywords": [ + "brackets" + ] + }, + "comment": { + "keywords": [ + "speak", + "bubble" + ] + }, + "comment-discussion": { + "keywords": [ + "converse", + "talk" + ] + }, + "credit-card": { + "keywords": [ + "money", + "billing", + "payments", + "transactions" + ] + }, + "dash": { + "keywords": [ + "hyphen", + "range" + ] + }, + "dashboard": { + "keywords": [ + "speed", + "dial" + ] + }, + "database": { + "keywords": [ + "disks", + "data" + ] + }, + "desktop-download": { + "keywords": [ + "clone", + "download" + ] + }, + "device-camera": { + "keywords": [ + "photo", + "picture", + "image", + "snapshot" + ] + }, + "device-camera-video": { + "keywords": [ + "watch", + "view", + "media", + "stream" + ] + }, + "device-desktop": { + "keywords": [ + "computer", + "monitor" + ] + }, + "device-mobile": { + "keywords": [ + "phone", + "iphone", + "cellphone" + ] + }, + "diff": { + "keywords": [ + "difference", + "changes", + "compare" + ] + }, + "diff-added": { + "keywords": [ + "new", + "addition" + ] + }, + "diff-ignored": { + "keywords": [ + "slash" + ] + }, + "diff-modified": { + "keywords": [ + "dot", + "changed", + "updated" + ] + }, + "diff-removed": { + "keywords": [ + "deleted", + "subtracted", + "dash" + ] + }, + "diff-renamed": { + "keywords": [ + "moved", + "arrow" + ] + }, + "ellipses": { + "keywords": [ + "dot", + "more" + ] + }, + "ellipsis": { + "keywords": [ + "read", + "more", + "hidden", + "expand" + ] + }, + "eye": { + "keywords": [ + "look", + "watch", + "see" + ] + }, + "file": { + "keywords": [ + "file" + ] + }, + "file-binary": { + "keywords": [ + "image", + "video", + "word", + "powerpoint", + "excel" + ] + }, + "file-code": { + "keywords": [ + "text", + "javascript", + "html", + "css", + "php", + "ruby", + "coffeescript", + "sass", + "scss" + ] + }, + "file-directory": { + "keywords": [ + "folder" + ] + }, + "file-media": { + "keywords": [ + "image", + "video", + "audio" + ] + }, + "file-pdf": { + "keywords": [ + "adobe" + ] + }, + "file-submodule": { + "keywords": [ + "folder" + ] + }, + "file-symlink-directory": { + "keywords": [ + "folder", + "subfolder", + "link", + "alias" + ] + }, + "file-symlink-file": { + "keywords": [ + "link", + "alias" + ] + }, + "file-text": { + "keywords": [ + "document" + ] + }, + "file-zip": { + "keywords": [ + "compress", + "archive" + ] + }, + "flame": { + "keywords": [ + "fire", + "hot", + "burn", + "trending" + ] + }, + "fold": { + "keywords": [ + "unfold", + "hide", + "collapse" + ] + }, + "gear": { + "keywords": [ + "settings" + ] + }, + "gift": { + "keywords": [ + "package", + "present", + "skill", + "craft", + "freebie" + ] + }, + "gist": { + "keywords": [ + "gist", + "github" + ] + }, + "gist-secret": { + "keywords": [ + "gist", + "secret", + "private" + ] + }, + "git-branch": { + "keywords": [ + "branch", + "git" + ] + }, + "git-commit": { + "keywords": [ + "save" + ] + }, + "git-compare": { + "keywords": [ + "difference", + "changes" + ] + }, + "git-merge": { + "keywords": [ + "join" + ] + }, + "git-pull-request": { + "keywords": [ + "review" + ] + }, + "globe": { + "keywords": [ + "world" + ] + }, + "grabber": { + "keywords": [ + "mover", + "drap", + "drop" + ] + }, + "graph": { + "keywords": [ + "trend", + "stats", + "statistics" + ] + }, + "heart": { + "keywords": [ + "love" + ] + }, + "history": { + "keywords": [ + "time", + "past", + "revert", + "back" + ] + }, + "home": { + "keywords": [ + "welcome", + "index", + "house", + "building" + ] + }, + "horizontal-rule": { + "keywords": [ + "hr" + ] + }, + "hubot": { + "keywords": [ + "robot" + ] + }, + "inbox": { + "keywords": [ + "mail", + "todo", + "new", + "messages" + ] + }, + "info": { + "keywords": [ + "help" + ] + }, + "issue-closed": { + "keywords": [ + "done", + "complete" + ] + }, + "issue-opened": { + "keywords": [ + "new" + ] + }, + "issue-reopened": { + "keywords": [ + "regression" + ] + }, + "italic": { + "keywords": [ + "font", + "italic", + "style" + ] + }, + "jersey": { + "keywords": [ + "team", + "game", + "basketball" + ] + }, + "key": { + "keywords": [ + "key", + "lock", + "secure", + "safe" + ] + }, + "keyboard": { + "keywords": [ + "type", + "keys", + "write", + "shortcuts" + ] + }, + "law": { + "keywords": [ + "legal", + "bill" + ] + }, + "light-bulb": { + "keywords": [ + "idea" + ] + }, + "link": { + "keywords": [ + "connect", + "hyperlink" + ] + }, + "link-external": { + "keywords": [ + "out", + "see", + "more", + "go", + "to" + ] + }, + "list-ordered": { + "keywords": [ + "numbers", + "tasks", + "todo", + "items" + ] + }, + "list-unordered": { + "keywords": [ + "bullet", + "point", + "tasks", + "todo", + "items" + ] + }, + "location": { + "keywords": [ + "here", + "marker" + ] + }, + "lock": { + "keywords": [ + "secure", + "safe", + "protected" + ] + }, + "logo-gist": { + "keywords": [ + "logo", + "gist" + ] + }, + "logo-github": { + "keywords": [ + "brand" + ] + }, + "mail": { + "keywords": [ + "email", + "unread" + ] + }, + "mail-read": { + "keywords": [ + "email", + "open" + ] + }, + "mail-reply": { + "keywords": [ + "email" + ] + }, + "mark-github": { + "keywords": [ + "octocat" + ] + }, + "markdown": { + "keywords": [ + "markup", + "style" + ] + }, + "megaphone": { + "keywords": [ + "bullhorn", + "loud", + "shout", + "broadcast" + ] + }, + "mention": { + "keywords": [ + "at", + "ping" + ] + }, + "milestone": { + "keywords": [ + "marker" + ] + }, + "mirror": { + "keywords": [ + "reflect" + ] + }, + "mortar-board": { + "keywords": [ + "education", + "learn", + "teach" + ] + }, + "mute": { + "keywords": [ + "quiet", + "sound", + "audio", + "turn", + "off" + ] + }, + "no-newline": { + "keywords": [ + "return" + ] + }, + "note": { + "keywords": [ + "card", + "paper", + "ticket" + ] + }, + "octoface": { + "keywords": [ + "octocat" + ] + }, + "organization": { + "keywords": [ + "people", + "group", + "team" + ] + }, + "package": { + "keywords": [ + "box", + "ship" + ] + }, + "paintcan": { + "keywords": [ + "style", + "theme", + "art", + "color" + ] + }, + "pencil": { + "keywords": [ + "edit", + "change", + "update", + "write" + ] + }, + "person": { + "keywords": [ + "people", + "man", + "woman", + "human" + ] + }, + "pin": { + "keywords": [ + "people", + "save", + "star", + "bookmark" + ] + }, + "plug": { + "keywords": [ + "hook", + "webhook" + ] + }, + "plus": { + "keywords": [ + "add", + "new", + "more" + ] + }, + "plus-small": { + "keywords": [ + "add", + "new", + "more", + "small" + ] + }, + "primitive-dot": { + "keywords": [ + "circle" + ] + }, + "primitive-square": { + "keywords": [ + "box" + ] + }, + "project": { + "keywords": [ + "board", + "kanban", + "columns", + "scrum" + ] + }, + "pulse": { + "keywords": [ + "graph", + "trend", + "line" + ] + }, + "question": { + "keywords": [ + "help", + "explain" + ] + }, + "quote": { + "keywords": [ + "quotation" + ] + }, + "radio-tower": { + "keywords": [ + "broadcast" + ] + }, + "reply": { + "keywords": [ + "reply all", + "back" + ] + }, + "repo": { + "keywords": [ + "book", + "journal" + ] + }, + "repo-clone": { + "keywords": [ + "book", + "journal" + ] + }, + "repo-force-push": { + "keywords": [ + "book", + "journal", + "put" + ] + }, + "repo-forked": { + "keywords": [ + "book", + "journal", + "copy" + ] + }, + "repo-pull": { + "keywords": [ + "book", + "journal", + "get" + ] + }, + "repo-push": { + "keywords": [ + "book", + "journal", + "put" + ] + }, + "rocket": { + "keywords": [ + "staff", + "stafftools", + "blast", + "off", + "space" + ] + }, + "rss": { + "keywords": [ + "broadcast", + "feed" + ] + }, + "ruby": { + "keywords": [ + "code" + ] + }, + "screen-full": { + "keywords": [ + "fullscreen", + "expand" + ] + }, + "screen-normal": { + "keywords": [ + "fullscreen", + "expand", + "exit" + ] + }, + "search": { + "keywords": [ + "magnifying", + "glass" + ] + }, + "server": { + "keywords": [ + "computers", + "racks", + "ops" + ] + }, + "settings": { + "keywords": [ + "sliders", + "filters" + ] + }, + "shield": { + "keywords": [ + "protect", + "shield", + "lock" + ] + }, + "sign-in": { + "keywords": [ + "door", + "arrow", + "direction", + "enter" + ] + }, + "sign-out": { + "keywords": [ + "door", + "arrow", + "direction", + "leave" + ] + }, + "smiley": { + "keywords": [ + "emoji", + "smile", + "mood", + "emotion" + ] + }, + "squirrel": { + "keywords": [ + "ship", + "shipit" + ] + }, + "star": { + "keywords": [ + "save", + "remember", + "like" + ] + }, + "stop": { + "keywords": [ + "block", + "spam" + ] + }, + "sync": { + "keywords": [ + "cycle", + "refresh", + "loop" + ] + }, + "tag": { + "keywords": [ + "release" + ] + }, + "tasklist": { + "keywords": [ + "todo" + ] + }, + "telescope": { + "keywords": [ + "science", + "space", + "look", + "view", + "explore" + ] + }, + "terminal": { + "keywords": [ + "code", + "ops", + "shell" + ] + }, + "text-size": { + "keywords": [ + "font", + "size", + "text" + ] + }, + "three-bars": { + "keywords": [ + "hamburger" + ] + }, + "thumbsdown": { + "keywords": [ + "thumb", + "thumbsdown", + "rejected" + ] + }, + "thumbsup": { + "keywords": [ + "thumb", + "thumbsup", + "prop", + "ship" + ] + }, + "tools": { + "keywords": [ + "screwdriver", + "wrench", + "settings" + ] + }, + "trashcan": { + "keywords": [ + "garbage", + "rubbish", + "recycle", + "delete" + ] + }, + "triangle-down": { + "keywords": [ + "arrow", + "point", + "direction" + ] + }, + "triangle-left": { + "keywords": [ + "arrow", + "point", + "direction" + ] + }, + "triangle-right": { + "keywords": [ + "arrow", + "point", + "direction" + ] + }, + "triangle-up": { + "keywords": [ + "arrow", + "point", + "direction" + ] + }, + "unfold": { + "keywords": [ + "expand", + "open", + "reveal" + ] + }, + "unmute": { + "keywords": [ + "loud", + "volume", + "audio", + "sound", + "play" + ] + }, + "unverified": { + "keywords": [ + "insecure", + "untrusted" + ] + }, + "verified": { + "keywords": [ + "trusted", + "secure", + "trustworthy" + ] + }, + "versions": { + "keywords": [ + "history" + ] + }, + "watch": { + "keywords": [ + "wait", + "hourglass" + ] + }, + "x": { + "keywords": [ + "remove", + "close", + "delete" + ] + }, + "zap": { + "keywords": [ + "electricity", + "lightning", + "props", + "like", + "star", + "save" + ] + } +} diff --git a/htdocs/theme/common/octicons/lib/octicons-master.sketch b/htdocs/theme/common/octicons/lib/octicons-master.sketch new file mode 100644 index 0000000000000000000000000000000000000000..85a3f1d431d4afa5fdc9c45e4ef9e1e2975d6487 GIT binary patch literal 1957888 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�k_EQ;c0q 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%0DkOv40s%P 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≫AG$w;A_CC{2_xHY<2gZ#&@R!GXS*l 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%0DNKE~{M+F{3e4h50CcAVFa^S+9? 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 literal 0 HcmV?d00001 diff --git a/htdocs/theme/common/octicons/lib/octicons.css b/htdocs/theme/common/octicons/lib/octicons.css new file mode 100644 index 00000000000..31d97867a12 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/octicons.css @@ -0,0 +1,5 @@ +.octicon { + display: inline-block; + vertical-align: text-top; + fill: currentColor; +} diff --git a/htdocs/theme/common/octicons/lib/svg/alert.svg b/htdocs/theme/common/octicons/lib/svg/alert.svg new file mode 100644 index 00000000000..3a75a6a4702 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/alert.svg @@ -0,0 +1,12 @@ + + + + alert + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/arrow-down.svg b/htdocs/theme/common/octicons/lib/svg/arrow-down.svg new file mode 100644 index 00000000000..49a04c4b48d --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/arrow-down.svg @@ -0,0 +1,12 @@ + + + + arrow-down + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/arrow-left.svg b/htdocs/theme/common/octicons/lib/svg/arrow-left.svg new file mode 100644 index 00000000000..b6153c0eff2 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/arrow-left.svg @@ -0,0 +1,12 @@ + + + + arrow-left + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/arrow-right.svg b/htdocs/theme/common/octicons/lib/svg/arrow-right.svg new file mode 100644 index 00000000000..5d7f96ac2d8 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/arrow-right.svg @@ -0,0 +1,12 @@ + + + + arrow-right + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/arrow-small-down.svg b/htdocs/theme/common/octicons/lib/svg/arrow-small-down.svg new file mode 100644 index 00000000000..bcb668d9c2c --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/arrow-small-down.svg @@ -0,0 +1,12 @@ + + + + arrow-small-down + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/arrow-small-left.svg b/htdocs/theme/common/octicons/lib/svg/arrow-small-left.svg new file mode 100644 index 00000000000..a98f8a13e03 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/arrow-small-left.svg @@ -0,0 +1,12 @@ + + + + arrow-small-left + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/arrow-small-right.svg b/htdocs/theme/common/octicons/lib/svg/arrow-small-right.svg new file mode 100644 index 00000000000..ac121726607 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/arrow-small-right.svg @@ -0,0 +1,12 @@ + + + + arrow-small-right + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/arrow-small-up.svg b/htdocs/theme/common/octicons/lib/svg/arrow-small-up.svg new file mode 100644 index 00000000000..9bd85161df4 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/arrow-small-up.svg @@ -0,0 +1,12 @@ + + + + arrow-small-up + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/arrow-up.svg b/htdocs/theme/common/octicons/lib/svg/arrow-up.svg new file mode 100644 index 00000000000..a015f862bb2 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/arrow-up.svg @@ -0,0 +1,12 @@ + + + + arrow-up + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/beaker.svg b/htdocs/theme/common/octicons/lib/svg/beaker.svg new file mode 100644 index 00000000000..48aa51a85a2 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/beaker.svg @@ -0,0 +1,12 @@ + + + + beaker + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/bell.svg b/htdocs/theme/common/octicons/lib/svg/bell.svg new file mode 100644 index 00000000000..70607e932e7 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/bell.svg @@ -0,0 +1,12 @@ + + + + bell + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/bold.svg b/htdocs/theme/common/octicons/lib/svg/bold.svg new file mode 100644 index 00000000000..a63af7ac7a1 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/bold.svg @@ -0,0 +1,12 @@ + + + + bold + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/book.svg b/htdocs/theme/common/octicons/lib/svg/book.svg new file mode 100644 index 00000000000..76026481ccb --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/book.svg @@ -0,0 +1,12 @@ + + + + book + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/bookmark.svg b/htdocs/theme/common/octicons/lib/svg/bookmark.svg new file mode 100644 index 00000000000..24fe161ed0d --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/bookmark.svg @@ -0,0 +1,12 @@ + + + + bookmark + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/briefcase.svg b/htdocs/theme/common/octicons/lib/svg/briefcase.svg new file mode 100644 index 00000000000..ae56b711b3c --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/briefcase.svg @@ -0,0 +1,12 @@ + + + + briefcase + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/broadcast.svg b/htdocs/theme/common/octicons/lib/svg/broadcast.svg new file mode 100644 index 00000000000..491048fb87a --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/broadcast.svg @@ -0,0 +1,12 @@ + + + + broadcast + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/browser.svg b/htdocs/theme/common/octicons/lib/svg/browser.svg new file mode 100644 index 00000000000..5c9251d0f91 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/browser.svg @@ -0,0 +1,12 @@ + + + + browser + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/bug.svg b/htdocs/theme/common/octicons/lib/svg/bug.svg new file mode 100644 index 00000000000..8f515ad1b98 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/bug.svg @@ -0,0 +1,12 @@ + + + + bug + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/calendar.svg b/htdocs/theme/common/octicons/lib/svg/calendar.svg new file mode 100644 index 00000000000..47755281156 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/calendar.svg @@ -0,0 +1,12 @@ + + + + calendar + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/check.svg b/htdocs/theme/common/octicons/lib/svg/check.svg new file mode 100644 index 00000000000..fffa457ac33 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/check.svg @@ -0,0 +1,12 @@ + + + + check + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/checklist.svg b/htdocs/theme/common/octicons/lib/svg/checklist.svg new file mode 100644 index 00000000000..2ba6b100be0 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/checklist.svg @@ -0,0 +1,12 @@ + + + + checklist + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/chevron-down.svg b/htdocs/theme/common/octicons/lib/svg/chevron-down.svg new file mode 100644 index 00000000000..a77c7eae8c0 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/chevron-down.svg @@ -0,0 +1,12 @@ + + + + chevron-down + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/chevron-left.svg b/htdocs/theme/common/octicons/lib/svg/chevron-left.svg new file mode 100644 index 00000000000..d09f2f39e97 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/chevron-left.svg @@ -0,0 +1,12 @@ + + + + chevron-left + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/chevron-right.svg b/htdocs/theme/common/octicons/lib/svg/chevron-right.svg new file mode 100644 index 00000000000..aaaa6f036f3 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/chevron-right.svg @@ -0,0 +1,12 @@ + + + + chevron-right + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/chevron-up.svg b/htdocs/theme/common/octicons/lib/svg/chevron-up.svg new file mode 100644 index 00000000000..4bd14a27c5d --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/chevron-up.svg @@ -0,0 +1,12 @@ + + + + chevron-up + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/circle-slash.svg b/htdocs/theme/common/octicons/lib/svg/circle-slash.svg new file mode 100644 index 00000000000..f0f5143a802 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/circle-slash.svg @@ -0,0 +1,12 @@ + + + + circle-slash + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/circuit-board.svg b/htdocs/theme/common/octicons/lib/svg/circuit-board.svg new file mode 100644 index 00000000000..5c4e07e59b1 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/circuit-board.svg @@ -0,0 +1,12 @@ + + + + circuit-board + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/clippy.svg b/htdocs/theme/common/octicons/lib/svg/clippy.svg new file mode 100644 index 00000000000..4001b75a296 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/clippy.svg @@ -0,0 +1,12 @@ + + + + clippy + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/clock.svg b/htdocs/theme/common/octicons/lib/svg/clock.svg new file mode 100644 index 00000000000..fb25a9ac451 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/clock.svg @@ -0,0 +1,12 @@ + + + + clock + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/cloud-download.svg b/htdocs/theme/common/octicons/lib/svg/cloud-download.svg new file mode 100644 index 00000000000..0cd213d57c4 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/cloud-download.svg @@ -0,0 +1,12 @@ + + + + cloud-download + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/cloud-upload.svg b/htdocs/theme/common/octicons/lib/svg/cloud-upload.svg new file mode 100644 index 00000000000..b8c24ce596d --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/cloud-upload.svg @@ -0,0 +1,12 @@ + + + + cloud-upload + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/code.svg b/htdocs/theme/common/octicons/lib/svg/code.svg new file mode 100644 index 00000000000..a297c9ba1d6 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/code.svg @@ -0,0 +1,12 @@ + + + + code + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/comment-discussion.svg b/htdocs/theme/common/octicons/lib/svg/comment-discussion.svg new file mode 100644 index 00000000000..85243149efe --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/comment-discussion.svg @@ -0,0 +1,12 @@ + + + + comment-discussion + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/comment.svg b/htdocs/theme/common/octicons/lib/svg/comment.svg new file mode 100644 index 00000000000..55279771463 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/comment.svg @@ -0,0 +1,12 @@ + + + + comment + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/credit-card.svg b/htdocs/theme/common/octicons/lib/svg/credit-card.svg new file mode 100644 index 00000000000..c801be4b18a --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/credit-card.svg @@ -0,0 +1,12 @@ + + + + credit-card + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/dash.svg b/htdocs/theme/common/octicons/lib/svg/dash.svg new file mode 100644 index 00000000000..6c000a991b0 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/dash.svg @@ -0,0 +1,12 @@ + + + + dash + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/dashboard.svg b/htdocs/theme/common/octicons/lib/svg/dashboard.svg new file mode 100644 index 00000000000..7c37b3a6637 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/dashboard.svg @@ -0,0 +1,12 @@ + + + + dashboard + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/database.svg b/htdocs/theme/common/octicons/lib/svg/database.svg new file mode 100644 index 00000000000..f1b798a970c --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/database.svg @@ -0,0 +1,12 @@ + + + + database + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/desktop-download.svg b/htdocs/theme/common/octicons/lib/svg/desktop-download.svg new file mode 100644 index 00000000000..6c9d08d97e0 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/desktop-download.svg @@ -0,0 +1,12 @@ + + + + desktop-download + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/device-camera-video.svg b/htdocs/theme/common/octicons/lib/svg/device-camera-video.svg new file mode 100644 index 00000000000..e8e8167a812 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/device-camera-video.svg @@ -0,0 +1,12 @@ + + + + device-camera-video + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/device-camera.svg b/htdocs/theme/common/octicons/lib/svg/device-camera.svg new file mode 100644 index 00000000000..f0fa4bc7cfd --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/device-camera.svg @@ -0,0 +1,12 @@ + + + + device-camera + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/device-desktop.svg b/htdocs/theme/common/octicons/lib/svg/device-desktop.svg new file mode 100644 index 00000000000..d2cae7ae900 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/device-desktop.svg @@ -0,0 +1,12 @@ + + + + device-desktop + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/device-mobile.svg b/htdocs/theme/common/octicons/lib/svg/device-mobile.svg new file mode 100644 index 00000000000..549767577c0 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/device-mobile.svg @@ -0,0 +1,12 @@ + + + + device-mobile + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/diff-added.svg b/htdocs/theme/common/octicons/lib/svg/diff-added.svg new file mode 100644 index 00000000000..0b1cd1779c3 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/diff-added.svg @@ -0,0 +1,12 @@ + + + + diff-added + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/diff-ignored.svg b/htdocs/theme/common/octicons/lib/svg/diff-ignored.svg new file mode 100644 index 00000000000..bd11f983b1e --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/diff-ignored.svg @@ -0,0 +1,12 @@ + + + + diff-ignored + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/diff-modified.svg b/htdocs/theme/common/octicons/lib/svg/diff-modified.svg new file mode 100644 index 00000000000..03929da650f --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/diff-modified.svg @@ -0,0 +1,12 @@ + + + + diff-modified + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/diff-removed.svg b/htdocs/theme/common/octicons/lib/svg/diff-removed.svg new file mode 100644 index 00000000000..b5c25a3ccc6 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/diff-removed.svg @@ -0,0 +1,12 @@ + + + + diff-removed + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/diff-renamed.svg b/htdocs/theme/common/octicons/lib/svg/diff-renamed.svg new file mode 100644 index 00000000000..3f385f1fab8 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/diff-renamed.svg @@ -0,0 +1,12 @@ + + + + diff-renamed + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/diff.svg b/htdocs/theme/common/octicons/lib/svg/diff.svg new file mode 100644 index 00000000000..0ea237dfbd7 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/diff.svg @@ -0,0 +1,12 @@ + + + + diff + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/ellipses.svg b/htdocs/theme/common/octicons/lib/svg/ellipses.svg new file mode 100644 index 00000000000..cba76eb46dd --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/ellipses.svg @@ -0,0 +1,12 @@ + + + + ellipses + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/ellipsis.svg b/htdocs/theme/common/octicons/lib/svg/ellipsis.svg new file mode 100644 index 00000000000..60acf955383 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/ellipsis.svg @@ -0,0 +1,3 @@ + + + diff --git a/htdocs/theme/common/octicons/lib/svg/eye.svg b/htdocs/theme/common/octicons/lib/svg/eye.svg new file mode 100644 index 00000000000..f2021488709 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/eye.svg @@ -0,0 +1,12 @@ + + + + eye + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/file-binary.svg b/htdocs/theme/common/octicons/lib/svg/file-binary.svg new file mode 100644 index 00000000000..8efbe8f5d2b --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file-binary.svg @@ -0,0 +1,12 @@ + + + + file-binary + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/file-code.svg b/htdocs/theme/common/octicons/lib/svg/file-code.svg new file mode 100644 index 00000000000..3bc2b299a42 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file-code.svg @@ -0,0 +1,12 @@ + + + + file-code + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/file-directory.svg b/htdocs/theme/common/octicons/lib/svg/file-directory.svg new file mode 100644 index 00000000000..3b0ea6d66cb --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file-directory.svg @@ -0,0 +1,12 @@ + + + + file-directory + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/file-media.svg b/htdocs/theme/common/octicons/lib/svg/file-media.svg new file mode 100644 index 00000000000..db00315d134 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file-media.svg @@ -0,0 +1,12 @@ + + + + file-media + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/file-pdf.svg b/htdocs/theme/common/octicons/lib/svg/file-pdf.svg new file mode 100644 index 00000000000..dcd774e9bc4 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file-pdf.svg @@ -0,0 +1,12 @@ + + + + file-pdf + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/file-submodule.svg b/htdocs/theme/common/octicons/lib/svg/file-submodule.svg new file mode 100644 index 00000000000..aa782e68dff --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file-submodule.svg @@ -0,0 +1,12 @@ + + + + file-submodule + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/file-symlink-directory.svg b/htdocs/theme/common/octicons/lib/svg/file-symlink-directory.svg new file mode 100644 index 00000000000..5d689bec548 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file-symlink-directory.svg @@ -0,0 +1,12 @@ + + + + file-symlink-directory + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/file-symlink-file.svg b/htdocs/theme/common/octicons/lib/svg/file-symlink-file.svg new file mode 100644 index 00000000000..710224295de --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file-symlink-file.svg @@ -0,0 +1,12 @@ + + + + file-symlink-file + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/file-text.svg b/htdocs/theme/common/octicons/lib/svg/file-text.svg new file mode 100644 index 00000000000..874cf5b9100 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file-text.svg @@ -0,0 +1,3 @@ + + + diff --git a/htdocs/theme/common/octicons/lib/svg/file-zip.svg b/htdocs/theme/common/octicons/lib/svg/file-zip.svg new file mode 100644 index 00000000000..c0d0f282fb5 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file-zip.svg @@ -0,0 +1,12 @@ + + + + file-zip + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/file.svg b/htdocs/theme/common/octicons/lib/svg/file.svg new file mode 100644 index 00000000000..a704f0c7118 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/file.svg @@ -0,0 +1,12 @@ + + + + file + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/flame.svg b/htdocs/theme/common/octicons/lib/svg/flame.svg new file mode 100644 index 00000000000..a9b69abaafc --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/flame.svg @@ -0,0 +1,12 @@ + + + + flame + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/fold.svg b/htdocs/theme/common/octicons/lib/svg/fold.svg new file mode 100644 index 00000000000..5446d76a46e --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/fold.svg @@ -0,0 +1,12 @@ + + + + fold + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/gear.svg b/htdocs/theme/common/octicons/lib/svg/gear.svg new file mode 100644 index 00000000000..2fdf75deec1 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/gear.svg @@ -0,0 +1,12 @@ + + + + gear + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/gift.svg b/htdocs/theme/common/octicons/lib/svg/gift.svg new file mode 100644 index 00000000000..1cb9cff4535 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/gift.svg @@ -0,0 +1,12 @@ + + + + gift + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/gist-secret.svg b/htdocs/theme/common/octicons/lib/svg/gist-secret.svg new file mode 100644 index 00000000000..e427060c776 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/gist-secret.svg @@ -0,0 +1,12 @@ + + + + gist-secret + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/gist.svg b/htdocs/theme/common/octicons/lib/svg/gist.svg new file mode 100644 index 00000000000..ff79f169b27 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/gist.svg @@ -0,0 +1,12 @@ + + + + gist + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/git-branch.svg b/htdocs/theme/common/octicons/lib/svg/git-branch.svg new file mode 100644 index 00000000000..0d2e53fd28f --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/git-branch.svg @@ -0,0 +1,12 @@ + + + + git-branch + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/git-commit.svg b/htdocs/theme/common/octicons/lib/svg/git-commit.svg new file mode 100644 index 00000000000..0ee7457f455 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/git-commit.svg @@ -0,0 +1,12 @@ + + + + git-commit + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/git-compare.svg b/htdocs/theme/common/octicons/lib/svg/git-compare.svg new file mode 100644 index 00000000000..6f7481f8293 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/git-compare.svg @@ -0,0 +1,12 @@ + + + + git-compare + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/git-merge.svg b/htdocs/theme/common/octicons/lib/svg/git-merge.svg new file mode 100644 index 00000000000..d0f41029b10 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/git-merge.svg @@ -0,0 +1,12 @@ + + + + git-merge + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/git-pull-request.svg b/htdocs/theme/common/octicons/lib/svg/git-pull-request.svg new file mode 100644 index 00000000000..492dda2ff46 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/git-pull-request.svg @@ -0,0 +1,12 @@ + + + + git-pull-request + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/globe.svg b/htdocs/theme/common/octicons/lib/svg/globe.svg new file mode 100644 index 00000000000..8e9d0196ff2 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/globe.svg @@ -0,0 +1,12 @@ + + + + globe + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/grabber.svg b/htdocs/theme/common/octicons/lib/svg/grabber.svg new file mode 100644 index 00000000000..cd3b15112ca --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/grabber.svg @@ -0,0 +1,12 @@ + + + + grabber + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/graph.svg b/htdocs/theme/common/octicons/lib/svg/graph.svg new file mode 100644 index 00000000000..f72a1875263 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/graph.svg @@ -0,0 +1,12 @@ + + + + graph + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/heart.svg b/htdocs/theme/common/octicons/lib/svg/heart.svg new file mode 100644 index 00000000000..688a14d9a5e --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/heart.svg @@ -0,0 +1,12 @@ + + + + heart + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/history.svg b/htdocs/theme/common/octicons/lib/svg/history.svg new file mode 100644 index 00000000000..77010ba5c6c --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/history.svg @@ -0,0 +1,12 @@ + + + + history + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/home.svg b/htdocs/theme/common/octicons/lib/svg/home.svg new file mode 100644 index 00000000000..d2862e50f48 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/home.svg @@ -0,0 +1,12 @@ + + + + home + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/horizontal-rule.svg b/htdocs/theme/common/octicons/lib/svg/horizontal-rule.svg new file mode 100644 index 00000000000..cf444e7b3d2 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/horizontal-rule.svg @@ -0,0 +1,12 @@ + + + + horizontal-rule + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/hubot.svg b/htdocs/theme/common/octicons/lib/svg/hubot.svg new file mode 100644 index 00000000000..d2fb9ad052b --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/hubot.svg @@ -0,0 +1,12 @@ + + + + hubot + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/inbox.svg b/htdocs/theme/common/octicons/lib/svg/inbox.svg new file mode 100644 index 00000000000..1b288f2570f --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/inbox.svg @@ -0,0 +1,12 @@ + + + + inbox + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/info.svg b/htdocs/theme/common/octicons/lib/svg/info.svg new file mode 100644 index 00000000000..7c6de3ebd66 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/info.svg @@ -0,0 +1,12 @@ + + + + info + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/issue-closed.svg b/htdocs/theme/common/octicons/lib/svg/issue-closed.svg new file mode 100644 index 00000000000..f06480bb847 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/issue-closed.svg @@ -0,0 +1,12 @@ + + + + issue-closed + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/issue-opened.svg b/htdocs/theme/common/octicons/lib/svg/issue-opened.svg new file mode 100644 index 00000000000..d8ba7c46c35 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/issue-opened.svg @@ -0,0 +1,12 @@ + + + + issue-opened + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/issue-reopened.svg b/htdocs/theme/common/octicons/lib/svg/issue-reopened.svg new file mode 100644 index 00000000000..1d5eab6bdba --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/issue-reopened.svg @@ -0,0 +1,12 @@ + + + + issue-reopened + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/italic.svg b/htdocs/theme/common/octicons/lib/svg/italic.svg new file mode 100644 index 00000000000..d488293cbe0 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/italic.svg @@ -0,0 +1,12 @@ + + + + italic + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/jersey.svg b/htdocs/theme/common/octicons/lib/svg/jersey.svg new file mode 100644 index 00000000000..56e85ee4cd8 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/jersey.svg @@ -0,0 +1,12 @@ + + + + jersey + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/key.svg b/htdocs/theme/common/octicons/lib/svg/key.svg new file mode 100644 index 00000000000..bc3b52382af --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/key.svg @@ -0,0 +1,12 @@ + + + + key + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/keyboard.svg b/htdocs/theme/common/octicons/lib/svg/keyboard.svg new file mode 100644 index 00000000000..77aa0f5fe04 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/keyboard.svg @@ -0,0 +1,12 @@ + + + + keyboard + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/law.svg b/htdocs/theme/common/octicons/lib/svg/law.svg new file mode 100644 index 00000000000..0b144363d9a --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/law.svg @@ -0,0 +1,12 @@ + + + + law + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/light-bulb.svg b/htdocs/theme/common/octicons/lib/svg/light-bulb.svg new file mode 100644 index 00000000000..364cb691527 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/light-bulb.svg @@ -0,0 +1,12 @@ + + + + light-bulb + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/link-external.svg b/htdocs/theme/common/octicons/lib/svg/link-external.svg new file mode 100644 index 00000000000..e4d0aecb71a --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/link-external.svg @@ -0,0 +1,12 @@ + + + + link-external + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/link.svg b/htdocs/theme/common/octicons/lib/svg/link.svg new file mode 100644 index 00000000000..b28cf283854 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/link.svg @@ -0,0 +1,12 @@ + + + + link + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/list-ordered.svg b/htdocs/theme/common/octicons/lib/svg/list-ordered.svg new file mode 100644 index 00000000000..16f458cbfe6 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/list-ordered.svg @@ -0,0 +1,12 @@ + + + + list-ordered + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/list-unordered.svg b/htdocs/theme/common/octicons/lib/svg/list-unordered.svg new file mode 100644 index 00000000000..e0fce86f0b1 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/list-unordered.svg @@ -0,0 +1,12 @@ + + + + list-unordered + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/location.svg b/htdocs/theme/common/octicons/lib/svg/location.svg new file mode 100644 index 00000000000..2bb33c679b6 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/location.svg @@ -0,0 +1,12 @@ + + + + location + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/lock.svg b/htdocs/theme/common/octicons/lib/svg/lock.svg new file mode 100644 index 00000000000..3e81d98ee0d --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/lock.svg @@ -0,0 +1,12 @@ + + + + lock + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/logo-gist.svg b/htdocs/theme/common/octicons/lib/svg/logo-gist.svg new file mode 100644 index 00000000000..c95f58fc0cc --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/logo-gist.svg @@ -0,0 +1,12 @@ + + + + logo-gist + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/logo-github.svg b/htdocs/theme/common/octicons/lib/svg/logo-github.svg new file mode 100644 index 00000000000..bb6c20a7b01 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/logo-github.svg @@ -0,0 +1,12 @@ + + + + logo-github + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/mail-read.svg b/htdocs/theme/common/octicons/lib/svg/mail-read.svg new file mode 100644 index 00000000000..9a6ee16730d --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/mail-read.svg @@ -0,0 +1,12 @@ + + + + mail-read + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/mail-reply.svg b/htdocs/theme/common/octicons/lib/svg/mail-reply.svg new file mode 100644 index 00000000000..03c8212f636 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/mail-reply.svg @@ -0,0 +1,12 @@ + + + + mail-reply + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/mail.svg b/htdocs/theme/common/octicons/lib/svg/mail.svg new file mode 100644 index 00000000000..1ff97533bb5 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/mail.svg @@ -0,0 +1,12 @@ + + + + mail + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/mark-github.svg b/htdocs/theme/common/octicons/lib/svg/mark-github.svg new file mode 100644 index 00000000000..2bda9afa081 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/mark-github.svg @@ -0,0 +1,12 @@ + + + + mark-github + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/markdown.svg b/htdocs/theme/common/octicons/lib/svg/markdown.svg new file mode 100644 index 00000000000..fb33c140c80 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/markdown.svg @@ -0,0 +1,12 @@ + + + + markdown + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/megaphone.svg b/htdocs/theme/common/octicons/lib/svg/megaphone.svg new file mode 100644 index 00000000000..e8f10228767 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/megaphone.svg @@ -0,0 +1,12 @@ + + + + megaphone + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/mention.svg b/htdocs/theme/common/octicons/lib/svg/mention.svg new file mode 100644 index 00000000000..f82be86a72a --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/mention.svg @@ -0,0 +1,12 @@ + + + + mention + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/milestone.svg b/htdocs/theme/common/octicons/lib/svg/milestone.svg new file mode 100644 index 00000000000..829bba95ef5 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/milestone.svg @@ -0,0 +1,12 @@ + + + + milestone + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/mirror.svg b/htdocs/theme/common/octicons/lib/svg/mirror.svg new file mode 100644 index 00000000000..39055e601eb --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/mirror.svg @@ -0,0 +1,12 @@ + + + + mirror + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/mortar-board.svg b/htdocs/theme/common/octicons/lib/svg/mortar-board.svg new file mode 100644 index 00000000000..ee3b5f8b9a3 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/mortar-board.svg @@ -0,0 +1,12 @@ + + + + mortar-board + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/mute.svg b/htdocs/theme/common/octicons/lib/svg/mute.svg new file mode 100644 index 00000000000..f29643f227d --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/mute.svg @@ -0,0 +1,12 @@ + + + + mute + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/no-newline.svg b/htdocs/theme/common/octicons/lib/svg/no-newline.svg new file mode 100644 index 00000000000..fae7c4fe789 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/no-newline.svg @@ -0,0 +1,12 @@ + + + + no-newline + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/note.svg b/htdocs/theme/common/octicons/lib/svg/note.svg new file mode 100644 index 00000000000..49c1a3892b3 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/note.svg @@ -0,0 +1,12 @@ + + + + note + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/octoface.svg b/htdocs/theme/common/octicons/lib/svg/octoface.svg new file mode 100644 index 00000000000..87aadac2360 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/octoface.svg @@ -0,0 +1,12 @@ + + + + octoface + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/organization.svg b/htdocs/theme/common/octicons/lib/svg/organization.svg new file mode 100644 index 00000000000..51cb253acfa --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/organization.svg @@ -0,0 +1,12 @@ + + + + organization + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/package.svg b/htdocs/theme/common/octicons/lib/svg/package.svg new file mode 100644 index 00000000000..cb41bcf6a03 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/package.svg @@ -0,0 +1,12 @@ + + + + package + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/paintcan.svg b/htdocs/theme/common/octicons/lib/svg/paintcan.svg new file mode 100644 index 00000000000..387195bff62 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/paintcan.svg @@ -0,0 +1,12 @@ + + + + paintcan + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/pencil.svg b/htdocs/theme/common/octicons/lib/svg/pencil.svg new file mode 100644 index 00000000000..354df31f3f2 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/pencil.svg @@ -0,0 +1,12 @@ + + + + pencil + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/person.svg b/htdocs/theme/common/octicons/lib/svg/person.svg new file mode 100644 index 00000000000..a26225e1c4a --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/person.svg @@ -0,0 +1,12 @@ + + + + person + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/pin.svg b/htdocs/theme/common/octicons/lib/svg/pin.svg new file mode 100644 index 00000000000..d08cb652dd1 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/pin.svg @@ -0,0 +1,12 @@ + + + + pin + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/plug.svg b/htdocs/theme/common/octicons/lib/svg/plug.svg new file mode 100644 index 00000000000..8f4ee9f46ba --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/plug.svg @@ -0,0 +1,12 @@ + + + + plug + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/plus-small.svg b/htdocs/theme/common/octicons/lib/svg/plus-small.svg new file mode 100644 index 00000000000..ccb9d2d6256 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/plus-small.svg @@ -0,0 +1,12 @@ + + + + plus-small + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/plus.svg b/htdocs/theme/common/octicons/lib/svg/plus.svg new file mode 100644 index 00000000000..3882ee5a1c7 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/plus.svg @@ -0,0 +1,12 @@ + + + + plus + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/primitive-dot.svg b/htdocs/theme/common/octicons/lib/svg/primitive-dot.svg new file mode 100644 index 00000000000..b9a2f416c28 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/primitive-dot.svg @@ -0,0 +1,12 @@ + + + + primitive-dot + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/primitive-square.svg b/htdocs/theme/common/octicons/lib/svg/primitive-square.svg new file mode 100644 index 00000000000..361b7c383ac --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/primitive-square.svg @@ -0,0 +1,12 @@ + + + + primitive-square + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/project.svg b/htdocs/theme/common/octicons/lib/svg/project.svg new file mode 100644 index 00000000000..606672f3f52 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/project.svg @@ -0,0 +1,12 @@ + + + + project + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/pulse.svg b/htdocs/theme/common/octicons/lib/svg/pulse.svg new file mode 100644 index 00000000000..a17f9c00d3a --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/pulse.svg @@ -0,0 +1,12 @@ + + + + pulse + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/question.svg b/htdocs/theme/common/octicons/lib/svg/question.svg new file mode 100644 index 00000000000..554fc5bf02c --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/question.svg @@ -0,0 +1,12 @@ + + + + question + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/quote.svg b/htdocs/theme/common/octicons/lib/svg/quote.svg new file mode 100644 index 00000000000..882882f0fbd --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/quote.svg @@ -0,0 +1,12 @@ + + + + quote + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/radio-tower.svg b/htdocs/theme/common/octicons/lib/svg/radio-tower.svg new file mode 100644 index 00000000000..6f27ab3d9ce --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/radio-tower.svg @@ -0,0 +1,12 @@ + + + + radio-tower + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/reply.svg b/htdocs/theme/common/octicons/lib/svg/reply.svg new file mode 100644 index 00000000000..7dbde79bfa5 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/reply.svg @@ -0,0 +1,12 @@ + + + + reply + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/repo-clone.svg b/htdocs/theme/common/octicons/lib/svg/repo-clone.svg new file mode 100644 index 00000000000..8fb3ea1688d --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/repo-clone.svg @@ -0,0 +1,12 @@ + + + + repo-clone + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/repo-force-push.svg b/htdocs/theme/common/octicons/lib/svg/repo-force-push.svg new file mode 100644 index 00000000000..a9cb1d62609 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/repo-force-push.svg @@ -0,0 +1,12 @@ + + + + repo-force-push + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/repo-forked.svg b/htdocs/theme/common/octicons/lib/svg/repo-forked.svg new file mode 100644 index 00000000000..b1db2556a49 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/repo-forked.svg @@ -0,0 +1,12 @@ + + + + repo-forked + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/repo-pull.svg b/htdocs/theme/common/octicons/lib/svg/repo-pull.svg new file mode 100644 index 00000000000..9ca11006c80 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/repo-pull.svg @@ -0,0 +1,12 @@ + + + + repo-pull + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/repo-push.svg b/htdocs/theme/common/octicons/lib/svg/repo-push.svg new file mode 100644 index 00000000000..be7433ce792 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/repo-push.svg @@ -0,0 +1,12 @@ + + + + repo-push + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/repo.svg b/htdocs/theme/common/octicons/lib/svg/repo.svg new file mode 100644 index 00000000000..613f72d52f3 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/repo.svg @@ -0,0 +1,12 @@ + + + + repo + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/rocket.svg b/htdocs/theme/common/octicons/lib/svg/rocket.svg new file mode 100644 index 00000000000..1f227b7dae8 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/rocket.svg @@ -0,0 +1,12 @@ + + + + rocket + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/rss.svg b/htdocs/theme/common/octicons/lib/svg/rss.svg new file mode 100644 index 00000000000..6d82aaf8c20 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/rss.svg @@ -0,0 +1,12 @@ + + + + rss + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/ruby.svg b/htdocs/theme/common/octicons/lib/svg/ruby.svg new file mode 100644 index 00000000000..fff3baf760c --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/ruby.svg @@ -0,0 +1,12 @@ + + + + ruby + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/screen-full.svg b/htdocs/theme/common/octicons/lib/svg/screen-full.svg new file mode 100644 index 00000000000..302ef1725d4 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/screen-full.svg @@ -0,0 +1,12 @@ + + + + screen-full + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/screen-normal.svg b/htdocs/theme/common/octicons/lib/svg/screen-normal.svg new file mode 100644 index 00000000000..e144fc5a04d --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/screen-normal.svg @@ -0,0 +1,12 @@ + + + + screen-normal + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/search.svg b/htdocs/theme/common/octicons/lib/svg/search.svg new file mode 100644 index 00000000000..547eb505ef6 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/search.svg @@ -0,0 +1,12 @@ + + + + search + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/server.svg b/htdocs/theme/common/octicons/lib/svg/server.svg new file mode 100644 index 00000000000..3f439fd5a7c --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/server.svg @@ -0,0 +1,12 @@ + + + + server + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/settings.svg b/htdocs/theme/common/octicons/lib/svg/settings.svg new file mode 100644 index 00000000000..70f860c411e --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/settings.svg @@ -0,0 +1,12 @@ + + + + settings + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/shield.svg b/htdocs/theme/common/octicons/lib/svg/shield.svg new file mode 100644 index 00000000000..f83a8d0c549 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/shield.svg @@ -0,0 +1,12 @@ + + + + shield + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/sign-in.svg b/htdocs/theme/common/octicons/lib/svg/sign-in.svg new file mode 100644 index 00000000000..99bc42b6ccc --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/sign-in.svg @@ -0,0 +1,12 @@ + + + + sign-in + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/sign-out.svg b/htdocs/theme/common/octicons/lib/svg/sign-out.svg new file mode 100644 index 00000000000..dca0faa5cb9 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/sign-out.svg @@ -0,0 +1,12 @@ + + + + sign-out + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/smiley.svg b/htdocs/theme/common/octicons/lib/svg/smiley.svg new file mode 100644 index 00000000000..6041ee91f61 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/smiley.svg @@ -0,0 +1,12 @@ + + + + smiley + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/squirrel.svg b/htdocs/theme/common/octicons/lib/svg/squirrel.svg new file mode 100644 index 00000000000..0cff65843f9 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/squirrel.svg @@ -0,0 +1,12 @@ + + + + squirrel + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/star.svg b/htdocs/theme/common/octicons/lib/svg/star.svg new file mode 100644 index 00000000000..9d8ca6a7fc0 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/star.svg @@ -0,0 +1,12 @@ + + + + star + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/stop.svg b/htdocs/theme/common/octicons/lib/svg/stop.svg new file mode 100644 index 00000000000..bc74f95f8d7 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/stop.svg @@ -0,0 +1,12 @@ + + + + stop + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/sync.svg b/htdocs/theme/common/octicons/lib/svg/sync.svg new file mode 100644 index 00000000000..59b5f3c4000 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/sync.svg @@ -0,0 +1,12 @@ + + + + sync + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/tag.svg b/htdocs/theme/common/octicons/lib/svg/tag.svg new file mode 100644 index 00000000000..1b425c8a69d --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/tag.svg @@ -0,0 +1,12 @@ + + + + tag + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/tasklist.svg b/htdocs/theme/common/octicons/lib/svg/tasklist.svg new file mode 100644 index 00000000000..e9291f70a26 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/tasklist.svg @@ -0,0 +1,12 @@ + + + + tasklist + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/telescope.svg b/htdocs/theme/common/octicons/lib/svg/telescope.svg new file mode 100644 index 00000000000..961891fcf90 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/telescope.svg @@ -0,0 +1,12 @@ + + + + telescope + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/terminal.svg b/htdocs/theme/common/octicons/lib/svg/terminal.svg new file mode 100644 index 00000000000..9980e86827f --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/terminal.svg @@ -0,0 +1,12 @@ + + + + terminal + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/text-size.svg b/htdocs/theme/common/octicons/lib/svg/text-size.svg new file mode 100644 index 00000000000..9c40dd04ab3 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/text-size.svg @@ -0,0 +1,12 @@ + + + + text-size + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/three-bars.svg b/htdocs/theme/common/octicons/lib/svg/three-bars.svg new file mode 100644 index 00000000000..585dfeee331 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/three-bars.svg @@ -0,0 +1,12 @@ + + + + three-bars + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/thumbsdown.svg b/htdocs/theme/common/octicons/lib/svg/thumbsdown.svg new file mode 100644 index 00000000000..e0e642d9455 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/thumbsdown.svg @@ -0,0 +1,12 @@ + + + + thumbsdown + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/thumbsup.svg b/htdocs/theme/common/octicons/lib/svg/thumbsup.svg new file mode 100644 index 00000000000..a9bc8529976 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/thumbsup.svg @@ -0,0 +1,12 @@ + + + + thumbsup + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/tools.svg b/htdocs/theme/common/octicons/lib/svg/tools.svg new file mode 100644 index 00000000000..52aafd2555e --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/tools.svg @@ -0,0 +1,12 @@ + + + + tools + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/trashcan.svg b/htdocs/theme/common/octicons/lib/svg/trashcan.svg new file mode 100644 index 00000000000..4a4ad77d458 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/trashcan.svg @@ -0,0 +1,12 @@ + + + + trashcan + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/triangle-down.svg b/htdocs/theme/common/octicons/lib/svg/triangle-down.svg new file mode 100644 index 00000000000..dba0fc4ac70 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/triangle-down.svg @@ -0,0 +1,12 @@ + + + + triangle-down + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/triangle-left.svg b/htdocs/theme/common/octicons/lib/svg/triangle-left.svg new file mode 100644 index 00000000000..7011b15bbe2 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/triangle-left.svg @@ -0,0 +1,12 @@ + + + + triangle-left + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/triangle-right.svg b/htdocs/theme/common/octicons/lib/svg/triangle-right.svg new file mode 100644 index 00000000000..8abb5e92e25 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/triangle-right.svg @@ -0,0 +1,12 @@ + + + + triangle-right + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/triangle-up.svg b/htdocs/theme/common/octicons/lib/svg/triangle-up.svg new file mode 100644 index 00000000000..e6488cf35f3 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/triangle-up.svg @@ -0,0 +1,12 @@ + + + + triangle-up + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/unfold.svg b/htdocs/theme/common/octicons/lib/svg/unfold.svg new file mode 100644 index 00000000000..3f40744b9d9 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/unfold.svg @@ -0,0 +1,12 @@ + + + + unfold + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/unmute.svg b/htdocs/theme/common/octicons/lib/svg/unmute.svg new file mode 100644 index 00000000000..71a5624a738 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/unmute.svg @@ -0,0 +1,12 @@ + + + + unmute + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/unverified.svg b/htdocs/theme/common/octicons/lib/svg/unverified.svg new file mode 100644 index 00000000000..c91b44e5f4a --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/unverified.svg @@ -0,0 +1,12 @@ + + + + unverified + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/verified.svg b/htdocs/theme/common/octicons/lib/svg/verified.svg new file mode 100644 index 00000000000..c0c0a7223e5 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/verified.svg @@ -0,0 +1,12 @@ + + + + verified + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/versions.svg b/htdocs/theme/common/octicons/lib/svg/versions.svg new file mode 100644 index 00000000000..62b9c199811 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/versions.svg @@ -0,0 +1,12 @@ + + + + versions + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/watch.svg b/htdocs/theme/common/octicons/lib/svg/watch.svg new file mode 100644 index 00000000000..705f441ae5f --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/watch.svg @@ -0,0 +1,12 @@ + + + + watch + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/x.svg b/htdocs/theme/common/octicons/lib/svg/x.svg new file mode 100644 index 00000000000..51466da25a4 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/x.svg @@ -0,0 +1,12 @@ + + + + x + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/lib/svg/zap.svg b/htdocs/theme/common/octicons/lib/svg/zap.svg new file mode 100644 index 00000000000..95641465029 --- /dev/null +++ b/htdocs/theme/common/octicons/lib/svg/zap.svg @@ -0,0 +1,12 @@ + + + + zap + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/htdocs/theme/common/octicons/package.json b/htdocs/theme/common/octicons/package.json new file mode 100644 index 00000000000..61def286fac --- /dev/null +++ b/htdocs/theme/common/octicons/package.json @@ -0,0 +1,39 @@ +{ + "version": "5.0.1", + "name": "octicons", + "description": "A scalable set of icons handcrafted with <3 by GitHub.", + "homepage": "https://octicons.github.com", + "author": "GitHub Inc.", + "license": "(OFL-1.1 OR MIT)", + "style": "build/octicons.css", + "main": "index.js", + "files": [ + "index.js", + "build" + ], + "repository": "https://github.com/primer/octicons.git", + "bugs": { + "url": "https://github.com/primer/octicons/issues" + }, + "scripts": { + "build": "grunt", + "prepublish": "npm run build", + "test": "npm run build && ava --verbose \"test/**/*.js\"" + }, + "devDependencies": { + "autoprefixer": "^6.3.6", + "ava": "^0.16.0", + "grunt": "^1.0.1", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-cssnano": "^2.1.0", + "grunt-svgmin": "^4.0.0", + "grunt-svgstore": "^1.0.0" + }, + "keywords": [ + "GitHub", + "icons", + "svg", + "octicons" + ] +} From ad308e33ccbcd313499ca5c71a976848c9b11126 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 9 Feb 2017 18:29:04 +0100 Subject: [PATCH 150/170] Fix scrutinizer warning --- htdocs/product/list.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/product/list.php b/htdocs/product/list.php index 4f1f97cef8a..9efdca6a4d0 100644 --- a/htdocs/product/list.php +++ b/htdocs/product/list.php @@ -75,8 +75,8 @@ if (! $sortorder) $sortorder="ASC"; // Initialize context for list $contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'productservicelist'; -if ($type === '1') { $contextpage='servicelist'; if ($search_type=='') $search_type='1'; } -if ($type === '0') { $contextpage='productlist'; if ($search_type=='') $search_type='0'; } +if ((string) $type == '1') { $contextpage='servicelist'; if ($search_type=='') $search_type='1'; } +if ((string) $type == '0') { $contextpage='productlist'; if ($search_type=='') $search_type='0'; } // Initialize technical object to manage hooks of thirdparties. Note that conf->hooks_modules contains array array $hookmanager->initHooks(array($contextpage)); From 2184eabe2f1e8657b6cc23e3be5f7fb2bb60710f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 9 Feb 2017 22:02:37 +0100 Subject: [PATCH 151/170] Fix english --- build/pad/pad_dolibarr.xml | 4 ++-- build/pad/pad_dolibarr_nos.xml | 4 ++-- build/pad/pad_doliwamp.xml | 2 +- build/pad/pad_doliwamp_nos.xml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/pad/pad_dolibarr.xml b/build/pad/pad_dolibarr.xml index 999b8db5142..77318eeb2d2 100644 --- a/build/pad/pad_dolibarr.xml +++ b/build/pad/pad_dolibarr.xml @@ -71,8 +71,8 @@ Dolibarr ERP & CRM Dolibarr ERP & CRM, the easy to use open source software to manage your activity Dolibarr ERP & CRM, the easy to use open source software to manage your activity (invoices, customers, suppliers, contracts, agenda, emailings...) and any other things a small or mid-sized business or a foundation needs to manage. - Dolibarr ERP & CRM is a software built by modules addition (you enable only features you need), to manage small or medium companies, freelancers or foundations. We can say Dolibarr is an ERP or CRM. Dolibarr is also available with an auto-installer for Windows users with no technical knowledge to install Dolibarr and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. See DoliWamp software for this. - Dolibarr ERP & CRM is a software built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations. We can say Dolibarr is an ERP or CRM (or both depending on activated modules). It's an OpenSource project base on a WAMP, MAMP or LAMP server (Apache, Mysql, PHP for all Operating Systems). Dolibarr differs from other ERP or CRM softwares (like OpenAguila, OpenBravo, OpenERP, Neogia, Compiere, etc) because everything was made to be more simple: + Dolibarr ERP & CRM is a software package built by modules addition (you enable only features you need), to manage small or medium companies, freelancers or foundations. We can say Dolibarr is an ERP or CRM. Dolibarr is also available with an auto-installer for Windows users with no technical knowledge to install Dolibarr and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. See DoliWamp software for this. + Dolibarr ERP & CRM is a software package built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations. We can say Dolibarr is an ERP or CRM (or both depending on activated modules). It's an OpenSource project base on a WAMP, MAMP or LAMP server (Apache, Mysql, PHP for all Operating Systems). Dolibarr differs from other ERP or CRM softwares (like OpenAguila, OpenBravo, Odoo, Neogia, Compiere, etc) because everything was made to be more simple: Simple to install Simple to use Simple to develop diff --git a/build/pad/pad_dolibarr_nos.xml b/build/pad/pad_dolibarr_nos.xml index 8effe4b771a..6d81793de79 100644 --- a/build/pad/pad_dolibarr_nos.xml +++ b/build/pad/pad_dolibarr_nos.xml @@ -71,8 +71,8 @@ Dolibarr ERP & CRM Dolibarr ERP & CRM, the easy to use open source software to manage your activity Dolibarr ERP & CRM, the easy to use open source software to manage your activity (invoices, customers, suppliers, contracts, agenda, emailings...) and any other things a small or mid-sized business or a foundation needs to manage. - Dolibarr ERP & CRM is a software built by modules addition (you enable only features you need), to manage small or medium companies, freelancers or foundations. We can say Dolibarr is an ERP or CRM. Dolibarr is also available with an auto-installer for Windows users with no technical knowledge to install Dolibarr and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. See DoliWamp software for this. - Dolibarr ERP & CRM is a software built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations. We can say Dolibarr is an ERP or CRM (or both depending on activated modules). It's an OpenSource project base on a WAMP, MAMP or LAMP server (Apache, Mysql, PHP for all Operating Systems). Dolibarr differs from other ERP or CRM softwares (like OpenAguila, OpenBravo, OpenERP, Neogia, Compiere, etc) because everything was made to be more simple: + Dolibarr ERP & CRM is a software package built by modules addition (you enable only features you need), to manage small or medium companies, freelancers or foundations. We can say Dolibarr is an ERP or CRM. Dolibarr is also available with an auto-installer for Windows users with no technical knowledge to install Dolibarr and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. See DoliWamp software for this. + Dolibarr ERP & CRM is a software package built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations. We can say Dolibarr is an ERP or CRM (or both depending on activated modules). It's an OpenSource project base on a WAMP, MAMP or LAMP server (Apache, Mysql, PHP for all Operating Systems). Dolibarr differs from other ERP or CRM softwares (like OpenAguila, OpenBravo, OpenERP, Neogia, Compiere, etc) because everything was made to be more simple: Simple to install Simple to use Simple to develop diff --git a/build/pad/pad_doliwamp.xml b/build/pad/pad_doliwamp.xml index 06eaec3af98..842d746dbcc 100644 --- a/build/pad/pad_doliwamp.xml +++ b/build/pad/pad_doliwamp.xml @@ -72,7 +72,7 @@ DoliWamp, the easy to use Dolibarr for Windows to manage your company,foundation DoliWamp is the Dolibarr ERP/CRM for Windows, the easy to use open source software to manage your activity (invoices, customers, suppliers, contracts, agenda, emailings...) and any other things a small or mid-sized business or a foundation needs. DoliWamp is the Dolibarr ERP/CRM autoinstaller for Windows users with no technical knowledge to install Dolibarr and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. Dolibarr ERP/CRM is a software package built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations. - DoliWamp is the Dolibarr ERP/CRM for Windows. Dolibarr ERP & CRM is a software built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations (You can manage or follow contacts, invoices, orders, commercial proposals, products, stock management, agenda, mass emailings, members of a foundation, bank accounts...). Based on a WAMP, MAMP or LAMP server (Apache, Mysql, PHP for all Operating Systems), you can install it as a standalone program or use it from anywhere with any web browser. Dolibarr is an OpenSource project. It differs from other ERP or CRM softwares (like OpenAguila, OpenBravo, OpenERP, Neogia, Compiere, etc) because everything was made to be more simple: Simple to install, Simple to use, Simple to develop. + DoliWamp is the Dolibarr ERP/CRM for Windows. Dolibarr ERP & CRM is a software package built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations (You can manage or follow contacts, invoices, orders, commercial proposals, products, stock management, agenda, mass emailings, members of a foundation, bank accounts...). Based on a WAMP, MAMP or LAMP server (Apache, Mysql, PHP for all Operating Systems), you can install it as a standalone program or use it from anywhere with any web browser. Dolibarr is an OpenSource project. It differs from other ERP or CRM softwares (like OpenAguila, OpenBravo, OpenERP, Neogia, Compiere, etc) because everything was made to be more simple: Simple to install, Simple to use, Simple to develop. DoliWamp is the auto-installer for Windows users with no technical knowledge to install Dolibarr ERP/CRM and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. diff --git a/build/pad/pad_doliwamp_nos.xml b/build/pad/pad_doliwamp_nos.xml index 4f7981a4314..61c739daff0 100644 --- a/build/pad/pad_doliwamp_nos.xml +++ b/build/pad/pad_doliwamp_nos.xml @@ -72,7 +72,7 @@ DoliWamp, the easy to use Dolibarr for Windows to manage your company,foundation DoliWamp is the Dolibarr ERP/CRM for Windows, the easy to use open source software to manage your activity (invoices, customers, suppliers, contracts, agenda, emailings...) and any other things a small or mid-sized business or a foundation needs. DoliWamp is the Dolibarr ERP/CRM autoinstaller for Windows users with no technical knowledge to install Dolibarr and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. Dolibarr ERP/CRM is a software package built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations. - DoliWamp is the Dolibarr ERP/CRM for Windows. Dolibarr ERP & CRM is a software built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations (You can manage or follow contacts, invoices, orders, commercial proposals, products, stock management, agenda, mass emailings, members of a foundation, bank accounts...). Based on a WAMP, MAMP or LAMP server (Apache, Mysql, PHP for all Operating Systems), you can install it as a standalone program or use it from anywhere with any web browser. Dolibarr is an OpenSource project. It differs from other ERP or CRM softwares (like OpenAguila, OpenBravo, OpenERP, Neogia, Compiere, etc) because everything was made to be more simple: Simple to install, Simple to use, Simple to develop. + DoliWamp is the Dolibarr ERP/CRM for Windows. Dolibarr ERP & CRM is a software package built by modules addition (you enable only features you need), to manage small or mid-sized businesses, freelancers or foundations (You can manage or follow contacts, invoices, orders, commercial proposals, products, stock management, agenda, mass emailings, members of a foundation, bank accounts...). Based on a WAMP, MAMP or LAMP server (Apache, Mysql, PHP for all Operating Systems), you can install it as a standalone program or use it from anywhere with any web browser. Dolibarr is an OpenSource project. It differs from other ERP or CRM softwares (like OpenAguila, OpenBravo, OpenERP, Neogia, Compiere, etc) because everything was made to be more simple: Simple to install, Simple to use, Simple to develop. DoliWamp is the auto-installer for Windows users with no technical knowledge to install Dolibarr ERP/CRM and all its prerequisites (Apache, Mysql, PHP) with just one auto-exe file. From 4fb4bc6f6cf51332a5776acfe9ce87e689ad51b6 Mon Sep 17 00:00:00 2001 From: De Coninck Laurent Date: Fri, 10 Feb 2017 10:20:32 +0100 Subject: [PATCH 152/170] [FIX] Dictionnaries without rowid It's possible to create a dictionnary without the key rowid. But in that case it's impossible to enable/disable the value because the dictionnary fonctionality uses always rowid even another primary key is defined. By this fix, the id property uses is the primary key defined on the dictionnary creation. --- htdocs/admin/dict.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/htdocs/admin/dict.php b/htdocs/admin/dict.php index 09ee313e487..2bb0bbe15dc 100644 --- a/htdocs/admin/dict.php +++ b/htdocs/admin/dict.php @@ -1537,7 +1537,8 @@ if ($id) $canbemodified=$iserasable; if ($obj->code == 'RECEP') $canbemodified=1; - $url = $_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'sortfield='.$sortfield.'&sortorder='.$sortorder.'&rowid='.(! empty($obj->rowid)?$obj->rowid:(! empty($obj->code)?$obj->code:'')).'&code='.(! empty($obj->code)?urlencode($obj->code):''); + $rowidcol=$tabrowid[$id]; + $url = $_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'sortfield='.$sortfield.'&sortorder='.$sortorder.'&rowid='.(! empty($obj->{$rowidcol})?$obj->{$rowidcol}:(! empty($obj->code)?$obj->code:'')).'&code='.(! empty($obj->code)?urlencode($obj->code):''); if ($param) $url .= '&'.$param; $url.='&'; From 66542b63675e7f608a23306b911fafd89d5fc0d9 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 10 Feb 2017 10:43:54 +0100 Subject: [PATCH 153/170] Fix translation and link to direct debit payment --- htdocs/compta/facture.php | 34 +++++++++++++++++------------ htdocs/langs/en_US/bills.lang | 4 ++-- htdocs/langs/en_US/withdrawals.lang | 4 ++-- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index f6aebb09703..19eb19ad00f 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -3939,7 +3939,7 @@ else if ($id > 0 || ! empty($ref)) } // Send by mail - if (($object->statut == 1 || $object->statut == 2) || ! empty($conf->global->FACTURE_SENDBYEMAIL_FOR_ALL_STATUS)) { + if (($object->statut == Facture::STATUS_VALIDATED || $object->statut == Facture::STATUS_CLOSED) || ! empty($conf->global->FACTURE_SENDBYEMAIL_FOR_ALL_STATUS)) { if ($objectidnext) { print '

'; } else { @@ -3950,21 +3950,27 @@ else if ($id > 0 || ! empty($ref)) } } - // deprecated. Useless because now we can use templates - if (! empty($conf->global->FACTURE_SHOW_SEND_REMINDER)) // For backward compatibility + // Request a direct debit order + if ($object->statut > Facture::STATUS_DRAFT && $object->paye == 0 && $num == 0) { - if (($object->statut == 1 || $object->statut == 2) && $resteapayer > 0) { - if ($objectidnext) { - print '
' . $langs->trans('SendRemindByMail') . '
'; - } else { - if (empty($conf->global->MAIN_USE_ADVANCED_PERMS) || $user->rights->facture->invoice_advance->send) { - print '
'; - } else - print ''; - } - } + if ($resteapayer > 0) + { + if ($user->rights->prelevement->bons->creer) + { + $langs->load("withdrawals"); + print ''.$langs->trans("MakeWithdrawRequest").''; + } + else + { + //print ''.$langs->trans("MakeWithdrawRequest").''; + } + } + else + { + //print ''.$langs->trans("MakeWithdrawRequest").''; + } } - + // Create payment if ($object->type != Facture::TYPE_CREDIT_NOTE && $object->statut == 1 && $object->paye == 0 && $user->rights->facture->paiement) { if ($objectidnext) { diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index 548bc370004..0e591ad880a 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -102,8 +102,8 @@ SearchACustomerInvoice=Search for a customer invoice SearchASupplierInvoice=Search for a supplier invoice CancelBill=Cancel an invoice SendRemindByMail=Send reminder by EMail -DoPayment=Do payment -DoPaymentBack=Do payment back +DoPayment=Enter payment +DoPaymentBack=Enter refund ConvertToReduc=Convert into future discount EnterPaymentReceivedFromCustomer=Enter payment received from customer EnterPaymentDueToCustomer=Make payment due to customer diff --git a/htdocs/langs/en_US/withdrawals.lang b/htdocs/langs/en_US/withdrawals.lang index a0706fe11a9..1e7f42c2f51 100644 --- a/htdocs/langs/en_US/withdrawals.lang +++ b/htdocs/langs/en_US/withdrawals.lang @@ -76,8 +76,8 @@ RUM=UMR RUMLong=Unique Mandate Reference RUMWillBeGenerated=UMR number will be generated once bank account information are saved WithdrawMode=Direct debit mode (FRST or RECUR) -WithdrawRequestAmount=Withdraw request amount: -WithdrawRequestErrorNilAmount=Unable to create withdraw request for nil amount. +WithdrawRequestAmount=Amount of Direct debit request: +WithdrawRequestErrorNilAmount=Unable to create direct debit request for empty amount. SepaMandate=SEPA Direct Debit Mandate SepaMandateShort=SEPA Mandate PleaseReturnMandate=Please return this mandate form by email to %s or by mail to From d461c0dfa4581cbecdd1664f030a0bf0c360a676 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 10 Feb 2017 10:47:19 +0100 Subject: [PATCH 154/170] Fix left align --- htdocs/compta/prelevement/create.php | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/htdocs/compta/prelevement/create.php b/htdocs/compta/prelevement/create.php index 8a6f4bb7ecc..a0b8e67365f 100644 --- a/htdocs/compta/prelevement/create.php +++ b/htdocs/compta/prelevement/create.php @@ -124,24 +124,16 @@ if ($nb < 0 || $nb1 < 0 || $nb11 < 0) print '
#'.$file['filename'].''.$file['expectedmd5'].''.$file['md5'].''.dol_print_size(dol_filesize(DOL_DOCUMENT_ROOT.'/'.$file['filename'])).''.dol_print_size($size).''.dol_print_date(dol_filemtime(DOL_DOCUMENT_ROOT.'/'.$file['filename']),'dayhour').'
'.$langs->trans("Total").''.dol_print_size($totalsize).'
'.\$langs->trans(\"Field".$prop['field']."\").'".$prop['field'].".'\">
".\$langs->trans("Field%s")."%s."\">
'.\$langs->trans(\"Field".$prop['field']."\").'\$object->".$prop['field']."
'.\$langs->trans(\"Field%s\").''.\$object->%s.'
'; print ''; -print ''; print ''; -print ''; print ''; -//print ''; - -//print ''; - print '
'.$langs->trans("NbOfInvoiceToWithdraw").''; +print ''; print $nb; print '
'.$langs->trans("AmountToWithdraw").''; +print ''; print price($pricetowithdraw); print '
'.$langs->trans("NbOfInvoiceToWithdraw").' + '.$langs->trans("ThirdPartyBankCode").'='.$conf->global->PRELEVEMENT_CODE_BANQUE.''; -//print $nb1; -//print '
'.$langs->trans("NbOfInvoiceToWithdrawWithInfo").''; -//print $nb11; -//print '
'; print '
'; @@ -158,8 +150,6 @@ else { print ''.$langs->trans("CreateAll")."\n"; } - //if ($nb11) print ''.$langs->trans("CreateBanque")."\n"; - //if ($nb1) print ''.$langs->trans("CreateGuichet")."\n"; print "
\n"; print '
'; @@ -247,7 +237,7 @@ else /* - * List of last withdraws + * List of latest withdraws */ $limit=5; From 44ab6ad9b7af0c6fa327dd846f1c90ca5b899d9c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 10 Feb 2017 17:05:45 +0100 Subject: [PATCH 155/170] NEW Can control constants values into file integrity checker --- build/generate_filelist_xml.php | 65 ++++++++++++++++++++++--------- htdocs/admin/system/filecheck.php | 46 ++++++++++++++++++++++ htdocs/langs/en_US/admin.lang | 1 + htdocs/langs/en_US/main.lang | 2 + 4 files changed, 95 insertions(+), 19 deletions(-) diff --git a/build/generate_filelist_xml.php b/build/generate_filelist_xml.php index 01809439bf1..36ea0e9542b 100755 --- a/build/generate_filelist_xml.php +++ b/build/generate_filelist_xml.php @@ -1,6 +1,6 @@ #!/usr/bin/env php +/* Copyright (C) 2015-2017 Laurent Destailleur * * 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 @@ -40,21 +40,33 @@ require_once(DOL_DOCUMENT_ROOT."/core/lib/files.lib.php"); * Main */ +$includeconstants=array(); + if (empty($argv[1])) { - print "Usage: ".$script_file." release=x.y.z[-...] [includecustom=1]\n"; + print "Usage: ".$script_file." release=x.y.z[-...] [includecustom=1] [includeconstant=MY_CONF_NAME:value]\n"; + print "Example: ".$script_file." release=6.0.0 includecustom=1 includeconstant=INVOICE_CAN_ALWAYS_BE_REMOVED:0 includeconstant=MAILING_NO_USING_PHPMAIL:1\n"; exit -1; } -parse_str($argv[1]); -if (! empty($argv[2])) parse_str($argv[2]); +$i=0; +while ($i < $argc) +{ + if (! empty($argv[$i])) parse_str($argv[$i]); + if (preg_match('/includeconstant=/',$argv[$i])) + { + $tmp=explode(':', $includeconstant); + if (count($tmp) == 2) $includeconstants[$tmp[0]] = $tmp[1]; + } + $i++; +} -if (empty($includecustom)) +if (empty($includecustom)) { $includecustom=0; - + if (DOL_VERSION != $release) { - print 'Error: When parameter "includecustom" is not set, version declared into filefunc.in.php ('.DOL_VERSION.') must be exact same value than "release" parmater.'."\n"; + print 'Error: When parameter "includecustom" is not set, version declared into filefunc.in.php ('.DOL_VERSION.') must be exact same value than "release" parameter ('.$release.')'."\n"; print "Usage: ".$script_file." release=x.y.z[-...] [includecustom=1]\n"; exit -1; } @@ -69,22 +81,37 @@ else } } -print "Version: ".$release."\n"; -print "Include custom: ".$includecustom."\n"; +print "Release : ".$release."\n"; +print "Include custom : ".$includecustom."\n"; +print "Include constants: "; +foreach ($includeconstants as $constname => $constvalue) +{ + print $constname.'='.$constvalue." "; +} +print "\n"; //$outputfile=dirname(__FILE__).'/../htdocs/install/filelist-'.$release.'.xml'; $outputdir=dirname(__FILE__).'/../htdocs/install'; print 'Delete current files '.$outputdir.'/filelist-'.$release.'.xml'."\n"; dol_delete_file($outputdir.'/filelist-'.$release.'.xml',0,1,1); +$checksumconcat=array(); + $outputfile=$outputdir.'/filelist-'.$release.'.xml'; $fp = fopen($outputfile,'w'); fputs($fp, ''."\n"); fputs($fp, ''."\n"); -fputs($fp, ''."\n"); +fputs($fp, ''."\n"); +foreach($includeconstants as $constname => $constvalue) +{ + $valueforchecksum=(empty($constvalue)?'0':$constvalue); + $checksumconcat[]=$valueforchecksum; + fputs($fp, ' '.$valueforchecksum.''."\n"); +} +fputs($fp, ''."\n"); -$checksumconcat=array(); +fputs($fp, ''."\n"); // TODO Replace RecursiveDirectoryIterator with dol_dir_list $dir_iterator1 = new RecursiveDirectoryIterator(dirname(__FILE__).'/../htdocs/'); @@ -97,18 +124,18 @@ foreach ($files as $file) { $newdir = str_replace(dirname(__FILE__).'/../htdocs', '', dirname($file)); if ($newdir!=$dir) { if ($needtoclose) - fputs($fp, ''."\n"); - fputs($fp, ''."\n"); + fputs($fp, ' '."\n"); + fputs($fp, ' '."\n"); $dir = $newdir; $needtoclose=1; } if (filetype($file)=="file") { $md5=md5_file($file); $checksumconcat[]=$md5; - fputs($fp, ''.$md5.''."\n"); + fputs($fp, ' '.$md5.''."\n"); } } -fputs($fp, ''."\n"); +fputs($fp, ' '."\n"); fputs($fp, ''."\n"); asort($checksumconcat); // Sort list of checksum @@ -133,18 +160,18 @@ foreach ($files as $file) { $newdir = str_replace(dirname(__FILE__).'/../scripts', '', dirname($file)); if ($newdir!=$dir) { if ($needtoclose) - fputs($fp, ''."\n"); - fputs($fp, ''."\n"); + fputs($fp, ' '."\n"); + fputs($fp, ' '."\n"); $dir = $newdir; $needtoclose=1; } if (filetype($file)=="file") { $md5=md5_file($file); $checksumconcat[]=$md5; - fputs($fp, ''.$md5.''."\n"); + fputs($fp, ' '.$md5.''."\n"); } } -fputs($fp, ''."\n"); +fputs($fp, ' '."\n"); fputs($fp, ''."\n"); asort($checksumconcat); // Sort list of checksum diff --git a/htdocs/admin/system/filecheck.php b/htdocs/admin/system/filecheck.php index 6499f16f76c..498e1ec54f0 100644 --- a/htdocs/admin/system/filecheck.php +++ b/htdocs/admin/system/filecheck.php @@ -160,6 +160,52 @@ if ($xml) $file_list = array(); $out = ''; + // Forced constants + if (is_object($xml->dolibarr_constants[0])) + { + $out.=load_fiche_titre($langs->trans("ForcedConstants")); + + $out.=''; + $out.=''; + $out.=''; + $out.=''; + $out.=''; + $out.=''; + $out.=''."\n"; + $var = true; + + $i = 0; + foreach ($xml->dolibarr_constants[0]->constant as $constant) // $constant is a simpleXMLElement + { + $constname=$constant['name']; + $constvalue=(string) $constant; + $constvalue = (empty($constvalue)?'0':$constvalue); + // Value found + $value=''; + if ($constname && $conf->global->$constname != '') $value=$conf->global->$constname; + $valueforchecksum=(empty($value)?'0':$value); + + $checksumconcat[]=$valueforchecksum; + + $i++; + $var = !$var; + $out.=''; + $out.='' . "\n"; + $out.='' . "\n"; + $out.='' . "\n"; + $out.='' . "\n"; + $out.="\n"; + } + + if ($i==0) + { + $out.=''; + } + $out.='
#' . $langs->trans("Constant") . '' . $langs->trans("ExpectedValue") . '' . $langs->trans("Value") . '
'.$i.''.$constname.''.$constvalue.''.$valueforchecksum.'
'.$langs->trans("None").'
'; + + $out.='
'; + } + // Scan htdocs if (is_object($xml->dolibarr_htdocs_dir[0])) { diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 2597a995043..987f02fe314 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -1611,6 +1611,7 @@ FixTZ=TimeZone fix FillFixTZOnlyIfRequired=Example: +2 (fill only if problem experienced) ExpectedChecksum=Expected Checksum CurrentChecksum=Current Checksum +ForcedConstants=Required constant values MailToSendProposal=To send customer proposal MailToSendOrder=To send customer order MailToSendInvoice=To send customer invoice diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index 603aff0000f..106b0a9f240 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -599,6 +599,8 @@ SessionName=Session name Method=Method Receive=Receive CompleteOrNoMoreReceptionExpected=Complete or nothing more expected +ExpectedValue=Expected Value +CurrentValue=Current Value PartialWoman=Partial TotalWoman=Total NeverReceived=Never received From 97eebe164cc83f1fe6bd93e53d9e521c37736e9e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 10 Feb 2017 17:09:56 +0100 Subject: [PATCH 156/170] Better translation --- htdocs/langs/en_US/admin.lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 987f02fe314..2783ea52594 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -9,7 +9,7 @@ VersionDevelopment=Development VersionUnknown=Unknown VersionRecommanded=Recommended FileCheck=Files integrity checker -FileCheckDesc=This tool allows you to check the integrity of files of your application, comparing each files with the official ones. You can use this tool to detect if some files were modified by a hacker for example. +FileCheckDesc=This tool allows you to check the integrity of files and setup of your application, comparing each files with the official ones. Value of some setup constants may also be checked. You can use this tool to detect if some files were modified by a hacker for example. FileIntegrityIsStrictlyConformedWithReference=Files integrity is strictly conformed with the reference. FileIntegrityIsOkButFilesWereAdded=Files integrity check has passed, however some new files were added. FileIntegritySomeFilesWereRemovedOrModified=Files integrity check has failed. Some files were modified, removed or added. From d463511cffc222dc86f2246bd3e7945c65322923 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 10 Feb 2017 17:24:47 +0100 Subject: [PATCH 157/170] Fix: no way to know type of invoice. --- htdocs/core/modules/modFacture.class.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/htdocs/core/modules/modFacture.class.php b/htdocs/core/modules/modFacture.class.php index 9ba9fae7322..a83f581a276 100644 --- a/htdocs/core/modules/modFacture.class.php +++ b/htdocs/core/modules/modFacture.class.php @@ -192,11 +192,11 @@ class modFacture extends DolibarrModules $this->export_label[$r]='CustomersInvoicesAndInvoiceLines'; // Translation key (used only if key ExportDataset_xxx_z not found) $this->export_icon[$r]='bill'; $this->export_permission[$r]=array(array("facture","facture","export","other")); - $this->export_fields_array[$r]=array('s.rowid'=>"IdCompany",'s.nom'=>'CompanyName','s.address'=>'Address','s.zip'=>'Zip','s.town'=>'Town','c.code'=>'CountryCode','s.phone'=>'Phone','s.siren'=>'ProfId1','s.siret'=>'ProfId2','s.ape'=>'ProfId3','s.idprof4'=>'ProfId4','s.code_compta'=>'CustomerAccountancyCode','s.code_compta_fournisseur'=>'SupplierAccountancyCode','s.tva_intra'=>'VATIntra','f.rowid'=>"InvoiceId",'f.facnumber'=>"InvoiceRef",'f.datec'=>"InvoiceDateCreation",'f.datef'=>"DateInvoice",'f.date_lim_reglement'=>"DateDue",'f.total'=>"TotalHT",'f.total_ttc'=>"TotalTTC",'f.tva'=>"TotalVAT",'f.paye'=>"InvoicePaid",'f.fk_statut'=>'InvoiceStatus','f.note_private'=>"NotePrivate",'f.note_public'=>"NotePublic",'f.fk_user_author'=>'CreatedById','uc.login'=>'CreatedByLogin','f.fk_user_valid'=>'ValidatedById','uv.login'=>'ValidatedByLogin', 'pj.ref'=>'ProjectRef', 'fd.rowid'=>'LineId','fd.description'=>"LineDescription",'fd.subprice'=>"LineUnitPrice",'fd.tva_tx'=>"LineVATRate",'fd.qty'=>"LineQty",'fd.total_ht'=>"LineTotalHT",'fd.total_tva'=>"LineTotalVAT",'fd.total_ttc'=>"LineTotalTTC",'fd.date_start'=>"DateStart",'fd.date_end'=>"DateEnd",'fd.special_code'=>'SpecialCode','fd.product_type'=>"TypeOfLineServiceOrProduct",'fd.fk_product'=>'ProductId','p.ref'=>'ProductRef','p.label'=>'ProductLabel','p.accountancy_code_sell'=>'ProductAccountancySellCode'); + $this->export_fields_array[$r]=array('s.rowid'=>"IdCompany",'s.nom'=>'CompanyName','s.address'=>'Address','s.zip'=>'Zip','s.town'=>'Town','c.code'=>'CountryCode','s.phone'=>'Phone','s.siren'=>'ProfId1','s.siret'=>'ProfId2','s.ape'=>'ProfId3','s.idprof4'=>'ProfId4','s.code_compta'=>'CustomerAccountancyCode','s.code_compta_fournisseur'=>'SupplierAccountancyCode','s.tva_intra'=>'VATIntra','f.rowid'=>"InvoiceId",'f.facnumber'=>"InvoiceRef",'f.type'=>"Type",'f.datec'=>"InvoiceDateCreation",'f.datef'=>"DateInvoice",'f.date_lim_reglement'=>"DateDue",'f.total'=>"TotalHT",'f.total_ttc'=>"TotalTTC",'f.tva'=>"TotalVAT",'f.paye'=>"InvoicePaid",'f.fk_statut'=>'InvoiceStatus','f.note_private'=>"NotePrivate",'f.note_public'=>"NotePublic",'f.fk_user_author'=>'CreatedById','uc.login'=>'CreatedByLogin','f.fk_user_valid'=>'ValidatedById','uv.login'=>'ValidatedByLogin', 'pj.ref'=>'ProjectRef', 'fd.rowid'=>'LineId','fd.description'=>"LineDescription",'fd.subprice'=>"LineUnitPrice",'fd.tva_tx'=>"LineVATRate",'fd.qty'=>"LineQty",'fd.total_ht'=>"LineTotalHT",'fd.total_tva'=>"LineTotalVAT",'fd.total_ttc'=>"LineTotalTTC",'fd.date_start'=>"DateStart",'fd.date_end'=>"DateEnd",'fd.special_code'=>'SpecialCode','fd.product_type'=>"TypeOfLineServiceOrProduct",'fd.fk_product'=>'ProductId','p.ref'=>'ProductRef','p.label'=>'ProductLabel','p.accountancy_code_sell'=>'ProductAccountancySellCode'); //Add 'fd.label'=>"Label" to export_fields_array if you use it. Not used by dolibarr currently. //$this->export_TypeFields_array[$r]=array('s.rowid'=>"List:societe:nom",'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.price'=>"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.product_type'=>"Numeric",'fd.fk_product'=>'List:product:label','p.ref'=>'Text','p.label'=>'Text'); - $this->export_TypeFields_array[$r]=array('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", 'pj.ref'=>'Text', 'fd.label'=>'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','c.code'=>'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','s.tva_intra'=>'company','f.rowid'=>"invoice",'f.facnumber'=>"invoice",'f.datec'=>"invoice",'f.datef'=>"invoice",'f.date_lim_reglement'=>"invoice",'f.total'=>"invoice",'f.total_ttc'=>"invoice",'f.tva'=>"invoice",'f.paye'=>"invoice",'f.fk_statut'=>'invoice','f.note_private'=>"invoice",'f.note_public'=>"invoice", 'pj.ref'=>'project', 'fd.rowid'=>'invoice_line','fd.label'=>"invoice_line",'fd.description'=>"invoice_line",'fd.subprice'=>"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.special_code'=>'invoice_line','fd.product_type'=>'invoice_line','fd.fk_product'=>'product','p.ref'=>'product','p.label'=>'product','p.accountancy_code_sell'=>'product','f.fk_user_author'=>'user','uc.login'=>'user','f.fk_user_valid'=>'user','uv.login'=>'user'); + $this->export_TypeFields_array[$r]=array('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.type'=>"Numeric",'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", 'pj.ref'=>'Text', 'fd.label'=>'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','c.code'=>'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','s.tva_intra'=>'company','f.rowid'=>"invoice",'f.facnumber'=>"invoice",'f.type'=>"invoice",'f.datec'=>"invoice",'f.datef'=>"invoice",'f.date_lim_reglement'=>"invoice",'f.total'=>"invoice",'f.total_ttc'=>"invoice",'f.tva'=>"invoice",'f.paye'=>"invoice",'f.fk_statut'=>'invoice','f.note_private'=>"invoice",'f.note_public'=>"invoice", 'pj.ref'=>'project', 'fd.rowid'=>'invoice_line','fd.label'=>"invoice_line",'fd.description'=>"invoice_line",'fd.subprice'=>"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.special_code'=>'invoice_line','fd.product_type'=>'invoice_line','fd.fk_product'=>'product','p.ref'=>'product','p.label'=>'product','p.accountancy_code_sell'=>'product','f.fk_user_author'=>'user','uc.login'=>'user','f.fk_user_valid'=>'user','uv.login'=>'user'); $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 $keyforselect='facture'; $keyforelement='invoice'; $keyforaliasextra='extra'; include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; @@ -224,10 +224,10 @@ class modFacture extends DolibarrModules $this->export_label[$r]='CustomersInvoicesAndPayments'; // Translation key (used only if key ExportDataset_xxx_z not found) $this->export_icon[$r]='bill'; $this->export_permission[$r]=array(array("facture","facture","export")); - $this->export_fields_array[$r]=array('s.rowid'=>"IdCompany",'s.nom'=>'CompanyName','s.address'=>'Address','s.zip'=>'Zip','s.town'=>'Town','c.code'=>'CountryCode','s.phone'=>'Phone','s.siren'=>'ProfId1','s.siret'=>'ProfId2','s.ape'=>'ProfId3','s.idprof4'=>'ProfId4','s.code_compta'=>'CustomerAccountancyCode','s.code_compta_fournisseur'=>'SupplierAccountancyCode','s.tva_intra'=>'VATIntra','f.rowid'=>"InvoiceId",'f.facnumber'=>"InvoiceRef",'f.datec'=>"InvoiceDateCreation",'f.datef'=>"DateInvoice",'f.date_lim_reglement'=>"DateDue",'f.total'=>"TotalHT",'f.total_ttc'=>"TotalTTC",'f.tva'=>"TotalVAT",'f.paye'=>"InvoicePaid",'f.fk_statut'=>'InvoiceStatus','f.note_private'=>"NotePrivate",'f.note_public'=>"NotePublic",'f.fk_user_author'=>'CreatedById','uc.login'=>'CreatedByLogin','f.fk_user_valid'=>'ValidatedById','uv.login'=>'ValidatedByLogin','pj.ref'=>'ProjectRef','p.rowid'=>'PaymentId','p.ref'=>'PaymentRef','p.amount'=>'AmountPayment','pf.amount'=>'AmountPaymentDistributedOnInvoice','p.datep'=>'DatePayment','p.num_paiement'=>'PaymentNumber','pt.code'=>'IdPaymentMode','pt.libelle'=>'LabelPaymentMode','p.note'=>'PaymentNote','p.fk_bank'=>'IdTransaction','ba.ref'=>'AccountRef'); + $this->export_fields_array[$r]=array('s.rowid'=>"IdCompany",'s.nom'=>'CompanyName','s.address'=>'Address','s.zip'=>'Zip','s.town'=>'Town','c.code'=>'CountryCode','s.phone'=>'Phone','s.siren'=>'ProfId1','s.siret'=>'ProfId2','s.ape'=>'ProfId3','s.idprof4'=>'ProfId4','s.code_compta'=>'CustomerAccountancyCode','s.code_compta_fournisseur'=>'SupplierAccountancyCode','s.tva_intra'=>'VATIntra','f.rowid'=>"InvoiceId",'f.facnumber'=>"InvoiceRef",'f.type'=>"Type",'f.datec'=>"InvoiceDateCreation",'f.datef'=>"DateInvoice",'f.date_lim_reglement'=>"DateDue",'f.total'=>"TotalHT",'f.total_ttc'=>"TotalTTC",'f.tva'=>"TotalVAT",'f.paye'=>"InvoicePaid",'f.fk_statut'=>'InvoiceStatus','f.note_private'=>"NotePrivate",'f.note_public'=>"NotePublic",'f.fk_user_author'=>'CreatedById','uc.login'=>'CreatedByLogin','f.fk_user_valid'=>'ValidatedById','uv.login'=>'ValidatedByLogin','pj.ref'=>'ProjectRef','p.rowid'=>'PaymentId','p.ref'=>'PaymentRef','p.amount'=>'AmountPayment','pf.amount'=>'AmountPaymentDistributedOnInvoice','p.datep'=>'DatePayment','p.num_paiement'=>'PaymentNumber','pt.code'=>'IdPaymentMode','pt.libelle'=>'LabelPaymentMode','p.note'=>'PaymentNote','p.fk_bank'=>'IdTransaction','ba.ref'=>'AccountRef'); //$this->export_TypeFields_array[$r]=array('s.rowid'=>"List:societe:nom",'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.rowid'=>"List:facture:facnumber",'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",'pf.amount'=>'Numeric','p.datep'=>'Date','p.num_paiement'=>'Numeric','p.fk_bank'=>'Numeric'); - $this->export_TypeFields_array[$r]=array('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.rowid'=>"Numeric",'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",'pj.ref'=>'Text','p.amount'=>'Numeric','pf.amount'=>'Numeric','p.rowid'=>'Numeric','p.ref'=>'Text','p.datep'=>'Date','p.num_paiement'=>'Numeric','p.fk_bank'=>'Numeric','p.note'=>'Text','pt.code'=>'Text','pt.libelle'=>'text','ba.ref'=>'Text'); - $this->export_entities_array[$r]=array('s.rowid'=>"company",'s.nom'=>'company','s.address'=>'company','s.zip'=>'company','s.town'=>'company','c.code'=>'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','s.tva_intra'=>'company','f.rowid'=>"invoice",'f.facnumber'=>"invoice",'f.datec'=>"invoice",'f.datef'=>"invoice",'f.date_lim_reglement'=>"invoice",'f.total'=>"invoice",'f.total_ttc'=>"invoice",'f.tva'=>"invoice",'f.paye'=>"invoice",'f.fk_statut'=>'invoice','f.note_private'=>"invoice",'f.note_public'=>"invoice",'pj.ref'=>'project','p.rowid'=>'payment','p.ref'=>'payment','p.amount'=>'payment','pf.amount'=>'payment','p.datep'=>'payment','p.num_paiement'=>'payment','pt.code'=>'payment','pt.libelle'=>'payment','p.note'=>'payment','f.fk_user_author'=>'user','uc.login'=>'user','f.fk_user_valid'=>'user','uv.login'=>'user','p.fk_bank'=>'account','ba.ref'=>'account'); + $this->export_TypeFields_array[$r]=array('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.rowid'=>"Numeric",'f.facnumber'=>"Text",'f.type'=>"Numeric",'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",'pj.ref'=>'Text','p.amount'=>'Numeric','pf.amount'=>'Numeric','p.rowid'=>'Numeric','p.ref'=>'Text','p.datep'=>'Date','p.num_paiement'=>'Numeric','p.fk_bank'=>'Numeric','p.note'=>'Text','pt.code'=>'Text','pt.libelle'=>'text','ba.ref'=>'Text'); + $this->export_entities_array[$r]=array('s.rowid'=>"company",'s.nom'=>'company','s.address'=>'company','s.zip'=>'company','s.town'=>'company','c.code'=>'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','s.tva_intra'=>'company','f.rowid'=>"invoice",'f.facnumber'=>"invoice",'f.type'=>"invoice",'f.datec'=>"invoice",'f.datef'=>"invoice",'f.date_lim_reglement'=>"invoice",'f.total'=>"invoice",'f.total_ttc'=>"invoice",'f.tva'=>"invoice",'f.paye'=>"invoice",'f.fk_statut'=>'invoice','f.note_private'=>"invoice",'f.note_public'=>"invoice",'pj.ref'=>'project','p.rowid'=>'payment','p.ref'=>'payment','p.amount'=>'payment','pf.amount'=>'payment','p.datep'=>'payment','p.num_paiement'=>'payment','pt.code'=>'payment','pt.libelle'=>'payment','p.note'=>'payment','f.fk_user_author'=>'user','uc.login'=>'user','f.fk_user_valid'=>'user','uv.login'=>'user','p.fk_bank'=>'account','ba.ref'=>'account'); $this->export_dependencies_array[$r]=array('payment'=>'p.rowid'); // To add unique key if we ask a field of a child to avoid the DISTINCT to discard them $keyforselect='facture'; $keyforelement='invoice'; $keyforaliasextra='extra'; include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; From f6a8aacc8d38578db214a68998834f59e8cd9b4d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 10 Feb 2017 18:42:59 +0100 Subject: [PATCH 158/170] Fix: deployement of a module --- htdocs/admin/tools/update.php | 30 +++++++++++++++++++----------- htdocs/langs/en_US/admin.lang | 5 +++-- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/htdocs/admin/tools/update.php b/htdocs/admin/tools/update.php index 5cebb1dac4e..912016b498c 100644 --- a/htdocs/admin/tools/update.php +++ b/htdocs/admin/tools/update.php @@ -143,7 +143,7 @@ if ($action=='install') if (! $error) { - setEventMessages($langs->trans("SetupIsReadyForUse"), null, 'mesgs'); + setEventMessages($langs->trans("SetupIsReadyForUse", DOL_URL_ROOT.'/admin/modules.php?mainmenu=home', $langs->transnoentitiesnoconv("Home").' - '.$langs->transnoentitiesnoconv("Setup").' - '.$langs->transnoentitiesnoconv("Modules")), null, 'warnings'); } } @@ -170,6 +170,8 @@ print $langs->trans("CurrentVersion").' : '.DOL_VERSION.'
'; if (function_exists('curl_init')) { + $conf->global->MAIN_USE_RESPONSE_TIMEOUT = 10; + $result = getURLContent('http://sourceforge.net/projects/dolibarr/rss'); //var_dump($result['content']); $sfurl = simplexml_load_string($result['content']); @@ -281,26 +283,32 @@ if ($allowfromweb < 1) if ($allowfromweb >= 0) { - if ($allowfromweb == 1) print $langs->trans("ThisIsProcessToFollow").'
'; - else print $langs->trans("ThisIsAlternativeProcessToFollow").'
'; - print ''.$langs->trans("StepNb",1).': '; - print $langs->trans("FindPackageFromWebSite",$fullurl).'
'; - print ''.$langs->trans("StepNb",2).': '; - print $langs->trans("DownloadPackageFromWebSite",$fullurl).'
'; - print ''.$langs->trans("StepNb",3).': '; + if ($allowfromweb == 1) + { + //print $langs->trans("ThisIsProcessToFollow").'
'; + } + else + { + print $langs->trans("ThisIsAlternativeProcessToFollow").'
'; + print ''.$langs->trans("StepNb",1).': '; + print $langs->trans("FindPackageFromWebSite",$fullurl).'
'; + print ''.$langs->trans("StepNb",2).': '; + print $langs->trans("DownloadPackageFromWebSite",$fullurl).'
'; + print ''.$langs->trans("StepNb",3).': '; + } if ($allowfromweb == 1) { - print $langs->trans("UnpackPackageInDolibarrRoot",$dirins).'
'; + print $langs->trans("UnpackPackageInModulesRoot",$dirins).'
'; print '
'; print ''; print $langs->trans("YouCanSubmitFile").' '; - print ''; + print ''; print '
'; } else { - print $langs->trans("UnpackPackageInDolibarrRoot",$dirins).'
'; + print $langs->trans("UnpackPackageInModulesRoot",$dirins).'
'; print ''.$langs->trans("StepNb",4).': '; print $langs->trans("SetupIsReadyForUse").'
'; } diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 45f55a20eba..93f76c49187 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -284,8 +284,9 @@ ThisIsAlternativeProcessToFollow=This is an alternative setup to process: StepNb=Step %s FindPackageFromWebSite=Find a package that provides feature you want (for example on official web site %s). DownloadPackageFromWebSite=Download package (for example from official web site %s). -UnpackPackageInDolibarrRoot=Unpack package file into Dolibarr server directory dedicated to external modules: %s -SetupIsReadyForUse=Install is finished and Dolibarr is ready to use with this new component. +UnpackPackageInDolibarrRoot=Unpack package files into Dolibarr server directory dedicated to Dolibarr: %s +UnpackPackageInModulesRoot=Unpack package files into Dolibarr server directory dedicated to modules: %s +SetupIsReadyForUse=Module deployement is finished. You must however enable and setup the module in your application by going on the page to setup modules: %s. NotExistsDirect=The alternative root directory is not defined.
InfDirAlt=Since version 3 it is possible to define an alternative root directory.This allows you to store, same place, plug-ins and custom templates.
Just create a directory at the root of Dolibarr (eg: custom).
InfDirExample=
Then declare it in the file conf.php
$dolibarr_main_url_root_alt='http://myserver/custom'
$dolibarr_main_document_root_alt='/path/of/dolibarr/htdocs/custom'
*These lines are commented with "#", to uncomment only remove the character. From b9db7eba1b97943295c6ce639b184431d85e7ac0 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 10 Feb 2017 19:14:58 +0100 Subject: [PATCH 159/170] Fix: deployement of a module --- htdocs/langs/en_US/admin.lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 93f76c49187..2858ca0c8d5 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -286,7 +286,7 @@ FindPackageFromWebSite=Find a package that provides feature you want (for exampl DownloadPackageFromWebSite=Download package (for example from official web site %s). UnpackPackageInDolibarrRoot=Unpack package files into Dolibarr server directory dedicated to Dolibarr: %s UnpackPackageInModulesRoot=Unpack package files into Dolibarr server directory dedicated to modules: %s -SetupIsReadyForUse=Module deployement is finished. You must however enable and setup the module in your application by going on the page to setup modules: %s. +oiSetupIsReadyForUse=Module deployement is finished. You must however enable and setup the module in your application by going on the page to setup modules: %s. NotExistsDirect=The alternative root directory is not defined.
InfDirAlt=Since version 3 it is possible to define an alternative root directory.This allows you to store, same place, plug-ins and custom templates.
Just create a directory at the root of Dolibarr (eg: custom).
InfDirExample=
Then declare it in the file conf.php
$dolibarr_main_url_root_alt='http://myserver/custom'
$dolibarr_main_document_root_alt='/path/of/dolibarr/htdocs/custom'
*These lines are commented with "#", to uncomment only remove the character. From f5569ee890e6e01580969639b24c7b2641ff8566 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 11 Feb 2017 13:50:30 +0100 Subject: [PATCH 160/170] Add tasktime_task_duration_sec tag in odt substitution --- .../core/modules/project/doc/doc_generic_project_odt.modules.php | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php b/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php index 2a236e6d62c..06b188496a5 100644 --- a/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php +++ b/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php @@ -274,6 +274,7 @@ class doc_generic_project_odt extends ModelePDFProjects return array( 'tasktime_rowid'=>$tasktime['rowid'], 'tasktime_task_date'=>dol_print_date($tasktime['task_date'],'day'), + 'tasktime_task_duration_sec'=>$tasktime['task_duration'], 'tasktime_task_duration'=>convertSecondToTime($tasktime['task_duration'],'all'), 'tasktime_note'=>$tasktime['note'], 'tasktime_fk_user'=>$tasktime['fk_user'], From 3fa19f84e31c7bd8b787425624feadc58ced87cd Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 11 Feb 2017 15:40:25 +0100 Subject: [PATCH 161/170] FIX the dolCopyDir fails if target dir does not exists. --- htdocs/core/lib/files.lib.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index 1f7974775b5..6bce1a443c5 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -515,13 +515,23 @@ function dolCopyDir($srcfile, $destfile, $newmask, $overwriteifexists) $result=0; - dol_syslog("files.lib.php::dolCopyr srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists); + dol_syslog("files.lib.php::dolCopyDir srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists); if (empty($srcfile) || empty($destfile)) return -1; $destexists=dol_is_dir($destfile); if (! $overwriteifexists && $destexists) return 0; - + + if (! $destexists) + { + // We must set mask just before creating dir, becaause it can be set differently by dol_copy + umask(0); + $dirmaskdec=octdec($newmask); + if (empty($newmask) && ! empty($conf->global->MAIN_UMASK)) $dirmaskdec=octdec($conf->global->MAIN_UMASK); + $dirmaskdec |= octdec('0200'); // Set w bit required to be able to create content for recursive subdirs files + dol_mkdir($destfile."/".$file, '', decoct($dirmaskdec)); + } + $srcfile=dol_osencode($srcfile); $destfile=dol_osencode($destfile); @@ -538,6 +548,7 @@ function dolCopyDir($srcfile, $destfile, $newmask, $overwriteifexists) { if (!is_dir($destfile."/".$file)) { + // We must set mask just before creating dir, becaause it can be set differently by dol_copy umask(0); $dirmaskdec=octdec($newmask); if (empty($newmask) && ! empty($conf->global->MAIN_UMASK)) $dirmaskdec=octdec($conf->global->MAIN_UMASK); From f34e0b1bdb1deebdfb85dc57970c9fc9794f68ee Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 11 Feb 2017 15:41:01 +0100 Subject: [PATCH 162/170] NEW Can deploy an external module from the module setup area. --- htdocs/admin/modules.php | 405 +++++++++++++++++++-------------- htdocs/admin/tools/update.php | 252 +++----------------- htdocs/langs/en_US/admin.lang | 22 +- htdocs/langs/en_US/errors.lang | 3 +- 4 files changed, 279 insertions(+), 403 deletions(-) diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php index b61e5a4a86e..e34861450eb 100644 --- a/htdocs/admin/modules.php +++ b/htdocs/admin/modules.php @@ -29,12 +29,15 @@ require '../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; -require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; $langs->load("errors"); $langs->load("admin"); -$mode=GETPOST('mode', 'alpha')?GETPOST('mode', 'alpha'):0; +$mode=GETPOST('mode', 'alpha'); +if (empty($mode)) $mode='common'; $action=GETPOST('action','alpha'); $value=GETPOST('value', 'alpha'); $page_y=GETPOST('page_y','int'); @@ -69,13 +72,14 @@ if ($search_status) $param.='&search_status='.urlencode($search_status); if ($search_nature) $param.='&search_nature='.urlencode($search_nature); if ($search_version) $param.='&search_version='.urlencode($search_version); +$dirins=DOL_DOCUMENT_ROOT.'/custom'; +$urldolibarrmodules='https://www.dolistore.com/'; /* * Actions */ - if (GETPOST('buttonreset')) { $search_keyword=''; @@ -84,6 +88,103 @@ if (GETPOST('buttonreset')) $search_version=''; } +if ($action=='install') +{ + $error=0; + + // $original_file should match format module_modulename-x.y[.z].zip + $original_file=basename($_FILES["fileinstall"]["name"]); + $newfile=$conf->admin->dir_temp.'/'.$original_file.'/'.$original_file; + + if (! $original_file) + { + $langs->load("Error"); + setEventMessages($langs->trans("ErrorModuleFileRequired"), null, 'warnings'); + $error++; + } + else + { + if (! preg_match('/\.zip/i',$original_file)) + { + $langs->load("errors"); + setEventMessages($langs->trans("ErrorFileMustBeADolibarrPackage",$original_file), null, 'errors'); + $error++; + } + } + + if (! $error) + { + if ($original_file) + { + @dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$original_file); + dol_mkdir($conf->admin->dir_temp.'/'.$original_file); + } + + $tmpdir=preg_replace('/\.zip$/','',$original_file).'.dir'; + if ($tmpdir) + { + @dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$tmpdir); + dol_mkdir($conf->admin->dir_temp.'/'.$tmpdir); + } + + $result=dol_move_uploaded_file($_FILES['fileinstall']['tmp_name'],$newfile,1,0,$_FILES['fileinstall']['error']); + if ($result > 0) + { + $result=dol_uncompress($newfile,$conf->admin->dir_temp.'/'.$tmpdir); + + if (! empty($result['error'])) + { + $langs->load("errors"); + setEventMessages($langs->trans($result['error'],$original_file), null, 'errors'); + $error++; + } + else + { + // Now we move the dir of the module + $modulename=preg_replace('/module_/', '', $original_file); + $modulename=preg_replace('/\-[\d]+\.[\d]+.*$/', '', $modulename); + // Search dir $modulename + $modulenamedir=$conf->admin->dir_temp.'/'.$tmpdir.'/'.$modulename; + //var_dump($modulenamedir); + if (! dol_is_dir($modulenamedir)) + { + $modulenamedir=$conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulename; + //var_dump($modulenamedir); + if (! dol_is_dir($modulenamedir)) + { + setEventMessages($langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat"), null, 'errors'); + $error++; + } + } + + if (! $error) + { + //var_dump($dirins); + @dol_delete_dir_recursive($dirins.'/'.$modulename); + dol_syslog("Uncompress of module file is a success. We copy it from ".$modulenamedir." into target dir ".$dirins.'/'.$modulename); + $result=dolCopyDir($modulenamedir, $dirins.'/'.$modulename, '0444', 1); + if ($result <= 0) + { + dol_syslog('Failed to call dolCopyDir result='.$result." with param ".$modulenamedir." and ".$dirins.'/'.$modulename, LOG_WARNING); + $langs->load("errors"); + setEventMessages($langs->trans("ErrorFailToCopyDir", $modulenamedir, $dirins.'/'.$modulename), null, 'errors'); + $error++; + } + } + } + } + else + { + $error++; + } + } + + if (! $error) + { + setEventMessages($langs->trans("SetupIsReadyForUse", DOL_URL_ROOT.'/admin/modules.php?mainmenu=home', $langs->transnoentitiesnoconv("Home").' - '.$langs->transnoentitiesnoconv("Setup").' - '.$langs->transnoentitiesnoconv("Modules")), null, 'warnings'); + } +} + if ($action == 'set' && $user->admin) { $resarray = activateModule($value); @@ -115,6 +216,13 @@ if ($action == 'reset' && $user->admin) * View */ +// Set dir where external modules are installed +if (! dol_is_dir($dirins)) +{ + dol_mkdir($dirins); +} +$dirins_ok=(dol_is_dir($dirins)); + $form = new Form($db); $help_url='EN:First_setup|FR:Premiers_paramétrages|ES:Primeras_configuraciones'; @@ -277,33 +385,35 @@ if ($nbofactivatedmodules <= 1) $moreinfo .= ' '.img_warning($langs->trans("YouM print load_fiche_titre($langs->trans("ModulesSetup"),$moreinfo,'title_setup'); // Start to show page -if (empty($mode)) $mode='common'; -if ($mode==='common') print $langs->trans("ModulesDesc")."
\n"; -if ($mode==='marketplace') print $langs->trans("ModulesMarketPlaceDesc")."
\n"; -if ($mode==='expdev') print $langs->trans("ModuleFamilyExperimental")."
\n"; +if ($mode=='common') print $langs->trans("ModulesDesc")."
\n"; +if ($mode=='marketplace') print $langs->trans("ModulesMarketPlaceDesc")."
\n"; +if ($mode=='deploy') print $langs->trans("ModulesDeployDesc", $langs->transnoentitiesnoconv("AvailableModules"))."
\n"; $h = 0; -$categidx='common'; // Main -$head[$h][0] = DOL_URL_ROOT."/admin/modules.php?mode=".$categidx; +$head[$h][0] = DOL_URL_ROOT."/admin/modules.php?mode=common"; $head[$h][1] = $langs->trans("AvailableModules"); $head[$h][2] = 'common'; $h++; -$categidx='marketplace'; -$head[$h][0] = DOL_URL_ROOT."/admin/modules.php?mode=".$categidx; +$head[$h][0] = DOL_URL_ROOT."/admin/modules.php?mode=marketplace"; $head[$h][1] = $langs->trans("ModulesMarketPlaces"); $head[$h][2] = 'marketplace'; $h++; +$head[$h][0] = DOL_URL_ROOT."/admin/modules.php?mode=deploy"; +$head[$h][1] = $langs->trans("AddExtensionThemeModuleOrOther"); +$head[$h][2] = 'deploy'; +$h++; + print "
\n"; $var=true; -if ($mode != 'marketplace') +if ($mode == 'common') { print '
'; @@ -351,7 +461,6 @@ if ($mode != 'marketplace') //print '
'; } - //dol_fiche_end(); print '

'; @@ -477,163 +586,6 @@ if ($mode != 'marketplace') $imginfo="info_black"; } - // Define text of description of module - /* - $text=''; - - if ($objMod->getDescLong()) $text.='
'.$objMod->getDesc().'

'.$objMod->getDescLong().'
'; - else $text.='
'.$objMod->getDesc().'

'; - - $text.='
'.$langs->trans("Version").': '.$version; - - $textexternal=''; - if ($objMod->isCoreOrExternalModule() == 'external') - { - $textexternal.='
'.$langs->trans("Origin").': '.$langs->trans("ExternalModule",$dirofmodule); - if ($objMod->editor_name != 'dolibarr') $textexternal.='
'.$langs->trans("Publisher").': '.(empty($objMod->editor_name)?$langs->trans("Unknown"):$objMod->editor_name); - if (! empty($objMod->editor_url) && ! preg_match('/dolibarr\.org/i',$objMod->editor_url)) $textexternal.='
'.$langs->trans("Url").': '.$objMod->editor_url; - $text.=$textexternal; - $text.='
'; - } - else - { - $text.='
'.$langs->trans("Origin").': '.$langs->trans("Core").'
'; - } - $text.='
'.$langs->trans("LastActivationDate").': '; - if (! empty($conf->global->$const_name)) $text.=dol_print_date($objMod->getLastActivationDate(), 'dayhour'); - else $text.=$langs->trans("Disabled"); - $text.='
'; - - $text.='
'.$langs->trans("AddRemoveTabs").': '; - if (isset($objMod->tabs) && is_array($objMod->tabs) && count($objMod->tabs)) - { - $i=0; - foreach($objMod->tabs as $val) - { - $tmp=explode(':',$val,3); - $text.=($i?', ':'').$tmp[0].':'.$tmp[1]; - $i++; - } - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddDictionaries").': '; - if (isset($objMod->dictionaries) && isset($objMod->dictionaries['tablib']) && is_array($objMod->dictionaries['tablib']) && count($objMod->dictionaries['tablib'])) - { - $i=0; - foreach($objMod->dictionaries['tablib'] as $val) - { - $text.=($i?', ':'').$val; - $i++; - } - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddBoxes").': '; - if (isset($objMod->boxes) && is_array($objMod->boxes) && count($objMod->boxes)) - { - $i=0; - foreach($objMod->boxes as $val) - { - $text.=($i?', ':'').($val['file']?$val['file']:$val[0]); - $i++; - } - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddModels").': '; - if (isset($objMod->module_parts) && isset($objMod->module_parts['models']) && $objMod->module_parts['models']) - { - $text.=$langs->trans("Yes"); - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddSubstitutions").': '; - if (isset($objMod->module_parts) && isset($objMod->module_parts['substitutions']) && $objMod->module_parts['substitutions']) - { - $text.=$langs->trans("Yes"); - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddSheduledJobs").': '; - if (isset($objMod->cronjobs) && is_array($objMod->cronjobs) && count($objMod->cronjobs)) - { - $i=0; - foreach($objMod->cronjobs as $val) - { - $text.=($i?', ':'').($val['label']); - $i++; - } - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddTriggers").': '; - if (isset($objMod->module_parts) && isset($objMod->module_parts['triggers']) && $objMod->module_parts['triggers']) - { - $text.=$langs->trans("Yes"); - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddHooks").': '; - if (isset($objMod->module_parts) && is_array($objMod->module_parts['hooks']) && count($objMod->module_parts['hooks'])) - { - $i=0; - foreach($objMod->module_parts['hooks'] as $val) - { - $text.=($i?', ':'').($val); - $i++; - } - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddPermissions").': '; - if (isset($objMod->rights) && is_array($objMod->rights) && count($objMod->rights)) - { - $i=0; - foreach($objMod->rights as $val) - { - $text.=($i?', ':'').($val[1]); - $i++; - } - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddMenus").': '; - if (isset($objMod->menu) && ! empty($objMod->menu)) // objMod can be an array or just an int 1 - { - $text.=$langs->trans("Yes"); - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddExportProfiles").': '; - if (isset($objMod->export_label) && is_array($objMod->export_label) && count($objMod->export_label)) - { - $i=0; - foreach($objMod->export_label as $val) - { - $text.=($i?', ':'').($val); - $i++; - } - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddImportProfiles").': '; - if (isset($objMod->import_label) && is_array($objMod->import_label) && count($objMod->import_label)) - { - $i=0; - foreach($objMod->import_label as $val) - { - $text.=($i?', ':'').($val); - $i++; - } - } - else $text.=$langs->trans("No"); - - $text.='
'.$langs->trans("AddOtherPagesOrServices").': '; - $text.=$langs->trans("DetectionNotPossible"); - */ - - //print "\n\n"; print '\n"; // Picto @@ -768,8 +720,11 @@ if ($mode != 'marketplace') } print "\n"; print '
'; + + dol_fiche_end(); } -else + +if ($mode == 'marketplace') { dol_fiche_head($head, $mode, ''); @@ -799,13 +754,115 @@ else print "\n"; - //dol_fiche_end(); + dol_fiche_end(); +} + + +// Install external module + +if ($mode == 'deploy') +{ + dol_fiche_head($head, $mode, ''); + + + $allowonlineinstall=true; + $allowfromweb=1; + if (dol_is_file($dolibarrdataroot.'/installmodules.lock')) $allowonlineinstall=false; + + $fullurl=''.$urldolibarrmodules.''; + $message=''; + if (! empty($allowonlineinstall)) + { + if (! in_array('/custom',explode(',',$dolibarr_main_url_root_alt))) + { + $message=info_admin($langs->trans("ConfFileMuseContainCustom", DOL_DOCUMENT_ROOT.'/custom', DOL_DOCUMENT_ROOT)); + $allowfromweb=-1; + } + else + { + if ($dirins_ok) + { + if (! is_writable(dol_osencode($dirins))) + { + $langs->load("errors"); + $message=info_admin($langs->trans("ErrorFailedToWriteInDir",$dirins)); + $allowfromweb=0; + } + } + else + { + + $message=info_admin($langs->trans("NotExistsDirect",$dirins).$langs->trans("InfDirAlt").$langs->trans("InfDirExample")); + $allowfromweb=0; + } + } + } + else + { + $message=info_admin($langs->trans("InstallModuleFromWebHasBeenDisabledByFile",$dolibarrdataroot.'/installmodules.lock')); + $allowfromweb=0; + } + + if ($allowfromweb < 1) + { + print $langs->trans("SomethingMakeInstallFromWebNotPossible"); + print $message; + //print $langs->trans("SomethingMakeInstallFromWebNotPossible2"); + print '
'; + } + + + if ($allowfromweb >= 0) + { + if ($allowfromweb == 1) + { + //print $langs->trans("ThisIsProcessToFollow").'
'; + } + else + { + print $langs->trans("ThisIsAlternativeProcessToFollow").'
'; + print ''.$langs->trans("StepNb",1).': '; + print $langs->trans("FindPackageFromWebSite",$fullurl).'
'; + print ''.$langs->trans("StepNb",2).': '; + print $langs->trans("DownloadPackageFromWebSite",$fullurl).'
'; + print ''.$langs->trans("StepNb",3).': '; + } + + if ($allowfromweb == 1) + { + print $langs->trans("UnpackPackageInModulesRoot",$dirins).'
'; + print ''; + print ''; + print ''; + print $langs->trans("YouCanSubmitFile").' '; + print ''; + print ''; + } + else + { + print $langs->trans("UnpackPackageInModulesRoot",$dirins).'
'; + print ''.$langs->trans("StepNb",4).': '; + print $langs->trans("SetupIsReadyForUse").'
'; + } + } + + + if (! empty($result['return'])) + { + print '
'; + + foreach($result['return'] as $value) + { + echo $value.'
'; + } + } + + dol_fiche_end(); } -dol_fiche_end(); // Show warning about external users -if ($mode != 'marketplace') print info_admin(showModulesExludedForExternal($modules))."\n"; +if ($mode == 'common') print info_admin(showModulesExludedForExternal($modules))."\n"; llxFooter(); diff --git a/htdocs/admin/tools/update.php b/htdocs/admin/tools/update.php index 912016b498c..d8b17ab989b 100644 --- a/htdocs/admin/tools/update.php +++ b/htdocs/admin/tools/update.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2007-2017 Laurent Destailleur * Copyright (C) 2009-2012 Regis Houssin * Copyright (C) 2012 Juanjo Menent * @@ -23,9 +23,9 @@ */ require '../../main.inc.php'; -include_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php'; -include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; -include_once DOL_DOCUMENT_ROOT . '/core/lib/geturl.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/geturl.lib.php'; $langs->load("admin"); $langs->load("other"); @@ -40,111 +40,23 @@ if (GETPOST('msg','alpha')) { $urldolibarr='https://www.dolibarr.org/downloads/'; -$urldolibarrmodules='https://www.dolistore.com/'; -$urldolibarrthemes='https://www.dolistore.com/'; $dolibarrroot=preg_replace('/([\\/]+)$/i','',DOL_DOCUMENT_ROOT); $dolibarrroot=preg_replace('/([^\\/]+)$/i','',$dolibarrroot); $dolibarrdataroot=preg_replace('/([\\/]+)$/i','',DOL_DATA_ROOT); -$dirins=DOL_DOCUMENT_ROOT.'/custom'; +$sfurl = ''; +$version='0.0'; /* * Actions */ -if ($action=='install') +if ($action == 'getlastversion') { - $error=0; - - // $original_file should match format module_modulename-x.y[.z].zip - $original_file=basename($_FILES["fileinstall"]["name"]); - $newfile=$conf->admin->dir_temp.'/'.$original_file.'/'.$original_file; - - if (! $original_file) - { - $langs->load("Error"); - setEventMessages($langs->trans("ErrorFileRequired"), null, 'warnings'); - $error++; - } - else - { - if (! preg_match('/\.zip/i',$original_file)) - { - $langs->load("errors"); - setEventMessages($langs->trans("ErrorFileMustBeADolibarrPackage",$original_file), null, 'errors'); - $error++; - } - } - - if (! $error) - { - if ($original_file) - { - @dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$original_file); - dol_mkdir($conf->admin->dir_temp.'/'.$original_file); - } - - $tmpdir=preg_replace('/\.zip$/','',$original_file).'.dir'; - if ($tmpdir) - { - @dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$tmpdir); - dol_mkdir($conf->admin->dir_temp.'/'.$tmpdir); - } - - $result=dol_move_uploaded_file($_FILES['fileinstall']['tmp_name'],$newfile,1,0,$_FILES['fileinstall']['error']); - if ($result > 0) - { - $result=dol_uncompress($newfile,$conf->admin->dir_temp.'/'.$tmpdir); - - if (! empty($result['error'])) - { - $langs->load("errors"); - setEventMessages($langs->trans($result['error'],$original_file), null, 'errors'); - $error++; - } - else - { - // Now we move the dir of the module - $modulename=preg_replace('/module_/', '', $original_file); - $modulename=preg_replace('/\-[\d]+\.[\d]+.*$/', '', $modulename); - // Search dir $modulename - $modulenamedir=$conf->admin->dir_temp.'/'.$tmpdir.'/'.$modulename; - //var_dump($modulenamedir); - if (! dol_is_dir($modulenamedir)) - { - $modulenamedir=$conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulename; - //var_dump($modulenamedir); - if (! dol_is_dir($modulenamedir)) - { - setEventMessages($langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat"), null, 'errors'); - $error++; - } - } - - if (! $error) - { - //var_dump($dirins); - @dol_delete_dir_recursive($dirins.'/'.$modulename); - $result=dolCopyDir($modulenamedir, $dirins.'/'.$modulename, '0444', 1); - if ($result <= 0) - { - setEventMessages($langs->trans("ErrorFailedToCopy"), null, 'errors'); - $error++; - } - } - } - } - else - { - $error++; - } - } - - if (! $error) - { - setEventMessages($langs->trans("SetupIsReadyForUse", DOL_URL_ROOT.'/admin/modules.php?mainmenu=home', $langs->transnoentitiesnoconv("Home").' - '.$langs->transnoentitiesnoconv("Setup").' - '.$langs->transnoentitiesnoconv("Modules")), null, 'warnings'); - } + $result = getURLContent('http://sourceforge.net/projects/dolibarr/rss'); + //var_dump($result['content']); + $sfurl = simplexml_load_string($result['content']); } @@ -152,15 +64,6 @@ if ($action=='install') * View */ - - -// Set dir where external modules are installed -if (! dol_is_dir($dirins)) -{ - dol_mkdir($dirins); -} -$dirins_ok=(dol_is_dir($dirins)); - $wikihelp='EN:Installation_-_Upgrade|FR:Installation_-_Mise_à_jour|ES:Instalación_-_Actualización'; llxHeader('',$langs->trans("Upgrade"),$wikihelp); @@ -172,36 +75,40 @@ if (function_exists('curl_init')) { $conf->global->MAIN_USE_RESPONSE_TIMEOUT = 10; - $result = getURLContent('http://sourceforge.net/projects/dolibarr/rss'); - //var_dump($result['content']); - $sfurl = simplexml_load_string($result['content']); - if ($sfurl) + if ($action == 'getlastversion') { - $i=0; - $version='0.0'; - while (! empty($sfurl->channel[0]->item[$i]->title) && $i < 10000) + if ($sfurl) { - $title=$sfurl->channel[0]->item[$i]->title; - if (preg_match('/([0-9]+\.([0-9\.]+))/', $title, $reg)) + $i=0; + while (! empty($sfurl->channel[0]->item[$i]->title) && $i < 10000) { - $newversion=$reg[1]; - $newversionarray=explode('.',$newversion); - $versionarray=explode('.',$version); - //var_dump($newversionarray);var_dump($versionarray); - if (versioncompare($newversionarray, $versionarray) > 0) $version=$newversion; + $title=$sfurl->channel[0]->item[$i]->title; + if (preg_match('/([0-9]+\.([0-9\.]+))/', $title, $reg)) + { + $newversion=$reg[1]; + $newversionarray=explode('.',$newversion); + $versionarray=explode('.',$version); + //var_dump($newversionarray);var_dump($versionarray); + if (versioncompare($newversionarray, $versionarray) > 0) $version=$newversion; + } + $i++; } - $i++; + + // Show version + print $langs->trans("LastStableVersion").' : '. (($version != '0.0')?$version:$langs->trans("Unknown")) .'
'; + } + else + { + print $langs->trans("LastStableVersion").' : ' .$langs->trans("UpdateServerOffline").'
'; } - - // Show version - print $langs->trans("LastStableVersion").' : '. (($version != '0.0')?$version:$langs->trans("Unknown")) .'
'; } else { - print $langs->trans("LastStableVersion").' : ' .$langs->trans("UpdateServerOffline").'
'; + print $langs->trans("LastStableVersion").' : ' .$langs->trans("Check").'
'; } } +print '
'; print '
'; // Upgrade @@ -225,106 +132,15 @@ print '
'; print '
'; -// Install external module - -$allowonlineinstall=true; -$allowfromweb=1; -if (dol_is_file($dolibarrdataroot.'/installmodules.lock')) $allowonlineinstall=false; - -$fullurl=''.$urldolibarrmodules.''; -$message=''; -if (! empty($allowonlineinstall)) -{ - if (! in_array('/custom',explode(',',$dolibarr_main_url_root_alt))) - { - $message=info_admin($langs->trans("ConfFileMuseContainCustom", DOL_DOCUMENT_ROOT.'/custom', DOL_DOCUMENT_ROOT)); - $allowfromweb=-1; - } - else - { - if ($dirins_ok) - { - if (! is_writable(dol_osencode($dirins))) - { - $langs->load("errors"); - $message=info_admin($langs->trans("ErrorFailedToWriteInDir",$dirins)); - $allowfromweb=0; - } - } - else - { - - $message=info_admin($langs->trans("NotExistsDirect",$dirins).$langs->trans("InfDirAlt").$langs->trans("InfDirExample")); - $allowfromweb=0; - } - } -} -else -{ - $message=info_admin($langs->trans("InstallModuleFromWebHasBeenDisabledByFile",$dolibarrdataroot.'/installmodules.lock')); - $allowfromweb=0; -} - - print $langs->trans("AddExtensionThemeModuleOrOther").'
'; print '
'; -if ($allowfromweb < 1) -{ - print $langs->trans("SomethingMakeInstallFromWebNotPossible"); - print $message; - //print $langs->trans("SomethingMakeInstallFromWebNotPossible2"); - print '
'; -} +print $langs->trans("GoModuleSetupArea", DOL_URL_ROOT.'/admin/modules.php?mode=deploy', $langs->transnoentities("Home").' - '.$langs->transnoentities("Setup").' - '.$langs->transnoentities("Modules")); -if ($allowfromweb >= 0) -{ - if ($allowfromweb == 1) - { - //print $langs->trans("ThisIsProcessToFollow").'
'; - } - else - { - print $langs->trans("ThisIsAlternativeProcessToFollow").'
'; - print ''.$langs->trans("StepNb",1).': '; - print $langs->trans("FindPackageFromWebSite",$fullurl).'
'; - print ''.$langs->trans("StepNb",2).': '; - print $langs->trans("DownloadPackageFromWebSite",$fullurl).'
'; - print ''.$langs->trans("StepNb",3).': '; - } - - if ($allowfromweb == 1) - { - print $langs->trans("UnpackPackageInModulesRoot",$dirins).'
'; - print '
'; - print ''; - print $langs->trans("YouCanSubmitFile").' '; - print ''; - print '
'; - } - else - { - print $langs->trans("UnpackPackageInModulesRoot",$dirins).'
'; - print ''.$langs->trans("StepNb",4).': '; - print $langs->trans("SetupIsReadyForUse").'
'; - } -} - - -if (! empty($result['return'])) -{ - print '
'; - - foreach($result['return'] as $value) - { - echo $value.'
'; - } -} - llxFooter(); $db->close(); diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 6ee9a1854f4..36d8772dccb 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -191,7 +191,9 @@ BoxesDesc=Widgets are components showing some information that you can add to pe OnlyActiveElementsAreShown=Only elements from enabled modules are shown. ModulesDesc=Dolibarr modules define which functionality is enabled in software. Some modules require permissions you must grant to users, after enabling module. Click on button on/off to enable a module/feature. ModulesMarketPlaceDesc=You can find more modules to download on external websites on the Internet... -ModulesMarketPlaces=More modules... +ModulesDeployDesc=If permissions on your file system allows it, you can use this tool to deploy an external module. The module wil then be visible on the tab %s. +ModulesMarketPlaces=Find external modules... +GoModuleSetupArea=To deploy/install a new module, go onto the Module setup area at %s. DoliStoreDesc=DoliStore, the official market place for Dolibarr ERP/CRM external modules DoliPartnersDesc=List of companies providing custom developed modules or features (Note: anyone experienced in PHP programming can provide custom development for an open source project) WebSiteDesc=Reference websites to find more modules... @@ -283,16 +285,16 @@ MenuHandlers=Menu handlers MenuAdmin=Menu editor DoNotUseInProduction=Do not use in production ThisIsProcessToFollow=This is steps to process: -ThisIsAlternativeProcessToFollow=This is an alternative setup to process: +ThisIsAlternativeProcessToFollow=This is an alternative setup to process manually: StepNb=Step %s FindPackageFromWebSite=Find a package that provides feature you want (for example on official web site %s). DownloadPackageFromWebSite=Download package (for example from official web site %s). -UnpackPackageInDolibarrRoot=Unpack package files into Dolibarr server directory dedicated to Dolibarr: %s -UnpackPackageInModulesRoot=Unpack package files into Dolibarr server directory dedicated to modules: %s -oiSetupIsReadyForUse=Module deployement is finished. You must however enable and setup the module in your application by going on the page to setup modules: %s. -NotExistsDirect=The alternative root directory is not defined.
-InfDirAlt=Since version 3 it is possible to define an alternative root directory.This allows you to store, same place, plug-ins and custom templates.
Just create a directory at the root of Dolibarr (eg: custom).
-InfDirExample=
Then declare it in the file conf.php
$dolibarr_main_url_root_alt='http://myserver/custom'
$dolibarr_main_document_root_alt='/path/of/dolibarr/htdocs/custom'
*These lines are commented with "#", to uncomment only remove the character. +UnpackPackageInDolibarrRoot=Unpack the packaged files into server directory dedicated to Dolibarr: %s +UnpackPackageInModulesRoot=To deploy/install an external module, unpack the packaged files into the server directory dedicated to modules: %s +SetupIsReadyForUse=Module deployment is finished. You must however enable and setup the module in your application by going on the page to setup modules: %s. +NotExistsDirect=The alternative root directory is not defined to an existing directory.
+InfDirAlt=Since version 3, it is possible to define an alternative root directory. This allows you to store, into a dedicated directory, plug-ins and custom templates.
Just create a directory at the root of Dolibarr (eg: custom).
+InfDirExample=
Then declare it in the file conf.php
$dolibarr_main_url_root_alt='http://myserver/custom'
$dolibarr_main_document_root_alt='/path/of/dolibarr/htdocs/custom'
If these lines are commented with "#", to enable them, just uncomment by removing the "#" character. YouCanSubmitFile=For this step, you can send package using this tool: Select module file CurrentVersion=Dolibarr current version CallUpdatePage=Go to the page that updates the database structure and data: %s. @@ -895,7 +897,7 @@ Offset=Offset AlwaysActive=Always active Upgrade=Upgrade MenuUpgrade=Upgrade / Extend -AddExtensionThemeModuleOrOther=Add extension (theme, module, ...) +AddExtensionThemeModuleOrOther=Deploy/install external module WebServer=Web server DocumentRootServer=Web server's root directory DataRootServer=Data files directory @@ -1582,7 +1584,7 @@ BackupDumpWizard=Wizard to build database backup dump file SomethingMakeInstallFromWebNotPossible=Installation of external module is not possible from the web interface for the following reason: SomethingMakeInstallFromWebNotPossible2=For this reason, process to upgrade described here is only manual steps a privileged user can do. InstallModuleFromWebHasBeenDisabledByFile=Install of external module from application has been disabled by your administrator. You must ask him to remove the file %s to allow this feature. -ConfFileMuseContainCustom=Installing an external module from application need to save the module files into directory %s. To have this directory processed by Dolibarr, you must setup your conf/conf.php to have option
$dolibarr_main_url_root_alt='/custom';
$dolibarr_main_document_root_alt='%s/custom'; +ConfFileMuseContainCustom=Installing an external module from application need to save the module files into directory %s. To have this directory processed by Dolibarr, you must setup your conf/conf.php to add the 2 directive lines:
$dolibarr_main_url_root_alt='/custom';
$dolibarr_main_document_root_alt='%s/custom'; HighlightLinesOnMouseHover=Highlight table lines when mouse move passes over HighlightLinesColor=Highlight color of the line when the mouse passes over (keep empty for no highlight) TextTitleColor=Color of page title diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang index edbf603e8fc..e493bc67524 100644 --- a/htdocs/langs/en_US/errors.lang +++ b/htdocs/langs/en_US/errors.lang @@ -11,6 +11,7 @@ ErrorLoginAlreadyExists=Login %s already exists. ErrorGroupAlreadyExists=Group %s already exists. ErrorRecordNotFound=Record not found. ErrorFailToCopyFile=Failed to copy file '%s' into '%s'. +ErrorFailToCopyDir=Failed to copy directory '%s' into '%s'. ErrorFailToRenameFile=Failed to rename file '%s' into '%s'. ErrorFailToDeleteFile=Failed to remove file '%s'. ErrorFailToCreateFile=Failed to create file '%s'. @@ -115,7 +116,7 @@ ErrorNoActivatedBarcode=No barcode type activated ErrUnzipFails=Failed to unzip %s with ZipArchive ErrNoZipEngine=No engine to unzip %s file in this PHP ErrorFileMustBeADolibarrPackage=The file %s must be a Dolibarr zip package -ErrorFileRequired=It takes a package Dolibarr file +ErrorModuleFileRequired=You must select a Dolibarr module package file ErrorPhpCurlNotInstalled=The PHP CURL is not installed, this is essential to talk with Paypal ErrorFailedToAddToMailmanList=Failed to add record %s to Mailman list %s or SPIP base ErrorFailedToRemoveToMailmanList=Failed to remove record %s to Mailman list %s or SPIP base From 7e3785d44686037c5d4fd44da49312508677d7a8 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 11 Feb 2017 15:40:25 +0100 Subject: [PATCH 163/170] FIX the dolCopyDir fails if target dir does not exists. --- htdocs/core/lib/files.lib.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index ed0e0725bed..33fa6e8b09f 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -436,13 +436,23 @@ function dolCopyDir($srcfile, $destfile, $newmask, $overwriteifexists) $result=0; - dol_syslog("files.lib.php::dolCopyr srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists); + dol_syslog("files.lib.php::dolCopyDir srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists); if (empty($srcfile) || empty($destfile)) return -1; $destexists=dol_is_dir($destfile); if (! $overwriteifexists && $destexists) return 0; - + + if (! $destexists) + { + // We must set mask just before creating dir, becaause it can be set differently by dol_copy + umask(0); + $dirmaskdec=octdec($newmask); + if (empty($newmask) && ! empty($conf->global->MAIN_UMASK)) $dirmaskdec=octdec($conf->global->MAIN_UMASK); + $dirmaskdec |= octdec('0200'); // Set w bit required to be able to create content for recursive subdirs files + dol_mkdir($destfile."/".$file, '', decoct($dirmaskdec)); + } + $srcfile=dol_osencode($srcfile); $destfile=dol_osencode($destfile); @@ -459,6 +469,7 @@ function dolCopyDir($srcfile, $destfile, $newmask, $overwriteifexists) { if (!is_dir($destfile."/".$file)) { + // We must set mask just before creating dir, becaause it can be set differently by dol_copy umask(0); $dirmaskdec=octdec($newmask); if (empty($newmask) && ! empty($conf->global->MAIN_UMASK)) $dirmaskdec=octdec($conf->global->MAIN_UMASK); From 05c9f57fd466c8f5a4b0c232f2a9718bdf26e2e3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 12 Feb 2017 18:13:49 +0100 Subject: [PATCH 164/170] Move edition of emails templates out of dictionaries so we would be able to have templates per user (not reserved to admin). --- htdocs/admin/dict.php | 121 +-- htdocs/admin/mails.php | 39 +- htdocs/admin/mails_templates.php | 869 ++++++++++++++++++++++ htdocs/admin/modulehelp.php | 271 +++---- htdocs/core/class/html.formmail.class.php | 34 +- htdocs/langs/en_US/admin.lang | 1 + htdocs/langs/en_US/mails.lang | 3 + 7 files changed, 1081 insertions(+), 257 deletions(-) create mode 100644 htdocs/admin/mails_templates.php diff --git a/htdocs/admin/dict.php b/htdocs/admin/dict.php index 09ee313e487..b272105fe16 100644 --- a/htdocs/admin/dict.php +++ b/htdocs/admin/dict.php @@ -86,7 +86,7 @@ $hookmanager->initHooks(array('admin')); // Put here declaration of dictionaries properties // Sort order to show dictionary (0 is space). All other dictionaries (added by modules) will be at end of this. -$taborder=array(9,0,4,3,2,0,1,8,19,16,27,0,5,11,0,33,34,0,6,0,29,0,7,17,24,28,0,10,23,12,13,0,14,0,22,20,18,21,0,15,30,0,25,0,26,0,32,0); +$taborder=array(9,0,4,3,2,0,1,8,19,16,27,0,5,11,0,33,34,0,6,0,29,0,7,17,24,28,0,10,23,12,13,0,14,0,22,20,18,21,0,15,30,0,26,0,32,0); // Name of SQL tables of dictionaries $tabname=array(); @@ -114,7 +114,7 @@ $tabname[21]= MAIN_DB_PREFIX."c_availability"; $tabname[22]= MAIN_DB_PREFIX."c_input_reason"; $tabname[23]= MAIN_DB_PREFIX."c_revenuestamp"; $tabname[24]= MAIN_DB_PREFIX."c_type_resource"; -$tabname[25]= MAIN_DB_PREFIX."c_email_templates"; +//$tabname[25]= MAIN_DB_PREFIX."c_email_templates"; $tabname[26]= MAIN_DB_PREFIX."c_units"; $tabname[27]= MAIN_DB_PREFIX."c_stcomm"; $tabname[28]= MAIN_DB_PREFIX."c_holiday_types"; @@ -151,7 +151,7 @@ $tablib[21]= "DictionaryAvailability"; $tablib[22]= "DictionarySource"; $tablib[23]= "DictionaryRevenueStamp"; $tablib[24]= "DictionaryResourceType"; -$tablib[25]= "DictionaryEMailTemplates"; +//$tablib[25]= "DictionaryEMailTemplates"; $tablib[26]= "DictionaryUnits"; $tablib[27]= "DictionaryProspectStatus"; $tablib[28]= "DictionaryHolidayTypes"; @@ -188,7 +188,7 @@ $tabsql[21]= "SELECT c.rowid as rowid, code, label, active FROM ".MAIN_DB_PREFIX $tabsql[22]= "SELECT rowid as rowid, code, label, active FROM ".MAIN_DB_PREFIX."c_input_reason"; $tabsql[23]= "SELECT t.rowid as rowid, t.taux, c.label as country, c.code as country_code, t.fk_pays as country_id, t.note, t.active, t.accountancy_code_sell, t.accountancy_code_buy FROM ".MAIN_DB_PREFIX."c_revenuestamp as t, ".MAIN_DB_PREFIX."c_country as c WHERE t.fk_pays=c.rowid"; $tabsql[24]= "SELECT rowid as rowid, code, label, active FROM ".MAIN_DB_PREFIX."c_type_resource"; -$tabsql[25]= "SELECT rowid as rowid, label, type_template, private, position, topic, content_lines, content, active FROM ".MAIN_DB_PREFIX."c_email_templates WHERE entity IN (".getEntity('email_template',1).")"; +//$tabsql[25]= "SELECT rowid as rowid, label, type_template, private, position, topic, content_lines, content, active FROM ".MAIN_DB_PREFIX."c_email_templates WHERE entity IN (".getEntity('email_template',1).")"; $tabsql[26]= "SELECT rowid as rowid, code, label, short_label, active FROM ".MAIN_DB_PREFIX."c_units"; $tabsql[27]= "SELECT id as rowid, code, libelle, active FROM ".MAIN_DB_PREFIX."c_stcomm"; $tabsql[28]= "SELECT h.rowid as rowid, h.code, h.label, h.affect, h.delay, h.newbymonth, h.fk_country as country_id, c.code as country_code, c.label as country, h.active FROM ".MAIN_DB_PREFIX."c_holiday_types as h LEFT JOIN ".MAIN_DB_PREFIX."c_country as c ON h.fk_country=c.rowid"; @@ -225,7 +225,7 @@ $tabsqlsort[21]="code ASC, label ASC"; $tabsqlsort[22]="code ASC, label ASC"; $tabsqlsort[23]="country ASC, taux ASC"; $tabsqlsort[24]="code ASC,label ASC"; -$tabsqlsort[25]="label ASC"; +//$tabsqlsort[25]="label ASC"; $tabsqlsort[26]="code ASC"; $tabsqlsort[27]="code ASC"; $tabsqlsort[28]="country ASC, code ASC"; @@ -262,7 +262,7 @@ $tabfield[21]= "code,label"; $tabfield[22]= "code,label"; $tabfield[23]= "country_id,country,taux,accountancy_code_sell,accountancy_code_buy,note"; $tabfield[24]= "code,label"; -$tabfield[25]= "label,type_template,private,position,topic,content_lines,content"; +//$tabfield[25]= "label,type_template,private,position,topic,content_lines,content"; $tabfield[26]= "code,label,short_label"; $tabfield[27]= "code,libelle"; $tabfield[28]= "code,label,affect,delay,newbymonth,country_id,country"; @@ -299,7 +299,7 @@ $tabfieldvalue[21]= "code,label"; $tabfieldvalue[22]= "code,label"; $tabfieldvalue[23]= "country,taux,accountancy_code_sell,accountancy_code_buy,note"; $tabfieldvalue[24]= "code,label"; -$tabfieldvalue[25]= "label,type_template,private,position,topic,content_lines,content"; +//$tabfieldvalue[25]= "label,type_template,private,position,topic,content_lines,content"; $tabfieldvalue[26]= "code,label,short_label"; $tabfieldvalue[27]= "code,libelle"; $tabfieldvalue[28]= "code,label,affect,delay,newbymonth,country"; @@ -336,7 +336,7 @@ $tabfieldinsert[21]= "code,label"; $tabfieldinsert[22]= "code,label"; $tabfieldinsert[23]= "fk_pays,taux,accountancy_code_sell,accountancy_code_buy,note"; $tabfieldinsert[24]= "code,label"; -$tabfieldinsert[25]= "label,type_template,private,position,topic,content_lines,content,entity"; +//$tabfieldinsert[25]= "label,type_template,private,position,topic,content_lines,content,entity"; $tabfieldinsert[26]= "code,label,short_label"; $tabfieldinsert[27]= "code,libelle"; $tabfieldinsert[28]= "code,label,affect,delay,newbymonth,fk_country"; @@ -375,7 +375,7 @@ $tabrowid[21]= "rowid"; $tabrowid[22]= "rowid"; $tabrowid[23]= ""; $tabrowid[24]= ""; -$tabrowid[25]= ""; +//$tabrowid[25]= ""; $tabrowid[26]= ""; $tabrowid[27]= "id"; $tabrowid[28]= ""; @@ -412,7 +412,7 @@ $tabcond[21]= ! empty($conf->propal->enabled); $tabcond[22]= (! empty($conf->commande->enabled) || ! empty($conf->propal->enabled)); $tabcond[23]= true; $tabcond[24]= ! empty($conf->resource->enabled); -$tabcond[25]= true; // && ! empty($conf->global->MAIN_EMAIL_EDIT_TEMPLATE_FROM_DIC); +//$tabcond[25]= true; // && ! empty($conf->global->MAIN_EMAIL_EDIT_TEMPLATE_FROM_DIC); $tabcond[26]= ! empty($conf->product->enabled); $tabcond[27]= ! empty($conf->societe->enabled); $tabcond[28]= ! empty($conf->holiday->enabled); @@ -449,7 +449,7 @@ $tabhelp[21] = array('code'=>$langs->trans("EnterAnyCode")); $tabhelp[22] = array('code'=>$langs->trans("EnterAnyCode")); $tabhelp[23] = array(); $tabhelp[24] = array('code'=>$langs->trans("EnterAnyCode")); -$tabhelp[25] = array('topic'=>$langs->trans('SeeSubstitutionVars'),'content'=>$langs->trans('SeeSubstitutionVars'),'content_lines'=>$langs->trans('SeeSubstitutionVars'),'type_template'=>$langs->trans("TemplateForElement"),'private'=>$langs->trans("TemplateIsVisibleByOwnerOnly"), 'position'=>$langs->trans("PositionIntoComboList")); +//$tabhelp[25] = array('topic'=>$langs->trans('SeeSubstitutionVars'),'content'=>$langs->trans('SeeSubstitutionVars'),'content_lines'=>$langs->trans('SeeSubstitutionVars'),'type_template'=>$langs->trans("TemplateForElement"),'private'=>$langs->trans("TemplateIsVisibleByOwnerOnly"), 'position'=>$langs->trans("PositionIntoComboList")); $tabhelp[26] = array('code'=>$langs->trans("EnterAnyCode")); $tabhelp[27] = array('code'=>$langs->trans("EnterAnyCode")); $tabhelp[28] = array('affect'=>$langs->trans("FollowedByACounter"),'delay'=>$langs->trans("MinimumNoticePeriod"), 'newbymonth'=>$langs->trans("NbAddedAutomatically")); @@ -486,7 +486,7 @@ $tabfieldcheck[21] = array(); $tabfieldcheck[22] = array(); $tabfieldcheck[23] = array(); $tabfieldcheck[24] = array(); -$tabfieldcheck[25] = array(); +//$tabfieldcheck[25] = array(); $tabfieldcheck[26] = array(); $tabfieldcheck[27] = array(); $tabfieldcheck[28] = array(); @@ -544,29 +544,6 @@ if ($id == 11) 'external' => $langs->trans('External') ); } -if ($id == 25) -{ - // We save list of template email Dolibarr can manage. This list can found by a grep into code on "->param['models']" - $elementList = array(); - if ($conf->propal->enabled) $elementList['propal_send']=$langs->trans('MailToSendProposal'); - if ($conf->commande->enabled) $elementList['order_send']=$langs->trans('MailToSendOrder'); - if ($conf->facture->enabled) $elementList['facture_send']=$langs->trans('MailToSendInvoice'); - if ($conf->expedition->enabled) $elementList['shipping_send']=$langs->trans('MailToSendShipment'); - if ($conf->ficheinter->enabled) $elementList['fichinter_send']=$langs->trans('MailToSendIntervention'); - if ($conf->supplier_proposal->enabled) $elementList['supplier_proposal_send']=$langs->trans('MailToSendSupplierRequestForQuotation'); - if ($conf->fournisseur->enabled) $elementList['order_supplier_send']=$langs->trans('MailToSendSupplierOrder'); - if ($conf->fournisseur->enabled) $elementList['invoice_supplier_send']=$langs->trans('MailToSendSupplierInvoice'); - if ($conf->societe->enabled) $elementList['thirdparty']=$langs->trans('MailToThirdparty'); - if ($conf->contrat->enabled) $elementList['contract']=$langs->trans('MailToSendContract'); - - $parameters=array('elementList'=>$elementList); - $reshook=$hookmanager->executeHooks('emailElementlist',$parameters); // Note that $action and $object may have been modified by some hooks - if ($reshook == 0) { - foreach ($hookmanager->resArray as $item => $value) { - $elementList[$item] = $value; - } - } -} // Define localtax_typeList (used for dictionary "llx_c_tva") $localtax_typeList = array(); @@ -1048,7 +1025,6 @@ if ($id) if ($fieldlist[$field]=='pcg_subtype') { $valuetoshow=$langs->trans("Pcg_subtype"); } if ($fieldlist[$field]=='sortorder') { $valuetoshow=$langs->trans("SortOrder"); } if ($fieldlist[$field]=='short_label') { $valuetoshow=$langs->trans("ShortLabel"); } - if ($fieldlist[$field]=='type_template') { $valuetoshow=$langs->trans("TypeOfTemplate"); } if ($fieldlist[$field]=='range_account') { $valuetoshow=$langs->trans("Range"); } if ($fieldlist[$field]=='sens') { $valuetoshow=$langs->trans("Sens"); } if ($fieldlist[$field]=='category_type') { $valuetoshow=$langs->trans("Calculated"); } @@ -1062,8 +1038,6 @@ if ($id) if ($fieldlist[$field]=='font_size') { $valuetoshow=$langs->trans("FontSize"); } if ($fieldlist[$field]=='custom_x') { $valuetoshow=$langs->trans("CustomX"); } if ($fieldlist[$field]=='custom_y') { $valuetoshow=$langs->trans("CustomY"); } - if ($fieldlist[$field]=='content') { $valuetoshow=$langs->trans("Content"); } - if ($fieldlist[$field]=='content_lines') { $valuetoshow=$langs->trans("ContentLines"); } if ($fieldlist[$field]=='percent') { $valuetoshow=$langs->trans("Percentage"); } if ($fieldlist[$field]=='affect') { $valuetoshow=$langs->trans("Info"); } if ($fieldlist[$field]=='delay') { $valuetoshow=$langs->trans("NoticePeriod"); } @@ -1117,35 +1091,18 @@ if ($id) if (empty($reshook)) { - if ($tabname[$id] == MAIN_DB_PREFIX.'c_email_templates' && $action == 'edit') - { - fieldList($fieldlist,$obj,$tabname[$id],'hide'); - } - else - { - fieldList($fieldlist,$obj,$tabname[$id],'add'); - } + fieldList($fieldlist,$obj,$tabname[$id],'add'); } if ($id == 4) print ''; print ''; - if ($tabname[$id] != MAIN_DB_PREFIX.'c_email_templates' || $action != 'edit') + if ($action != 'edit') { print ''; } print ''; print ""; - if ($tabname[$id] == MAIN_DB_PREFIX.'c_email_templates') - { - print '* '.$langs->trans("AvailableVariables").": "; - require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; - $formmail=new FormMail($db); - $tmp=$formmail->getAvailableSubstitKey('form'); - print implode(', ', $tmp); - print ''; - } - $colspan=count($fieldlist)+3; if ($id == 4) $colspan++; @@ -1240,7 +1197,6 @@ if ($id) if ($fieldlist[$field]=='pcg_subtype') { $valuetoshow=$langs->trans("Pcg_subtype"); } if ($fieldlist[$field]=='sortorder') { $valuetoshow=$langs->trans("SortOrder"); } if ($fieldlist[$field]=='short_label') { $valuetoshow=$langs->trans("ShortLabel"); } - if ($fieldlist[$field]=='type_template') { $valuetoshow=$langs->trans("TypeOfTemplate"); } if ($fieldlist[$field]=='range_account') { $valuetoshow=$langs->trans("Range"); } if ($fieldlist[$field]=='sens') { $valuetoshow=$langs->trans("Sens"); } if ($fieldlist[$field]=='category_type') { $valuetoshow=$langs->trans("Calculated"); } @@ -1254,8 +1210,6 @@ if ($id) if ($fieldlist[$field]=='font_size') { $valuetoshow=$langs->trans("FontSize"); } if ($fieldlist[$field]=='custom_x') { $valuetoshow=$langs->trans("CustomX"); } if ($fieldlist[$field]=='custom_y') { $valuetoshow=$langs->trans("CustomY"); } - if ($fieldlist[$field]=='content') { $valuetoshow=$langs->trans("Content"); } - if ($fieldlist[$field]=='content_lines') { $valuetoshow=$langs->trans("ContentLines"); } if ($fieldlist[$field]=='percent') { $valuetoshow=$langs->trans("Percentage"); } if ($fieldlist[$field]=='affect') { $valuetoshow=$langs->trans("Info"); } if ($fieldlist[$field]=='delay') { $valuetoshow=$langs->trans("NoticePeriod"); } @@ -1354,10 +1308,6 @@ if ($id) $showfield=1; $align="left"; $valuetoshow=$obj->{$fieldlist[$field]}; - if ($value == 'type_template') - { - $valuetoshow = isset($elementList[$valuetoshow])?$elementList[$valuetoshow]:$valuetoshow; - } if ($value == 'element') { $valuetoshow = isset($elementList[$valuetoshow])?$elementList[$valuetoshow]:$valuetoshow; @@ -1726,13 +1676,6 @@ function fieldList($fieldlist, $obj='', $tabname='', $context='') print $formadmin->select_language($conf->global->MAIN_LANG_DEFAULT,'lang'); print ''; } - // Le type de template - elseif ($fieldlist[$field] == 'type_template') - { - print ''; - print $form->selectarray('type_template', $elementList,(! empty($obj->{$fieldlist[$field]})?$obj->{$fieldlist[$field]}:'')); - print ''; - } // Le type de l'element (pour les type de contact) elseif ($fieldlist[$field] == 'element') { @@ -1773,42 +1716,6 @@ function fieldList($fieldlist, $obj='', $tabname='', $context='') elseif (in_array($fieldlist[$field], array('libelle_facture'))) { print ''; } - elseif (in_array($fieldlist[$field], array('content_lines'))) - { - if ($tabname == MAIN_DB_PREFIX.'c_email_templates') - { - print ''; // To create an artificial CR for the current tr we are on - } - else print ''; - if ($context != 'hide') - { - //print ''; - $okforextended=true; - if ($tabname == MAIN_DB_PREFIX.'c_email_templates' && empty($conf->global->FCKEDITOR_ENABLE_MAIL)) $okforextended=false; - $doleditor = new DolEditor($fieldlist[$field], (! empty($obj->{$fieldlist[$field]})?$obj->{$fieldlist[$field]}:''), '', 100, 'dolibarr_mailings', 'In', 0, false, $okforextended, ROWS_1, '90%'); - print $doleditor->Create(1); - } - else print ' '; - print ''; - } - elseif (in_array($fieldlist[$field], array('content'))) - { - if ($tabname == MAIN_DB_PREFIX.'c_email_templates') - { - print ''; // To create an artificial CR for the current tr we are on - } - else print ''; - if ($context != 'hide') - { - //print ''; - $okforextended=true; - if ($tabname == MAIN_DB_PREFIX.'c_email_templates' && empty($conf->global->FCKEDITOR_ENABLE_MAIL)) $okforextended=false; - $doleditor = new DolEditor($fieldlist[$field], (! empty($obj->{$fieldlist[$field]})?$obj->{$fieldlist[$field]}:''), '', 140, 'dolibarr_mailings', 'In', 0, false, $okforextended, ROWS_8, '90%'); - print $doleditor->Create(1); - } - else print ' '; - print ''; - } elseif ($fieldlist[$field] == 'price' || preg_match('/^amount/i',$fieldlist[$field])) { print ''; } diff --git a/htdocs/admin/mails.php b/htdocs/admin/mails.php index ed290060370..e0e23f75d18 100644 --- a/htdocs/admin/mails.php +++ b/htdocs/admin/mails.php @@ -114,17 +114,24 @@ $server=! empty($conf->global->MAIN_MAIL_SMTP_SERVER)?$conf->global->MAIN_MAIL_S if (! $server) $server='127.0.0.1'; -/* - * View - */ - $wikihelp='EN:Setup EMails|FR:Paramétrage EMails|ES:Configuración EMails'; llxHeader('',$langs->trans("Setup"),$wikihelp); print load_fiche_titre($langs->trans("EMailsSetup"),'','title_setup'); -print $langs->trans("EMailsDesc")."
\n"; -print "
\n"; + +$h = 0; + +$head[$h][0] = DOL_URL_ROOT."/admin/mails.php"; +$head[$h][1] = $langs->trans("OutGoingEmailSetup"); +$head[$h][2] = 'common'; +$h++; + +$head[$h][0] = DOL_URL_ROOT."/admin/mails_templates.php"; +$head[$h][1] = $langs->trans("DictionaryEMailTemplates"); +$head[$h][2] = 'templates'; +$h++; + // List of sending methods $listofmethods=array(); @@ -221,6 +228,12 @@ if ($action == 'edit') print ''; print ''; + dol_fiche_head($head, 'common', ''); + + print $langs->trans("EMailsDesc")."
\n"; + print "
\n"; + + clearstatcache(); $var=true; @@ -428,6 +441,8 @@ if ($action == 'edit') print ''; + dol_fiche_end(); + print '
'; print ''; print '     '; @@ -438,6 +453,12 @@ if ($action == 'edit') } else { + dol_fiche_head($head, 'common', ''); + + print $langs->trans("EMailsDesc")."
\n"; + print "
\n"; + + $var=true; print ''; @@ -570,10 +591,12 @@ else print ' '; } print ''; - print '
'; + dol_fiche_end(); + + if ($conf->global->MAIN_MAIL_SENDMODE == 'mail' && empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA)) { print '
'; @@ -619,7 +642,7 @@ else print '
'; - if ($conf->global->MAIN_MAIL_SENDMODE == 'mail') + if ($conf->global->MAIN_MAIL_SENDMODE == 'mail' && ! in_array($action, array('testconnect', 'test', 'testhtml'))) { $text = $langs->trans("WarningPHPMail"); print info_admin($text); diff --git a/htdocs/admin/mails_templates.php b/htdocs/admin/mails_templates.php new file mode 100644 index 00000000000..78dd7aeba76 --- /dev/null +++ b/htdocs/admin/mails_templates.php @@ -0,0 +1,869 @@ + + * Copyright (C) 2004-2015 Laurent Destailleur + * Copyright (C) 2004 Benoit Mortier + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2010-2016 Juanjo Menent + * Copyright (C) 2011-2015 Philippe Grand + * Copyright (C) 2011 Remy Younes + * Copyright (C) 2012-2015 Marcos García + * Copyright (C) 2012 Christophe Battarel + * Copyright (C) 2011-2016 Alexandre Spangaro + * Copyright (C) 2015 Ferran Marcet + * Copyright (C) 2016 Raphaël Doursenaud + * + * 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/admin/dict.php + * \ingroup setup + * \brief Page to administer data tables + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; +if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT . '/accountancy/class/html.formventilation.class.php'; + +$langs->load("errors"); +$langs->load("admin"); +$langs->load("main"); +$langs->load("mails"); + +$action=GETPOST('action','alpha')?GETPOST('action','alpha'):'view'; +$confirm=GETPOST('confirm','alpha'); +$id=GETPOST('id','int'); +$rowid=GETPOST('rowid','alpha'); + +$allowed=$user->admin; +if (! $allowed) accessforbidden(); + +$acts[0] = "activate"; +$acts[1] = "disable"; +$actl[0] = img_picto($langs->trans("Disabled"),'switch_off'); +$actl[1] = img_picto($langs->trans("Activated"),'switch_on'); + +$listoffset=GETPOST('listoffset'); +$listlimit=GETPOST('listlimit')>0?GETPOST('listlimit'):1000; +$active = 1; + +$sortfield = GETPOST("sortfield",'alpha'); +$sortorder = GETPOST("sortorder",'alpha'); +$page = GETPOST("page",'int'); +if ($page == -1) { $page = 0 ; } +$offset = $listlimit * $page ; +$pageprev = $page - 1; +$pagenext = $page + 1; + +// Initialize technical object to manage hooks of thirdparties. Note that conf->hooks_modules contains array array +$hookmanager->initHooks(array('emailtemplates')); + +// Name of SQL tables of dictionaries +$tabname=array(); +$tabname[25]= MAIN_DB_PREFIX."c_email_templates"; + +// Requests to extract data +$tabsql=array(); +$tabsql[25]= "SELECT rowid as rowid, label, type_template, private, position, topic, content_lines, content, active FROM ".MAIN_DB_PREFIX."c_email_templates WHERE entity IN (".getEntity('email_template',1).")"; + +// Criteria to sort dictionaries +$tabsqlsort=array(); +$tabsqlsort[25]="label ASC"; + +// Nom des champs en resultat de select pour affichage du dictionnaire +$tabfield=array(); +$tabfield[25]= "label,type_template,private,position,topic,content"; +if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfield[25].=',content_lines'; + +// Nom des champs d'edition pour modification d'un enregistrement +$tabfieldvalue=array(); +$tabfieldvalue[25]= "label,type_template,private,position,topic,content"; +if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfield[25].=',content_lines'; + +// Nom des champs dans la table pour insertion d'un enregistrement +$tabfieldinsert=array(); +$tabfieldinsert[25]= "label,type_template,private,position,topic,content"; +if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfield[25].=',content_lines'; +$tabfieldinsert[25].=',entity'; // Must be at end because not into other arrays + +// Nom du rowid si le champ n'est pas de type autoincrement +// Example: "" if id field is "rowid" and has autoincrement on +// "nameoffield" if id field is not "rowid" or has not autoincrement on +$tabrowid=array(); +$tabrowid[25]= ""; + +// Condition to show dictionary in setup page +$tabcond=array(); +$tabcond[25]= true; + +// List of help for fields +// Set MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES to allow edit of template for lines +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; +$formmail=new FormMail($db); +if (empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) +{ + $tmp=$formmail->getAvailableSubstitKey('form'); + $helpsubstit = $langs->trans("AvailableVariables").':
'.implode('
', $tmp); + $helpsubstitforlines = $langs->trans("AvailableVariables").':
'.implode('
', $tmp); +} +else +{ + $tmp=$formmail->getAvailableSubstitKey('formwithlines'); + $helpsubstit = $langs->trans("AvailableVariables").':
'.implode('
', $tmp); + $tmp=$formmail->getAvailableSubstitKey('formforlines'); + $helpsubstitforlines = $langs->trans("AvailableVariables").':
'.implode('
', $tmp); +} + + +$tabhelp=array(); +$tabhelp[25] = array('topic'=>$helpsubstit,'content'=>$helpsubstit,'content_lines'=>$helpsubstitforlines,'type_template'=>$langs->trans("TemplateForElement"),'private'=>$langs->trans("TemplateIsVisibleByOwnerOnly"), 'position'=>$langs->trans("PositionIntoComboList")); + +// List of check for fields (NOT USED YET) +$tabfieldcheck=array(); +$tabfieldcheck[25] = array(); + + +// Define elementList and sourceList (used for dictionary type of contacts "llx_c_type_contact") +$elementList = array(); +$sourceList=array(); + +// We save list of template email Dolibarr can manage. This list can found by a grep into code on "->param['models']" +$elementList = array(); +if ($conf->propal->enabled) $elementList['propal_send']=$langs->trans('MailToSendProposal'); +if ($conf->commande->enabled) $elementList['order_send']=$langs->trans('MailToSendOrder'); +if ($conf->facture->enabled) $elementList['facture_send']=$langs->trans('MailToSendInvoice'); +if ($conf->expedition->enabled) $elementList['shipping_send']=$langs->trans('MailToSendShipment'); +if ($conf->ficheinter->enabled) $elementList['fichinter_send']=$langs->trans('MailToSendIntervention'); +if ($conf->supplier_proposal->enabled) $elementList['supplier_proposal_send']=$langs->trans('MailToSendSupplierRequestForQuotation'); +if ($conf->fournisseur->enabled) $elementList['order_supplier_send']=$langs->trans('MailToSendSupplierOrder'); +if ($conf->fournisseur->enabled) $elementList['invoice_supplier_send']=$langs->trans('MailToSendSupplierInvoice'); +if ($conf->societe->enabled) $elementList['thirdparty']=$langs->trans('MailToThirdparty'); +if ($conf->contrat->enabled) $elementList['contract']=$langs->trans('MailToSendContract'); + +$parameters=array('elementList'=>$elementList); +$reshook=$hookmanager->executeHooks('emailElementlist',$parameters); // Note that $action and $object may have been modified by some hooks +if ($reshook == 0) { + foreach ($hookmanager->resArray as $item => $value) { + $elementList[$item] = $value; + } +} + +$id = 25; + + +/* + * Actions + */ + +if (GETPOST('button_removefilter') || GETPOST('button_removefilter.x') || GETPOST('button_removefilter_x')) +{ + //$search_country_id = ''; +} + +// Actions add or modify an entry into a dictionary +if (GETPOST('actionadd') || GETPOST('actionmodify')) +{ + $listfield=explode(',', str_replace(' ', '',$tabfield[$id])); + $listfieldinsert=explode(',',$tabfieldinsert[$id]); + $listfieldmodify=explode(',',$tabfieldinsert[$id]); + $listfieldvalue=explode(',',$tabfieldvalue[$id]); + + // Check that all fields are filled + $ok=1; + foreach ($listfield as $f => $value) + { + if ($value == 'content') continue; + if ($value == 'content_lines') continue; + if ($value == 'content') $value='content-'.$rowid; + if ($value == 'content_lines') $value='content_lines-'.$rowid; + + if (! isset($_POST[$value]) || $_POST[$value]=='') + { + $ok=0; + $fieldnamekey=$listfield[$f]; + // We take translate key of field + if ($fieldnamekey == 'libelle' || ($fieldnamekey == 'label')) $fieldnamekey='Label'; + if ($fieldnamekey == 'libelle_facture') $fieldnamekey = 'LabelOnDocuments'; + if ($fieldnamekey == 'code') $fieldnamekey = 'Code'; + if ($fieldnamekey == 'note') $fieldnamekey = 'Note'; + if ($fieldnamekey == 'type') $fieldnamekey = 'Type'; + + setEventMessages($langs->transnoentities("ErrorFieldRequired", $langs->transnoentities($fieldnamekey)), null, 'errors'); + } + } + + // Si verif ok et action add, on ajoute la ligne + if ($ok && GETPOST('actionadd')) + { + if ($tabrowid[$id]) + { + // Recupere id libre pour insertion + $newid=0; + $sql = "SELECT max(".$tabrowid[$id].") newid from ".$tabname[$id]; + $result = $db->query($sql); + if ($result) + { + $obj = $db->fetch_object($result); + $newid=($obj->newid + 1); + + } else { + dol_print_error($db); + } + } + + // Add new entry + $sql = "INSERT INTO ".$tabname[$id]." ("; + // List of fields + if ($tabrowid[$id] && ! in_array($tabrowid[$id],$listfieldinsert)) + $sql.= $tabrowid[$id].","; + $sql.= $tabfieldinsert[$id]; + $sql.=",active)"; + $sql.= " VALUES("; + + // List of values + if ($tabrowid[$id] && ! in_array($tabrowid[$id],$listfieldinsert)) + $sql.= $newid.","; + $i=0; + foreach ($listfieldinsert as $f => $value) + { + //var_dump($i.' - '.$listfieldvalue[$i].' - '.$_POST[$listfieldvalue[$i]].' - '.$value); + if ($value == 'entity') { + $_POST[$listfieldvalue[$i]] = $conf->entity; + } + if ($i) $sql.=","; + if ($value == 'private' && ! is_numeric($_POST[$listfieldvalue[$i]])) $_POST[$listfieldvalue[$i]]='0'; + if ($value == 'position' && ! is_numeric($_POST[$listfieldvalue[$i]])) $_POST[$listfieldvalue[$i]]='1'; + if ($_POST[$listfieldvalue[$i]] == '') $sql.="null"; // For vat, we want/accept code = '' + else $sql.="'".$db->escape($_POST[$listfieldvalue[$i]])."'"; + $i++; + } + $sql.=",1)"; + + dol_syslog("actionadd", LOG_DEBUG); + $result = $db->query($sql); + if ($result) // Add is ok + { + setEventMessages($langs->transnoentities("RecordSaved"), null, 'mesgs'); + $_POST=array('id'=>$id); // Clean $_POST array, we keep only + } + else + { + if ($db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') { + setEventMessages($langs->transnoentities("ErrorRecordAlreadyExists"), null, 'errors'); + } + else { + dol_print_error($db); + } + } + } + + // Si verif ok et action modify, on modifie la ligne + if ($ok && GETPOST('actionmodify')) + { + if ($tabrowid[$id]) { $rowidcol=$tabrowid[$id]; } + else { $rowidcol="rowid"; } + + // Modify entry + $sql = "UPDATE ".$tabname[$id]." SET "; + // Modifie valeur des champs + if ($tabrowid[$id] && ! in_array($tabrowid[$id],$listfieldmodify)) + { + $sql.= $tabrowid[$id]."="; + $sql.= "'".$db->escape($rowid)."', "; + } + $i = 0; + foreach ($listfieldmodify as $field) + { + if ($value == 'content') $_POST['content']=$_POST['content-'.$rowid]; + if ($value == 'content_lines') $_POST['content_lines']=$_POST['content_lines-'.$rowid]; + if ($field == 'entity') { + $_POST[$listfieldvalue[$i]] = $conf->entity; + } + if ($i) $sql.=","; + $sql.= $field."="; + if ($_POST[$listfieldvalue[$i]] == '') $sql.="null"; // For vat, we want/accept code = '' + else $sql.="'".$db->escape($_POST[$listfieldvalue[$i]])."'"; + $i++; + } + $sql.= " WHERE ".$rowidcol." = '".$rowid."'"; + + dol_syslog("actionmodify", LOG_DEBUG); + //print $sql; + $resql = $db->query($sql); + if (! $resql) + { + setEventMessages($db->error(), null, 'errors'); + } + } +} + +if ($action == 'confirm_delete' && $confirm == 'yes') // delete +{ + if ($tabrowid[$id]) { $rowidcol=$tabrowid[$id]; } + else { $rowidcol="rowid"; } + + $sql = "DELETE from ".$tabname[$id]." WHERE ".$rowidcol."='".$rowid."'"; + + dol_syslog("delete", LOG_DEBUG); + $result = $db->query($sql); + if (! $result) + { + if ($db->errno() == 'DB_ERROR_CHILD_EXISTS') + { + setEventMessages($langs->transnoentities("ErrorRecordIsUsedByChild"), null, 'errors'); + } + else + { + dol_print_error($db); + } + } +} + +// activate +if ($action == $acts[0]) +{ + if ($tabrowid[$id]) { $rowidcol=$tabrowid[$id]; } + else { $rowidcol="rowid"; } + + if ($rowid) { + $sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE ".$rowidcol."='".$rowid."'"; + } + elseif ($_GET["code"]) { + $sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE code='".$_GET["code"]."'"; + } + + $result = $db->query($sql); + if (!$result) + { + dol_print_error($db); + } +} + +// disable +if ($action == $acts[1]) +{ + if ($tabrowid[$id]) { $rowidcol=$tabrowid[$id]; } + else { $rowidcol="rowid"; } + + if ($rowid) { + $sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE ".$rowidcol."='".$rowid."'"; + } + elseif ($_GET["code"]) { + $sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE code='".$_GET["code"]."'"; + } + + $result = $db->query($sql); + if (!$result) + { + dol_print_error($db); + } +} + + + +/* + * View + */ + +$form = new Form($db); +$formadmin=new FormAdmin($db); + +llxHeader(); + +$titre=$langs->trans("EMailsSetup"); +$linkback=''; +$titlepicto='title_setup'; + +print load_fiche_titre($titre,$linkback,$titlepicto); + +$h = 0; + +$head[$h][0] = DOL_URL_ROOT."/admin/mails.php"; +$head[$h][1] = $langs->trans("OutGoingEmailSetup"); +$head[$h][2] = 'common'; +$h++; + +$head[$h][0] = DOL_URL_ROOT."/admin/mails_templates.php"; +$head[$h][1] = $langs->trans("DictionaryEMailTemplates"); +$head[$h][2] = 'templates'; +$h++; + + +dol_fiche_head($head, 'templates', ''); + +// Confirmation de la suppression de la ligne +if ($action == 'delete') +{ + print $form->formconfirm($_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'sortfield='.$sortfield.'&sortorder='.$sortorder.'&rowid='.$rowid.'&code='.$_GET["code"].'&id='.$id, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_delete','',0,1); +} +//var_dump($elementList); + +// Complete requete recherche valeurs avec critere de tri +$sql=$tabsql[$id]; + +if ($search_country_id > 0) +{ + if (preg_match('/ WHERE /',$sql)) $sql.= " AND "; + else $sql.=" WHERE "; + $sql.= " c.rowid = ".$search_country_id; +} + +if ($sortfield) +{ + // If sort order is "country", we use country_code instead + if ($sortfield == 'country') $sortfield='country_code'; + $sql.= " ORDER BY ".$sortfield; + if ($sortorder) + { + $sql.=" ".strtoupper($sortorder); + } + $sql.=", "; + // Clear the required sort criteria for the tabsqlsort to be able to force it with selected value + $tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.' '.$sortorder.',/i','',$tabsqlsort[$id]); + $tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.',/i','',$tabsqlsort[$id]); +} +else { + $sql.=" ORDER BY "; +} +$sql.=$tabsqlsort[$id]; +$sql.=$db->plimit($listlimit+1,$offset); +//print $sql; + +$fieldlist=explode(',',$tabfield[$id]); + +print '
'; +print ''; +print ''; + +print ''; + +// Form to add a new line +$alabelisused=0; +$var=false; + +$fieldlist=explode(',',$tabfield[$id]); + +if ($action != 'edit') +{ + // Line for title + print ''; + foreach ($fieldlist as $field => $value) + { + // Determine le nom du champ par rapport aux noms possibles + // dans les dictionnaires de donnees + $valuetoshow=ucfirst($fieldlist[$field]); // Par defaut + $valuetoshow=$langs->trans($valuetoshow); // try to translate + $align="left"; + if ($fieldlist[$field]=='lang') { $valuetoshow=$langs->trans("Language"); } + if ($fieldlist[$field]=='type') { $valuetoshow=$langs->trans("Type"); } + if ($fieldlist[$field]=='code') { $valuetoshow=$langs->trans("Code"); } + if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') { $valuetoshow=$langs->trans("Label"); } + if ($fieldlist[$field]=='type_template') { $valuetoshow=$langs->trans("TypeOfTemplate"); } + if ($fieldlist[$field]=='content') { $valuetoshow=''; } + if ($fieldlist[$field]=='content_lines') { $valuetoshow=''; } + + if ($valuetoshow != '') + { + print ''; + } + if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') $alabelisused=1; + } + + print ''; + print ''; + + // Line to enter new values + print ""; + + $obj = new stdClass(); + // If data was already input, we define them in obj to populate input fields. + if (GETPOST('actionadd')) + { + foreach ($fieldlist as $key=>$val) + { + if (GETPOST($val) != '') + $obj->$val=GETPOST($val); + } + } + + $tmpaction = 'create'; + $parameters=array('fieldlist'=>$fieldlist, 'tabname'=>$tabname[$id]); + $reshook=$hookmanager->executeHooks('createDictionaryFieldlist',$parameters, $obj, $tmpaction); // Note that $action and $object may have been modified by some hooks + $error=$hookmanager->error; $errors=$hookmanager->errors; + + if (empty($reshook)) + { + if ($tabname[$id] == MAIN_DB_PREFIX.'c_email_templates' && $action == 'edit') + { + fieldList($fieldlist,$obj,$tabname[$id],'hide'); + } + else + { + fieldList($fieldlist,$obj,$tabname[$id],'add'); + } + } + + print ''; + print ""; + + $fieldsforcontent = array('content'); + if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) + { + $fieldsforcontent = array('content', 'content_lines'); + } + foreach ($fieldsforcontent as $tmpfieldlist) + { + print ''; + if ($tmpfieldlist == 'content') + { + print ''; + } + //else print ''; + print ''; + } + + + + $colspan=count($fieldlist)+1; + print ''; // Keep   to have a line with enough height +} + + +// List of available record in database +dol_syslog("htdocs/admin/dict", LOG_DEBUG); +$resql=$db->query($sql); +if ($resql) +{ + $num = $db->num_rows($resql); + $i = 0; + $var=true; + + $param = '&id='.$id; + $paramwithsearch = $param; + if ($sortorder) $paramwithsearch.= '&sortorder='.$sortorder; + if ($sortfield) $paramwithsearch.= '&sortfield='.$sortfield; + if (GETPOST('from')) $paramwithsearch.= '&from='.GETPOST('from','alpha'); + + // There is several pages + if ($num > $listlimit) + { + print ''; + } + + // Title of lines + print ''; + foreach ($fieldlist as $field => $value) + { + // Determine le nom du champ par rapport aux noms possibles + // dans les dictionnaires de donnees + $showfield=1; // By defaut + $align="left"; + $sortable=1; + $valuetoshow=''; + /* + $tmparray=getLabelOfField($fieldlist[$field]); + $showfield=$tmp['showfield']; + $valuetoshow=$tmp['valuetoshow']; + $align=$tmp['align']; + $sortable=$tmp['sortable']; + */ + $valuetoshow=ucfirst($fieldlist[$field]); // By defaut + $valuetoshow=$langs->trans($valuetoshow); // try to translate + if ($fieldlist[$field]=='lang') { $valuetoshow=$langs->trans("Language"); } + if ($fieldlist[$field]=='type') { $valuetoshow=$langs->trans("Type"); } + if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') { $valuetoshow=$langs->trans("Label"); } + if ($fieldlist[$field]=='type_template') { $valuetoshow=$langs->trans("TypeOfTemplate"); } + if ($fieldlist[$field]=='content') { $valuetoshow=$langs->trans("Content"); $showfield=0;} + if ($fieldlist[$field]=='content_lines') { $valuetoshow=$langs->trans("ContentLines"); $showfield=0; } + + // Affiche nom du champ + if ($showfield) + { + if (! empty($tabhelp[$id][$value])) $valuetoshow = $form->textwithpicto($valuetoshow,$tabhelp[$id][$value]); + print getTitleFieldOfList($valuetoshow, 0, $_SERVER["PHP_SELF"], ($sortable?$fieldlist[$field]:''), ($page?'page='.$page.'&':''), $param, "align=".$align, $sortfield, $sortorder); + } + } + + print getTitleFieldOfList($langs->trans("Status"), 0, $_SERVER["PHP_SELF"], "active", ($page?'page='.$page.'&':''), $param, 'align="center"', $sortfield, $sortorder); + print getTitleFieldOfList(''); + print getTitleFieldOfList(''); + print ''; + + // Title line with search boxes + print ''; + $filterfound=0; + foreach ($fieldlist as $field => $value) + { + if (! in_array($field, array('content', 'content_lines'))) print ''; + } + print ''; + print ''; + print ''; + print ''; + + if ($num) + { + // Lines with values + while ($i < $num) + { + $var = ! $var; + + $obj = $db->fetch_object($resql); + //print_r($obj); + print ''; + if ($action == 'edit' && ($rowid == (! empty($obj->rowid)?$obj->rowid:$obj->code))) + { + $tmpaction='edit'; + $parameters=array('fieldlist'=>$fieldlist, 'tabname'=>$tabname[$id]); + $reshook=$hookmanager->executeHooks('editDictionaryFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks + $error=$hookmanager->error; $errors=$hookmanager->errors; + + // Show fields + if (empty($reshook)) fieldList($fieldlist,$obj,$tabname[$id],'edit'); + + print ''; + + $fieldsforcontent = array('content'); + if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) + { + $fieldsforcontent = array('content', 'content_lines'); + } + foreach ($fieldsforcontent as $tmpfieldlist) + { + $showfield = 1; + $align = "left"; + $valuetoshow = $obj->{$tmpfieldlist}; + + $class = 'tddict'; + // Show value for field + if ($showfield) { + + print ''; + print ''; + + } + } + } + else + { + $tmpaction = 'view'; + $parameters=array('var'=>$var, 'fieldlist'=>$fieldlist, 'tabname'=>$tabname[$id]); + $reshook=$hookmanager->executeHooks('viewDictionaryFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks + + $error=$hookmanager->error; $errors=$hookmanager->errors; + + if (empty($reshook)) + { + foreach ($fieldlist as $field => $value) + { + if (in_array($fieldlist[$field], array('content','content_lines'))) continue; + $showfield=1; + $align="left"; + $valuetoshow=$obj->{$fieldlist[$field]}; + if ($value == 'type_template') + { + $valuetoshow = isset($elementList[$valuetoshow])?$elementList[$valuetoshow]:$valuetoshow; + } + + $class='tddict'; + // Show value for field + if ($showfield) + { + print ''; + } + } + } + + // Can an entry be erased or disabled ? + $iserasable=1;$canbedisabled=1;$canbemodified=1; // true by default + $canbemodified=$iserasable; + + $url = $_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'sortfield='.$sortfield.'&sortorder='.$sortorder.'&rowid='.(! empty($obj->rowid)?$obj->rowid:(! empty($obj->code)?$obj->code:'')).'&code='.(! empty($obj->code)?urlencode($obj->code):''); + if ($param) $url .= '&'.$param; + $url.='&'; + + // Active + print '"; + + // Modify link + if ($canbemodified) print ''; + else print ''; + + // Delete link + if ($iserasable) + { + print ''; + } + else print ''; + + /* + $fieldsforcontent = array('content'); + if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) + { + $fieldsforcontent = array('content', 'content_lines'); + } + foreach ($fieldsforcontent as $tmpfieldlist) + { + $showfield = 1; + $align = "left"; + $valuetoshow = $obj->{$tmpfieldlist}; + + $class = 'tddict'; + // Show value for field + if ($showfield) { + + print ''; + print ''; + + } + }*/ + } + print "\n"; + + + $i++; + } + } +} +else { + dol_print_error($db); +} + +print '
'; + if (! empty($tabhelp[$id][$value]) && preg_match('/^http(s*):/i',$tabhelp[$id][$value])) print ''.$valuetoshow.' '.img_help(1,$valuetoshow).''; + else if (! empty($tabhelp[$id][$value])) print $form->textwithpicto($valuetoshow,$tabhelp[$id][$value]); + else print $valuetoshow; + print ''; + print ''; + print '
'; + print '
'; + if ($tmpfieldlist == 'content') + { + print ''.$form->textwithpicto($langs->trans("Content"),$tabhelp[$id][$tmpfieldlist]).'
'; + } + if ($context != 'hide') + { + //print ''; + $okforextended=true; + if (empty($conf->global->FCKEDITOR_ENABLE_MAIL)) $okforextended=false; + $doleditor = new DolEditor($tmpfieldlist, (! empty($obj->{$tmpfieldlist})?$obj->{$tmpfieldlist}:''), '', 120, 'dolibarr_mailings', 'In', 0, false, $okforextended, ROWS_4, '90%'); + print $doleditor->Create(1); + } + else print ' '; + print '
'; + if ($action != 'edit') + { + print ''; + } + print '
 
'; + print_fleche_navigation($page, $_SERVER["PHP_SELF"], $paramwithsearch, ($num > $listlimit), ''); + print '
'; + print ''; + print ''; + print ''; + print '
'; + print ''; + print '
'; // To create an artificial CR for the current tr we are on + $okforextended = true; + if (empty($conf->global->FCKEDITOR_ENABLE_MAIL)) + $okforextended = false; + $doleditor = new DolEditor($tmpfieldlist.'-'.($i+1), (! empty($obj->{$tmpfieldlist}) ? $obj->{$tmpfieldlist} : ''), '', 140, 'dolibarr_mailings', 'In', 0, false, $okforextended, ROWS_6, '90%'); + print $doleditor->Create(1); + print ''.$valuetoshow.''; + if ($canbedisabled) print ''.$actl[$obj->active].''; + else + { + if (in_array($obj->code, array('AC_OTH','AC_OTH_AUTO'))) print $langs->trans("AlwaysActive"); + else if (isset($obj->type) && in_array($obj->type, array('systemauto')) && empty($obj->active)) print $langs->trans("Deprecated"); + else if (isset($obj->type) && in_array($obj->type, array('system')) && ! empty($obj->active) && $obj->code != 'AC_OTH') print $langs->trans("UsedOnlyWithTypeOption"); + else print $langs->trans("AlwaysActive"); + } + print "'.img_edit().' '; + if ($user->admin) print ''.img_delete().''; + //else print ''.img_delete().''; // Some dictionnary can be edited by other profile than admin + print ' 
'; // To create an artificial CR for the current tr we are on + $okforextended = true; + if (empty($conf->global->FCKEDITOR_ENABLE_MAIL)) + $okforextended = false; + $doleditor = new DolEditor($tmpfieldlist.'-'.$i, (! empty($obj->{$tmpfieldlist}) ? $obj->{$tmpfieldlist} : ''), '', 140, 'dolibarr_mailings', 'In', 0, false, $okforextended, ROWS_6, '90%', 1); + print $doleditor->Create(1); + print '
'; + +print '
'; + + +dol_fiche_end(); + +llxFooter(); +$db->close(); + + +/** + * Show fields in insert/edit mode + * + * @param array $fieldlist Array of fields + * @param Object $obj If we show a particular record, obj is filled with record fields + * @param string $tabname Name of SQL table + * @param string $context 'add'=Output field for the "add form", 'edit'=Output field for the "edit form", 'hide'=Output field for the "add form" but we dont want it to be rendered + * @return void + */ +function fieldList($fieldlist, $obj='', $tabname='', $context='') +{ + global $conf,$langs,$db; + global $form; + global $region_id; + global $elementList,$sourceList,$localtax_typeList; + global $bc; + + $formadmin = new FormAdmin($db); + $formcompany = new FormCompany($db); + if (! empty($conf->accounting->enabled)) $formaccountancy = new FormVentilation($db); + + foreach ($fieldlist as $field => $value) + { + if ($fieldlist[$field] == 'lang') + { + print ''; + print $formadmin->select_language($conf->global->MAIN_LANG_DEFAULT,'lang'); + print ''; + } + // Le type de template + elseif ($fieldlist[$field] == 'type_template') + { + print ''; + print $form->selectarray('type_template', $elementList,(! empty($obj->{$fieldlist[$field]})?$obj->{$fieldlist[$field]}:'')); + print ''; + } + elseif (in_array($fieldlist[$field], array('content','content_lines'))) continue; + else + { + print ''; + $size=''; $class=''; + if ($fieldlist[$field]=='code') $class='maxwidth100'; + if ($fieldlist[$field]=='private') $class='maxwidth50'; + if ($fieldlist[$field]=='position') $class='maxwidth50'; + if ($fieldlist[$field]=='libelle') $class='quatrevingtpercent'; + if ($fieldlist[$field]=='topic') $class='quatrevingtpercent'; + if ($fieldlist[$field]=='sortorder' || $fieldlist[$field]=='sens' || $fieldlist[$field]=='category_type') $size='size="2" '; + print ''; + print ''; + } + } +} + diff --git a/htdocs/admin/modulehelp.php b/htdocs/admin/modulehelp.php index e701d4ead03..6e93bbcda54 100644 --- a/htdocs/admin/modulehelp.php +++ b/htdocs/admin/modulehelp.php @@ -46,9 +46,10 @@ require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php'; $langs->load("errors"); $langs->load("admin"); -$mode=GETPOST('mode', 'alpha')?GETPOST('mode', 'alpha'):0; +$mode=GETPOST('mode', 'alpha'); $action=GETPOST('action','alpha'); $id = GETPOST('id', 'int'); +if (empty($mode)) $mode='desc'; if (! $user->admin) accessforbidden(); @@ -232,17 +233,15 @@ asort($orders); $h = 0; -$categidx='desc'; // Main $head[$h][0] = DOL_URL_ROOT."/admin/modulehelp.php?id=".$id.'&mode=desc'; $head[$h][1] = $langs->trans("Description"); $head[$h][2] = 'desc'; $h++; -/*$categidx='feature'; $head[$h][0] = DOL_URL_ROOT."/admin/modulehelp.php?id=".$id.'&mode=feature'; $head[$h][1] = $langs->trans("Features"); $head[$h][2] = 'feature'; -$h++;*/ +$h++; $i=0; @@ -356,159 +355,165 @@ if ($objMod->isCoreOrExternalModule() == 'external') // Define text of description of module $text=''; - -if ($objMod->getDescLong()) $text.=$objMod->getDesc().'
'; - -$text.='
'.$langs->trans("Version").': '.$version; - -$textexternal=''; -if ($objMod->isCoreOrExternalModule() == 'external') + +if ($mode == 'desc') { - $textexternal.='
'.$langs->trans("Origin").': '.$langs->trans("ExternalModule",$dirofmodule); - if ($objMod->editor_name != 'dolibarr') $textexternal.='
'.$langs->trans("Publisher").': '.(empty($objMod->editor_name)?$langs->trans("Unknown"):$objMod->editor_name); - if (! empty($objMod->editor_url) && ! preg_match('/dolibarr\.org/i',$objMod->editor_url)) $textexternal.='
'.$langs->trans("Url").': '.$objMod->editor_url; - $text.=$textexternal; + $text.=''.$langs->trans("Version").': '.$version; + + $textexternal=''; + if ($objMod->isCoreOrExternalModule() == 'external') + { + $textexternal.='
'.$langs->trans("Origin").': '.$langs->trans("ExternalModule",$dirofmodule); + if ($objMod->editor_name != 'dolibarr') $textexternal.='
'.$langs->trans("Publisher").': '.(empty($objMod->editor_name)?$langs->trans("Unknown"):$objMod->editor_name); + if (! empty($objMod->editor_url) && ! preg_match('/dolibarr\.org/i',$objMod->editor_url)) $textexternal.='
'.$langs->trans("Url").': '.$objMod->editor_url; + $text.=$textexternal; + $text.='
'; + } + else + { + $text.='
'.$langs->trans("Origin").': '.$langs->trans("Core").'
'; + } + $text.='
'.$langs->trans("LastActivationDate").': '; + if (! empty($conf->global->$const_name)) $text.=dol_print_date($objMod->getLastActivationDate(), 'dayhour'); + else $text.=$langs->trans("Disabled"); $text.='
'; -} -else -{ - $text.='
'.$langs->trans("Origin").': '.$langs->trans("Core").'
'; -} -$text.='
'.$langs->trans("LastActivationDate").': '; -if (! empty($conf->global->$const_name)) $text.=dol_print_date($objMod->getLastActivationDate(), 'dayhour'); -else $text.=$langs->trans("Disabled"); -$text.='
'; -$text.='
'.$langs->trans("AddRemoveTabs").': '; -if (isset($objMod->tabs) && is_array($objMod->tabs) && count($objMod->tabs)) + if ($objMod->getDescLong()) $text.=$objMod->getDesc().'
'; +} + +if ($mode == 'feature') { - $i=0; - foreach($objMod->tabs as $val) + $text.=''.$langs->trans("AddRemoveTabs").': '; + if (isset($objMod->tabs) && is_array($objMod->tabs) && count($objMod->tabs)) { - $tmp=explode(':',$val,3); - $text.=($i?', ':'').$tmp[0].':'.$tmp[1]; - $i++; + $i=0; + foreach($objMod->tabs as $val) + { + $tmp=explode(':',$val,3); + $text.=($i?', ':'').$tmp[0].':'.$tmp[1]; + $i++; + } } -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddDictionaries").': '; -if (isset($objMod->dictionaries) && isset($objMod->dictionaries['tablib']) && is_array($objMod->dictionaries['tablib']) && count($objMod->dictionaries['tablib'])) -{ - $i=0; - foreach($objMod->dictionaries['tablib'] as $val) + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddDictionaries").': '; + if (isset($objMod->dictionaries) && isset($objMod->dictionaries['tablib']) && is_array($objMod->dictionaries['tablib']) && count($objMod->dictionaries['tablib'])) { - $text.=($i?', ':'').$val; - $i++; + $i=0; + foreach($objMod->dictionaries['tablib'] as $val) + { + $text.=($i?', ':'').$val; + $i++; + } } -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddBoxes").': '; -if (isset($objMod->boxes) && is_array($objMod->boxes) && count($objMod->boxes)) -{ - $i=0; - foreach($objMod->boxes as $val) + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddBoxes").': '; + if (isset($objMod->boxes) && is_array($objMod->boxes) && count($objMod->boxes)) { - $text.=($i?', ':'').($val['file']?$val['file']:$val[0]); - $i++; + $i=0; + foreach($objMod->boxes as $val) + { + $text.=($i?', ':'').($val['file']?$val['file']:$val[0]); + $i++; + } } -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddModels").': '; -if (isset($objMod->module_parts) && isset($objMod->module_parts['models']) && $objMod->module_parts['models']) -{ - $text.=$langs->trans("Yes"); -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddSubstitutions").': '; -if (isset($objMod->module_parts) && isset($objMod->module_parts['substitutions']) && $objMod->module_parts['substitutions']) -{ - $text.=$langs->trans("Yes"); -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddSheduledJobs").': '; -if (isset($objMod->cronjobs) && is_array($objMod->cronjobs) && count($objMod->cronjobs)) -{ - $i=0; - foreach($objMod->cronjobs as $val) + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddModels").': '; + if (isset($objMod->module_parts) && isset($objMod->module_parts['models']) && $objMod->module_parts['models']) { - $text.=($i?', ':'').($val['label']); - $i++; + $text.=$langs->trans("Yes"); } -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddTriggers").': '; -if (isset($objMod->module_parts) && isset($objMod->module_parts['triggers']) && $objMod->module_parts['triggers']) -{ - $text.=$langs->trans("Yes"); -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddHooks").': '; -if (isset($objMod->module_parts) && is_array($objMod->module_parts['hooks']) && count($objMod->module_parts['hooks'])) -{ - $i=0; - foreach($objMod->module_parts['hooks'] as $val) + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddSubstitutions").': '; + if (isset($objMod->module_parts) && isset($objMod->module_parts['substitutions']) && $objMod->module_parts['substitutions']) { - $text.=($i?', ':'').($val); - $i++; + $text.=$langs->trans("Yes"); } -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddPermissions").': '; -if (isset($objMod->rights) && is_array($objMod->rights) && count($objMod->rights)) -{ - $i=0; - foreach($objMod->rights as $val) + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddSheduledJobs").': '; + if (isset($objMod->cronjobs) && is_array($objMod->cronjobs) && count($objMod->cronjobs)) { - $text.=($i?', ':'').($val[1]); - $i++; + $i=0; + foreach($objMod->cronjobs as $val) + { + $text.=($i?', ':'').($val['label']); + $i++; + } } -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddMenus").': '; -if (isset($objMod->menu) && ! empty($objMod->menu)) // objMod can be an array or just an int 1 -{ - $text.=$langs->trans("Yes"); -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddExportProfiles").': '; -if (isset($objMod->export_label) && is_array($objMod->export_label) && count($objMod->export_label)) -{ - $i=0; - foreach($objMod->export_label as $val) + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddTriggers").': '; + if (isset($objMod->module_parts) && isset($objMod->module_parts['triggers']) && $objMod->module_parts['triggers']) { - $text.=($i?', ':'').($val); - $i++; + $text.=$langs->trans("Yes"); } -} -else $text.=$langs->trans("No"); - -$text.='
'.$langs->trans("AddImportProfiles").': '; -if (isset($objMod->import_label) && is_array($objMod->import_label) && count($objMod->import_label)) -{ - $i=0; - foreach($objMod->import_label as $val) + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddHooks").': '; + if (isset($objMod->module_parts) && is_array($objMod->module_parts['hooks']) && count($objMod->module_parts['hooks'])) { - $text.=($i?', ':'').($val); - $i++; + $i=0; + foreach($objMod->module_parts['hooks'] as $val) + { + $text.=($i?', ':'').($val); + $i++; + } } + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddPermissions").': '; + if (isset($objMod->rights) && is_array($objMod->rights) && count($objMod->rights)) + { + $i=0; + foreach($objMod->rights as $val) + { + $text.=($i?', ':'').($val[1]); + $i++; + } + } + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddMenus").': '; + if (isset($objMod->menu) && ! empty($objMod->menu)) // objMod can be an array or just an int 1 + { + $text.=$langs->trans("Yes"); + } + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddExportProfiles").': '; + if (isset($objMod->export_label) && is_array($objMod->export_label) && count($objMod->export_label)) + { + $i=0; + foreach($objMod->export_label as $val) + { + $text.=($i?', ':'').($val); + $i++; + } + } + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddImportProfiles").': '; + if (isset($objMod->import_label) && is_array($objMod->import_label) && count($objMod->import_label)) + { + $i=0; + foreach($objMod->import_label as $val) + { + $text.=($i?', ':'').($val); + $i++; + } + } + else $text.=$langs->trans("No"); + + $text.='
'.$langs->trans("AddOtherPagesOrServices").': '; + $text.=$langs->trans("DetectionNotPossible"); } -else $text.=$langs->trans("No"); -$text.='
'.$langs->trans("AddOtherPagesOrServices").': '; -$text.=$langs->trans("DetectionNotPossible"); print $text; - dol_fiche_end(); diff --git a/htdocs/core/class/html.formmail.class.php b/htdocs/core/class/html.formmail.class.php index 10fb88bbe64..a85b8157732 100644 --- a/htdocs/core/class/html.formmail.class.php +++ b/htdocs/core/class/html.formmail.class.php @@ -984,7 +984,7 @@ class FormMail extends Form */ function setSubstitFromObject($object, $outputlangs=null) { - global $user; + global $conf, $user; $this->substit['__REF__'] = $object->ref; $this->substit['__REFCLIENT__'] = isset($object->ref_client) ? $object->ref_client : ''; $this->substit['__REFSUPPLIER__'] = isset($object->ref_supplier) ? $object->ref_supplier : ''; @@ -993,6 +993,7 @@ class FormMail extends Form $this->substit['__DATE_DUE_YMD__'] = isset($object->date_lim_reglement)? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : ''; $this->substit['__AMOUNT__'] = price($object->total_ttc); $this->substit['__AMOUNT_WO_TAX__'] = price($object->total_ht); + $this->substit['__AMOUNT_VAT__'] = price($object->total_tva); $this->substit['__THIRDPARTY_ID__'] = (is_object($object->thirdparty)?$object->thirdparty->id:''); $this->substit['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty)?$object->thirdparty->name:''); @@ -1005,6 +1006,7 @@ class FormMail extends Form $this->substit['__PERSONALIZED__'] = ''; $this->substit['__CONTACTCIVNAME__'] = ''; // Will be replace just before sending + //Fill substit_lines with each object lines content if (is_array($object->lines)) { @@ -1022,10 +1024,10 @@ class FormMail extends Form '__SUBPRICE__' => price($line->subprice), '__AMOUNT__' => price($line->total_ttc), '__AMOUNT_WO_TAX__' => price($line->total_ht), - //'__PRODUCT_EXTRAFIELD_FIELD__' Done dinamically + //'__PRODUCT_EXTRAFIELD_FIELD__' Done dinamically just after ); - // Create dinamic tags for __PRODUCT_EXTRAFIELD_FIELD__ + // Create dynamic tags for __PRODUCT_EXTRAFIELD_FIELD__ if (!empty($line->fk_product)) { $extrafields = new ExtraFields($this->db); @@ -1045,7 +1047,7 @@ class FormMail extends Form /** * Set substit array from object * - * @param string $mode 'form' or 'emailing' + * @param string $mode 'form', 'formwithlines', 'formforlines' or 'emailing' * @return void */ function getAvailableSubstitKey($mode='form') @@ -1054,18 +1056,32 @@ class FormMail extends Form $vars=array(); - if ($mode == 'form') + if ($mode == 'form' || $mode == 'formwithlines' || $mode == 'formforlines') { $vars=array( '__REF__', '__REFCLIENT__', - '__THIRDPARTY_NAME__', - '__PROJECT_REF__', - '__PROJECT_NAME__', + '__REFSUPPLIER__', + '__THIRDPARTY_ID__', + '__THIRDPARTY_NAME__', + '__PROJECT_ID__', + '__PROJECT_REF__', + '__PROJECT_NAME__', '__CONTACTCIVNAME__', - '__PERSONALIZED__', // Paypal link will be added here in form mode + '__AMOUNT__', + '__AMOUNT_WO_TAX__', + '__AMOUNT_VAT__', + '__PERSONALIZED__', // Paypal link will be added here in form mode '__SIGNATURE__', ); + if ($mode == 'formwithlines') + { + $vars[] = '__LINES__'; // Will be set by the get_form function + } + if ($mode == 'formforlines') + { + $vars[] = '__QUANTITY__'; // Will be set by the get_form function + } } if ($mode == 'emailing') { diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 36d8772dccb..18c0373e993 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -1623,6 +1623,7 @@ MailToSendIntervention=To send intervention MailToSendSupplierRequestForQuotation=To send quotation request to supplier MailToSendSupplierOrder=To send supplier order MailToSendSupplierInvoice=To send supplier invoice +MailToSendContract=To send a contract MailToThirdparty=To send email from third party page ByDefaultInList=Show by default on list view YouUseLastStableVersion=You use the latest stable version diff --git a/htdocs/langs/en_US/mails.lang b/htdocs/langs/en_US/mails.lang index 897085ec423..93c3e584ff6 100644 --- a/htdocs/langs/en_US/mails.lang +++ b/htdocs/langs/en_US/mails.lang @@ -150,3 +150,6 @@ AdvTgtCreateFilter=Create filter AdvTgtOrCreateNewFilter=Name of new filter NoContactWithCategoryFound=No contact/address with a category found NoContactLinkedToThirdpartieWithCategoryFound=No contact/address with a category found +OutGoingEmailSetup=Outgoing email setup +InGoingEmailSetup=Incoming email setup + From 5233b8c87ff7ca53ca31b7446b73f41d7ce6c7f7 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 12 Feb 2017 18:38:24 +0100 Subject: [PATCH 165/170] Fix support for option MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES --- htdocs/admin/mails_templates.php | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/htdocs/admin/mails_templates.php b/htdocs/admin/mails_templates.php index 78dd7aeba76..150f288e45d 100644 --- a/htdocs/admin/mails_templates.php +++ b/htdocs/admin/mails_templates.php @@ -94,12 +94,12 @@ if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfield[25] // Nom des champs d'edition pour modification d'un enregistrement $tabfieldvalue=array(); $tabfieldvalue[25]= "label,type_template,private,position,topic,content"; -if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfield[25].=',content_lines'; +if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfieldvalue[25].=',content_lines'; // Nom des champs dans la table pour insertion d'un enregistrement $tabfieldinsert=array(); $tabfieldinsert[25]= "label,type_template,private,position,topic,content"; -if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfield[25].=',content_lines'; +if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfieldinsert[25].=',content_lines'; $tabfieldinsert[25].=',entity'; // Must be at end because not into other arrays // Nom du rowid si le champ n'est pas de type autoincrement @@ -290,8 +290,8 @@ if (GETPOST('actionadd') || GETPOST('actionmodify')) $i = 0; foreach ($listfieldmodify as $field) { - if ($value == 'content') $_POST['content']=$_POST['content-'.$rowid]; - if ($value == 'content_lines') $_POST['content_lines']=$_POST['content_lines-'.$rowid]; + if ($field == 'content') $_POST['content']=$_POST['content-'.$rowid]; + if ($field == 'content_lines') $_POST['content_lines']=$_POST['content_lines-'.$rowid]; if ($field == 'entity') { $_POST[$listfieldvalue[$i]] = $conf->entity; } @@ -537,10 +537,9 @@ if ($action != 'edit') foreach ($fieldsforcontent as $tmpfieldlist) { print ''; - if ($tmpfieldlist == 'content') - { - print ''.$form->textwithpicto($langs->trans("Content"),$tabhelp[$id][$tmpfieldlist]).'
'; - } + if ($tmpfieldlist == 'content') print ''.$form->textwithpicto($langs->trans("Content"),$tabhelp[$id][$tmpfieldlist]).'
'; + if ($tmpfieldlist == 'content_lines') print ''.$form->textwithpicto($langs->trans("ContentForLines"),$tabhelp[$id][$tmpfieldlist]).'
'; + if ($context != 'hide') { //print ''; @@ -640,7 +639,7 @@ if ($resql) { if (! in_array($field, array('content', 'content_lines'))) print ''; } - print ''; + if (empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) print ''; print ''; print ''; print ''; @@ -688,11 +687,11 @@ if ($resql) // Show value for field if ($showfield) { - print ''; // To create an artificial CR for the current tr we are on + print ''; // To create an artificial CR for the current tr we are on $okforextended = true; if (empty($conf->global->FCKEDITOR_ENABLE_MAIL)) $okforextended = false; - $doleditor = new DolEditor($tmpfieldlist.'-'.($i+1), (! empty($obj->{$tmpfieldlist}) ? $obj->{$tmpfieldlist} : ''), '', 140, 'dolibarr_mailings', 'In', 0, false, $okforextended, ROWS_6, '90%'); + $doleditor = new DolEditor($tmpfieldlist.'-'.$rowid, (! empty($obj->{$tmpfieldlist}) ? $obj->{$tmpfieldlist} : ''), '', 140, 'dolibarr_mailings', 'In', 0, false, $okforextended, ROWS_6, '90%'); print $doleditor->Create(1); print ''; print ''; From a8c5ba2983dadd12325ea7f4d2bb30700e539c8c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 12 Feb 2017 20:40:20 +0100 Subject: [PATCH 166/170] Fix php7 error --- htdocs/admin/modulehelp.php | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/htdocs/admin/modulehelp.php b/htdocs/admin/modulehelp.php index 6e93bbcda54..9ff7a4478ff 100644 --- a/htdocs/admin/modulehelp.php +++ b/htdocs/admin/modulehelp.php @@ -290,42 +290,6 @@ $moduledesc=$objMod->getDesc(); $moduledesclong=$objMod->getDescLong(); $moduleauthor=$objMod->getPublisher(); -// We discard showing according to filters -if ($search_keyword) -{ - $qualified=0; - if (preg_match('/'.preg_quote($search_keyword).'/i', $modulename) - || preg_match('/'.preg_quote($search_keyword).'/i', $moduledesc) - || preg_match('/'.preg_quote($search_keyword).'/i', $moduledesclong) - || preg_match('/'.preg_quote($search_keyword).'/i', $moduleauthor) - ) $qualified=1; - if (! $qualified) continue; -} -if ($search_status) -{ - if ($search_status == 'active' && empty($conf->global->$const_name)) continue; - if ($search_status == 'disabled' && ! empty($conf->global->$const_name)) continue; -} -if ($search_nature) -{ - if (preg_match('/^external/',$search_nature) && $objMod->isCoreOrExternalModule() != 'external') continue; - if (preg_match('/^external_(.*)$/',$search_nature, $reg)) - { - //print $reg[1].'-'.dol_escape_htmltag($objMod->getPublisher()); - $publisher=dol_escape_htmltag($objMod->getPublisher()); - if ($reg[1] && dol_escape_htmltag($reg[1]) != $publisher) continue; - if (! $reg[1] && ! empty($publisher)) continue; - } - if ($search_nature == 'core' && $objMod->isCoreOrExternalModule() == 'external') continue; -} -if ($search_version) -{ - if (($objMod->version == 'development' || $objMod->version == 'experimental' || preg_match('/deprecated/', $objMod->version)) && $search_version == 'stable') continue; - if ($objMod->version != 'development' && ($search_version == 'development')) continue; - if ($objMod->version != 'experimental' && ($search_version == 'experimental')) continue; - if (! preg_match('/deprecated/', $objMod->version) && ($search_version == 'deprecated')) continue; -} - // Load all lang files of module if (isset($objMod->langfiles) && is_array($objMod->langfiles)) { From abffd29179cac5686aa8e45fc5acc2f28368a233 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 12 Feb 2017 20:55:54 +0100 Subject: [PATCH 167/170] Try a fix for #6334, #6416 --- htdocs/user/class/user.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php index 188195b04bf..2616b816bb5 100644 --- a/htdocs/user/class/user.class.php +++ b/htdocs/user/class/user.class.php @@ -1668,7 +1668,7 @@ class User extends CommonObject $subject = $outputlangs->transnoentitiesnoconv("SubjectNewPassword", $appli); // Define $urlwithroot - //$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root)); + $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root)); $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current From bf32acaadda0a8c0908387bb95f9c0df9c6a2109 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 13 Feb 2017 01:13:41 +0100 Subject: [PATCH 168/170] Fix control of module format. --- htdocs/admin/tools/update.php | 12 +++++++++--- htdocs/langs/en_US/errors.lang | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/htdocs/admin/tools/update.php b/htdocs/admin/tools/update.php index e306862b67a..b062f509388 100644 --- a/htdocs/admin/tools/update.php +++ b/htdocs/admin/tools/update.php @@ -69,12 +69,18 @@ if ($action=='install') } else { - if (! preg_match('/\.zip/i',$original_file)) + if (! preg_match('/\.zip$/i',$original_file)) { $langs->load("errors"); setEventMessages($langs->trans("ErrorFileMustBeADolibarrPackage",$original_file), null, 'errors'); $error++; } + if (! preg_match('/module_.*\-[\d]+\.[\d]+.*$/i',$original_file)) + { + $langs->load("errors"); + setEventMessages($langs->trans("ErrorFilenameDosNotMatchDolibarrPackageRules",$original_file, 'module_*-x.y*.zip'), null, 'errors'); + $error++; + } } if (! $error) @@ -117,7 +123,7 @@ if ($action=='install') //var_dump($modulenamedir); if (! dol_is_dir($modulenamedir)) { - setEventMessages($langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat"), null, 'errors'); + setEventMessages($langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat").'
Dir not found: '.$conf->admin->dir_temp.'/'.$tmpdir.'/'.$modulename.'
'.$conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulename, null, 'errors'); $error++; } } @@ -168,7 +174,7 @@ print load_fiche_titre($langs->trans("Upgrade"),'','title_setup'); print $langs->trans("CurrentVersion").' : '.DOL_VERSION.'
'; -if (function_exists('curl_init')) +if (!function_exists('curl_init')) { $result = getURLContent('http://sourceforge.net/projects/dolibarr/rss'); //var_dump($result['content']); diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang index 0e0b3e40960..2596aef5ee8 100644 --- a/htdocs/langs/en_US/errors.lang +++ b/htdocs/langs/en_US/errors.lang @@ -177,6 +177,8 @@ ErrorStockIsNotEnoughToAddProductOnProposal=Stock is not enough for product %s t ErrorFailedToLoadLoginFileForMode=Failed to get the login key for mode '%s'. ErrorPhpMailDelivery=Check that you don't use a too high number of recipients and that your email content is not similar to a Spam. Ask also your administrator to check firewall and server logs files for a more complete information. ErrorUserNotAssignedToTask=User must be assigned to task to be able to enter time consumed. +ErrorModuleFileSeemsToHaveAWrongFormat=The module package seems to have a wrong format. +ErrorFilenameDosNotMatchDolibarrPackageRules=The name of the module package (%s) does not match expected name syntax: %s # Warnings WarningPasswordSetWithNoAccount=A password was set for this member. However, no user account was created. So this password is stored but can't be used to login to Dolibarr. It may be used by an external module/interface but if you don't need to define any login nor password for a member, you can disable option "Manage a login for each member" from Member module setup. If you need to manage a login but don't need any password, you can keep this field empty to avoid this warning. Note: Email can also be used as a login if the member is linked to a user. From 5de0d81dff43a8fbb76e4b3a66819314adca5c32 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 13 Feb 2017 10:59:33 +0100 Subject: [PATCH 169/170] Can force option according to country --- build/generate_filelist_xml.php | 37 +++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/build/generate_filelist_xml.php b/build/generate_filelist_xml.php index 36ea0e9542b..ff580fad58c 100755 --- a/build/generate_filelist_xml.php +++ b/build/generate_filelist_xml.php @@ -44,8 +44,8 @@ $includeconstants=array(); if (empty($argv[1])) { - print "Usage: ".$script_file." release=x.y.z[-...] [includecustom=1] [includeconstant=MY_CONF_NAME:value]\n"; - print "Example: ".$script_file." release=6.0.0 includecustom=1 includeconstant=INVOICE_CAN_ALWAYS_BE_REMOVED:0 includeconstant=MAILING_NO_USING_PHPMAIL:1\n"; + print "Usage: ".$script_file." release=x.y.z[-...] [includecustom=1] [includeconstant=CC:MY_CONF_NAME:value]\n"; + print "Example: ".$script_file." release=6.0.0 includecustom=1 includeconstant=FR:INVOICE_CAN_ALWAYS_BE_REMOVED:0 includeconstant=all:MAILING_NO_USING_PHPMAIL:1\n"; exit -1; } $i=0; @@ -54,8 +54,13 @@ while ($i < $argc) if (! empty($argv[$i])) parse_str($argv[$i]); if (preg_match('/includeconstant=/',$argv[$i])) { - $tmp=explode(':', $includeconstant); - if (count($tmp) == 2) $includeconstants[$tmp[0]] = $tmp[1]; + $tmp=explode(':', $includeconstant, 3); + if (count($tmp) != 3) + { + print "Error: Bad parameter includeconstant ".$includeconstant."\n"; + exit -1; + } + $includeconstants[$tmp[0]][$tmp[1]] = $tmp[2]; } $i++; } @@ -84,9 +89,12 @@ else print "Release : ".$release."\n"; print "Include custom : ".$includecustom."\n"; print "Include constants: "; -foreach ($includeconstants as $constname => $constvalue) +foreach ($includeconstants as $countrycode => $tmp) { - print $constname.'='.$constvalue." "; + foreach($tmp as $constname => $constvalue) + { + print $constname.'='.$constvalue." "; + } } print "\n"; @@ -100,16 +108,19 @@ $checksumconcat=array(); $outputfile=$outputdir.'/filelist-'.$release.'.xml'; $fp = fopen($outputfile,'w'); fputs($fp, ''."\n"); -fputs($fp, ''."\n"); +fputs($fp, ''."\n"); -fputs($fp, ''."\n"); -foreach($includeconstants as $constname => $constvalue) +foreach ($includeconstants as $countrycode => $tmp) { - $valueforchecksum=(empty($constvalue)?'0':$constvalue); - $checksumconcat[]=$valueforchecksum; - fputs($fp, ' '.$valueforchecksum.''."\n"); + fputs($fp, ''."\n"); + foreach($tmp as $constname => $constvalue) + { + $valueforchecksum=(empty($constvalue)?'0':$constvalue); + $checksumconcat[]=$valueforchecksum; + fputs($fp, ' '.$valueforchecksum.''."\n"); + } + fputs($fp, ''."\n"); } -fputs($fp, ''."\n"); fputs($fp, ''."\n"); From eb70d6eea122b09d8869b576a74f2f0a136ff8c9 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 14 Feb 2017 18:04:12 +0100 Subject: [PATCH 170/170] Debug of feature for browser notifications --- htdocs/core/ajax/check_notifications.php | 64 +++++++++++++++++------- htdocs/core/class/conf.class.php | 2 + htdocs/core/js/lib_notification.js.php | 49 +++++++++--------- htdocs/main.inc.php | 2 +- htdocs/user/class/user.class.php | 3 -- 5 files changed, 73 insertions(+), 47 deletions(-) diff --git a/htdocs/core/ajax/check_notifications.php b/htdocs/core/ajax/check_notifications.php index af56cb23700..4db67aef939 100644 --- a/htdocs/core/ajax/check_notifications.php +++ b/htdocs/core/ajax/check_notifications.php @@ -24,31 +24,57 @@ if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1'); if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1'); require '../../main.inc.php'; -require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php'; global $user, $db, $langs, $conf; -$time = GETPOST('time'); +$time = (int) GETPOST('time'); // Use the time parameter that is always increased by time_update, even if call is late //$time=dol_now(); -session_start(); -$time_update = (empty($conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY)?'3':(int) $conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY); +$eventfound = array(); +//Uncomment this to force a test +//$eventfound[]=array('type'=>'agenda', 'id'=>1, 'tipo'=>'eee', 'location'=>'aaa'); -$eventos = array(); -//$eventos[]=array('type'=>'agenda', 'id'=>1, 'tipo'=>'eee', 'location'=>'aaa'); +//dol_syslog('time='.$time.' $_SESSION[auto_ck_events_not_before]='.$_SESSION['auto_check_events_not_before']); -// TODO Remove test on session. Timer should be managed by a javascript timer -if ($_SESSION['auto_check_events'] <= (int) $time) +// TODO Try to make a solution with only a javascript timer that is easier. Difficulty is to avoid notification twice when several tabs are opened. +if ($time >= $_SESSION['auto_check_events_not_before']) { - $_SESSION['auto_check_events'] = $time + $time_update; - + $time_update = (int) $conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY; // Always defined + if (! empty($_SESSION['auto_check_events_not_before'])) + { + // We start scan from the not before so if two tabs were opend at differents seconds and we close one (so the js timer), + // then we are not losing periods + $starttime = $_SESSION['auto_check_events_not_before']; + // Protection to avoid too long sessions + if ($starttime < ($time - (int) $conf->global->MAIN_SESSION_TIMEOUT)) + { + dol_syslog("We ask to check browser notification on a too large period. We fix this with current date."); + $starttime = $time; + } + } + else + { + $starttime = $time; + } + + $_SESSION['auto_check_events_not_before'] = $time + $time_update; + + // Force save of session change we did. + // WARNING: Any change in sessions after that will not be saved ! + session_write_close(); + + require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php'; + + + dol_syslog('NEW $_SESSION[auto_check_events_not_before]='.$_SESSION['auto_check_events_not_before']); + $sql = 'SELECT id'; $sql .= ' FROM ' . MAIN_DB_PREFIX . 'actioncomm a, ' . MAIN_DB_PREFIX . 'actioncomm_resources ar'; $sql .= ' WHERE a.id = ar.fk_actioncomm'; - // TODO Try to make a solution with only a javascript timer that is easier. Difficulty is to avoid notification twice when. - // This need to extend period to be sure to not miss and save what we notified to avoid duplicate (save is not done yet). - $sql .= " AND datep BETWEEN '" . $db->idate($time + 1) . "' AND '" . $db->idate($time + $time_update) . "'"; + // TODO Try to make a solution with only a javascript timer that is easier. Difficulty is to avoid notification twice when several tabs are opened. + // This need to extend period to be sure to not miss and save in session what we notified to avoid duplicate (save is not done yet). + $sql .= " AND datep BETWEEN '" . $db->idate($starttime) . "' AND '" . $db->idate($time + $time_update - 1) . "'"; $sql .= ' AND a.code <> "AC_OTH_AUTO"'; $sql .= ' AND ar.element_type = "user"'; $sql .= ' AND ar.fk_element = ' . $user->id; @@ -59,8 +85,10 @@ if ($_SESSION['auto_check_events'] <= (int) $time) $actionmod = new ActionComm($db); - while ($obj = $db->fetch_object($resql)) { - + while ($obj = $db->fetch_object($resql)) + { + $langs->load("agenda"); + $actionmod->fetch($obj->id); $event = array(); @@ -68,13 +96,13 @@ if ($_SESSION['auto_check_events'] <= (int) $time) $event['id'] = $actionmod->id; $event['tipo'] = $langs->transnoentities('Action' . $actionmod->code); $event['titulo'] = $actionmod->label; - $event['location'] = $actionmod->location; + $event['location'] = $langs->transnoentities('Location').': '.$actionmod->location; - $eventos[] = $event; + $eventfound[] = $event; } } } -print json_encode($eventos); +print json_encode($eventfound); diff --git a/htdocs/core/class/conf.class.php b/htdocs/core/class/conf.class.php index 064beaa8226..bb63e4c3560 100644 --- a/htdocs/core/class/conf.class.php +++ b/htdocs/core/class/conf.class.php @@ -420,6 +420,8 @@ class Conf if (empty($this->global->MAIN_MONNAIE)) $this->global->MAIN_MONNAIE='EUR'; $this->currency=$this->global->MAIN_MONNAIE; + if (empty($conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY)) $conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY = 30; // Less than 1 minutes to be sure + // conf->global->ACCOUNTING_MODE = Option des modules Comptabilites (simple ou expert). Defini le mode de calcul des etats comptables (CA,...) if (empty($this->global->ACCOUNTING_MODE)) $this->global->ACCOUNTING_MODE='RECETTES-DEPENSES'; // By default. Can be 'RECETTES-DEPENSES' ou 'CREANCES-DETTES' diff --git a/htdocs/core/js/lib_notification.js.php b/htdocs/core/js/lib_notification.js.php index a870066c775..af8e0492cdc 100644 --- a/htdocs/core/js/lib_notification.js.php +++ b/htdocs/core/js/lib_notification.js.php @@ -35,20 +35,23 @@ if (!($_SERVER['HTTP_REFERER'] === $dolibarr_main_url_root . '/' || $_SERVER['HT // Define javascript type header('Content-type: text/javascript; charset=UTF-8'); + $nowtime = time(); + //$nowtimeprevious = floor($nowtime / 60) * 60; // auto_check_events_not_before is rounded to previous minute + // TODO Try to make a solution with only a javascript timer that is easier. Difficulty is to avoid notification twice when. session_cache_limiter(FALSE); header('Cache-Control: no-cache'); session_start(); - if (!isset($_SESSION['auto_check_events'])) { - // Round to eliminate the second part - $_SESSION['auto_check_events'] = floor(time() / 60) * 60; - print 'var time_session = ' . $_SESSION['auto_check_events'] . ';'."\n"; - print 'var now = ' . $_SESSION['auto_check_events'] . ';' . "\n"; - } else { - print 'var time_session = ' . $_SESSION['auto_check_events'] . ';' . "\n"; - print 'var now = ' . time() . ';' . "\n"; + if (! isset($_SESSION['auto_check_events_not_before'])) + { + print 'console.log("_SESSION[auto_check_events_not_before] is not set");'."\n"; + // Round to eliminate the seconds + $_SESSION['auto_check_events_not_before'] = $nowtime; // auto_check_events_not_before is rounded to previous minute } - print 'var time_auto_update = '.(empty($conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY)?'3':(int) $conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY).';' . "\n"; + print 'var nowtime = ' . $nowtime . ';' . "\n"; + print 'var auto_check_events_not_before = '.$_SESSION['auto_check_events_not_before']. ';'."\n"; + print 'var time_js_next_test = Math.max(nowtime, auto_check_events_not_before);'."\n"; + print 'var time_auto_update = '.$conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY; // Always defined ?> /* Check if permission ok */ @@ -56,31 +59,27 @@ if (!($_SERVER['HTTP_REFERER'] === $dolibarr_main_url_root . '/' || $_SERVER['HT Notification.requestPermission() } - if (now > (time_session + time_auto_update) || now == time_session) { - - first_execution(); //firts run auto check - } else { - - var time_first_execution = (time_auto_update - (now - time_session)) * 1000; //need milliseconds - - setTimeout(first_execution, time_first_execution); //first run auto check - } + /* Launch timer */ + // We set a delay before launching first test so next check will arrive after the time_auto_update compared to previous one. + var time_first_execution = (time_auto_update - (nowtime - time_js_next_test)) * 1000; //need milliseconds + console.log("Launch browser notif check: setTimeout to wait time_first_execution="+time_first_execution+" before first check - nowtime = "+nowtime+" auto_check_events_not_before = "+auto_check_events_not_before+" time_js_next_test = "+time_js_next_test+" time_auto_update="+time_auto_update); + setTimeout(first_execution, time_first_execution); //first run auto check function first_execution() { - console.log("Call first_execution"); - check_events(); - setInterval(check_events, time_auto_update * 1000); //program time for run check events + console.log("Call first_execution time_auto_update (MAIN_BROWSER_NOTIFICATION_FREQUENCY) = "+time_auto_update); + check_events(); //one check before launching timer to launch other checks + setInterval(check_events, time_auto_update * 1000); //program time to run next check events } function check_events() { if (Notification.permission === "granted") { - console.log("Call check_events"); + console.log("Call check_events time_js_next_test="+time_js_next_test); $.ajax("", { type: "post", // Usually post o get async: true, - data: {time: time_session}, + data: {time: time_js_next_test}, success: function (result) { var arr = JSON.parse(result); if (arr.length > 0) { @@ -95,7 +94,7 @@ if (!($_SERVER['HTTP_REFERER'] === $dolibarr_main_url_root . '/' || $_SERVER['HT var title="Not defined"; var body = value['tipo'] + ': ' + value['titulo']; if (value['type'] == 'agenda' && value['location'] != null && value['location'] != '') { - body += '\n transnoentities('Location')?>: ' + value['location']; + body += '\n' + value['location']; } if (value['type'] == 'agenda') @@ -132,7 +131,7 @@ if (!($_SERVER['HTTP_REFERER'] === $dolibarr_main_url_root . '/' || $_SERVER['HT console.log("Cancel check_events. Useless because Notification.permission is "+Notification.permission); } - time_session += time_auto_update; + time_js_next_test += time_auto_update; } agenda->enabled) && ! empty($conf->global->AGENDA_NOTIFICATION) && ! empty($conf->global->AGENDA_NOTIFICATION_SOUND)) $enablebrowsernotif=true; + if (! empty($conf->agenda->enabled) && ! empty($conf->global->AGENDA_NOTIFICATION)) $enablebrowsernotif=true; if ($enablebrowsernotif) { print ''."\n"; diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php index 8281ef244d1..f087fc3db9b 100644 --- a/htdocs/user/class/user.class.php +++ b/htdocs/user/class/user.class.php @@ -235,7 +235,6 @@ class User extends CommonObject $sql.= " AND u.rowid = ".$id; } - dol_syslog(get_class($this)."::fetch", LOG_DEBUG); $result = $this->db->query($sql); if ($result) { @@ -618,7 +617,6 @@ class User extends CommonObject $sql.= " AND r.perms IS NOT NULL"; if ($moduletag) $sql.= " AND r.module = '".$this->db->escape($moduletag)."'"; - dol_syslog(get_class($this).'::getrights', LOG_DEBUG); $resql = $this->db->query($sql); if ($resql) { @@ -670,7 +668,6 @@ class User extends CommonObject $sql.= " AND r.perms IS NOT NULL"; if ($moduletag) $sql.= " AND r.module = '".$this->db->escape($moduletag)."'"; - dol_syslog(get_class($this).'::getrights', LOG_DEBUG); $resql = $this->db->query($sql); if ($resql) {