From d7337c4d4f2faed4a475e7d66e4f663404c6a8a1 Mon Sep 17 00:00:00 2001 From: rMalie Date: Wed, 9 May 2012 15:05:39 +0000 Subject: [PATCH] // Multishop interface reworked --- .../default/img/tree-multishop-groups.png | Bin 0 -> 632 bytes .../default/img/tree-multishop-root.png | Bin 0 -> 565 bytes .../default/img/tree-multishop-shop.png | Bin 0 -> 781 bytes .../themes/default/img/tree-multishop-url.png | Bin 0 -> 649 bytes .../template/controllers/shop/content.tpl | 42 + .../controllers/shop/helpers/form/form.tpl | 9 +- .../shop/helpers/list/list_content.tpl | 8 +- .../template/controllers/shop/tree.tpl | 216 + .../controllers/shop_group/content.tpl | 27 + .../shop_group/helpers/form/form.tpl | 29 +- .../template/controllers/shop_url/content.tpl | 27 + .../shop_url/helpers/form/form.tpl | 23 + .../shop_url/helpers/list/list_content.tpl | 35 + .../default/template/helpers/form/form.tpl | 4 +- .../template/helpers/list/list_header.tpl | 6 +- .../template/helpers/options/options.tpl | 2 +- classes/AdminTab.php | 8 +- classes/Collection.php | 13 + classes/Tab.php | 4 +- classes/controller/AdminController.php | 8 +- classes/shop/Shop.php | 49 +- classes/shop/ShopGroup.php | 38 + classes/shop/ShopUrl.php | 37 - controllers/admin/AdminMetaController.php | 107 +- .../admin/AdminPreferencesController.php | 24 +- controllers/admin/AdminShopController.php | 252 +- .../admin/AdminShopGroupController.php | 68 +- controllers/admin/AdminShopUrlController.php | 309 +- controllers/admin/AdminThemesController.php | 2 +- css/admin.css | 53 +- install-dev/data/xml/tab.xml | 21 +- install-dev/langs/de/data/tab.xml | 7 +- install-dev/langs/en/data/tab.xml | 7 +- install-dev/langs/es/data/tab.xml | 7 +- install-dev/langs/fr/data/tab.xml | 7 +- install-dev/langs/it/data/tab.xml | 7 +- install-dev/models/install.php | 1 + js/admin.js | 6 +- js/jquery/plugins/jquery.cookie.js | 47 + js/jquery/plugins/jstree/jquery.jstree.js | 4551 +++++++++++++++++ js/jquery/plugins/jstree/themes/apple/bg.jpg | Bin 0 -> 331 bytes js/jquery/plugins/jstree/themes/apple/d.png | Bin 0 -> 7765 bytes .../jstree/themes/apple/dot_for_ie.gif | Bin 0 -> 43 bytes .../plugins/jstree/themes/apple/style.css | 61 + .../plugins/jstree/themes/apple/throbber.gif | Bin 0 -> 1849 bytes js/jquery/plugins/jstree/themes/classic/d.gif | Bin 0 -> 3003 bytes js/jquery/plugins/jstree/themes/classic/d.png | Bin 0 -> 7535 bytes .../jstree/themes/classic/dot_for_ie.gif | Bin 0 -> 43 bytes .../plugins/jstree/themes/classic/style.css | 77 + .../jstree/themes/classic/throbber.gif | Bin 0 -> 1849 bytes .../plugins/jstree/themes/default-rtl/d.gif | Bin 0 -> 2872 bytes .../plugins/jstree/themes/default-rtl/d.png | Bin 0 -> 7459 bytes .../jstree/themes/default-rtl/dots.gif | Bin 0 -> 132 bytes .../jstree/themes/default-rtl/style.css | 84 + .../jstree/themes/default-rtl/throbber.gif | Bin 0 -> 1849 bytes js/jquery/plugins/jstree/themes/default/d.gif | Bin 0 -> 2944 bytes js/jquery/plugins/jstree/themes/default/d.png | Bin 0 -> 7635 bytes .../plugins/jstree/themes/default/style.css | 74 + .../jstree/themes/default/throbber.gif | Bin 0 -> 1849 bytes modules/blockreinsurance/blockreinsurance.php | 11 +- translations/fr/admin.php | 66 +- 61 files changed, 6043 insertions(+), 391 deletions(-) create mode 100644 admin-dev/themes/default/img/tree-multishop-groups.png create mode 100644 admin-dev/themes/default/img/tree-multishop-root.png create mode 100644 admin-dev/themes/default/img/tree-multishop-shop.png create mode 100644 admin-dev/themes/default/img/tree-multishop-url.png create mode 100644 admin-dev/themes/default/template/controllers/shop/content.tpl create mode 100644 admin-dev/themes/default/template/controllers/shop/tree.tpl create mode 100644 admin-dev/themes/default/template/controllers/shop_group/content.tpl create mode 100644 admin-dev/themes/default/template/controllers/shop_url/content.tpl create mode 100644 admin-dev/themes/default/template/controllers/shop_url/helpers/list/list_content.tpl create mode 100644 js/jquery/plugins/jquery.cookie.js create mode 100644 js/jquery/plugins/jstree/jquery.jstree.js create mode 100644 js/jquery/plugins/jstree/themes/apple/bg.jpg create mode 100644 js/jquery/plugins/jstree/themes/apple/d.png create mode 100644 js/jquery/plugins/jstree/themes/apple/dot_for_ie.gif create mode 100644 js/jquery/plugins/jstree/themes/apple/style.css create mode 100644 js/jquery/plugins/jstree/themes/apple/throbber.gif create mode 100644 js/jquery/plugins/jstree/themes/classic/d.gif create mode 100644 js/jquery/plugins/jstree/themes/classic/d.png create mode 100644 js/jquery/plugins/jstree/themes/classic/dot_for_ie.gif create mode 100644 js/jquery/plugins/jstree/themes/classic/style.css create mode 100644 js/jquery/plugins/jstree/themes/classic/throbber.gif create mode 100644 js/jquery/plugins/jstree/themes/default-rtl/d.gif create mode 100644 js/jquery/plugins/jstree/themes/default-rtl/d.png create mode 100644 js/jquery/plugins/jstree/themes/default-rtl/dots.gif create mode 100644 js/jquery/plugins/jstree/themes/default-rtl/style.css create mode 100644 js/jquery/plugins/jstree/themes/default-rtl/throbber.gif create mode 100644 js/jquery/plugins/jstree/themes/default/d.gif create mode 100644 js/jquery/plugins/jstree/themes/default/d.png create mode 100644 js/jquery/plugins/jstree/themes/default/style.css create mode 100644 js/jquery/plugins/jstree/themes/default/throbber.gif diff --git a/admin-dev/themes/default/img/tree-multishop-groups.png b/admin-dev/themes/default/img/tree-multishop-groups.png new file mode 100644 index 0000000000000000000000000000000000000000..f1ed9abe0338be5a0401f4aee1b3bb168b4d815f GIT binary patch literal 632 zcmV-;0*C#HP)4gUbq(tw1lgoXx`HVs0gK|-QT;Nk*gBnKlTn_z-{ zmhYU)-7d3t&KJ-ik94}dotgJ$-pr9P27{QufTqgxGmAP3uNoaE7bGM|sxpx{$6x!S zseeO+ZJ)Bq=VzY*TTvP$C03r?ILjc0=nnu+A|W7D<`Wa0NdS!2oVvxShDKmPGuu1Z{X#D3g|-0BZ&qN9*G%zO46N zD4sVu^%@Qrc`Ds1baRPAMG7LbeA>HftOGIA&>{S+y14WB!(!4xyYUUh=}X*79hA*; zs@NUjK4So91b$-;-y6->tMfCSvO&Q{3%OG#fnJLTF^s=9aIa;Dv9bIfwI98O6QfM# z&RzC+V0j(IuuEljHs;@Ei!vnIlXu^Ym7iYmY~+PRvHvr>_inmd1OCpx1sDLU%fF)P S!b7P50000 literal 0 HcmV?d00001 diff --git a/admin-dev/themes/default/img/tree-multishop-root.png b/admin-dev/themes/default/img/tree-multishop-root.png new file mode 100644 index 0000000000000000000000000000000000000000..477e7da065c3d10a2b840a936fdb455435c347c6 GIT binary patch literal 565 zcmV-50?Pe~P)maL_nZxvLbQ&z5*e^ zl#5WdP)O6q z7>leSX)%atjW2@I9*e?eQJXdqJ%o^mHU&~jkCiD58g9feXksL6D9TKTtSs6HE$lI6 zHu)$Yq|J=?`FH-C#URn9|8h9b@7(|V=L42bYZ(UGE5WV?@bGsmx*O9M{g}o-j*w>i zChE)I#}qdT>t7(byn&o%zco!0omCi97hvXX---40 zZP>tnp(XP_^*umiFj(BBXvTwa^m_Fmw$o+{_k3)1XmoW#xX9H1|T0aI?D#A^Qf+MTmI3Ili|EA!d7m=(-hUDVAj2M>n&kT(BF*O4+-~*R1P!1#4IXTe!_*JjZKzZIZsu zaFmH!asir_#BBd7%ndTkK6(j3anXYfVqe8Nk^+pA&0{=g*HEoa7=y5)p{Y>G$#wxl z!?3jp3oaVpk?Dbw<$-%CmR`d#2EC1_>jn~u@5rg~<5Z(Sx)_?O!b$saWVp~28HP&& z{ZX}ZziRw8hGS*JFtC`KM^WQ_F#d=Pn3x%bLJcI^*QFz6c-;348WGBW{w{EVR!2mb zoB05am327-Dy=6z6(fQWnpOx=gkq3l8U%7>okk#D#K%V{C=say$chY(6}rpqwZkGT zvq-0at#>A2nkr}9FS_#WN=^*z&4uVDpKd1#b}yUC#rTi&Q-A>gX_iQEIL>6700000 LNkvXXu0mjfGd5k{ literal 0 HcmV?d00001 diff --git a/admin-dev/themes/default/img/tree-multishop-url.png b/admin-dev/themes/default/img/tree-multishop-url.png new file mode 100644 index 0000000000000000000000000000000000000000..c4f00cb2231006331f76e8fb34fe98ba3e6c4c5a GIT binary patch literal 649 zcmV;40(Sk0P)R-wVQ6jZWcdI0SI7HLUn+ckeHpfG*^&WNl8M*$>e_m+%~e1e$TbFH z(ca$P8S6G|`k$7O^&jK{CVU1k0`c?bFBpFR`o)l$lXnKleSPl2rS#vw|1$jj^9QU3 zuK|C7fi{2PLWYLgYKAv2UoylcB>fKv2xQp4Z3_cGzu;Pe0a#X1_5bVFum6KX!v1gG zy8ZvEHS7P!CnWywn=ol7P+AV;1xBO*o3?uOnoLGUMh1|?@87=}8tSSU5)xAxUcPwB zpslOV@ZSJml#C3+_wPS|_&dRXX88q- z<2Uc#FtD(3lrLGb_|3`F=ejkuv>3K*-o&7#twS)X7?&1GCf1H)a#g?Xt!HIMLl0ThjLv**n3J9+X%Fi7wZFolVUi|^~|Xai}!M<@(n=>QZp jqDXS@32BBI01#jRVaY0la%Xjb00000NkvXXu0mjfT~I+< literal 0 HcmV?d00001 diff --git a/admin-dev/themes/default/template/controllers/shop/content.tpl b/admin-dev/themes/default/template/controllers/shop/content.tpl new file mode 100644 index 000000000..ca58d318e --- /dev/null +++ b/admin-dev/themes/default/template/controllers/shop/content.tpl @@ -0,0 +1,42 @@ +{* +* 2007-2012 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2012 PrestaShop SA +* @version Release: $Revision: 14740 $ +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*} + +{if $toolbar_btn} + {include file="toolbar.tpl" toolbar_btn=$toolbar_btn toolbar_scroll=$toolbar_scroll title=$title} +{/if} + +
+
{l s='Multishop tree'}
+ {include file="controllers/shop/tree.tpl" selected_tree_id=$selected_tree_id} +
+
{$content}
+ + \ No newline at end of file diff --git a/admin-dev/themes/default/template/controllers/shop/helpers/form/form.tpl b/admin-dev/themes/default/template/controllers/shop/helpers/form/form.tpl index 227005779..b45b3d71f 100644 --- a/admin-dev/themes/default/template/controllers/shop/helpers/form/form.tpl +++ b/admin-dev/themes/default/template/controllers/shop/helpers/form/form.tpl @@ -25,13 +25,6 @@ *} {extends file="helpers/form/form.tpl"} -{block name="label"} - {if $input.type == 'text' && $input.name == 'name'} -
{l s='You can\'t change the shop group when you have more than one Shop'}

- {/if} - {$smarty.block.parent} -{/block} - {block name="input"} {if $input.type == 'theme'} {foreach $input.values as $theme} @@ -43,7 +36,7 @@ {/foreach}
 
{elseif $input.type == 'textShopGroup'} - {$input.value} +

{$input.value}

{else} {if $input.type == 'select' && $input.name == 'id_category'} \ No newline at end of file diff --git a/admin-dev/themes/default/template/controllers/shop_group/content.tpl b/admin-dev/themes/default/template/controllers/shop_group/content.tpl new file mode 100644 index 000000000..c364b671b --- /dev/null +++ b/admin-dev/themes/default/template/controllers/shop_group/content.tpl @@ -0,0 +1,27 @@ +{* +* 2007-2012 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2012 PrestaShop SA +* @version Release: $Revision: 14740 $ +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*} + +{include file="controllers/shop/content.tpl"} \ No newline at end of file diff --git a/admin-dev/themes/default/template/controllers/shop_group/helpers/form/form.tpl b/admin-dev/themes/default/template/controllers/shop_group/helpers/form/form.tpl index c4c0d3e77..39faab89b 100644 --- a/admin-dev/themes/default/template/controllers/shop_group/helpers/form/form.tpl +++ b/admin-dev/themes/default/template/controllers/shop_group/helpers/form/form.tpl @@ -47,15 +47,18 @@ } $(document).ready(function() { - toggleShareOrders(); - $('input[name=share_customer]').click(function() + if (!$("input[name=share_order]").prop('disabled')) { toggleShareOrders(); - }); - $('input[name=share_stock]').click(function() - { - toggleShareOrders(); - }); + $('input[name=share_customer]').click(function() + { + toggleShareOrders(); + }); + $('input[name=share_stock]').click(function() + { + toggleShareOrders(); + }); + } $('#useImportData').click(function() { $('#importList').slideToggle('slow'); @@ -63,15 +66,3 @@ }); {/block} - -{block name="label"} - - {if $input.type == 'text' && $input.name == 'name'} -
{l s='You can\'t edit the shop group when you have more than one shop'}

- {/if} - - {if isset($input.label)} - - {/if} - -{/block} \ No newline at end of file diff --git a/admin-dev/themes/default/template/controllers/shop_url/content.tpl b/admin-dev/themes/default/template/controllers/shop_url/content.tpl new file mode 100644 index 000000000..c364b671b --- /dev/null +++ b/admin-dev/themes/default/template/controllers/shop_url/content.tpl @@ -0,0 +1,27 @@ +{* +* 2007-2012 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2012 PrestaShop SA +* @version Release: $Revision: 14740 $ +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*} + +{include file="controllers/shop/content.tpl"} \ No newline at end of file diff --git a/admin-dev/themes/default/template/controllers/shop_url/helpers/form/form.tpl b/admin-dev/themes/default/template/controllers/shop_url/helpers/form/form.tpl index f04eb53b6..fe8a50fad 100644 --- a/admin-dev/themes/default/template/controllers/shop_url/helpers/form/form.tpl +++ b/admin-dev/themes/default/template/controllers/shop_url/helpers/form/form.tpl @@ -30,6 +30,29 @@ fillShopUrl(); checkMainUrlInfo(); $('#domain, #physical_uri, #virtual_uri').keyup(fillShopUrl); + + var change_domain_value = false; + $('#domain').keydown(function() + { + if (!$('#domain_ssl').val() || $('#domain_ssl').val() == $('#domain').val()) + { + change_domain_value = true; + } + }); + + $('#domain_ssl').keydown(function() + { + change_domain_value = false; + }); + + $('#domain').keyup(function() + { + if (change_domain_value) + { + change_domain_value = false; + $('#domain_ssl').val($('#domain').val()); + } + }); }); var shopUrl = {$js_shop_url}; diff --git a/admin-dev/themes/default/template/controllers/shop_url/helpers/list/list_content.tpl b/admin-dev/themes/default/template/controllers/shop_url/helpers/list/list_content.tpl new file mode 100644 index 000000000..f565dbe5b --- /dev/null +++ b/admin-dev/themes/default/template/controllers/shop_url/helpers/list/list_content.tpl @@ -0,0 +1,35 @@ +{* +* 2007-2012 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2012 PrestaShop SA +* @version Release: $Revision: 9608 $ +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*} + +{extends file="helpers/list/list_content.tpl"} + +{block name="td_content"} + {if $key == 'url'} + {$tr.$key} + {else} + {$smarty.block.parent} + {/if} +{/block} \ No newline at end of file diff --git a/admin-dev/themes/default/template/helpers/form/form.tpl b/admin-dev/themes/default/template/helpers/form/form.tpl index ea3b7c1b1..f1aaf0bf0 100644 --- a/admin-dev/themes/default/template/helpers/form/form.tpl +++ b/admin-dev/themes/default/template/helpers/form/form.tpl @@ -43,7 +43,7 @@ {if isset($field.image)}{$field.title|escape:'htmlall':'UTF-8'}}{/if} {$field.title} - {elseif $key == 'description'} + {elseif $key == 'description' && $field}

{$field}

{elseif $key == 'input'} {foreach $field as $input} @@ -378,7 +378,7 @@ {/if} {block name="other_fieldsets"}{/block} - {if isset($fields[$f+1])}
{/if} + {if isset($fields[$f+1])}
{/if} {/foreach} {/block} diff --git a/admin-dev/themes/default/template/helpers/list/list_header.tpl b/admin-dev/themes/default/template/helpers/list/list_header.tpl index ba63e20a1..a7f1abc93 100644 --- a/admin-dev/themes/default/template/helpers/list/list_header.tpl +++ b/admin-dev/themes/default/template/helpers/list/list_header.tpl @@ -133,7 +133,7 @@ {/if} - + {if $has_bulk_actions} @@ -149,7 +149,7 @@
- {else} + {elseif !$simple_header}
  {/if} @@ -165,7 +165,7 @@ {/if} {if $has_actions} - {l s='Actions'}
  + {l s='Actions'}{if !$simple_header}
 {/if} {/if} {if !$simple_header} diff --git a/admin-dev/themes/default/template/helpers/options/options.tpl b/admin-dev/themes/default/template/helpers/options/options.tpl index 731991f2b..919d32b16 100644 --- a/admin-dev/themes/default/template/helpers/options/options.tpl +++ b/admin-dev/themes/default/template/helpers/options/options.tpl @@ -70,7 +70,7 @@
{if ($field['multishop_default'])}
- +
{/if} {block name="label"} diff --git a/classes/AdminTab.php b/classes/AdminTab.php index 542bcf6b9..224acb996 100644 --- a/classes/AdminTab.php +++ b/classes/AdminTab.php @@ -957,7 +957,7 @@ abstract class AdminTabCore /* Check required fields */ foreach ($fields as $field => $values) - if (isset($values['required']) && $values['required'] && !isset($_POST['configUseDefault'][$field])) + if (isset($values['required']) && $values['required'] && !empty($_POST['multishopOverrideOption'][$field])) if (isset($values['type']) && $values['type'] == 'textLang') { foreach ($languages as $language) @@ -985,14 +985,14 @@ abstract class AdminTabCore if (!Tools::getValue($field) && isset($values['default'])) $_POST[$field] = $values['default']; - if (1||!count($this->_errors)) + if (!count($this->_errors)) { foreach ($fields as $key => $options) { if (isset($options['visibility']) && $options['visibility'] > Shop::getContext()) continue; - if (Shop::isFeatureActive() && isset($_POST['configUseDefault'][$key])) + if (Shop::isFeatureActive() && empty($_POST['multishopOverrideOption'][$key])) { Configuration::deleteFromContext($key); continue; @@ -1891,7 +1891,7 @@ abstract class AdminTabCore if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_ALL && !$isInvisible) echo '
'; diff --git a/classes/Collection.php b/classes/Collection.php index a76cf9b19..a8476bc96 100644 --- a/classes/Collection.php +++ b/classes/Collection.php @@ -346,6 +346,19 @@ class CollectionCore implements Iterator, ArrayAccess, Countable return $this; } + /** + * Retrieve the first result + * + * @return ObjectModel + */ + public function getFirst() + { + $this->getAll(); + if (!count($this)) + return false; + return $this[0]; + } + /** * Get results array * diff --git a/classes/Tab.php b/classes/Tab.php index 2e0f926b2..2d0ef336a 100644 --- a/classes/Tab.php +++ b/classes/Tab.php @@ -89,6 +89,7 @@ class TabCore extends ObjectModel $class_name = Tab::getClassNameById($this->id_parent); if (isset($retro[$class_name])) $this->id_parent = Tab::getIdFromClassName($retro[$class_name]); + self::$_cache_tabs = array(); // Set good position for new tab $this->position = Tab::getNewLastPosition($this->id_parent); @@ -220,7 +221,7 @@ class TabCore extends ObjectModel * * @return array tabs */ - static $_cache_tabs = array(); + protected static $_cache_tabs = array(); public static function getTabs($id_lang, $id_parent = null) { if (!isset(self::$_cache_tabs[$id_lang])) @@ -479,6 +480,7 @@ class TabCore extends ObjectModel if ($current_tab->id_parent != $this->id_parent) $this->position = Tab::getNewLastPosition($this->id_parent); + self::$_cache_tabs = array(); return parent::update($null_values); } diff --git a/classes/controller/AdminController.php b/classes/controller/AdminController.php index 6cd82ac66..1912a1c3d 100644 --- a/classes/controller/AdminController.php +++ b/classes/controller/AdminController.php @@ -866,7 +866,7 @@ class AdminControllerCore extends Controller continue; // Check if field is required - if (isset($values['required']) && $values['required'] && !isset($_POST['configUseDefault'][$field])) + if (isset($values['required']) && $values['required'] && !empty($_POST['multishopOverrideOption'][$field])) if (isset($values['type']) && $values['type'] == 'textLang') { foreach ($languages as $language) @@ -897,10 +897,10 @@ class AdminControllerCore extends Controller { foreach ($fields as $key => $options) { - if (Shop::isFeatureActive() && isset($options['visibility']) && ($options['visibility'] > Shop::getContext())) + if (Shop::isFeatureActive() && isset($options['visibility']) && $options['visibility'] > Shop::getContext()) continue; - if (Shop::isFeatureActive() && isset($_POST['configUseDefault'][$key])) + if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_ALL && empty($_POST['multishopOverrideOption'][$key])) { Configuration::deleteFromContext($key); continue; @@ -941,11 +941,11 @@ class AdminControllerCore extends Controller } } } + if (empty($this->errors)) $this->confirmations[] = $this->_conf[6]; } - /** * assign default action in toolbar_btn smarty var, if they are not set. * uses override to specifically add, modify or remove items diff --git a/classes/shop/Shop.php b/classes/shop/Shop.php index 751f60b8b..f11f6e507 100644 --- a/classes/shop/Shop.php +++ b/classes/shop/Shop.php @@ -198,6 +198,7 @@ class ShopCore extends ObjectModel /** * Detect dependency with customer or orders * + * @param int $id_shop * @return bool */ public static function hasDependency($id_shop) @@ -224,17 +225,6 @@ class ShopCore extends ObjectModel return $has_dependency; } - /** - * Get a new instance of a shop - * - * @param int $id shop ID - * @return Shop - */ - public static function getInstance($id) - { - return new Shop($id); - } - /** * Find the shop from current domain / uri and get an instance of this shop * if INSTALL_VERSION is defined, will return an empty shop object @@ -533,6 +523,25 @@ class ShopCore extends ObjectModel return $results; } + /** + * Get a collection of shops + * + * @param bool $active + * @param int $id_shop_group + * @return Collection + */ + public static function getShopsCollection($active = true, $id_shop_group = null) + { + $shops = new Collection('Shop'); + if ($active) + $shops->where('active', '=', 1); + + if ($id_shop_group) + $shops->where('id_shop_group', '=', $id_shop_group); + + return $shops; + } + /** * Return some informations cached for one shop * @@ -749,15 +758,11 @@ class ShopCore extends ObjectModel } /** - * @return bool Return true if there is more than one shop + * @return bool Return true if multishop feature is activated */ public static function isFeatureActive() { - static $total = null; - - if (is_null($total)) - $total = Db::getInstance()->getValue('SELECT COUNT(*) FROM '._DB_PREFIX_.'shop'); - return ($total > 1) ? true : false; + return Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE'); } public function copyShopData($old_id, $tables_import = false, $deleted = false) @@ -813,16 +818,6 @@ class ShopCore extends ObjectModel ), $m['id_module']); } - public function checkIfShopExist($id) - { - return (int)Db::getInstance()->getValue('SELECT COUNT(*) FROM`'._DB_PREFIX_.'shop` WHERE `id_shop` = '.(int)$id); - } - - public function checkIfShopGroupExist($id) - { - return (int)Db::getInstance()->getValue('SELECT COUNT(*) FROM`'._DB_PREFIX_.'shop_group` WHERE `id_shop_group` = '.(int)$id); - } - /** * @deprecated 1.5.0 Use shop->id */ diff --git a/classes/shop/ShopGroup.php b/classes/shop/ShopGroup.php index bf6c01ca1..f5acd3565 100644 --- a/classes/shop/ShopGroup.php +++ b/classes/shop/ShopGroup.php @@ -108,4 +108,42 @@ class ShopGroupCore extends ObjectModel WHERE name = \''.pSQL($name).'\''; return (int)Db::getInstance()->getValue($sql); } + + /** + * Detect dependency with customer or orders + * + * @param int $id_shop_group + * @param string $check all|customer|order + * @return bool + */ + public static function hasDependency($id_shop_group, $check = 'all') + { + $list_shops = Shop::getShops(false, $id_shop_group, true); + if (!$list_shops) + return false; + + if ($check == 'all' || $check == 'customer') + { + $total_customer = (int)Db::getInstance()->getValue(' + SELECT count(*) + FROM `'._DB_PREFIX_.'customer` + WHERE `id_shop` IN ('.implode(', ', $list_shops).')' + ); + if ($total_customer) + return true; + } + + if ($check == 'all' || $check == 'order') + { + $total_order = (int)Db::getInstance()->getValue(' + SELECT count(*) + FROM `'._DB_PREFIX_.'orders` + WHERE `id_shop` IN ('.implode(', ', $list_shops).')' + ); + if ($total_order) + return true; + } + + return false; + } } diff --git a/classes/shop/ShopUrl.php b/classes/shop/ShopUrl.php index b1331b9ce..25c2bbabc 100644 --- a/classes/shop/ShopUrl.php +++ b/classes/shop/ShopUrl.php @@ -164,41 +164,4 @@ class ShopUrlCore extends ObjectModel } return self::$main_domain_ssl; } - - /** - * @static - * @param string $virtual_uri - * @param int $id_shop_tested - * @return bool - */ - public static function virtualUriExists($virtual_uri, $id_shop_tested) - { - Tools::displayAsDeprecated(); - $virtual_uri = trim($virtual_uri); - if (substr($virtual_uri, -1) != '/') - $virtual_uri .= '/'; - return (bool) Db::getInstance()->getValue(' - SELECT `virtual_uri` - FROM `'._DB_PREFIX_.'shop_url` - WHERE `id_shop` != '.(int)$id_shop_tested.' - AND `virtual_uri` = "'.pSQL($virtual_uri).'"'); - } - - /** - * @static - * @param string $domain - * @param string $physical_uri - * @param string $virtual_uri - * @param int $id_shop_tested - * @return bool - */ - public static function urlExists($domain, $physical_uri, $virtual_uri, $id_shop_tested) - { - $url = $domain.$physical_uri.$virtual_uri; - return (bool)Db::getInstance()->getValue(' - SELECT * - FROM `'._DB_PREFIX_.'shop_url` - WHERE `id_shop` != '.(int)$id_shop_tested.' - AND CONCAT(`domain`, `physical_uri`, `virtual_uri`) ="'.pSQL($url).'"'); - } } diff --git a/controllers/admin/AdminMetaController.php b/controllers/admin/AdminMetaController.php index a67c0fd31..b2115c743 100644 --- a/controllers/admin/AdminMetaController.php +++ b/controllers/admin/AdminMetaController.php @@ -112,6 +112,46 @@ class AdminMetaControllerCore extends AdminController if (isset($robots_submit)) $robots_options['submit'] = $robots_submit; + // Options for shop URL if multishop is disabled + $shop_url_options = array( + 'title' => $this->l('Set shop URL'), + 'fields' => array(), + ); + + if (!Shop::isFeatureActive()) + { + $this->url = ShopUrl::getShopUrls($this->context->shop->id)->where('main', '=', 1)->getFirst(); + if ($this->url) + { + $shop_url_options['description'] = $this->l('You can set here the URL for your shop. If you migrate your shop to a new URL, remember to change the values bellow.'); + $shop_url_options['fields'] = array( + 'domain' => array( + 'title' => $this->l('Shop domain'), + 'validation' => 'isString', + 'type' => 'text', + 'size' => 70, + 'defaultValue' => $this->url->domain, + ), + 'domain_ssl' => array( + 'title' => $this->l('SSL domain'), + 'validation' => 'isString', + 'type' => 'text', + 'size' => 70, + 'defaultValue' => $this->url->domain_ssl, + ), + 'uri' => array( + 'title' => $this->l('Base URI'), + 'validation' => 'isString', + 'type' => 'text', + 'size' => 70, + 'defaultValue' => $this->url->physical_uri, + ), + ); + } + } + else + $shop_url_options['description'] = $this->l('Multishop option is enabled, if you want to change the url of your shop you have to go in "Advanced parameters" -> "multishop" tab.'); + // List of options $this->fields_options = array( 'general' => array( @@ -120,12 +160,13 @@ class AdminMetaControllerCore extends AdminController 'fields' => $general_fields, 'submit' => array() ), - 'robots' => $robots_options, + 'shop_url' => $shop_url_options, 'routes' => array( 'title' => $this->l('Schema of URLs'), 'description' => $this->l('Change the pattern of your links. There are some available keywords for each route listed below, keywords with * are required. To add a keyword in your URL use {keyword} syntax. You can add some text before or after the keyword IF the keyword is not empty with syntax {prepend:keyword:append}, for example {-hey-:meta_title} will add "-hey-my-title" in URL if meta title is set, or nothing. Friendly URL and rewriting Apache option must be activated on your web server to use this functionality.'), 'fields' => array(), ), + 'robots' => $robots_options, ); // Add display route options to options form @@ -147,20 +188,20 @@ class AdminMetaControllerCore extends AdminController $this->addJqueryUi('ui.widget'); $this->addJqueryPlugin('tagify'); } - - public function addFieldRoute($routeID, $title) + + public function addFieldRoute($route_id, $title) { $keywords = array(); - foreach (Dispatcher::getInstance()->default_routes[$routeID]['keywords'] as $keyword => $data) + foreach (Dispatcher::getInstance()->default_routes[$route_id]['keywords'] as $keyword => $data) $keywords[] = ((isset($data['param'])) ? ''.$keyword.'*' : $keyword); - $this->fields_options['routes']['fields']['PS_ROUTE_'.$routeID] = array( + $this->fields_options['routes']['fields']['PS_ROUTE_'.$route_id] = array( 'title' => $title, 'desc' => sprintf($this->l('Keywords: %s'), implode(', ', $keywords)), 'validation' => 'isString', 'type' => 'text', 'size' => 70, - 'defaultValue' => Dispatcher::getInstance()->default_routes[$routeID]['rule'], + 'defaultValue' => Dispatcher::getInstance()->default_routes[$route_id]['rule'], ); } @@ -363,29 +404,29 @@ class AdminMetaControllerCore extends AdminController /** * Validate route syntax and save it in configuration * - * @param string $routeID + * @param string $route_id */ - public function checkAndUpdateRoute($routeID) + public function checkAndUpdateRoute($route_id) { $default_routes = Dispatcher::getInstance()->default_routes; - if (!isset($default_routes[$routeID])) + if (!isset($default_routes[$route_id])) return; - $rule = Tools::getValue('PS_ROUTE_'.$routeID); - if (!$rule || $rule == $default_routes[$routeID]['rule']) + $rule = Tools::getValue('PS_ROUTE_'.$route_id); + if (!$rule || $rule == $default_routes[$route_id]['rule']) { - Configuration::updateValue('PS_ROUTE_'.$routeID, ''); + Configuration::updateValue('PS_ROUTE_'.$route_id, ''); return; } $errors = array(); - if (!Dispatcher::getInstance()->validateRoute($routeID, $rule, $errors)) + if (!Dispatcher::getInstance()->validateRoute($route_id, $rule, $errors)) { foreach ($errors as $error) - $this->errors[] = sprintf('Keyword "{%1$s}" required for route "%2$s" (rule: "%3$s")', $error, $routeID, htmlspecialchars($rule)); + $this->errors[] = sprintf('Keyword "{%1$s}" required for route "%2$s" (rule: "%3$s")', $error, $route_id, htmlspecialchars($rule)); } else - Configuration::updateValue('PS_ROUTE_'.$routeID, $rule); + Configuration::updateValue('PS_ROUTE_'.$route_id, $rule); } /** @@ -432,6 +473,42 @@ class AdminMetaControllerCore extends AdminController $this->checkAndUpdateRoute('cms_category_rule'); } + /** + * Update shop domain (for mono shop) + */ + public function updateOptionDomain($value) + { + if (!Shop::isFeatureActive() && $this->url && $this->url->domain != $value) + { + $this->url->domain = $value; + $this->url->update(); + } + } + + /** + * Update shop SSL domain (for mono shop) + */ + public function updateOptionDomainSsl($value) + { + if (!Shop::isFeatureActive() && $this->url && $this->url->domain_ssl != $value) + { + $this->url->domain_ssl = $value; + $this->url->update(); + } + } + + /** + * Update shop physical uri for mono shop) + */ + public function updateOptionUri($value) + { + if (!Shop::isFeatureActive() && $this->url && $this->url->physical_uri != $value) + { + $this->url->physical_uri = $value; + $this->url->update(); + } + } + /** * Function used to render the options for this controller */ diff --git a/controllers/admin/AdminPreferencesController.php b/controllers/admin/AdminPreferencesController.php index 0ffe33d1e..47f507047 100644 --- a/controllers/admin/AdminPreferencesController.php +++ b/controllers/admin/AdminPreferencesController.php @@ -34,8 +34,6 @@ class AdminPreferencesControllerCore extends AdminController $this->className = 'Configuration'; $this->table = 'configuration'; - - // Prevent classes which extend AdminPreferences to load useless data if (get_class($this) == 'AdminPreferencesController') { @@ -88,6 +86,14 @@ class AdminPreferencesControllerCore extends AdminController 'cast' => 'intval', 'type' => 'bool' ), + 'PS_MULTISHOP_FEATURE_ACTIVE' => array( + 'title' => $this->l('Enable multishop'), + 'desc' => $this->l('Multishop feature allows you to manage several shops with one back-office. If this feature is enabled, a tab "multishop" will be available in "advanced parameters" menu.'), + 'validation' => 'isBool', + 'cast' => 'intval', + 'type' => 'bool', + 'visibility' => Shop::CONTEXT_ALL + ), ); // No HTTPS activation if you haven't already. @@ -110,4 +116,18 @@ class AdminPreferencesControllerCore extends AdminController parent::__construct(); } + + /** + * Enable / disable multishop menu if multishop feature is activated + * + * @param string $value + */ + public function updateOptionPsMultishopFeatureActive($value) + { + Configuration::updateValue('PS_MULTISHOP_FEATURE_ACTIVE', $value); + + $tab = Tab::getInstanceFromClassName('AdminShopGroup'); + $tab->active = (bool)Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE'); + $tab->update(); + } } diff --git a/controllers/admin/AdminShopController.php b/controllers/admin/AdminShopController.php index ee41b1a72..7e5076343 100755 --- a/controllers/admin/AdminShopController.php +++ b/controllers/admin/AdminShopController.php @@ -33,7 +33,8 @@ class AdminShopControllerCore extends AdminController $this->context = Context::getContext(); $this->table = 'shop'; $this->className = 'Shop'; - $this->multishop_context = Shop::CONTEXT_ALL | Shop::CONTEXT_GROUP; + $this->multishop_context = Shop::CONTEXT_ALL; + $this->id_shop_group = Tools::getValue('id_shop_group'); $this->fields_list = array( 'id_shop' => array( @@ -53,7 +54,7 @@ class AdminShopControllerCore extends AdminController 'filter_key' => 'gs!name' ), 'category_name' => array( - 'title' => $this->l('Category Root'), + 'title' => $this->l('Category'), 'width' => 150, 'filter_key' => 'cl!name' ), @@ -72,31 +73,20 @@ class AdminShopControllerCore extends AdminController ) ); - $this->bulk_actions = array('delete' => array('text' => $this->l('Delete selected'),'confirm' => $this->l('Delete selected items?'))); - - $this->fields_options = array( - 'general' => array( - 'title' => $this->l('Shops options'), - 'fields' => array( - 'PS_SHOP_DEFAULT' => array( - 'title' => $this->l('Default shop:'), - 'desc' => $this->l('The default shop'), - 'cast' => 'intval', - 'type' => 'select', - 'identifier' => 'id_shop', - 'list' => Shop::getShops(), - 'visibility' => Shop::CONTEXT_ALL - ) - ), - 'submit' => array() - ) - ); - parent::__construct(); } public function initToolbar() { + if (!$this->id_shop_group && $this->object && $this->object->id_shop_group) + $this->id_shop_group = $this->object->id_shop_url; + + if (!$this->display && $this->id_shop_group) + $this->toolbar_btn['edit'] = array( + 'desc' => $this->l('Edit this shop group'), + 'href' => $this->context->link->getAdminLink('AdminShopGroup').'&updateshop_group&id_shop_group='.$this->id_shop_group, + ); + if ($this->display == 'edit' || $this->display == 'add') { if ($shop = $this->loadObject(true)) @@ -111,21 +101,11 @@ class AdminShopControllerCore extends AdminController 'desc' => $this->l('Delete this shop'), 'confirm' => 1); - // adding button for preview this shop - if ($url_preview = $shop->getBaseURL()) - $this->toolbar_btn['preview'] = array( - 'href' => $url_preview, - 'desc' => $this->l('Home page'), - 'target' => true, - 'class' => 'previewUrl' - ); - $this->toolbar_btn['new-url'] = array( - 'href' => $this->context->link->getAdminLink('AdminShopUrl').'&id_shop='.$shop->id.'&addshop_url', - 'desc' => $this->l('Add URL'), - 'class' => 'addShopUrl' - ); - + 'href' => $this->context->link->getAdminLink('AdminShopUrl').'&id_shop='.$shop->id.'&addshop_url', + 'desc' => $this->l('Add URL'), + 'class' => 'addShopUrl' + ); } if ($this->tabAccess['edit']) @@ -147,6 +127,41 @@ class AdminShopControllerCore extends AdminController parent::initToolbar(); $this->context->smarty->assign('toolbar_scroll', 1); + + $this->show_toolbar = false; + if (isset($this->toolbar_btn['new'])) + $this->toolbar_btn['new'] = array( + 'desc' => $this->l('Add new shop'), + 'href' => $this->context->link->getAdminLink('AdminShop').'&add'.$this->table.'&id_shop_group='.$this->id_shop_group, + ); + + if (isset($this->toolbar_btn['back'])) + $this->toolbar_btn['back']['href'] .= '&id_shop_group='.$this->id_shop_group; + } + + public function initContent() + { + $this->list_simple_header = true; + parent::initContent(); + + $this->addJqueryPlugin('cookie'); + $this->addJqueryPlugin('jstree'); + $this->addCSS(_PS_JS_DIR_.'jquery/plugins/jstree/themes/classic/style.css'); + + if ($this->display == 'edit') + $this->toolbar_title[] = $this->object->name; + else if (!$this->display && $this->id_shop_group) + { + $group = new ShopGroup($this->id_shop_group); + $this->toolbar_title[] = $group->name; + } + + $this->context->smarty->assign(array( + 'toolbar_scroll' => 1, + 'toolbar_btn' => $this->toolbar_btn, + 'title' => $this->toolbar_title, + 'selected_tree_id' => ($this->display == 'edit' ? 'tree-shop-'.$this->id_object : (Tools::getValue('id_shop_group') ? 'tree-group-'.Tools::getValue('id_shop_group') : '')), + )); } public function renderList() @@ -165,6 +180,9 @@ class AdminShopControllerCore extends AdminController '; $this->_group = 'GROUP BY a.id_shop'; + if ($id_shop_group = (int)Tools::getValue('id_shop_group')) + $this->_where = 'AND a.id_shop_group = '.$id_shop_group; + return parent::renderList(); } @@ -204,7 +222,12 @@ class AdminShopControllerCore extends AdminController if ($this->errors) return false; - return parent::postProcess(); + $result = parent::postProcess(); + + if ($this->redirect_after) + $this->redirect_after .= '&id_shop_group='.$this->id_shop_group; + + return $result; } public function processDelete() @@ -274,33 +297,37 @@ class AdminShopControllerCore extends AdminController ) ); - if (Shop::getTotalShops() > 1 && $obj->id) + $display_group_list = true; + if ($this->display == 'edit') { - $shop_group = new ShopGroup($obj->id_shop_group); - $this->fields_form['input'][] = array( - 'type' => 'hidden', - 'name' => 'id_shop_group', - 'default' => $shop_group->name - ); - $this->fields_form['input'][] = array( - 'type' => 'textShopGroup', - 'label' => $this->l('Group Shop:'), - 'name' => 'id_shop_group', - 'value' => $shop_group->name - ); + $group = new ShopGroup($obj->id_shop_group); + if ($group->share_customer || $group->share_order || $group->share_stock) + $display_group_list = false; } - else + + if (!$display_group_list) { $options = array(); foreach (ShopGroup::getShopGroups() as $group) + { + if ($this->display == 'edit' && ($group->share_customer || $group->share_order || $group->share_stock) && ShopGroup::hasDependency($group->id)) + continue; + $options[] = array( 'id_shop_group' => $group->id, 'name' => $group->name, ); + } + + if ($this->display == 'add') + $group_desc = $this->l('Warning: you won\'t be able to change the group of this shop if this shop belong to a group with one of these options "share customers" or "share quantities" or "share orders" activated.'); + else + $group_desc = $this->l('You can only move your shop to a shop group with all "share" options disabled or to a shop group with no customers / orders.'); $this->fields_form['input'][] = array( 'type' => 'select', 'label' => $this->l('Group Shop:'), + 'desc' => $group_desc, 'name' => 'id_shop_group', 'options' => array( 'query' => $options, @@ -309,6 +336,22 @@ class AdminShopControllerCore extends AdminController ), ); } + else + { + $this->fields_form['input'][] = array( + 'type' => 'hidden', + 'name' => 'id_shop_group', + 'default' => $group->name + ); + $this->fields_form['input'][] = array( + 'type' => 'textShopGroup', + 'label' => $this->l('Shop group:'), + 'desc' => $this->l('You can\'t edit the shop group because the current shop belongs to a group with a "share" option enabled.'), + 'name' => 'id_shop_group', + 'value' => $group->name + ); + } + $categories = Category::getRootCategories($this->context->language->id); $this->fields_form['input'][] = array( 'type' => 'select', @@ -558,4 +601,111 @@ class AdminShopControllerCore extends AdminController $helper = new Helper(); return $helper->renderCategoryTree($root_category, $selected_cat, 'categoryBox', false, true); } + + public function ajaxProcessTree() + { + $tree = array(); + $sql = 'SELECT g.id_shop_group, g.name as group_name, s.id_shop, s.name as shop_name, u.id_shop_url, u.domain, u.physical_uri, u.virtual_uri + FROM '._DB_PREFIX_.'shop_group g + LEFT JOIN '._DB_PREFIX_.'shop s ON g.id_shop_group = s.id_shop_group + LEFT JOIN '._DB_PREFIX_.'shop_url u ON u.id_shop = s.id_shop + ORDER BY g.name, s.name, u.main, u.domain'; + $results = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); + foreach ($results as $row) + { + $id_shop_group = $row['id_shop_group']; + $id_shop = $row['id_shop']; + $id_shop_url = $row['id_shop_url']; + + // Group list + if (!isset($tree[$id_shop_group])) + $tree[$id_shop_group] = array( + 'data' => array( + 'title' => ''.$this->l('Group:').' '.$row['group_name'], + 'icon' => 'themes/'.$this->context->employee->bo_theme.'/img/tree-multishop-groups.png', + 'attr' => array( + 'href' => $this->context->link->getAdminLink('AdminShop').'&id_shop_group='.$id_shop_group, + 'title' => sprintf($this->l('Click here to display shops of group %s'), $row['group_name']), + ) + ), + 'attr' => array( + 'id' => 'tree-group-'.$id_shop_group, + ), + 'children' => array(), + ); + + // Shop list + if (!$id_shop) + continue; + + if (!isset($tree[$id_shop_group]['children'][$id_shop])) + $tree[$id_shop_group]['children'][$id_shop] = array( + 'data' => array( + 'title' => $row['shop_name'], + 'icon' => 'themes/'.$this->context->employee->bo_theme.'/img/tree-multishop-shop.png', + 'attr' => array( + 'href' => $this->context->link->getAdminLink('AdminShopUrl').'&id_shop='.$id_shop, + 'title' => sprintf($this->l('Click here to display URLs of shop %s'), $row['shop_name']), + ) + ), + 'attr' => array( + 'id' => 'tree-shop-'.$id_shop, + ), + 'children' => array(), + ); + + // Url list + if (!$id_shop_url) + continue; + + if (!isset($tree[$id_shop_group]['children'][$id_shop]['children'][$id_shop_url])) + { + $url = $row['domain'].$row['physical_uri'].$row['virtual_uri']; + if (strlen($url) > 23) + $url = substr($url, 0, 23).'...'; + + $tree[$id_shop_group]['children'][$id_shop]['children'][$id_shop_url] = array( + 'data' => array( + 'title' => $url, + 'icon' => 'themes/'.$this->context->employee->bo_theme.'/img/tree-multishop-url.png', + 'attr' => array( + 'href' => $this->context->link->getAdminLink('AdminShopUrl').'&updateshop_url&id_shop_url='.$id_shop_url, + 'title' => $row['domain'].$row['physical_uri'].$row['virtual_uri'], + ) + ), + 'attr' => array( + 'id' => 'tree-url-'.$id_shop_url, + ), + ); + } + } + + // jstree need to have children as array and not object, so we use sort to get clean keys + // DO NOT REMOVE this code, even if it seems really strange ;) + sort($tree); + foreach ($tree as &$groups) + { + sort($groups['children']); + foreach ($groups['children'] as &$shops) + sort($shops['children']); + } + + $tree = array(array( + 'data' => array( + 'title' => ''.$this->l('Shop groups list').'', + 'icon' => 'themes/'.$this->context->employee->bo_theme.'/img/tree-multishop-root.png', + 'attr' => array( + 'href' => $this->context->link->getAdminLink('AdminShopGroup'), + 'title' => $this->l('Click here to display group shops list'), + ) + ), + 'attr' => array( + 'id' => 'tree-root', + ), + 'state' => 'open', + 'children' => $tree, + )); + + die(Tools::jsonEncode($tree)); + } } diff --git a/controllers/admin/AdminShopGroupController.php b/controllers/admin/AdminShopGroupController.php index 94d9cc4b2..2342582e3 100644 --- a/controllers/admin/AdminShopGroupController.php +++ b/controllers/admin/AdminShopGroupController.php @@ -36,9 +36,7 @@ class AdminShopGroupControllerCore extends AdminController $this->multishop_context = Shop::CONTEXT_ALL; $this->addRowAction('edit'); - $this->addRowAction('delete'); - $this->bulk_actions = array('delete' => array('text' => $this->l('Delete selected'),'confirm' => $this->l('Delete selected items?'))); $this->context = Context::getContext(); @@ -67,15 +65,62 @@ class AdminShopGroupControllerCore extends AdminController ), ); + $this->fields_options = array( + 'general' => array( + 'title' => $this->l('Multishop options'), + 'fields' => array( + 'PS_SHOP_DEFAULT' => array( + 'title' => $this->l('Default shop:'), + 'cast' => 'intval', + 'type' => 'select', + 'identifier' => 'id_shop', + 'list' => Shop::getShops(), + 'visibility' => Shop::CONTEXT_ALL + ) + ), + 'submit' => array() + ) + ); + parent::__construct(); } + public function initToolbar() + { + parent::initToolbar(); + + $this->show_toolbar = false; + if (isset($this->toolbar_btn['new'])) + $this->toolbar_btn['new']['desc'] = $this->l('Add new shop group'); + } + + public function initContent() + { + $this->list_simple_header = true; + parent::initContent(); + + $this->addJqueryPlugin('cookie'); + $this->addJqueryPlugin('jstree'); + $this->addCSS(_PS_JS_DIR_.'jquery/plugins/jstree/themes/classic/style.css'); + + if ($this->display == 'edit') + $this->toolbar_title[] = $this->object->name; + + $this->context->smarty->assign(array( + 'toolbar_scroll' => 1, + 'toolbar_btn' => $this->toolbar_btn, + 'title' => $this->toolbar_title, + 'selected_tree_id' => ($this->display == 'edit') ? 'tree-group-'.$this->id_object : 'tree-root', + )); + } + public function renderForm() { $this->fields_form = array( 'legend' => array( 'title' => $this->l('ShopGroup') ), + 'description' => $this->l('Warning: it is not recommended to enable the "share customers" and "share orders" options, because once they are activated and customers or orders are created, you won\'t be able to disable these options anymore. If you need these options, try to first consider using several categories instead on several shops.'), 'input' => array( array( 'type' => 'text', @@ -90,6 +135,7 @@ class AdminShopGroupControllerCore extends AdminController 'required' => true, 'class' => 't', 'is_bool' => true, + 'disabled' => ($this->display == 'edit' && ShopGroup::hasDependency($this->id_object, 'customer')) ? true : false, 'values' => array( array( 'id' => 'share_customer_on', @@ -102,7 +148,7 @@ class AdminShopGroupControllerCore extends AdminController 'label' => $this->l('Disabled') ) ), - 'desc' => $this->l('Share customers between shops of this group') + 'desc' => $this->l('Once the option is enabled, the shops in this group will share their customers: if a customer registers on one of this group\'s shops, the account will automatically be available on the others shops of this goup. Warning: you won\'t be able to disable this option once you have customers registered on at least one shop of this group.'), ), array( 'type' => 'radio', @@ -132,6 +178,7 @@ class AdminShopGroupControllerCore extends AdminController 'required' => true, 'class' => 't', 'is_bool' => true, + 'disabled' => ($this->display == 'edit' && ShopGroup::hasDependency($this->id_object, 'order')) ? true : false, 'values' => array( array( 'id' => 'share_order_on', @@ -144,8 +191,7 @@ class AdminShopGroupControllerCore extends AdminController 'label' => $this->l('Disabled') ) ), - 'desc' => - $this->l('Share orders and carts between shops of this group (you can share orders only if you share customers and available quantities)') + 'desc' => $this->l('Once this option is enabled (which is only possible if customers and available quantities are shared among shops), the customer\'s cart will be shared among all the shops in this group. This way, any purchase started on one of the shops in this group will be able to be finished in another shop from the same shop group. Warning: you won\'t be able to disable this option once you have orders on at least one shop of this group.') ), array( 'type' => 'radio', @@ -188,18 +234,6 @@ class AdminShopGroupControllerCore extends AdminController else $disabled = false; - $import_data = array( - 'attribute_group' => $this->l('Attribute groups'), - 'attribute' => $this->l('Attributes'), - //'customer_group' => $this->l('Customer groups'), - 'feature' => $this->l('Features'), - 'group' => $this->l('Groups'), - 'manufacturer' => $this->l('Manufacturers'), - 'supplier' => $this->l('Suppliers'), - 'tax_rules_group' => $this->l('Tax rule groups'), - 'zone' => $this->l('Zones'), - ); - $default_shop = new Shop(Configuration::get('PS_SHOP_DEFAULT')); $this->tpl_form_vars = array( 'disabled' => $disabled, diff --git a/controllers/admin/AdminShopUrlController.php b/controllers/admin/AdminShopUrlController.php index 300d08148..d968c17af 100644 --- a/controllers/admin/AdminShopUrlController.php +++ b/controllers/admin/AdminShopUrlController.php @@ -34,6 +34,7 @@ class AdminShopUrlControllerCore extends AdminController $this->lang = false; $this->requiredDatabase = true; $this->multishop_context = Shop::CONTEXT_ALL; + $this->id_shop = Tools::getValue('id_shop'); $this->context = Context::getContext(); @@ -51,20 +52,9 @@ class AdminShopUrlControllerCore extends AdminController 'width' => 150, 'filter_key' => 's!name' ), - 'domain' => array( - 'title' => $this->l('Domain'), - 'width' => 'auto', - 'filter_key' => 'domain' - ), - 'domain_ssl' => array( - 'title' => $this->l('Domain SSL'), - 'width' => 130, - 'filter_key' => 'domain' - ), - 'uri' => array( - 'title' => $this->l('URI'), - 'width' => 200, - 'filter_key' => 'uri', + 'url' => array( + 'title' => $this->l('URL'), + 'filter_key' => 'url', 'havingFilter' => true ), 'main' => array( @@ -86,7 +76,6 @@ class AdminShopUrlControllerCore extends AdminController 'width' => 50, ), ); - $this->bulk_actions = array('delete' => array('text' => $this->l('Delete selected'), 'confirm' => $this->l('Delete selected items?'))); parent::__construct(); } @@ -98,126 +87,143 @@ class AdminShopUrlControllerCore extends AdminController $this->addRowAction('edit'); $this->addRowAction('delete'); - $this->_select = 's.name AS shop_name, CONCAT(a.physical_uri, a.virtual_uri) AS uri'; + $this->_select = 's.name AS shop_name, CONCAT(\'http://\', a.domain, a.physical_uri, a.virtual_uri) AS url'; $this->_join = 'LEFT JOIN `'._DB_PREFIX_.'shop` s ON (s.id_shop = a.id_shop)'; + if ($id_shop = (int)Tools::getValue('id_shop')) + $this->_where = 'AND a.id_shop = '.$id_shop; + return parent::renderList(); } public function renderForm() { + $this->multiple_fieldsets = true; $this->fields_form = array( - 'legend' => array( - 'title' => $this->l('Shop URL') + array( + 'form' => array( + 'legend' => array( + 'title' => $this->l('URL options') + ), + 'input' => array( + array( + 'type' => 'select', + 'label' => $this->l('Shop:'), + 'name' => 'id_shop', + 'onchange' => 'checkMainUrlInfo(this.value);', + 'options' => array( + 'optiongroup' => array ( + 'query' => Shop::getTree(), + 'label' => 'name' + ), + 'options' => array ( + 'query' => 'shops', + 'id' => 'id_shop', + 'name' => 'name' + ) + ) + ), + array( + 'type' => 'radio', + 'label' => $this->l('Main URL:'), + 'name' => 'main', + 'class' => 't', + 'values' => array( + array( + 'id' => 'main_on', + 'value' => 1, + 'label' => ''.$this->l('Enabled').'' + ), + array( + 'id' => 'main_off', + 'value' => 0, + 'label' => ''.$this->l('Disabled').'' + ) + ), + 'desc' => array( + $this->l('If you set this URL as the Main URL for the selected shop, all URLs set to this shop will be redirected to this URL (you can only have one Main URL per shop).'), + array( + 'text' => $this->l('Since the selected shop has no Main URL, you have to set this URL as the Main URL'), + 'id' => 'mainUrlInfo' + ), + array( + 'text' => $this->l('The selected shop has already a Main URL, if you set this one as the Main URL, the older one will be set as the Normal URL.'), + 'id' => 'mainUrlInfoExplain' + ) + ) + ), + array( + 'type' => 'radio', + 'label' => $this->l('Status:'), + 'name' => 'active', + 'required' => false, + 'class' => 't', + 'values' => array( + array( + 'id' => 'active_on', + 'value' => 1, + 'label' => ''.$this->l('Enabled').'' + ), + array( + 'id' => 'active_off', + 'value' => 0, + 'label' => ''.$this->l('Disabled').'' + ) + ), + 'desc' => $this->l('Enabled or disabled') + ) + ), + 'submit' => array( + 'title' => $this->l('Save'), + 'class' => 'button' + ), + ), ), - 'input' => array( - array( - 'type' => 'text', - 'label' => $this->l('Domain:'), - 'name' => 'domain', - 'size' => 50, - ), - array( - 'type' => 'text', - 'label' => $this->l('Domain SSL:'), - 'name' => 'domain_ssl', - 'size' => 50, - ), - array( - 'type' => 'text', - 'label' => $this->l('Physical URI:'), - 'name' => 'physical_uri', - 'desc' => $this->l('Physical folder of your store on your server. Leave this field empty if your store is installed on the root path (e.g. if your store is available at www.my-prestashop.com/my-store/, you would set my-store/ in this field).'), - 'size' => 50, - ), - array( - 'type' => 'text', - 'label' => $this->l('Virtual URI:'), - 'name' => 'virtual_uri', - 'desc' => array( - $this->l('You can use this option if you want to create a store with an URI that doesn\'t exist on your server (e.g. if you want your store to be available with the URL www.my-prestashop.com/my-store/shoes/, you have to set shoes/ in this field, assuming that my-store/ is your Physical URI).'), - ''.$this->l('URL rewriting must be activated on your server to use this feature.').'' + array( + 'form' => array( + 'legend' => array( + 'title' => $this->l('Shop URL') ), - 'size' => 50, - ), - array( - 'type' => 'text', - 'label' => $this->l('Your final URL will be:'), - 'name' => 'final_url', - 'size' => 76, - 'readonly' => true - ), - array( - 'type' => 'select', - 'label' => $this->l('Shop:'), - 'name' => 'id_shop', - 'onchange' => 'checkMainUrlInfo(this.value);', - 'options' => array( - 'optiongroup' => array ( - 'query' => Shop::getTree(), - 'label' => 'name' - ), - 'options' => array ( - 'query' => 'shops', - 'id' => 'id_shop', - 'name' => 'name' - ) - ) - ), - array( - 'type' => 'radio', - 'label' => $this->l('Main URL:'), - 'name' => 'main', - 'class' => 't', - 'values' => array( + 'input' => array( array( - 'id' => 'main_on', - 'value' => 1, - 'label' => ''.$this->l('Enabled').'' + 'type' => 'text', + 'label' => $this->l('Domain:'), + 'name' => 'domain', + 'size' => 50, ), array( - 'id' => 'main_off', - 'value' => 0, - 'label' => ''.$this->l('Disabled').'' - ) + 'type' => 'text', + 'label' => $this->l('Domain SSL:'), + 'name' => 'domain_ssl', + 'size' => 50, + ), + array( + 'type' => 'text', + 'label' => $this->l('Physical URI:'), + 'name' => 'physical_uri', + 'desc' => $this->l('Physical folder of your store on your server. Leave this field empty if your store is installed on the root path (e.g. if your store is available at www.my-prestashop.com/my-store/, you would set my-store/ in this field).'), + 'size' => 50, + ), + array( + 'type' => 'text', + 'label' => $this->l('Virtual URI:'), + 'name' => 'virtual_uri', + 'desc' => array( + $this->l('You can use this option if you want to create a store with an URI that doesn\'t exist on your server (e.g. if you want your store to be available with the URL www.my-prestashop.com/my-store/shoes/, you have to set shoes/ in this field, assuming that my-store/ is your Physical URI).'), + ''.$this->l('URL rewriting must be activated on your server to use this feature.').'' + ), + 'size' => 50, + ), + array( + 'type' => 'text', + 'label' => $this->l('Your final URL will be:'), + 'name' => 'final_url', + 'size' => 76, + 'readonly' => true + ), ), - 'desc' => array( - $this->l('If you set this URL as the Main URL for the selected shop, all URLs set to this shop will be redirected to this URL (you can only have one Main URL per shop).'), - array( - 'text' => $this->l('Since the selected shop has no Main URL, you have to set this URL as the Main URL'), - 'id' => 'mainUrlInfo' - ), - array( - 'text' => $this->l('The selected shop has already a Main URL, if you set this one as the Main URL, the older one will be set as the Normal URL.'), - 'id' => 'mainUrlInfoExplain' - ) - ) ), - array( - 'type' => 'radio', - 'label' => $this->l('Status:'), - 'name' => 'active', - 'required' => false, - 'class' => 't', - 'values' => array( - array( - 'id' => 'active_on', - 'value' => 1, - 'label' => ''.$this->l('Enabled').'' - ), - array( - 'id' => 'active_off', - 'value' => 0, - 'label' => ''.$this->l('Disabled').'' - ) - ), - 'desc' => $this->l('Enabled or disabled') - ) ), - 'submit' => array( - 'title' => $this->l('Save'), - 'class' => 'button' - ) ); if (!($obj = $this->loadObject(true))) @@ -242,10 +248,61 @@ class AdminShopUrlControllerCore extends AdminController return parent::renderForm(); } + public function initToolbar() + { + if ($this->id_object) + $this->loadObject(); + + if (!$this->id_shop && $this->object && $this->object->id_shop) + $this->id_shop = $this->object->id_shop; + + if (!$this->display && $this->id_shop) + $this->toolbar_btn['edit'] = array( + 'desc' => $this->l('Edit this shop'), + 'href' => $this->context->link->getAdminLink('AdminShop').'&updateshop&id_shop='.$this->id_shop, + ); + + parent::initToolbar(); + + $this->show_toolbar = false; + if (isset($this->toolbar_btn['new'])) + $this->toolbar_btn['new'] = array( + 'desc' => $this->l('Add new url'), + 'href' => $this->context->link->getAdminLink('AdminShopUrl').'&add'.$this->table.'&id_shop='.$this->id_shop, + ); + + if (isset($this->toolbar_btn['back'])) + $this->toolbar_btn['back']['href'] .= '&id_shop='.$this->id_shop; + } + + public function initContent() + { + $this->list_simple_header = true; + parent::initContent(); + + $this->addJqueryPlugin('cookie'); + $this->addJqueryPlugin('jstree'); + $this->addCSS(_PS_JS_DIR_.'jquery/plugins/jstree/themes/classic/style.css'); + + if (!$this->display && $this->id_shop) + { + $shop = new Shop($this->id_shop); + $this->toolbar_title[] = $shop->name; + } + + $this->context->smarty->assign(array( + 'toolbar_scroll' => 1, + 'toolbar_btn' => $this->toolbar_btn, + 'title' => $this->toolbar_title, + 'selected_tree_id' => ($this->display == 'edit' ? 'tree-url-'.$this->id_object : (Tools::getValue('id_shop') ? 'tree-shop-'.Tools::getValue('id_shop') : '')), + )); + } + public function postProcess() { $token = Tools::getValue('token') ? Tools::getValue('token') : $this->token; + $result = true; if ((isset($_GET['status'.$this->table]) || isset($_GET['status'])) && Tools::getValue($this->identifier)) { if ($this->tabAccess['edit'] === '1') @@ -265,15 +322,13 @@ class AdminShopUrlControllerCore extends AdminController else $this->errors[] = Tools::displayError('You do not have permission to edit here.'); } - else if (Tools::isSubmit('submitAdd'.$this->table) && $this->tabAccess['add'] === '1') - { - if (ShopUrl::urlExists(Tools::getValue('domain'), Tools::getValue('physical_uri'), Tools::getValue('virtual_uri'), Tools::getValue('id_shop'))) - $this->errors[] = Tools::displayError('Virtual URI already used.'); - else - return parent::postProcess(); - } else - return parent::postProcess(); + $result = parent::postProcess(); + + if ($this->redirect_after) + $this->redirect_after .= '&id_shop='.$this->id_shop; + + return $result; } public function processSave() diff --git a/controllers/admin/AdminThemesController.php b/controllers/admin/AdminThemesController.php index ec6ae69bd..bb986425a 100644 --- a/controllers/admin/AdminThemesController.php +++ b/controllers/admin/AdminThemesController.php @@ -551,7 +551,7 @@ class AdminThemesControllerCore extends AdminController $this->uploadIco('PS_FAVICON', _PS_IMG_DIR_.'favicon.ico'); if ($this->uploadIco('PS_FAVICON', _PS_IMG_DIR_.'favicon-'.(int)$id_shop.'.ico')) Configuration::updateValue('PS_FAVICON', 'favicon-'.(int)$id_shop.'.ico'); - + Configuration::updateGlobalValue('PS_FAVICON', 'favicon.ico'); } diff --git a/css/admin.css b/css/admin.css index f4bd1d405..a95001b6e 100644 --- a/css/admin.css +++ b/css/admin.css @@ -1757,7 +1757,7 @@ a.action_module_delete { border: 1px dotted #000000; } -fieldset .optionsDescription{ +fieldset .optionsDescription, fieldset p.description, .info{ background: url("../img/admin/icon-info.png") no-repeat scroll 6px 6px #BDE5F8; border: 1px solid #00529B; color: #00529B; @@ -2291,3 +2291,54 @@ margin-bottom:7px; #block_partner_tips {float:right; width:48.1%} .row-news { width:100%;float:left;} #table_info_news { border:1px solid #ccc;} + +#multishop-tree{ + +} + +.multishop-left{ + float: left; + width: 250px; + border: 1px solid #CCCED7; + background-color: #F9F9F9; + overflow: auto; + padding: 3px; + max-height: 400px; + min-height: 200px; + border-radius:3px; + -moz-border-radius:3px; + -webkit-border-radius:3px; +} + + .multishop-left .multishop-title{ + border-bottom: 1px solid #CCCED7; + color: #333333; + background-color: #F0F0F0; + font-size: 14px; + font-weight: bold; + text-align: left; + padding: 2px; + width: 247px; + } + +.multishop-right{ + margin-left: 270px; +} + +#container-multishop { + background-color:#EBEDF4; + border:1px solid #CCCED7; + color: #585A69; + padding:10px; +} + +#multishop-tree{ + font-size: 13px; + margin-top: 3px; +} + + #multishop-tree a.selected{ + background-color: #49B2FF; + padding: 3px; + color: #FFFFFF; + } \ No newline at end of file diff --git a/install-dev/data/xml/tab.xml b/install-dev/data/xml/tab.xml index 88b901df7..eb5336ee5 100644 --- a/install-dev/data/xml/tab.xml +++ b/install-dev/data/xml/tab.xml @@ -24,6 +24,12 @@ AdminLogin + + AdminShop + + + AdminShopUrl + AdminCatalog @@ -57,9 +63,6 @@ AdminParentStats - - AdminParentShop - AdminStock @@ -240,6 +243,9 @@ AdminEmails + + AdminShopGroup + AdminImport @@ -291,15 +297,6 @@ AdminAccountingExport - - AdminShop - - - AdminShopGroup - - - AdminShopUrl - AdminWarehouses diff --git a/install-dev/langs/de/data/tab.xml b/install-dev/langs/de/data/tab.xml index 316a57d2f..82f3a3846 100644 --- a/install-dev/langs/de/data/tab.xml +++ b/install-dev/langs/de/data/tab.xml @@ -6,6 +6,8 @@ + + @@ -17,7 +19,6 @@ - @@ -78,6 +79,7 @@ + @@ -95,9 +97,6 @@ - - - diff --git a/install-dev/langs/en/data/tab.xml b/install-dev/langs/en/data/tab.xml index 14b77d02c..80f0a483b 100644 --- a/install-dev/langs/en/data/tab.xml +++ b/install-dev/langs/en/data/tab.xml @@ -6,6 +6,8 @@ + + @@ -17,7 +19,6 @@ - @@ -78,6 +79,7 @@ + @@ -95,9 +97,6 @@ - - - diff --git a/install-dev/langs/es/data/tab.xml b/install-dev/langs/es/data/tab.xml index 4fac82a4a..05ba8bfd2 100644 --- a/install-dev/langs/es/data/tab.xml +++ b/install-dev/langs/es/data/tab.xml @@ -6,6 +6,8 @@ + + @@ -17,7 +19,6 @@ - @@ -78,6 +79,7 @@ + @@ -95,9 +97,6 @@ - - - diff --git a/install-dev/langs/fr/data/tab.xml b/install-dev/langs/fr/data/tab.xml index bf313adbb..149bb6952 100644 --- a/install-dev/langs/fr/data/tab.xml +++ b/install-dev/langs/fr/data/tab.xml @@ -6,6 +6,8 @@ + + @@ -17,7 +19,6 @@ - @@ -78,6 +79,7 @@ + @@ -95,9 +97,6 @@ - - - diff --git a/install-dev/langs/it/data/tab.xml b/install-dev/langs/it/data/tab.xml index dc82dae79..f1fd4f607 100644 --- a/install-dev/langs/it/data/tab.xml +++ b/install-dev/langs/it/data/tab.xml @@ -6,6 +6,8 @@ + + @@ -17,7 +19,6 @@ - @@ -78,6 +79,7 @@ + @@ -95,9 +97,6 @@ - - - diff --git a/install-dev/models/install.php b/install-dev/models/install.php index c46ae48e1..0ca709a4c 100644 --- a/install-dev/models/install.php +++ b/install-dev/models/install.php @@ -593,6 +593,7 @@ class InstallModelInstall extends InstallAbstractModel continue; $module = Module::getInstanceByName($module_name); + $module->uninstall(); if (!$module->install()) $errors[] = $this->language->l('Cannot install module "%s"', $module_name); } diff --git a/js/admin.js b/js/admin.js index 64b522085..dc46c7695 100644 --- a/js/admin.js +++ b/js/admin.js @@ -606,11 +606,11 @@ function submitAddcmsAndPreview() function checkAllMultishopDefaultValue(item) { - $(item).parent().find('input[name^=\'configUseDefault\']').each(function(k, v) + $(item).parent().find('input[name^=\'multishopOverrideOption\']').each(function(k, v) { $(v).attr('checked', item.checked); var name = $(v).attr('name'); - checkMultishopDefaultValue(v, name.substr(17, name.length - 18)); + checkMultishopDefaultValue(v, name.substr(24, name.length - 25)); }) } @@ -692,7 +692,7 @@ $(document).ready(function() { var key = $(v).attr('name'); var len = key.length; - checkMultishopDefaultValue(v, key.substr(17, len - 18)); + checkMultishopDefaultValue(v, key.substr(24, len - 25)); }); $(".copy2friendlyUrl").live('keyup change',function(e){ diff --git a/js/jquery/plugins/jquery.cookie.js b/js/jquery/plugins/jquery.cookie.js new file mode 100644 index 000000000..f084e787c --- /dev/null +++ b/js/jquery/plugins/jquery.cookie.js @@ -0,0 +1,47 @@ +/*! + * jQuery Cookie Plugin + * https://github.com/carhartl/jquery-cookie + * + * Copyright 2011, Klaus Hartl + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://www.opensource.org/licenses/mit-license.php + * http://www.opensource.org/licenses/GPL-2.0 + */ +(function($) { + $.cookie = function(key, value, options) { + + // key and at least value given, set cookie... + if (arguments.length > 1 && (!/Object/.test(Object.prototype.toString.call(value)) || value === null || value === undefined)) { + options = $.extend({}, options); + + if (value === null || value === undefined) { + options.expires = -1; + } + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setDate(t.getDate() + days); + } + + value = String(value); + + return (document.cookie = [ + encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // key and possibly options given, get cookie... + options = value || {}; + var decode = options.raw ? function(s) { return s; } : decodeURIComponent; + + var pairs = document.cookie.split('; '); + for (var i = 0, pair; pair = pairs[i] && pairs[i].split('='); i++) { + if (decode(pair[0]) === key) return decode(pair[1] || ''); // IE saves cookies with empty string as "c; ", e.g. without "=" as opposed to EOMB, thus pair[1] may be undefined + } + return null; + }; +})(jQuery); diff --git a/js/jquery/plugins/jstree/jquery.jstree.js b/js/jquery/plugins/jstree/jquery.jstree.js new file mode 100644 index 000000000..baba5e8a1 --- /dev/null +++ b/js/jquery/plugins/jstree/jquery.jstree.js @@ -0,0 +1,4551 @@ +/* + * jsTree 1.0-rc3 + * http://jstree.com/ + * + * Copyright (c) 2010 Ivan Bozhanov (vakata.com) + * + * Licensed same as jquery - under the terms of either the MIT License or the GPL Version 2 License + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * $Date: 2011-02-09 01:17:14 +0200 (ср, 09 февр 2011) $ + * $Revision: 236 $ + */ + +/*jslint browser: true, onevar: true, undef: true, bitwise: true, strict: true */ +/*global window : false, clearInterval: false, clearTimeout: false, document: false, setInterval: false, setTimeout: false, jQuery: false, navigator: false, XSLTProcessor: false, DOMParser: false, XMLSerializer: false*/ + +"use strict"; + +// top wrapper to prevent multiple inclusion (is this OK?) +(function () { if(jQuery && jQuery.jstree) { return; } + var is_ie6 = false, is_ie7 = false, is_ff2 = false; + +/* + * jsTree core + */ +(function ($) { + // Common functions not related to jsTree + // decided to move them to a `vakata` "namespace" + $.vakata = {}; + // CSS related functions + $.vakata.css = { + get_css : function(rule_name, delete_flag, sheet) { + rule_name = rule_name.toLowerCase(); + var css_rules = sheet.cssRules || sheet.rules, + j = 0; + do { + if(css_rules.length && j > css_rules.length + 5) { return false; } + if(css_rules[j].selectorText && css_rules[j].selectorText.toLowerCase() == rule_name) { + if(delete_flag === true) { + if(sheet.removeRule) { sheet.removeRule(j); } + if(sheet.deleteRule) { sheet.deleteRule(j); } + return true; + } + else { return css_rules[j]; } + } + } + while (css_rules[++j]); + return false; + }, + add_css : function(rule_name, sheet) { + if($.jstree.css.get_css(rule_name, false, sheet)) { return false; } + if(sheet.insertRule) { sheet.insertRule(rule_name + ' { }', 0); } else { sheet.addRule(rule_name, null, 0); } + return $.vakata.css.get_css(rule_name); + }, + remove_css : function(rule_name, sheet) { + return $.vakata.css.get_css(rule_name, true, sheet); + }, + add_sheet : function(opts) { + var tmp = false, is_new = true; + if(opts.str) { + if(opts.title) { tmp = $("style[id='" + opts.title + "-stylesheet']")[0]; } + if(tmp) { is_new = false; } + else { + tmp = document.createElement("style"); + tmp.setAttribute('type',"text/css"); + if(opts.title) { tmp.setAttribute("id", opts.title + "-stylesheet"); } + } + if(tmp.styleSheet) { + if(is_new) { + document.getElementsByTagName("head")[0].appendChild(tmp); + tmp.styleSheet.cssText = opts.str; + } + else { + tmp.styleSheet.cssText = tmp.styleSheet.cssText + " " + opts.str; + } + } + else { + tmp.appendChild(document.createTextNode(opts.str)); + document.getElementsByTagName("head")[0].appendChild(tmp); + } + return tmp.sheet || tmp.styleSheet; + } + if(opts.url) { + if(document.createStyleSheet) { + try { tmp = document.createStyleSheet(opts.url); } catch (e) { } + } + else { + tmp = document.createElement('link'); + tmp.rel = 'stylesheet'; + tmp.type = 'text/css'; + tmp.media = "all"; + tmp.href = opts.url; + document.getElementsByTagName("head")[0].appendChild(tmp); + return tmp.styleSheet; + } + } + } + }; + + // private variables + var instances = [], // instance array (used by $.jstree.reference/create/focused) + focused_instance = -1, // the index in the instance array of the currently focused instance + plugins = {}, // list of included plugins + prepared_move = {}; // for the move_node function + + // jQuery plugin wrapper (thanks to jquery UI widget function) + $.fn.jstree = function (settings) { + var isMethodCall = (typeof settings == 'string'), // is this a method call like $().jstree("open_node") + args = Array.prototype.slice.call(arguments, 1), + returnValue = this; + + // if a method call execute the method on all selected instances + if(isMethodCall) { + if(settings.substring(0, 1) == '_') { return returnValue; } + this.each(function() { + var instance = instances[$.data(this, "jstree_instance_id")], + methodValue = (instance && $.isFunction(instance[settings])) ? instance[settings].apply(instance, args) : instance; + if(typeof methodValue !== "undefined" && (settings.indexOf("is_") === 0 || (methodValue !== true && methodValue !== false))) { returnValue = methodValue; return false; } + }); + } + else { + this.each(function() { + // extend settings and allow for multiple hashes and $.data + var instance_id = $.data(this, "jstree_instance_id"), + a = [], + b = settings ? $.extend({}, true, settings) : {}, + c = $(this), + s = false, + t = []; + a = a.concat(args); + if(c.data("jstree")) { a.push(c.data("jstree")); } + b = a.length ? $.extend.apply(null, [true, b].concat(a)) : b; + + // if an instance already exists, destroy it first + if(typeof instance_id !== "undefined" && instances[instance_id]) { instances[instance_id].destroy(); } + // push a new empty object to the instances array + instance_id = parseInt(instances.push({}),10) - 1; + // store the jstree instance id to the container element + $.data(this, "jstree_instance_id", instance_id); + // clean up all plugins + b.plugins = $.isArray(b.plugins) ? b.plugins : $.jstree.defaults.plugins.slice(); + b.plugins.unshift("core"); + // only unique plugins + b.plugins = b.plugins.sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(","); + + // extend defaults with passed data + s = $.extend(true, {}, $.jstree.defaults, b); + s.plugins = b.plugins; + $.each(plugins, function (i, val) { + if($.inArray(i, s.plugins) === -1) { s[i] = null; delete s[i]; } + else { t.push(i); } + }); + s.plugins = t; + + // push the new object to the instances array (at the same time set the default classes to the container) and init + instances[instance_id] = new $.jstree._instance(instance_id, $(this).addClass("jstree jstree-" + instance_id), s); + // init all activated plugins for this instance + $.each(instances[instance_id]._get_settings().plugins, function (i, val) { instances[instance_id].data[val] = {}; }); + $.each(instances[instance_id]._get_settings().plugins, function (i, val) { if(plugins[val]) { plugins[val].__init.apply(instances[instance_id]); } }); + // initialize the instance + setTimeout(function() { if(instances[instance_id]) { instances[instance_id].init(); } }, 0); + }); + } + // return the jquery selection (or if it was a method call that returned a value - the returned value) + return returnValue; + }; + // object to store exposed functions and objects + $.jstree = { + defaults : { + plugins : [] + }, + _focused : function () { return instances[focused_instance] || null; }, + _reference : function (needle) { + // get by instance id + if(instances[needle]) { return instances[needle]; } + // get by DOM (if still no luck - return null + var o = $(needle); + if(!o.length && typeof needle === "string") { o = $("#" + needle); } + if(!o.length) { return null; } + return instances[o.closest(".jstree").data("jstree_instance_id")] || null; + }, + _instance : function (index, container, settings) { + // for plugins to store data in + this.data = { core : {} }; + this.get_settings = function () { return $.extend(true, {}, settings); }; + this._get_settings = function () { return settings; }; + this.get_index = function () { return index; }; + this.get_container = function () { return container; }; + this.get_container_ul = function () { return container.children("ul:eq(0)"); }; + this._set_settings = function (s) { + settings = $.extend(true, {}, settings, s); + }; + }, + _fn : { }, + plugin : function (pname, pdata) { + pdata = $.extend({}, { + __init : $.noop, + __destroy : $.noop, + _fn : {}, + defaults : false + }, pdata); + plugins[pname] = pdata; + + $.jstree.defaults[pname] = pdata.defaults; + $.each(pdata._fn, function (i, val) { + val.plugin = pname; + val.old = $.jstree._fn[i]; + $.jstree._fn[i] = function () { + var rslt, + func = val, + args = Array.prototype.slice.call(arguments), + evnt = new $.Event("before.jstree"), + rlbk = false; + + if(this.data.core.locked === true && i !== "unlock" && i !== "is_locked") { return; } + + // Check if function belongs to the included plugins of this instance + do { + if(func && func.plugin && $.inArray(func.plugin, this._get_settings().plugins) !== -1) { break; } + func = func.old; + } while(func); + if(!func) { return; } + + // context and function to trigger events, then finally call the function + if(i.indexOf("_") === 0) { + rslt = func.apply(this, args); + } + else { + rslt = this.get_container().triggerHandler(evnt, { "func" : i, "inst" : this, "args" : args, "plugin" : func.plugin }); + if(rslt === false) { return; } + if(typeof rslt !== "undefined") { args = rslt; } + + rslt = func.apply( + $.extend({}, this, { + __callback : function (data) { + this.get_container().triggerHandler( i + '.jstree', { "inst" : this, "args" : args, "rslt" : data, "rlbk" : rlbk }); + }, + __rollback : function () { + rlbk = this.get_rollback(); + return rlbk; + }, + __call_old : function (replace_arguments) { + return func.old.apply(this, (replace_arguments ? Array.prototype.slice.call(arguments, 1) : args ) ); + } + }), args); + } + + // return the result + return rslt; + }; + $.jstree._fn[i].old = val.old; + $.jstree._fn[i].plugin = pname; + }); + }, + rollback : function (rb) { + if(rb) { + if(!$.isArray(rb)) { rb = [ rb ]; } + $.each(rb, function (i, val) { + instances[val.i].set_rollback(val.h, val.d); + }); + } + } + }; + // set the prototype for all instances + $.jstree._fn = $.jstree._instance.prototype = {}; + + // load the css when DOM is ready + $(function() { + // code is copied from jQuery ($.browser is deprecated + there is a bug in IE) + var u = navigator.userAgent.toLowerCase(), + v = (u.match( /.+?(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1], + css_string = '' + + '.jstree ul, .jstree li { display:block; margin:0 0 0 0; padding:0 0 0 0; list-style-type:none; } ' + + '.jstree li { display:block; min-height:18px; line-height:18px; white-space:nowrap; margin-left:18px; min-width:18px; } ' + + '.jstree-rtl li { margin-left:0; margin-right:18px; } ' + + '.jstree > ul > li { margin-left:0px; } ' + + '.jstree-rtl > ul > li { margin-right:0px; } ' + + '.jstree ins { display:inline-block; text-decoration:none; width:18px; height:18px; margin:0 0 0 0; padding:0; } ' + + '.jstree a { display:inline-block; line-height:16px; height:16px; color:black; white-space:nowrap; text-decoration:none; padding:1px 2px; margin:0; } ' + + '.jstree a:focus { outline: none; } ' + + '.jstree a > ins { height:16px; width:16px; } ' + + '.jstree a > .jstree-icon { margin-right:3px; } ' + + '.jstree-rtl a > .jstree-icon { margin-left:3px; margin-right:0; } ' + + 'li.jstree-open > ul { display:block; } ' + + 'li.jstree-closed > ul { display:none; } '; + // Correct IE 6 (does not support the > CSS selector) + if(/msie/.test(u) && parseInt(v, 10) == 6) { + is_ie6 = true; + + // fix image flicker and lack of caching + try { + document.execCommand("BackgroundImageCache", false, true); + } catch (err) { } + + css_string += '' + + '.jstree li { height:18px; margin-left:0; margin-right:0; } ' + + '.jstree li li { margin-left:18px; } ' + + '.jstree-rtl li li { margin-left:0px; margin-right:18px; } ' + + 'li.jstree-open ul { display:block; } ' + + 'li.jstree-closed ul { display:none !important; } ' + + '.jstree li a { display:inline; border-width:0 !important; padding:0px 2px !important; } ' + + '.jstree li a ins { height:16px; width:16px; margin-right:3px; } ' + + '.jstree-rtl li a ins { margin-right:0px; margin-left:3px; } '; + } + // Correct IE 7 (shifts anchor nodes onhover) + if(/msie/.test(u) && parseInt(v, 10) == 7) { + is_ie7 = true; + css_string += '.jstree li a { border-width:0 !important; padding:0px 2px !important; } '; + } + // correct ff2 lack of display:inline-block + if(!/compatible/.test(u) && /mozilla/.test(u) && parseFloat(v, 10) < 1.9) { + is_ff2 = true; + css_string += '' + + '.jstree ins { display:-moz-inline-box; } ' + + '.jstree li { line-height:12px; } ' + // WHY?? + '.jstree a { display:-moz-inline-box; } ' + + '.jstree .jstree-no-icons .jstree-checkbox { display:-moz-inline-stack !important; } '; + /* this shouldn't be here as it is theme specific */ + } + // the default stylesheet + $.vakata.css.add_sheet({ str : css_string, title : "jstree" }); + }); + + // core functions (open, close, create, update, delete) + $.jstree.plugin("core", { + __init : function () { + this.data.core.locked = false; + this.data.core.to_open = this.get_settings().core.initially_open; + this.data.core.to_load = this.get_settings().core.initially_load; + }, + defaults : { + html_titles : false, + animation : 500, + initially_open : [], + initially_load : [], + open_parents : true, + notify_plugins : true, + rtl : false, + load_open : false, + strings : { + loading : "Loading ...", + new_node : "New node", + multiple_selection : "Multiple selection" + } + }, + _fn : { + init : function () { + this.set_focus(); + if(this._get_settings().core.rtl) { + this.get_container().addClass("jstree-rtl").css("direction", "rtl"); + } + this.get_container().html(""); + this.data.core.li_height = this.get_container_ul().find("li.jstree-closed, li.jstree-leaf").eq(0).height() || 18; + + this.get_container() + .delegate("li > ins", "click.jstree", $.proxy(function (event) { + var trgt = $(event.target); + // if(trgt.is("ins") && event.pageY - trgt.offset().top < this.data.core.li_height) { this.toggle_node(trgt); } + this.toggle_node(trgt); + }, this)) + .bind("mousedown.jstree", $.proxy(function () { + this.set_focus(); // This used to be setTimeout(set_focus,0) - why? + }, this)) + .bind("dblclick.jstree", function (event) { + var sel; + if(document.selection && document.selection.empty) { document.selection.empty(); } + else { + if(window.getSelection) { + sel = window.getSelection(); + try { + sel.removeAllRanges(); + sel.collapse(); + } catch (err) { } + } + } + }); + if(this._get_settings().core.notify_plugins) { + this.get_container() + .bind("load_node.jstree", $.proxy(function (e, data) { + var o = this._get_node(data.rslt.obj), + t = this; + if(o === -1) { o = this.get_container_ul(); } + if(!o.length) { return; } + o.find("li").each(function () { + var th = $(this); + if(th.data("jstree")) { + $.each(th.data("jstree"), function (plugin, values) { + if(t.data[plugin] && $.isFunction(t["_" + plugin + "_notify"])) { + t["_" + plugin + "_notify"].call(t, th, values); + } + }); + } + }); + }, this)); + } + if(this._get_settings().core.load_open) { + this.get_container() + .bind("load_node.jstree", $.proxy(function (e, data) { + var o = this._get_node(data.rslt.obj), + t = this; + if(o === -1) { o = this.get_container_ul(); } + if(!o.length) { return; } + o.find("li.jstree-open:not(:has(ul))").each(function () { + t.load_node(this, $.noop, $.noop); + }); + }, this)); + } + this.__callback(); + this.load_node(-1, function () { this.loaded(); this.reload_nodes(); }); + }, + destroy : function () { + var i, + n = this.get_index(), + s = this._get_settings(), + _this = this; + + $.each(s.plugins, function (i, val) { + try { plugins[val].__destroy.apply(_this); } catch(err) { } + }); + this.__callback(); + // set focus to another instance if this one is focused + if(this.is_focused()) { + for(i in instances) { + if(instances.hasOwnProperty(i) && i != n) { + instances[i].set_focus(); + break; + } + } + } + // if no other instance found + if(n === focused_instance) { focused_instance = -1; } + // remove all traces of jstree in the DOM (only the ones set using jstree*) and cleans all events + this.get_container() + .unbind(".jstree") + .undelegate(".jstree") + .removeData("jstree_instance_id") + .find("[class^='jstree']") + .andSelf() + .attr("class", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); }); + $(document) + .unbind(".jstree-" + n) + .undelegate(".jstree-" + n); + // remove the actual data + instances[n] = null; + delete instances[n]; + }, + + _core_notify : function (n, data) { + if(data.opened) { + this.open_node(n, false, true); + } + }, + + lock : function () { + this.data.core.locked = true; + this.get_container().children("ul").addClass("jstree-locked").css("opacity","0.7"); + this.__callback({}); + }, + unlock : function () { + this.data.core.locked = false; + this.get_container().children("ul").removeClass("jstree-locked").css("opacity","1"); + this.__callback({}); + }, + is_locked : function () { return this.data.core.locked; }, + save_opened : function () { + var _this = this; + this.data.core.to_open = []; + this.get_container_ul().find("li.jstree-open").each(function () { + if(this.id) { _this.data.core.to_open.push("#" + this.id.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:")); } + }); + this.__callback(_this.data.core.to_open); + }, + save_loaded : function () { }, + reload_nodes : function (is_callback) { + var _this = this, + done = true, + current = [], + remaining = []; + if(!is_callback) { + this.data.core.reopen = false; + this.data.core.refreshing = true; + this.data.core.to_open = $.map($.makeArray(this.data.core.to_open), function (n) { return "#" + n.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:"); }); + this.data.core.to_load = $.map($.makeArray(this.data.core.to_load), function (n) { return "#" + n.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:"); }); + if(this.data.core.to_open.length) { + this.data.core.to_load = this.data.core.to_load.concat(this.data.core.to_open); + } + } + if(this.data.core.to_load.length) { + $.each(this.data.core.to_load, function (i, val) { + if(val == "#") { return true; } + if($(val).length) { current.push(val); } + else { remaining.push(val); } + }); + if(current.length) { + this.data.core.to_load = remaining; + $.each(current, function (i, val) { + if(!_this._is_loaded(val)) { + _this.load_node(val, function () { _this.reload_nodes(true); }, function () { _this.reload_nodes(true); }); + done = false; + } + }); + } + } + if(this.data.core.to_open.length) { + $.each(this.data.core.to_open, function (i, val) { + _this.open_node(val, false, true); + }); + } + if(done) { + // TODO: find a more elegant approach to syncronizing returning requests + if(this.data.core.reopen) { clearTimeout(this.data.core.reopen); } + this.data.core.reopen = setTimeout(function () { _this.__callback({}, _this); }, 50); + this.data.core.refreshing = false; + this.reopen(); + } + }, + reopen : function () { + var _this = this; + if(this.data.core.to_open.length) { + $.each(this.data.core.to_open, function (i, val) { + _this.open_node(val, false, true); + }); + } + this.__callback({}); + }, + refresh : function (obj) { + var _this = this; + this.save_opened(); + if(!obj) { obj = -1; } + obj = this._get_node(obj); + if(!obj) { obj = -1; } + if(obj !== -1) { obj.children("UL").remove(); } + else { this.get_container_ul().empty(); } + this.load_node(obj, function () { _this.__callback({ "obj" : obj}); _this.reload_nodes(); }); + }, + // Dummy function to fire after the first load (so that there is a jstree.loaded event) + loaded : function () { + this.__callback(); + }, + // deal with focus + set_focus : function () { + if(this.is_focused()) { return; } + var f = $.jstree._focused(); + if(f) { f.unset_focus(); } + + this.get_container().addClass("jstree-focused"); + focused_instance = this.get_index(); + this.__callback(); + }, + is_focused : function () { + return focused_instance == this.get_index(); + }, + unset_focus : function () { + if(this.is_focused()) { + this.get_container().removeClass("jstree-focused"); + focused_instance = -1; + } + this.__callback(); + }, + + // traverse + _get_node : function (obj) { + var $obj = $(obj, this.get_container()); + if($obj.is(".jstree") || obj == -1) { return -1; } + $obj = $obj.closest("li", this.get_container()); + return $obj.length ? $obj : false; + }, + _get_next : function (obj, strict) { + obj = this._get_node(obj); + if(obj === -1) { return this.get_container().find("> ul > li:first-child"); } + if(!obj.length) { return false; } + if(strict) { return (obj.nextAll("li").size() > 0) ? obj.nextAll("li:eq(0)") : false; } + + if(obj.hasClass("jstree-open")) { return obj.find("li:eq(0)"); } + else if(obj.nextAll("li").size() > 0) { return obj.nextAll("li:eq(0)"); } + else { return obj.parentsUntil(".jstree","li").next("li").eq(0); } + }, + _get_prev : function (obj, strict) { + obj = this._get_node(obj); + if(obj === -1) { return this.get_container().find("> ul > li:last-child"); } + if(!obj.length) { return false; } + if(strict) { return (obj.prevAll("li").length > 0) ? obj.prevAll("li:eq(0)") : false; } + + if(obj.prev("li").length) { + obj = obj.prev("li").eq(0); + while(obj.hasClass("jstree-open")) { obj = obj.children("ul:eq(0)").children("li:last"); } + return obj; + } + else { var o = obj.parentsUntil(".jstree","li:eq(0)"); return o.length ? o : false; } + }, + _get_parent : function (obj) { + obj = this._get_node(obj); + if(obj == -1 || !obj.length) { return false; } + var o = obj.parentsUntil(".jstree", "li:eq(0)"); + return o.length ? o : -1; + }, + _get_children : function (obj) { + obj = this._get_node(obj); + if(obj === -1) { return this.get_container().children("ul:eq(0)").children("li"); } + if(!obj.length) { return false; } + return obj.children("ul:eq(0)").children("li"); + }, + get_path : function (obj, id_mode) { + var p = [], + _this = this; + obj = this._get_node(obj); + if(obj === -1 || !obj || !obj.length) { return false; } + obj.parentsUntil(".jstree", "li").each(function () { + p.push( id_mode ? this.id : _this.get_text(this) ); + }); + p.reverse(); + p.push( id_mode ? obj.attr("id") : this.get_text(obj) ); + return p; + }, + + // string functions + _get_string : function (key) { + return this._get_settings().core.strings[key] || key; + }, + + is_open : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-open"); }, + is_closed : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-closed"); }, + is_leaf : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-leaf"); }, + correct_state : function (obj) { + obj = this._get_node(obj); + if(!obj || obj === -1) { return false; } + obj.removeClass("jstree-closed jstree-open").addClass("jstree-leaf").children("ul").remove(); + this.__callback({ "obj" : obj }); + }, + // open/close + open_node : function (obj, callback, skip_animation) { + obj = this._get_node(obj); + if(!obj.length) { return false; } + if(!obj.hasClass("jstree-closed")) { if(callback) { callback.call(); } return false; } + var s = skip_animation || is_ie6 ? 0 : this._get_settings().core.animation, + t = this; + if(!this._is_loaded(obj)) { + obj.children("a").addClass("jstree-loading"); + this.load_node(obj, function () { t.open_node(obj, callback, skip_animation); }, callback); + } + else { + if(this._get_settings().core.open_parents) { + obj.parentsUntil(".jstree",".jstree-closed").each(function () { + t.open_node(this, false, true); + }); + } + if(s) { obj.children("ul").css("display","none"); } + obj.removeClass("jstree-closed").addClass("jstree-open").children("a").removeClass("jstree-loading"); + if(s) { obj.children("ul").stop(true, true).slideDown(s, function () { this.style.display = ""; t.after_open(obj); }); } + else { t.after_open(obj); } + this.__callback({ "obj" : obj }); + if(callback) { callback.call(); } + } + }, + after_open : function (obj) { this.__callback({ "obj" : obj }); }, + close_node : function (obj, skip_animation) { + obj = this._get_node(obj); + var s = skip_animation || is_ie6 ? 0 : this._get_settings().core.animation, + t = this; + if(!obj.length || !obj.hasClass("jstree-open")) { return false; } + if(s) { obj.children("ul").attr("style","display:block !important"); } + obj.removeClass("jstree-open").addClass("jstree-closed"); + if(s) { obj.children("ul").stop(true, true).slideUp(s, function () { this.style.display = ""; t.after_close(obj); }); } + else { t.after_close(obj); } + this.__callback({ "obj" : obj }); + }, + after_close : function (obj) { this.__callback({ "obj" : obj }); }, + toggle_node : function (obj) { + obj = this._get_node(obj); + if(obj.hasClass("jstree-closed")) { return this.open_node(obj); } + if(obj.hasClass("jstree-open")) { return this.close_node(obj); } + }, + open_all : function (obj, do_animation, original_obj) { + obj = obj ? this._get_node(obj) : -1; + if(!obj || obj === -1) { obj = this.get_container_ul(); } + if(original_obj) { + obj = obj.find("li.jstree-closed"); + } + else { + original_obj = obj; + if(obj.is(".jstree-closed")) { obj = obj.find("li.jstree-closed").andSelf(); } + else { obj = obj.find("li.jstree-closed"); } + } + var _this = this; + obj.each(function () { + var __this = this; + if(!_this._is_loaded(this)) { _this.open_node(this, function() { _this.open_all(__this, do_animation, original_obj); }, !do_animation); } + else { _this.open_node(this, false, !do_animation); } + }); + // so that callback is fired AFTER all nodes are open + if(original_obj.find('li.jstree-closed').length === 0) { this.__callback({ "obj" : original_obj }); } + }, + close_all : function (obj, do_animation) { + var _this = this; + obj = obj ? this._get_node(obj) : this.get_container(); + if(!obj || obj === -1) { obj = this.get_container_ul(); } + obj.find("li.jstree-open").andSelf().each(function () { _this.close_node(this, !do_animation); }); + this.__callback({ "obj" : obj }); + }, + clean_node : function (obj) { + obj = obj && obj != -1 ? $(obj) : this.get_container_ul(); + obj = obj.is("li") ? obj.find("li").andSelf() : obj.find("li"); + obj.removeClass("jstree-last") + .filter("li:last-child").addClass("jstree-last").end() + .filter(":has(li)") + .not(".jstree-open").removeClass("jstree-leaf").addClass("jstree-closed"); + obj.not(".jstree-open, .jstree-closed").addClass("jstree-leaf").children("ul").remove(); + this.__callback({ "obj" : obj }); + }, + // rollback + get_rollback : function () { + this.__callback(); + return { i : this.get_index(), h : this.get_container().children("ul").clone(true), d : this.data }; + }, + set_rollback : function (html, data) { + this.get_container().empty().append(html); + this.data = data; + this.__callback(); + }, + // Dummy functions to be overwritten by any datastore plugin included + load_node : function (obj, s_call, e_call) { this.__callback({ "obj" : obj }); }, + _is_loaded : function (obj) { return true; }, + + // Basic operations: create + create_node : function (obj, position, js, callback, is_loaded) { + obj = this._get_node(obj); + position = typeof position === "undefined" ? "last" : position; + var d = $("
  • "), + s = this._get_settings().core, + tmp; + + if(obj !== -1 && !obj.length) { return false; } + if(!is_loaded && !this._is_loaded(obj)) { this.load_node(obj, function () { this.create_node(obj, position, js, callback, true); }); return false; } + + this.__rollback(); + + if(typeof js === "string") { js = { "data" : js }; } + if(!js) { js = {}; } + if(js.attr) { d.attr(js.attr); } + if(js.metadata) { d.data(js.metadata); } + if(js.state) { d.addClass("jstree-" + js.state); } + if(!js.data) { js.data = this._get_string("new_node"); } + if(!$.isArray(js.data)) { tmp = js.data; js.data = []; js.data.push(tmp); } + $.each(js.data, function (i, m) { + tmp = $(""); + if($.isFunction(m)) { m = m.call(this, js); } + if(typeof m == "string") { tmp.attr('href','#')[ s.html_titles ? "html" : "text" ](m); } + else { + if(!m.attr) { m.attr = {}; } + if(!m.attr.href) { m.attr.href = '#'; } + tmp.attr(m.attr)[ s.html_titles ? "html" : "text" ](m.title); + if(m.language) { tmp.addClass(m.language); } + } + tmp.prepend(" "); + if(!m.icon && js.icon) { m.icon = js.icon; } + if(m.icon) { + if(m.icon.indexOf("/") === -1) { tmp.children("ins").addClass(m.icon); } + else { tmp.children("ins").css("background","url('" + m.icon + "') center center no-repeat"); } + } + d.append(tmp); + }); + d.prepend(" "); + if(obj === -1) { + obj = this.get_container(); + if(position === "before") { position = "first"; } + if(position === "after") { position = "last"; } + } + switch(position) { + case "before": obj.before(d); tmp = this._get_parent(obj); break; + case "after" : obj.after(d); tmp = this._get_parent(obj); break; + case "inside": + case "first" : + if(!obj.children("ul").length) { obj.append("