OSF_Integrator_for_Synnex - Version 1.0.0

Version Notes

1.0.0

Download this release

Release Info

Developer OSF Global
Extension OSF_Integrator_for_Synnex
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

Files changed (117) hide show
  1. app/code/community/Osf/Synnex/Block/Adminhtml/Shipments.php +57 -0
  2. app/code/community/Osf/Synnex/Block/Adminhtml/Shipments/Grid.php +92 -0
  3. app/code/community/Osf/Synnex/Block/Adminhtml/Shipments/Renderer/Status.php +41 -0
  4. app/code/community/Osf/Synnex/Block/Adminhtml/System/Config/Button/Import.php +50 -0
  5. app/code/community/Osf/Synnex/Block/Adminhtml/System/Config/Button/Notice.php +50 -0
  6. app/code/community/Osf/Synnex/Helper/Connect.php +195 -0
  7. app/code/community/Osf/Synnex/Helper/Data.php +80 -0
  8. app/code/community/Osf/Synnex/Helper/Ftp.php +131 -0
  9. app/code/community/Osf/Synnex/Helper/Validate.php +173 -0
  10. app/code/community/Osf/Synnex/LICENSE.txt +20 -0
  11. app/code/community/Osf/Synnex/Model/Import.php +254 -0
  12. app/code/community/Osf/Synnex/Model/Observer.php +120 -0
  13. app/code/community/Osf/Synnex/Model/Order.php +405 -0
  14. app/code/community/Osf/Synnex/Model/Queue.php +39 -0
  15. app/code/community/Osf/Synnex/Model/Resource/Queue.php +26 -0
  16. app/code/community/Osf/Synnex/Model/Resource/Queue/Collection.php +26 -0
  17. app/code/community/Osf/Synnex/Model/Shipping/Notice.php +215 -0
  18. app/code/community/Osf/Synnex/Model/Source.php +52 -0
  19. app/code/community/Osf/Synnex/Sql/synnex_setup/install-0.1.0.php +79 -0
  20. app/code/community/Osf/Synnex/controllers/IndexController.php +65 -0
  21. app/code/community/Osf/Synnex/etc/config.xml +161 -0
  22. app/code/community/Osf/Synnex/etc/system.xml +196 -0
  23. app/code/community/Osf/Synnex/lib/magmi/conf/ColumnMappingItemProcessor.conf +11 -0
  24. app/code/community/Osf/Synnex/lib/magmi/conf/DefaultValuesItemProcessor.conf +3 -0
  25. app/code/community/Osf/Synnex/lib/magmi/conf/ItemIndexer.conf +3 -0
  26. app/code/community/Osf/Synnex/lib/magmi/conf/Magmi_ReindexingPlugin.conf +3 -0
  27. app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/CategoryImporter.conf +5 -0
  28. app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/ColumnMappingItemProcessor.conf +11 -0
  29. app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/DefaultValuesItemProcessor.conf +3 -0
  30. app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/ItemIndexer.conf +3 -0
  31. app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/Magmi_CSVDataSource.conf +11 -0
  32. app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/Magmi_ReindexingPlugin.conf +3 -0
  33. app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/ValueReplacerItemProcessor.conf +9 -0
  34. app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/plugins.conf +6 -0
  35. app/code/community/Osf/Synnex/lib/magmi/conf/ValueReplacerItemProcessor.conf +9 -0
  36. app/code/community/Osf/Synnex/lib/magmi/conf/magmi.ini +17 -0
  37. app/code/community/Osf/Synnex/lib/magmi/conf/magmi.ini.default +0 -0
  38. app/code/community/Osf/Synnex/lib/magmi/conf/plugins.conf +6 -0
  39. app/code/community/Osf/Synnex/lib/magmi/engines/magmi_productimportengine.php +1924 -0
  40. app/code/community/Osf/Synnex/lib/magmi/engines/magmi_utilityengine.php +86 -0
  41. app/code/community/Osf/Synnex/lib/magmi/inc/dbhelper.class.php +661 -0
  42. app/code/community/Osf/Synnex/lib/magmi/inc/fshelper.php +478 -0
  43. app/code/community/Osf/Synnex/lib/magmi/inc/license.txt +19 -0
  44. app/code/community/Osf/Synnex/lib/magmi/inc/magmi_config.php +274 -0
  45. app/code/community/Osf/Synnex/lib/magmi/inc/magmi_defs.php +14 -0
  46. app/code/community/Osf/Synnex/lib/magmi/inc/magmi_engine.php +559 -0
  47. app/code/community/Osf/Synnex/lib/magmi/inc/magmi_loggers.php +71 -0
  48. app/code/community/Osf/Synnex/lib/magmi/inc/magmi_mixin.php +44 -0
  49. app/code/community/Osf/Synnex/lib/magmi/inc/magmi_pluginhelper.php +197 -0
  50. app/code/community/Osf/Synnex/lib/magmi/inc/magmi_statemanager.php +71 -0
  51. app/code/community/Osf/Synnex/lib/magmi/inc/magmi_utils.php +180 -0
  52. app/code/community/Osf/Synnex/lib/magmi/inc/magmi_valueparser.php +62 -0
  53. app/code/community/Osf/Synnex/lib/magmi/inc/magmi_version.php +5 -0
  54. app/code/community/Osf/Synnex/lib/magmi/inc/properties.php +218 -0
  55. app/code/community/Osf/Synnex/lib/magmi/inc/remotefilegetter.php +381 -0
  56. app/code/community/Osf/Synnex/lib/magmi/inc/timecounter.php +278 -0
  57. app/code/community/Osf/Synnex/lib/magmi/integration/inc/magmi_datapump.php +38 -0
  58. app/code/community/Osf/Synnex/lib/magmi/integration/inc/magmi_datapumpdatasource.php +15 -0
  59. app/code/community/Osf/Synnex/lib/magmi/integration/inc/productimport_datapump.php +87 -0
  60. app/code/community/Osf/Synnex/lib/magmi/integration/inc/pumpfactory.ini +2 -0
  61. app/code/community/Osf/Synnex/lib/magmi/plugins/base/general/optimizer/magmi_optimizer_plugin.php +39 -0
  62. app/code/community/Osf/Synnex/lib/magmi/plugins/base/general/reindex/magmi_reindexing_plugin.php +112 -0
  63. app/code/community/Osf/Synnex/lib/magmi/plugins/base/general/reindex/options_panel.php +54 -0
  64. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/columnmapper/000_columnmapper.php +113 -0
  65. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/columnmapper/options_panel.php +25 -0
  66. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/defaultvalues/00_default_values.php +93 -0
  67. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/defaultvalues/options_panel.php +23 -0
  68. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/02_genericmapper.php +118 -0
  69. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/__common__.csv +4 -0
  70. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/backorders.csv +3 -0
  71. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/country_of_manufacture.csv +246 -0
  72. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/msrp_display_actual_price_type.csv +4 -0
  73. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/msrp_enabled.csv +3 -0
  74. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/options_container.csv +2 -0
  75. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/page_layout.csv +6 -0
  76. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/status.csv +2 -0
  77. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/tax_class_id.csv +4 -0
  78. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/visibility.csv +4 -0
  79. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/options_panel.php +8 -0
  80. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/importlimiter/01_importlimiter.php +167 -0
  81. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/importlimiter/options_panel.php +54 -0
  82. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/productdeleter/options_panel.php +8 -0
  83. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/productdeleter/productdeleter.php +61 -0
  84. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/skufinder/001_skufinder.php +95 -0
  85. app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/skufinder/options_panel.php +10 -0
  86. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/categories/categoryimport.php +464 -0
  87. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/categories/options_panel.php +56 -0
  88. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/customoptions/options_panel.php +2 -0
  89. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/customoptions/pablo_customoptions.php +372 -0
  90. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/downloadable/downloadableprocessor.php +302 -0
  91. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/downloadable/options_panel.php +10 -0
  92. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/imageprocessor/imageitattributeemprocessor.php +654 -0
  93. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/imageprocessor/options_panel.php +144 -0
  94. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/itemindexer/options_panel.php +22 -0
  95. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/itemindexer/otfindexer.php +396 -0
  96. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/valuereplacer/03_valuereplacer.php +102 -0
  97. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/valuereplacer/helper/remapper.php +77 -0
  98. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/valuereplacer/options_panel.php +61 -0
  99. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/valuetrim/options_panel.php +2 -0
  100. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/valuetrim/valuetrimmer.php +90 -0
  101. app/code/community/Osf/Synnex/lib/magmi/plugins/extra/obsolete.txt +1 -0
  102. app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_datasource.php +24 -0
  103. app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_default_options_panel.php +1 -0
  104. app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_defaultattributehandler.php +433 -0
  105. app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_generalimport_plugin.php +16 -0
  106. app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_item_processor.php +102 -0
  107. app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_plugin.php +337 -0
  108. app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_utility_plugin.php +15 -0
  109. app/code/community/Osf/Synnex/lib/magmi/state/magmistate +1 -0
  110. app/code/community/Osf/Synnex/lib/magmi/state/progress.txt +0 -0
  111. app/code/community/Osf/Synnex/lib/magmi/state/timings.txt +50 -0
  112. app/code/community/Osf/Synnex/lib/magmi/state/trace.txt +0 -0
  113. app/design/adminhtml/default/default/template/synnex/system/config/import.phtml +16 -0
  114. app/design/adminhtml/default/default/template/synnex/system/config/notice.phtml +16 -0
  115. app/etc/modules/Osf_Synnex.xml +9 -0
  116. app/locale/en_US/template/email/synnex/synnex_import_error.html +38 -0
  117. package.xml +18 -0
app/code/community/Osf/Synnex/Block/Adminhtml/Shipments.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Shipments Block
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Block_Adminhtml_Shipments extends Mage_Adminhtml_Block_Sales_Order_View_Tab_Shipments
18
+ {
19
+ /**
20
+ * Prepare the columns for the grid
21
+ */
22
+ protected function _prepareColumns()
23
+ {
24
+ $this->addColumn('increment_id', array(
25
+ 'header' => Mage::helper('sales')->__('Shipment #'),
26
+ 'index' => 'increment_id',
27
+ ));
28
+
29
+ $this->addColumn('shipping_name', array(
30
+ 'header' => Mage::helper('sales')->__('Ship to Name'),
31
+ 'index' => 'shipping_name',
32
+ ));
33
+
34
+ $this->addColumn('shipment_status', array(
35
+ 'header' => Mage::helper('sales')->__('Synnex Shipment Status'),
36
+ 'index' => 'shipment_status',
37
+ 'renderer' => 'Osf_Synnex_Block_Adminhtml_Shipments_Renderer_Status',
38
+ ));
39
+
40
+ $this->addColumn('created_at', array(
41
+ 'header' => Mage::helper('sales')->__('Date Shipped'),
42
+ 'index' => 'created_at',
43
+ 'type' => 'datetime',
44
+ ));
45
+
46
+ $this->addColumn('total_qty', array(
47
+ 'header' => Mage::helper('sales')->__('Total Qty'),
48
+ 'index' => 'total_qty',
49
+ 'type' => 'number',
50
+ ));
51
+
52
+ return parent::_prepareColumns();
53
+ }
54
+ }
55
+
56
+ /* Filename: Shipments.php */
57
+ /* Location: app/code/local/Osf/Synnex/Block/Adminhtml/Shipments.php */
app/code/community/Osf/Synnex/Block/Adminhtml/Shipments/Grid.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Grid Block
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Block_Adminhtml_Shipments_Grid extends Mage_Adminhtml_Block_Sales_Shipment_Grid
18
+ {
19
+ /**
20
+ * Prepare the columns for the grid
21
+ */
22
+ protected function _prepareColumns()
23
+ {
24
+ $this->addColumn('increment_id', array(
25
+ 'header' => Mage::helper('sales')->__('Shipment #'),
26
+ 'index' => 'increment_id',
27
+ 'type' => 'text',
28
+ ));
29
+
30
+ $this->addColumn('created_at', array(
31
+ 'header' => Mage::helper('sales')->__('Date Shipped'),
32
+ 'index' => 'created_at',
33
+ 'type' => 'datetime',
34
+ ));
35
+
36
+ $this->addColumn('order_increment_id', array(
37
+ 'header' => Mage::helper('sales')->__('Order #'),
38
+ 'index' => 'order_increment_id',
39
+ 'type' => 'text',
40
+ ));
41
+
42
+ $this->addColumn('order_created_at', array(
43
+ 'header' => Mage::helper('sales')->__('Order Date'),
44
+ 'index' => 'order_created_at',
45
+ 'type' => 'datetime',
46
+ ));
47
+
48
+ $this->addColumn('shipment_status', array(
49
+ 'header' => Mage::helper('sales')->__('Synnex Shipment Status'),
50
+ 'index' => 'shipment_status',
51
+ 'type' => 'text',
52
+ 'renderer' => 'Osf_Synnex_Block_Adminhtml_Shipments_Renderer_Status',
53
+ ));
54
+
55
+ $this->addColumn('shipping_name', array(
56
+ 'header' => Mage::helper('sales')->__('Ship to Name'),
57
+ 'index' => 'shipping_name',
58
+ ));
59
+
60
+ $this->addColumn('total_qty', array(
61
+ 'header' => Mage::helper('sales')->__('Total Qty'),
62
+ 'index' => 'total_qty',
63
+ 'type' => 'number',
64
+ ));
65
+
66
+ $this->addColumn('action',
67
+ array(
68
+ 'header' => Mage::helper('sales')->__('Action'),
69
+ 'width' => '50px',
70
+ 'type' => 'action',
71
+ 'getter' => 'getId',
72
+ 'actions' => array(
73
+ array(
74
+ 'caption' => Mage::helper('sales')->__('View'),
75
+ 'url' => array('base'=>'*/sales_shipment/view'),
76
+ 'field' => 'shipment_id'
77
+ )
78
+ ),
79
+ 'filter' => false,
80
+ 'sortable' => false,
81
+ 'is_system' => true
82
+ ));
83
+
84
+ $this->addExportType('*/*/exportCsv', Mage::helper('sales')->__('CSV'));
85
+ $this->addExportType('*/*/exportExcel', Mage::helper('sales')->__('Excel XML'));
86
+
87
+ return parent::_prepareColumns();
88
+ }
89
+ }
90
+
91
+ /* Filename: Grid.php */
92
+ /* Location: app/code/local/Osf/Synnex/Block/Adminhtml/Shipments/Grid.php */
app/code/community/Osf/Synnex/Block/Adminhtml/Shipments/Renderer/Status.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Status Block
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Block_Adminhtml_Shipments_Renderer_Status extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract
18
+ {
19
+ /**
20
+ * Return the label of the status
21
+ *
22
+ * @param Varien_Object
23
+ * @return string
24
+ *
25
+ */
26
+ public function render(Varien_Object $row)
27
+ {
28
+ $label = '';
29
+ if(!is_null($row->getShipmentStatus())){
30
+ $label = Mage::getModel('synnex/source')->getStatusText($row->getShipmentStatus());
31
+ } else {
32
+ $shipment = Mage::getModel('sales/order_shipment')->load($row->getId());
33
+ $label = Mage::getModel('synnex/source')->getStatusText($shipment->getShipmentStatus());
34
+ }
35
+
36
+ return $label;
37
+ }
38
+ }
39
+
40
+ /* Filename: Status.php */
41
+ /* Location: app/code/local/Osf/Synnex/Block/Adminhtml/Shipments/Renderer/Status.php */
app/code/community/Osf/Synnex/Block/Adminhtml/System/Config/Button/Import.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Import Block
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+ class Osf_Synnex_Block_Adminhtml_System_Config_Button_Import extends Mage_Adminhtml_Block_System_Config_Form_Field
17
+ {
18
+
19
+ protected function _construct()
20
+ {
21
+ parent::_construct();
22
+ $this->setTemplate('synnex/system/config/import.phtml');
23
+ }
24
+
25
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
26
+ {
27
+ return $this->_toHtml();
28
+ }
29
+
30
+ public function getAjaxUrl()
31
+ {
32
+ return Mage::helper('adminhtml')->getUrl('synnex/index/startImport');
33
+ }
34
+
35
+ public function getButtonHtml()
36
+ {
37
+ $button = $this->getLayout()->createBlock('adminhtml/widget_button')
38
+ ->setData(array(
39
+ 'id' => 'synnex_import_button',
40
+ 'label' => $this->helper('adminhtml')->__('Manual Import'),
41
+ 'onclick' => 'javascript:startImport(); return false;'
42
+ ));
43
+
44
+ return $button->toHtml();
45
+ }
46
+
47
+ }
48
+
49
+ /* Filename: Import.php */
50
+ /* Location: app/code/community/Osf/Synnex/Block/Adminhtml/System/Config/Button/Import.php */
app/code/community/Osf/Synnex/Block/Adminhtml/System/Config/Button/Notice.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Notice Block
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+ class Osf_Synnex_Block_Adminhtml_System_Config_Button_Notice extends Mage_Adminhtml_Block_System_Config_Form_Field
17
+ {
18
+
19
+ protected function _construct()
20
+ {
21
+ parent::_construct();
22
+ $this->setTemplate('synnex/system/config/notice.phtml');
23
+ }
24
+
25
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
26
+ {
27
+ return $this->_toHtml();
28
+ }
29
+
30
+ public function getAjaxUrl()
31
+ {
32
+ return Mage::helper('adminhtml')->getUrl('synnex/index/shipNoticeCron');
33
+ }
34
+
35
+ public function getButtonHtml()
36
+ {
37
+ $button = $this->getLayout()->createBlock('adminhtml/widget_button')
38
+ ->setData(array(
39
+ 'id' => 'synnex_import_button',
40
+ 'label' => $this->helper('adminhtml')->__('Manual Import'),
41
+ 'onclick' => 'javascript:startNotice(); return false;'
42
+ ));
43
+
44
+ return $button->toHtml();
45
+ }
46
+
47
+ }
48
+
49
+ /* Filename: Notice.php */
50
+ /* Location: app/code/community/Osf/Synnex/Block/Adminhtml/System/Config/Button/Notice.php */
app/code/community/Osf/Synnex/Helper/Connect.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Connect Helper
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Helper_Connect extends Mage_Core_Helper_Data
18
+ {
19
+ public $ftpHost;
20
+ protected $ftpUser;
21
+ protected $ftpPass;
22
+ public $XMLEndpoint;
23
+ public $tmpLocation;
24
+ public $prodFileName;
25
+ public $logFile = 'synnex.log';
26
+ protected $logH = 'Synnex: Error: ';
27
+
28
+ /**
29
+ * The class constructor method
30
+ */
31
+ public function __construct()
32
+ {
33
+ $this->ftpHost = Mage::getStoreConfig('synnex/ftplogin/ftp_host');
34
+ $this->ftpUser = Mage::getStoreConfig('synnex/ftplogin/ftp_user');
35
+ $this->ftpPass = Mage::helper('core')->decrypt(Mage::getStoreConfig('synnex/ftplogin/ftp_password'));
36
+ $this->prodFileName = Mage::getStoreConfig('synnex/ftplogin/ftp_prod_file');
37
+ $this->XMLEndpoint = Mage::getStoreConfig('synnex/xmllogin/xml_endpoint');
38
+ $this->tmpLocation = Mage::getBaseDir('var') . DS . 'synnex' . DS;
39
+ // check if the synnex folder exists if not create it
40
+ return $this->checkDir();
41
+ }
42
+
43
+ /**
44
+ * Get the products file from Synnex FTP
45
+ * @return string
46
+ * @throws Exception
47
+ * @internal param $none
48
+ */
49
+ public function getProductsFile()
50
+ {
51
+ // init the ftp and download the products file
52
+ $ftp = Mage::helper('synnex/ftp');
53
+ if(is_null($this->ftpHost) || is_null($this->ftpUser) || is_null($this->ftpPass)){
54
+ Mage::log($this->logH . 'ftphost or credentials can not be empty',
55
+ null,
56
+ $this->logFile);
57
+ return false;
58
+ }
59
+ $ftp->ftpConnect($this->ftpHost, $this->ftpUser, $this->ftpPass);
60
+ $ftp->downloadFile($this->tmpLocation . $this->prodFileName, $this->prodFileName);
61
+ $ftp->ftpClose();
62
+
63
+ // Checking the download file is an archive
64
+ $fileObj = new SplFileInfo($this->tmpLocation . $this->prodFileName);
65
+ $filename = ($fileObj->getExtension() === 'zip')? $this->extractProductsFile() : $this->prodFileName;
66
+ $fullPath = $this->tmpLocation . $filename;
67
+ return $fullPath;
68
+ }
69
+
70
+ /**
71
+ * Gets the shipping files from Synnex FTP
72
+ *
73
+ * @param none
74
+ * @return bool
75
+ */
76
+ public function getShippingFiles()
77
+ {
78
+ $shippingNotices = array();
79
+ $files = array();
80
+ $reqDel = array();
81
+
82
+ $ftp = Mage::helper('synnex/ftp');
83
+ $ftp->ftpConnect($this->ftpHost, $this->ftpUser, $this->ftpPass);
84
+ // getting a list of all the files on the server
85
+ $remoteFiles = $ftp->directoryListing();
86
+ foreach ($remoteFiles as $remote) {
87
+ $remoteArr = explode('.', $remote);
88
+ $ext = array_pop($remoteArr);
89
+ // the ship notice is an xml file so check it
90
+ if($ext != 'xml'){
91
+ continue;
92
+ }
93
+
94
+ $file['local'] = $this->tmpLocation . $remote;
95
+ $file['remote'] = $remote;
96
+ $files[] = $file;
97
+ $reqDel[] = $remote;
98
+ $shippingNotices[] = $this->tmpLocation . $remote; // same as local
99
+ }
100
+
101
+ // check if the files exist on the server
102
+ if(empty($shippingNotices)){
103
+ return false;
104
+ }
105
+ // download the files
106
+ $downloadConfirm = $ftp->downloadFiles($files);
107
+
108
+ // if download confirmed delete the files on the server
109
+ if($downloadConfirm === true){
110
+ $deleteConfirm = $ftp->deleteFiles($reqDel);
111
+ if($deleteConfirm === false){
112
+ Mage::log($this->logH . 'Deleting the ship notice files from the server failed',
113
+ null,
114
+ $this->logFile);
115
+ }
116
+ } else {
117
+ Mage::log($this->logH . 'Downloading the ship notice files from the server failed',
118
+ null,
119
+ $this->logFile);
120
+ }
121
+
122
+ // close the ftp connection
123
+ $ftp->ftpClose();
124
+
125
+ return $shippingNotices;
126
+ }
127
+
128
+ /**
129
+ * Send the process order xml request
130
+ *
131
+ * @param string
132
+ * @return string
133
+ */
134
+ public function sendXMLRequest($xml)
135
+ {
136
+ /* init the http client */
137
+ $client = new Zend_Http_Client($this->XMLEndpoint);
138
+ /* set the method type and the timeout for the http call */
139
+ $client->setMethod(Zend_Http_Client::POST);
140
+ $client->setConfig(array(
141
+ 'timeout' => 30)
142
+ );
143
+
144
+ /* adding the post params */
145
+ $client->setParameterPost(array(
146
+ 'xmldata' => $xml
147
+ ));
148
+
149
+ /* making the request to the server and receiving the request */
150
+ $response = $client->request();
151
+ return $response->getBody();
152
+ }
153
+
154
+ /**
155
+ * Check if the Synnex temporary file exists, if not create it, if we are allowed
156
+ *
157
+ * @return bool
158
+ */
159
+ public function checkDir()
160
+ {
161
+ if(file_exists($this->tmpLocation) === false){
162
+ if(!mkdir($this->tmpLocation, 0777, true)){
163
+ Mage::log($this->logH . 'Synnex folder does not exist in var folder and it could not be created',
164
+ null,
165
+ $this->logFile);
166
+ return false;
167
+ }
168
+ }
169
+ return true;
170
+ }
171
+
172
+ /**
173
+ * Send the process order xml request
174
+ *
175
+ * @return string|bool
176
+ */
177
+ public function extractProductsFile()
178
+ {
179
+ // init the zip archive object and open
180
+ $zip = new ZipArchive;
181
+ $result = $zip->open($this->tmpLocation . $this->prodFileName);
182
+ if($result === true){
183
+ $filename = $zip->getNameIndex(0);
184
+ $zip->extractTo($this->tmpLocation);
185
+ $zip->close();
186
+ } else {
187
+ return false;
188
+ }
189
+ return $filename;
190
+ }
191
+
192
+ }
193
+
194
+ /* Filename: Connect.php */
195
+ /* Location: app/code/local/Osf/Synnex/Helper/Connect.php */
app/code/community/Osf/Synnex/Helper/Data.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Data Helper
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ /* Require the Magmi files */
18
+ require_once(Mage::getModuleDir('', 'Osf_Synnex') . DS . 'lib' . DS . "magmi/inc/magmi_defs.php");
19
+ require_once(Mage::getModuleDir('', 'Osf_Synnex') . DS . 'lib' . DS . "magmi/integration/inc/magmi_datapump.php");
20
+ /* End require Magmi files */
21
+
22
+ class Osf_Synnex_Helper_Data extends Mage_Core_Helper_Data
23
+ {
24
+ protected $importArray = array();
25
+ protected $profile = "Synnex";
26
+ public $resource;
27
+ public $writeConn;
28
+ public $readConn;
29
+ public $logFile;
30
+
31
+ public function __construct(){
32
+ // getting the resources
33
+ $this->resource = Mage::getSingleton('core/resource');
34
+ $this->writeConn = $this->resource->getConnection('core_write');
35
+ $this->readConn = $this->resource->getConnection('core_read');
36
+ $this->logFile = 'synnex.log';
37
+ }
38
+
39
+ /**
40
+ * Starts the import using Magmi
41
+ *
42
+ * @param none
43
+ * @return bool
44
+ */
45
+ public function startMagmiImport()
46
+ {
47
+ /* Factory Create the product import */
48
+ $magmiDp = Magmi_DataPumpFactory::getDataPumpInstance("productimport");
49
+
50
+ /* Init the profile and the import method */
51
+ $magmiDp->beginImportSession($this->profile,"create");
52
+
53
+ /* looping thought the entire array of products */
54
+ foreach ($this->importArray as $productKey => $productValue) {
55
+ $res = $magmiDp->ingest($productValue);
56
+ if($res['ok'] !== true){
57
+ Mage::log('Magmi: Import of product failed, sku:' . $productValue['sku'], null,$this->logFile);
58
+ }
59
+ }
60
+
61
+ /* End import session */
62
+ $magmiDp->endImportsession();
63
+ return;
64
+ }
65
+
66
+ /**
67
+ * Set the import array data
68
+ *
69
+ * @param array
70
+ * @return array
71
+ */
72
+ public function setImportArray($dataIn)
73
+ {
74
+ return $this->importArray = $dataIn;
75
+ }
76
+
77
+ }
78
+
79
+ /* Filename: Data.php */
80
+ /* Location: app/code/local/Osf/Synnex/Helper/Data.php */
app/code/community/Osf/Synnex/Helper/Ftp.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * FTP Helper
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Helper_Ftp extends Mage_Core_Helper_Data
18
+ {
19
+ private $conn;
20
+
21
+ /**
22
+ * Connect to FTP
23
+ *
24
+ * @param $host
25
+ * @param $user
26
+ * @param $pass
27
+ * @return string
28
+ * @throws Exception
29
+ * @internal param $none
30
+ */
31
+ public function ftpConnect($host,$user,$pass)
32
+ {
33
+ $this->conn = ftp_connect($host);
34
+ if($this->conn === false){
35
+ throw new Exception("Ftp: Could not connect!", 1);
36
+ }
37
+ if(!ftp_login($this->conn, $user, $pass)){
38
+ throw new Exception("Ftp: Could not login!", 1);
39
+ }
40
+ if(!ftp_pasv($this->conn, true)){
41
+ throw new Exception("Ftp: Could not enter passive mode!", 1);
42
+ }
43
+ return;
44
+ }
45
+
46
+ /**
47
+ * Close the ftp connection
48
+ *
49
+ * @param none
50
+ * @return bool
51
+ */
52
+ public function ftpClose()
53
+ {
54
+ return ftp_close($this->conn);
55
+ }
56
+
57
+ /**
58
+ * Get the directory file structure
59
+ *
60
+ * @param string $dir
61
+ * @return array
62
+ * @internal param string $params
63
+ */
64
+ public function directoryListing($dir='.')
65
+ {
66
+ return ftp_nlist($this->conn, $dir );
67
+ }
68
+
69
+ /**
70
+ * Download a file from the server
71
+ *
72
+ * @param string $localFile
73
+ * @param string $remoteFile
74
+ * @param int|string $type
75
+ * @return bool
76
+ */
77
+ public function downloadFile($localFile, $remoteFile, $type=FTP_BINARY)
78
+ {
79
+ return ftp_get($this->conn, $localFile, $remoteFile, $type);
80
+ }
81
+
82
+ /**
83
+ * Download multiple files from the server
84
+ *
85
+ * @param none
86
+ * @return bool
87
+ */
88
+ public function downloadFiles($files)
89
+ {
90
+ $errors = array();
91
+ foreach ($files as $file) {
92
+ $errors[] = $this->downloadFile($file['local'], $file['remote']);
93
+ }
94
+
95
+ if(in_array(false, $errors)){
96
+ return false;
97
+ } else {
98
+ return true;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Delete multiple files from the server
104
+ *
105
+ * @param none
106
+ * @return bool
107
+ */
108
+ public function deleteFiles($files)
109
+ {
110
+ $errors = array();
111
+ foreach ($files as $file) {
112
+ $errors[] = $this->deleteFile($file);
113
+ }
114
+ if(in_array(false, $errors)){
115
+ return false;
116
+ } else {
117
+ return true;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Delete a file from the server
123
+ *
124
+ * @param none
125
+ * @return bool
126
+ */
127
+ public function deleteFile($filePath)
128
+ {
129
+ return ftp_delete($this->conn, $filePath);
130
+ }
131
+ }
app/code/community/Osf/Synnex/Helper/Validate.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Validate Helper
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Helper_Validate extends Mage_Core_Helper_Data
18
+ {
19
+ public $strictRules = array();
20
+ protected $availOperands = array();
21
+ public $customeConditions = array();
22
+
23
+ public function __construct()
24
+ {
25
+ $this->availOperands['='] = 'equal';
26
+ $this->availOperands['!='] = 'notEqual';
27
+ $this->availOperands['>'] = 'bigger';
28
+ $this->availOperands['<'] = 'smaller';
29
+ $this->getConditions();
30
+ }
31
+
32
+ /**
33
+ * Run the validation
34
+ *
35
+ * @param array product row
36
+ * @return bool
37
+ */
38
+ public function runValidation($row)
39
+ {
40
+ // simplified version of validation because a dynamic validation is overkill for one condition
41
+ if($row[22] == 'SWL'){
42
+ return false;
43
+ }
44
+
45
+ return true;
46
+ }
47
+
48
+ /**
49
+ * Run the custom validation from the backend configuration
50
+ *
51
+ * @param array product row
52
+ * @return string
53
+ *
54
+ */
55
+ public function runCustomValidation($row)
56
+ {
57
+ foreach ($this->customeConditions as $condition) {
58
+ if(!isset($row[$condition[0]])) continue;
59
+
60
+ if(!$this->{$condition[1]}($row,$condition[0], $condition[2])){
61
+ return false;
62
+ }
63
+ }
64
+ return true;
65
+ }
66
+
67
+ /**
68
+ * Set the strict rules
69
+ *
70
+ * @return bool
71
+ */
72
+ public function setStrictRules()
73
+ {
74
+ $strictRules = array(22=>'RTL');
75
+
76
+ return $this->strictRules = $strictRules;
77
+ }
78
+
79
+ /**
80
+ * Get and process the conditions set in admin
81
+ *
82
+ * @return bool
83
+ *
84
+ */
85
+ public function getConditions()
86
+ {
87
+ $strConditions = Mage::getStoreConfig('synnex/synnex_import/import_conditions');
88
+ $conditions = explode(";", $strConditions);
89
+ $theOperands = array_keys($this->availOperands);
90
+ foreach ($conditions as $condition) {
91
+ if(empty($condition)){
92
+ continue;
93
+ }
94
+
95
+ $conditionElements = explode(' ', trim($condition));
96
+ if(!in_array($conditionElements[1], $theOperands)){
97
+ continue;
98
+ }
99
+
100
+ if(!is_numeric($conditionElements[0])){
101
+ continue;
102
+ }
103
+
104
+ $this->customeConditions[] = array(
105
+ $conditionElements[0] - 1,
106
+ $this->availOperands[$conditionElements[1]],
107
+ $conditionElements[2]
108
+ );
109
+
110
+ }
111
+
112
+ return true;
113
+ }
114
+
115
+ /**
116
+ * The equal condition function, if a product value is equal with the codition value then import product
117
+ *
118
+ * @param array product row
119
+ * @param string condition column
120
+ * @param string condition value
121
+ * @return bool
122
+ *
123
+ */
124
+ public function equal($row, $a, $b)
125
+ {
126
+ return ($row[$a] != $b)? false : true;
127
+ }
128
+
129
+ /**
130
+ * The non equal condition function, if a product value is not equal with the condition value then import product
131
+ *
132
+ * @param array product row
133
+ * @param string condition column
134
+ * @param string condition value
135
+ * @return bool
136
+ *
137
+ */
138
+ public function notEqual($row, $a, $b)
139
+ {
140
+ return ($row[$a] == $b)? false : true;
141
+ }
142
+
143
+ /**
144
+ * The bigger condition function, if a product value is bigger then the conditon value then import product
145
+ *
146
+ * @param array product row
147
+ * @param string condition column
148
+ * @param string condition value
149
+ * @return bool
150
+ *
151
+ */
152
+ public function bigger($row, $a, $b)
153
+ {
154
+ return ($row[$a] < $b)? false : true;
155
+ }
156
+
157
+ /**
158
+ * The smaller condition function, if a product value is smaller then the conditon value then import product
159
+ *
160
+ * @param array product row
161
+ * @param string condition column
162
+ * @param string condition value
163
+ * @return bool
164
+ *
165
+ */
166
+ public function smaller($row, $a, $b)
167
+ {
168
+ return ($row[$a] > $b)? false : true;
169
+ }
170
+ }
171
+
172
+ /* Filename: Validate.php */
173
+ /* Location: app/code/local/Osf/Synnex/Helper/Validate.php */
app/code/community/Osf/Synnex/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ IMPORTANT-READ CAREFULLY: This End-User License Agreement ("EULA") is a legal agreement between you (either an individual or a single entity) and OSF GLOBAL SERVICES for the use of OSF GLOBAL SERVICES software, which includes computer software and may include other associated media. YOU AGREE TO BE BOUND BY THE TERMS OF THIS EULA BY INSTALLING, COPYING, OR OTHERWISE USING THE SOFTWARE. IF YOU DO NOT AGREE, DO NOT INSTALL, COPY, OR USE THE SOFTWARE;
3
+
4
+ 1. RESERVATION OF RIGHTS AND OWNERSHIP. OSF GLOBAL SERVICES reserves all rights not expressly granted to you in this EULA. The Software is protected by copyright and other intellectual property laws and treaties. OSF GLOBAL SERVICES owns the title, copyright, and other intellectual property rights in the Software. The Software is licensed, not sold.
5
+
6
+ 2. LIMITATIONS ON REVERSE ENGINEERING, DECOMPILATION, AND DISASSEMBLY. You may not reverse engineer, decompile, or disassemble the Software
7
+
8
+ 3. NO RENTAL/COMMERCIAL HOSTING. You may not rent, lease, lend or provide commercial hosting services with the Software.
9
+
10
+ 4. LINKS TO THIRD PARTY ENTITIES OR PRODUCTS. You may link to third party entities or products through the use of the Software. The third party entities or products are not under the control of OSF GLOBAL SERVICES, and OSF GLOBAL SERVICES is not responsible for the contents or functionality of any third party entities or products. OSF GLOBAL SERVICES does not promise functionality or compatibility with any 3rd party products, which are not mentioned explicitly in a product description
11
+
12
+ 5. SUPPORT, UPDATES AND UPGRADES. This licence doesn't include any support, updates and upgrades. If you would require updates, upgrades and support services you will be required to subscribe to support, updates and upgrades plan for the software. Support will include guidance regarding installation, configuration or use of software as well as fixes of software errors and bugs. Depending on the nature of the problem OSF GLOBAL SERVICES at its own discretion may agree to provide additional assistance for a fee subject to a separate written agreement.
13
+
14
+ 6. TERMINATION. Without prejudice to any other rights, OSF GLOBAL SERVICES may terminate this EULA if you fail to comply with the terms and conditions of this EULA. In such event, you must destroy all copies of the Software and all of its component parts.
15
+
16
+ 7. LIMITED WARRANTY. FOR ANY DEFECTS THERE IS NO WARRANTY OR CONDITION OF ANY KIND.
17
+
18
+ 8. EXCLUSION OF INCIDENTAL, CONSEQUENTIAL AND CERTAIN OTHER DAMAGES. IN NO EVENT SHALL OSF GLOBAL SERVICES BE LIABLE FOR ANY SPECIAL, INCIDENTAL, PUNITIVE, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, BUT NOT LIMITED TO, DAMAGES FOR LOSS OF PROFITS OR CONFIDENTIAL OR OTHER INFORMATION, FOR BUSINESS INTERRUPTION, FOR PERSONAL INJURY, FOR LOSS OF PRIVACY, FOR FAILURE TO MEET ANY DUTY INCLUDING OF GOOD FAITH OR OF REASONABLE CARE, FOR NEGLIGENCE, AND FOR ANY OTHER PECUNIARY OR OTHER LOSS WHATSOEVER) ARISING OUT OF OR IN ANY WAY RELATED TO THE USE OF OR INABILITY TO USE THE SOFTWARE, THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT OR OTHER SERVICES, INFORMATON, SOFTWARE, AND RELATED CONTENT THROUGH THE SOFTWARE OR OTHERWISE ARISING OUT OF THE USE OF THE SOFTWARE, OR OTHERWISE UNDER OR IN CONNECTION WITH ANY PROVISION OF THIS EULA.
19
+
20
+
app/code/community/Osf/Synnex/Model/Import.php ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Import Model
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Model_Import extends Mage_Core_Model_Abstract
18
+ {
19
+ public $resource;
20
+ public $readConnection;
21
+ public $categoryMap = array();
22
+ public $synnexVendorId;
23
+ protected $logFile = 'synnex.log';
24
+
25
+ public function _construct()
26
+ {
27
+ $this->resource = Mage::getSingleton('core/resource');
28
+ $this->readConnection = $this->resource->getConnection('core_read');
29
+ $this->buildCategoryMap();
30
+ if(!$this->checkMagmiConfig()){
31
+ echo "Could not create magmi config file. Please make the folder ... temporary writeable";
32
+ die();
33
+ }
34
+ parent::_construct();
35
+ }
36
+
37
+ /**
38
+ * Process the received csv
39
+ *
40
+ * @param none
41
+ * @return bool
42
+ */
43
+ public function processData()
44
+ {
45
+ /* getting the products file from the server */
46
+ $productsFile = Mage::helper('synnex/connect')->getProductsFile();
47
+ if($productsFile === false){
48
+ Mage::log("Synnex: Error in getting products file", null, $this->logFile);
49
+ return false;
50
+ }
51
+
52
+ /* init vars */
53
+ $file = new SplFileObject($productsFile);
54
+ $productsData = array();
55
+ $row1 = 0;
56
+
57
+ /* looping through the csv */
58
+ while(!$file->eof()){
59
+ $row = $file->fgetcsv("~");
60
+ /* omit the first row that has the header of the file */
61
+ if($row1 == 0){
62
+ $row1 = 1;
63
+ continue;
64
+ }
65
+ if(empty($row[4])){
66
+ continue;
67
+ }
68
+ /* validate data */
69
+ if(!$this->validate($row)){
70
+ continue;
71
+ }
72
+
73
+ /* setting the product data */
74
+ $productData = $this->createDataArr($row);
75
+ if($productData !== false){
76
+ $productsData[] = $productData;
77
+ }
78
+
79
+ }
80
+
81
+ /* setting the data to be imported and starting the import */
82
+ Mage::helper('synnex/data')->setImportArray($productsData);
83
+ Mage::helper('synnex/data')->startMagmiImport();
84
+
85
+ return true;
86
+ }
87
+
88
+ /**
89
+ * Validate the product based on the required validations
90
+ *
91
+ * @param array $row
92
+ * @return bool
93
+ */
94
+ public function validate($row)
95
+ {
96
+ // Validate strict rules
97
+ Mage::helper('synnex/validate')->setStrictRules();
98
+ $valid = Mage::helper('synnex/validate')->runValidation($row);
99
+ if(!$valid){
100
+ return false;
101
+ }
102
+
103
+ $valid2 = Mage::helper('synnex/validate')->runCustomValidation($row);
104
+ if(!$valid2){
105
+ return false;
106
+ }
107
+
108
+ $productExists = $this->productExists($row[4]);
109
+ # Check if ABC Code is equal with active
110
+ if($row[39] !== 'A' && $productExists === false){
111
+ return false;
112
+ }
113
+
114
+ # Check if Kit/Stand Alone Flag is equal with S
115
+ if($row[40] !== 'S' && $productExists === false){
116
+ return false;
117
+ }
118
+
119
+ return true;
120
+ }
121
+
122
+ /**
123
+ * Construct the product array for import
124
+ *
125
+ * @param array $row
126
+ * @return array $productData
127
+ */
128
+ public function createDataArr($row)
129
+ {
130
+ $importedCatId = ltrim($row[24],'0');
131
+ $catMap = $this->getCategoryId($importedCatId);
132
+ $websites = Mage::app()->getWebsites();
133
+ $storeCode = $websites[1]->getDefaultStore()->getCode();
134
+
135
+ $productData = array();
136
+ $productData['sku'] = $row[4];
137
+ $productData['url_key'] = Mage::getModel('catalog/product_url')->formatUrlKey($row[6]);
138
+ $productData['url_rewrite'] = 1;
139
+ $productData['mpn'] = $row[2];
140
+ $productData['name'] = $row[6];
141
+ $productData['short_description'] = $row[6];
142
+ $productData['description'] = $row[6];
143
+ $productData['manufacturer'] = $row[7];
144
+ $productData['qty'] = $row[9];
145
+ $productData['msrp'] = $row[13];
146
+ $productData['is_oversized'] = ($row[18] === 'N')? 1 : 0;
147
+ $productData['osf_product_cost'] = $row[20];
148
+ $productData['osf_product_vendor'] = 'Synnex';
149
+ $productData['weight'] = (!empty($row[27]))? $row[27] : 1;
150
+ $productData['upc'] = $row[33];
151
+ $productData['item_length'] = $row[52];
152
+ $productData['item_width'] = $row[53];
153
+ $productData['item_height'] = $row[54];
154
+ $productData['status'] = Mage_Catalog_Model_Product_Status::STATUS_DISABLED;
155
+ $productData['visibility'] = Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE;
156
+ $productData['tax_class_id'] = 2;
157
+ $productData['type_id'] = Mage_Catalog_Model_Product_Type::TYPE_SIMPLE;
158
+ $productData['attribute_set_id'] = 4;
159
+ $productData['store'] = $storeCode;
160
+ $productData['categories'] = (!is_null($catMap))? $catMap['category_paths'] : null;
161
+
162
+ return $productData;
163
+ }
164
+
165
+ /**
166
+ * Check if a product with a specific sku exists in Magento
167
+ *
168
+ * @param string $sku
169
+ * @return bool
170
+ */
171
+ public function productExists($sku)
172
+ {
173
+ $tableName = $this->resource->getTableName('catalog_product_entity');
174
+ $select = $this->readConnection
175
+ ->select()
176
+ ->from($tableName, array(new Zend_Db_Expr('count(entity_id)')))
177
+ ->where($this->readConnection->quoteInto('sku=?', $sku));
178
+ $countSku = $this->readConnection->fetchOne($select);
179
+
180
+ return ($countSku != 0)? true :false;
181
+ }
182
+
183
+ public function getCategoryId($catId)
184
+ {
185
+ return (array_key_exists($catId, $this->categoryMap))? $this->categoryMap[$catId] : null;
186
+ }
187
+
188
+ /**
189
+ * Build the category array map based of the category map file
190
+ */
191
+ public function buildCategoryMap()
192
+ {
193
+ $filename = Mage::getStoreConfig('synnex/synnex_import/upload_file');
194
+ $categoryMapFile = Mage::getBaseDir('media') . DS . 'admin-config-uploads' . DS . $filename;
195
+
196
+ if(!file_exists($categoryMapFile)){
197
+ return;
198
+ }
199
+
200
+ $mapFile = new SplFileObject($categoryMapFile);
201
+
202
+ $row1 = 0;
203
+ /* looping thought the csv */
204
+ while(!$mapFile->eof()){
205
+ $row = $mapFile->fgetcsv();
206
+
207
+ /* omit the first row that has the header of the file */
208
+ if($row1 == 0){
209
+ $row1 = 1;
210
+ continue;
211
+ }
212
+
213
+ /* setting the product data */
214
+ $this->categoryMap[$row[0]]['category_paths'] = $row[1];
215
+ }
216
+
217
+ return;
218
+ }
219
+
220
+ /**
221
+ * Check if the db configuration for magmi exists and if not create it
222
+ *
223
+ * @return bool
224
+ */
225
+ public function checkMagmiConfig()
226
+ {
227
+ $filename = Mage::getModuleDir('', 'Osf_Synnex') . DS . 'lib' . DS . "magmi/conf/magmi.ini";
228
+ if(file_exists($filename)){
229
+ return true;
230
+ }
231
+
232
+ $config = Mage::getConfig()->getResourceConnectionConfig("default_setup");
233
+ $file = new SplFileObject($filename,"w");
234
+ $fileText = "[DATABASE]\nconnectivity = \"net\"\nhost = \""
235
+ . $config->host . "\"\nport = \"3306\"\nunix_socket = \ndbname = \""
236
+ . $config->dbname . "\"\nuser = \""
237
+ . $config->username . "\"\npassword = "
238
+ . $config->password . "\ntable_prefix = \n[MAGENTO]\nversion = \"1.7.x\"\nbasedir = \"../../\"\n[GLOBAL]\n"
239
+ ."step = \"0.5\"\nmultiselect_sep = \",\"\ndirmask = \"755\"\nfilemask = \"644\"\n";
240
+
241
+ try{
242
+ $file->fwrite($fileText);
243
+ } catch (Exception $e){
244
+ Mage::log('Magmi Conf folder not writeable');
245
+ return false;
246
+ }
247
+
248
+ return true;
249
+ }
250
+
251
+ }
252
+
253
+ /* Filename: Import.php */
254
+ /* Location: app/code/local/Osf/Synnex/Model/Import.php */
app/code/community/Osf/Synnex/Model/Observer.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Observer Model
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Model_Observer extends Mage_Core_Model_Abstract
18
+ {
19
+ protected $vendor = 'Synnex';
20
+ protected $logFile = 'synnex.log';
21
+
22
+ /**
23
+ * Process the order so it can be sent to Synnex
24
+ *
25
+ * @param object
26
+ * @return object
27
+ */
28
+ public function processOrder($observer)
29
+ {
30
+ Mage::log($this->vendor . ': Start processing order', null, $this->logFile);
31
+ $order = $observer->getOrder();
32
+
33
+ if($order->getStatus() == Mage_Sales_Model_Order::STATE_PROCESSING){
34
+ Mage::log($this->vendor . ': End processing order, order already processed', null, $this->logFile);
35
+ return $this;
36
+ }
37
+
38
+ $orderItems = $this->checkItems($order->getAllItems());
39
+ if($orderItems === false){
40
+ Mage::log($this->vendor . ': End processing order, no ' . $this->vendor .' items', null, $this->logFile);
41
+ return $this;
42
+ }
43
+
44
+ // check that the order has shipments
45
+ $shipments = $order->getShipmentsCollection();
46
+ if(count($shipments) > 0){
47
+ return $this;
48
+ }
49
+
50
+ $shipment = $this->createShipment($order,$orderItems);
51
+
52
+ // build the xml that will be sent to Synnex
53
+ $xmlData = Mage::getModel('synnex/order')->buildOrderArray($order, $shipment);
54
+ Mage::log($xmlData, null, $this->logFile);
55
+
56
+ // send the process order request
57
+ $xmlResponse = Mage::helper('synnex/connect')->sendXMLRequest($xmlData);
58
+ Mage::log($xmlResponse, null, $this->logFile);
59
+
60
+ // process the response that has arrived from Synnex
61
+ $response = Mage::getModel('synnex/order')->processOrderResponse($xmlResponse, $order, $xmlData);
62
+ Mage::log($this->vendor . ': After process response', null, $this->logFile);
63
+
64
+ Mage::log($this->vendor . ': End processing order', null, $this->logFile);
65
+ return $this;
66
+ }
67
+
68
+ /**
69
+ * Check if the order contains products from synnex and return the items
70
+ * @param $items
71
+ * @return bool
72
+ */
73
+ public function checkItems($items)
74
+ {
75
+ $outItems = array();
76
+ foreach ($items as $item) {
77
+ $p_id = $item->getProduct()->getId();
78
+ $vendor = Mage::getModel('catalog/product')->load($p_id)->getData('osf_product_vendor');
79
+ if(trim($vendor) == $this->vendor){
80
+ $outItems[] = $item;
81
+ }
82
+ }
83
+
84
+ return (empty($outItems))? false : $outItems;
85
+ }
86
+
87
+ /**
88
+ * Create the shipment for the order
89
+ *
90
+ * @param $items
91
+ * @return bool
92
+ */
93
+ public function createShipment($order, $orderItems)
94
+ {
95
+ $itemsQtys = array();
96
+ foreach ($orderItems as $orderItem) {
97
+ $itemsQtys[$orderItem->getId()] = $orderItem->getData('qty_ordered');
98
+ }
99
+
100
+ $shipment = $order->prepareShipment($itemsQtys);
101
+ if ($shipment) {
102
+ $shipment->register();
103
+ $shipment->setShipmentStatus(Osf_Synnex_Model_Source::SHIPMENT_STATUS_PENDING);
104
+ $shipment->addComment($this->vendor . ' Items');
105
+ $shipment->getOrder()->setIsInProcess(true);
106
+
107
+ try {
108
+ $shipment->save();
109
+ } catch (Mage_Core_Exception $e) {
110
+ Mage::log($this->vendor . ': ' . $e->getMessage(), null, $this->logFile);
111
+ }
112
+ }
113
+
114
+ return $shipment;
115
+ }
116
+
117
+ }
118
+
119
+ /* Filename: Observer.php */
120
+ /* Location: app/code/local/Osf/Synnex/Model/Observer.php */
app/code/community/Osf/Synnex/Model/Order.php ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Order Model
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Model_Order extends Mage_Core_Model_Abstract
18
+ {
19
+ protected $synnexAccount;
20
+ protected $XMLUser;
21
+ protected $XMLPass;
22
+ protected $XMLObj;
23
+ protected $order;
24
+ protected $queue;
25
+ protected $logFile = 'synnex.log';
26
+ protected $knownErrors = array('DatabaseIssue', 'IncompleteXML01', 'Internal API Fault',
27
+ 'PO_TIMEOUT', 'ServerError', 'IP Address Invalid');
28
+
29
+ public function _construct()
30
+ {
31
+ $this->synnexAccount = Mage::getStoreConfig('synnex/synnex/synnex_account_number');
32
+ $this->XMLUser = Mage::getStoreConfig('synnex/xmllogin/xml_username');
33
+ $this->XMLPass = Mage::helper('core')->decrypt(Mage::getStoreConfig('synnex/xmllogin/xml_password'));
34
+ parent::_construct();
35
+ }
36
+
37
+ /**
38
+ * Build the xml from array
39
+ *
40
+ * @param array
41
+ * @return string
42
+ */
43
+ public function buildXMLData($orderData){
44
+ $this->XMLObj = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><SynnexB2B></SynnexB2B>');
45
+
46
+ // credentials
47
+ $credentials = $this->XMLObj->addChild('Credential');
48
+ $credentials->addChild('UserID', $this->XMLUser);
49
+ $credentials->addChild('Password', $this->XMLPass);
50
+
51
+ // Order Request
52
+ $orderReq = $this->XMLObj->addChild('OrderRequest');
53
+ foreach ($orderData['orderReq'] as $reqKey => $reqValue) {
54
+ $orderReq->addChild($reqKey, $reqValue);
55
+ }
56
+
57
+ // Order Request Items
58
+ $items = $orderReq->addChild('Items');
59
+ $i = 1;
60
+ foreach ($orderData['items'] as $item) {
61
+ $itemNode = $items->addChild('Item');
62
+ $itemNode->addAttribute('lineNumber', $i);
63
+ $itemNode->addChild('SKU', $item['sku']);
64
+ $itemNode->addChild('CustomerPartNumber', $item['customerPartNumber']);
65
+ $itemNode->addChild('ProductName', $item['name']);
66
+ $itemNode->addChild('UnitPrice', $item['price']);
67
+ $itemNode->addChild('OrderQuantity', $item['qty']);
68
+ $itemNode->addChild('Comment', $item['comment1']);
69
+ $itemNode->addChild('Comment', $item['comment2']);
70
+ $itemNode->addChild('ShipFromWarehouse', $item['shipfrom']);
71
+ $itemNode->addChild('SpecialPriceReferenceNumber', $item['specialPriceRef']);
72
+ $i++;
73
+ }
74
+
75
+ // Add the shipment node of the xml
76
+ $shipmentNode = $orderReq->addChild('Shipment');
77
+ foreach ($orderData['shipment'] as $shipmentKey => $shipment) {
78
+ if(!is_array($shipment)){
79
+ $shipmentNode->addChild($shipmentKey, $shipment);
80
+ } else {
81
+ $node = $shipmentNode->addChild($shipmentKey);
82
+ foreach ($shipment as $shipKey => $shipValue) {
83
+ $node->addChild($shipKey,$shipValue);
84
+ }
85
+ }
86
+ }
87
+
88
+ // Add the payment node in the xml
89
+ $paymentNode = $orderReq->addChild('Payment');
90
+ foreach ($orderData['payment'] as $paymentKey => $payment) {
91
+ if(!is_array($payment)){
92
+ $paymentNode->addChild($paymentKey, $payment);
93
+ } else {
94
+ $node = $paymentNode->addChild($paymentKey);
95
+ if($paymentKey == 'BillTo'){
96
+ $node->addAttribute('code', trim($this->synnexAccount));
97
+ }
98
+ foreach ($payment as $shipKey => $shipValue) {
99
+ $node->addChild($shipKey,$shipValue);
100
+ }
101
+ }
102
+ }
103
+
104
+ return $this->XMLObj->asXML();
105
+ }
106
+
107
+ /**
108
+ * Build the order array so it can be sent to xml process
109
+ *
110
+ * @param object
111
+ * @param array
112
+ * @return string
113
+ */
114
+ public function buildOrderArray($order, $shipment)
115
+ {
116
+ $this->order = $order;
117
+ $data = array(
118
+ 'orderReq' => $this->buildOrderReq($shipment),
119
+ 'items' => $this->buildItems($shipment),
120
+ 'payment' => $this->buildPayment($order),
121
+ 'shipment' => $this->buildShipping($shipment)
122
+ );
123
+
124
+ return $this->buildXMLData($data);
125
+ }
126
+
127
+ /**
128
+ * Map order shipping to array for xml
129
+ *
130
+ * @param object
131
+ * @return array
132
+ */
133
+ public function buildShipping($shipment)
134
+ {
135
+ $shippingAddress = $shipment->getShippingAddress();
136
+ $streetArr = $shippingAddress->getStreet();
137
+
138
+ $shipment = array(
139
+ "ShipTo" => array(
140
+ "AddressName1" => $shippingAddress->getFirstname() . $shippingAddress->getLastname(),
141
+ "AddressName2" => null,
142
+ "AddressLine1" => $streetArr[0],
143
+ "AddressLine2" => (count($streetArr) > 1)? $streetArr[1] : null,
144
+ "City" => $shippingAddress->getCity(),
145
+ "State" => $shippingAddress->getRegionCode(),
146
+ "ZipCode" => $shippingAddress->getPostcode(),
147
+ "Country" => $shippingAddress->getCountryId()
148
+ ),
149
+ "ShipToContact" => array(
150
+ "ContactName" => $shippingAddress->getFirstname() . $shippingAddress->getLastname(),
151
+ "PhoneNumber" => $shippingAddress->getTelephone(),
152
+ "EmailAddress" => $shippingAddress->getEmail()
153
+ ),
154
+ "ShipMethod" => array(
155
+ "Code" => null,
156
+ "Description" => null,
157
+ ),
158
+ "FreightAccountNumber" => null
159
+ );
160
+ return $shipment;
161
+ }
162
+
163
+ /**
164
+ * Map order billing to array for xml
165
+ *
166
+ * @param object
167
+ * @return array
168
+ */
169
+ public function buildPayment($order)
170
+ {
171
+ $origin = Mage::getStoreConfig('shipping/origin', $this->getStore());
172
+ $region = Mage::getModel('directory/region')->load($origin["region_id"]);
173
+ $payment = array(
174
+ "BillTo" => array(
175
+ "AddressName1" => Mage::app()->getStore()->getFrontendName(),
176
+ "AddressName2" => null,
177
+ "AddressLine1" => $origin['street_line1'],
178
+ "AddressLine2" => $origin['street_line2'],
179
+ "City" => $origin['city'],
180
+ "State" => $region->getCode(),
181
+ "ZipCode" => $origin['postcode'],
182
+ "Country" => $origin['country_id'],
183
+ "SynnexLocationNumber" => null
184
+ )
185
+ );
186
+ return $payment;
187
+ }
188
+
189
+ /**
190
+ * Map order items to array for xml
191
+ *
192
+ * @param array
193
+ * @return array
194
+ */
195
+ public function buildItems($shipment)
196
+ {
197
+ $items = array();
198
+ foreach ($shipment->getAllItems() as $itemObj) {
199
+ $item = array();
200
+ $item['sku'] = $itemObj->getSku();
201
+ $item['customerPartNumber'] = $itemObj->getSku();
202
+ $item['name'] = $itemObj->getName();
203
+ $item['price'] = $itemObj->getPrice();
204
+ $item['qty'] = (int)$itemObj->getQty();
205
+ $item['comment1'] = null;
206
+ $item['comment2'] = null;
207
+ $item['shipfrom'] = null;
208
+ $item['specialPriceRef'] = null;
209
+ $items[] = $item;
210
+ }
211
+
212
+ return $items;
213
+ }
214
+
215
+ /**
216
+ * Map order details to array for xml
217
+ *
218
+ * @param object
219
+ * @return array
220
+ */
221
+ public function buildOrderReq($shipment)
222
+ {
223
+ // build the root xml nodes for order request
224
+ $orderReq = array(
225
+ "CustomerNumber" => trim($this->synnexAccount),
226
+ "PONumber" => $shipment->getIncrementId(),
227
+ "PODateTime" => $shipment->getCreatedAt(),
228
+ "XMLPOSubmitDateTime" => null,
229
+ "ExpectedDate" => null,
230
+ "ExpectedShipDate" => null,
231
+ "DropShipFlag" => 'TBD', // to be determined
232
+ "SpecialHandle" => 'N',
233
+ "BackOrderFlag" => 'N',
234
+ "BackOrderWarehouseSelection" => null,
235
+ "ShipComplete" => 'Y',
236
+ "POLineShipComplete" => 'Y',
237
+ "WarehouseSplit" => 'N',
238
+ "ShipFromWarehouse" => null,
239
+ "SpecialPriceType" => null,
240
+ "SpecialPriceReferenceNumber" => null,
241
+ "SynnexB2BAssignedID" => '6',
242
+ "Comment_1" => null,
243
+ "Comment_2" => null,
244
+ "ShipComment_1" => null,
245
+ "ShipComment_2" => null
246
+ );
247
+
248
+ return $orderReq;
249
+ }
250
+
251
+ /**
252
+ * Process the order response
253
+ *
254
+ * @param $xmlResponse
255
+ * @param $order
256
+ * @param $xmlData
257
+ * @internal param $object
258
+ * @return array
259
+ */
260
+ public function processOrderResponse($xmlResponse, $order, $xmlData)
261
+ {
262
+ $this->order = $order;
263
+ $xmlResponseObject = simplexml_load_string($xmlResponse);
264
+ if($xmlResponseObject === false){
265
+ Mage::log('Synnex: The response was not a xml string', null, $this->logFile);
266
+ return;
267
+ }
268
+
269
+ $orderResponse = $xmlResponseObject->OrderResponse;
270
+ if(is_null($orderResponse)){
271
+ Mage::log('Synnex: The response xml does not contain the OrderResponse node', null, $this->logFile);
272
+ return;
273
+ }
274
+
275
+ if(isset($xmlResponse->errorMessage)){
276
+ Mage::log('Order Response Error: ' .
277
+ $xmlResponse->errorMessage .
278
+ 'Order Response Error Details' .
279
+ $xmlResponse->errorDetail, null, $this->logFile);
280
+ $this->errorResponse($xmlResponseObject, $xmlData);
281
+ return;
282
+ }
283
+
284
+ $poCode = $xmlResponseObject->OrderResponse->Code;
285
+ if($poCode == 'accepted'){
286
+ $this->acceptedResponse($xmlResponseObject);
287
+ } else {
288
+ $this->rejectedResponse($xmlResponseObject);
289
+ }
290
+
291
+ return;
292
+ }
293
+
294
+ /**
295
+ * The accepted response handle
296
+ *
297
+ * @param object
298
+ */
299
+ public function acceptedResponse($xmlObject)
300
+ {
301
+ $orderResponse = $xmlObject->OrderResponse;
302
+ Mage::log('Synnex PO accepted: '. $orderResponse->PONumber, null, $this->logFile);
303
+
304
+ $this->order->addStatusHistoryComment(
305
+ $this->order->getStatus(),
306
+ "Synnex PO Number: " . $orderResponse->Items->Item->OrderNumber
307
+ );
308
+ $shipment = Mage::getModel('sales/order_shipment')->loadByIncrementId($orderResponse->PONumber);
309
+ $shipment->addComment('Purchase Order Accepted');
310
+ $shipment->setShipmentStatus(Osf_Synnex_Model_Source::SHIPMENT_STATUS_READY);
311
+ try{
312
+ $shipment->save();
313
+ } catch (Exception $e){
314
+ Mage::log('Synnex: Response Error: ', null, $this->logFile);
315
+ }
316
+
317
+ return;
318
+ }
319
+
320
+ /**
321
+ * The rejected response handle
322
+ *
323
+ * @param object
324
+ */
325
+ public function rejectedResponse($xmlObject)
326
+ {
327
+ $orderResponse = $xmlObject->OrderResponse;
328
+ Mage::log('Synnex PO rejected: '. $orderResponse->Reason, null, $this->logFile);
329
+ // load the shipment and cancel it
330
+ $shipment = Mage::getModel('sales/order_shipment')->loadByIncrementId($orderResponse->PONumber);
331
+ $shipment->addComment('Purchase Order Rejected: ' . $orderResponse->Reason);
332
+ $shipment->setShipmentStatus(Osf_Synnex_Model_Source::SHIPMENT_STATUS_CANCELED);
333
+ try{
334
+ $shipment->save();
335
+ } catch (Exception $e){
336
+ Mage::log('Synnex: Response Error: '.$e->getMessage(), null, $this->logFile);
337
+ }
338
+
339
+ return;
340
+ }
341
+
342
+ /**
343
+ * The error response handle
344
+ *
345
+ * @param $orderResponse
346
+ * @param $xmlData
347
+ * @throws Exception
348
+ * @internal param $object
349
+ * @return null
350
+ */
351
+ public function errorResponse($xmlResponseObject,$xmlData)
352
+ {
353
+ $xmlObject = simplexml_load_string($xmlData);
354
+ $queue = Mage::getModel('synnex/queue')->loadByField('order_id', $this->order->getId());
355
+ if($queue->getId() === null){
356
+ $queue = Mage::getModel('synnex/queue');
357
+ $queue->setOrderId($this->order->getId());
358
+ $queue->setPoId($xmlObject->OrderRequest->PONumber);
359
+ $queue->setOrderXml(trim($xmlData));
360
+ $queue->setRetry(0);
361
+ $queue->setPrevError($xmlResponseObject->errorMessage . ' : ' . $xmlResponseObject->errorDetail);
362
+ } else {
363
+ $retry = $queue->getRetry();
364
+ if($retry >= 5){
365
+ $queue->delete();
366
+ }
367
+ $retry++;
368
+ $queue->setRetry($retry);
369
+ }
370
+
371
+ if(in_array($xmlResponseObject->errorMessage, $this->knownErrors)){
372
+ $queue->save();
373
+ } else {
374
+ $shipment->addComment($xmlResponseObject->errorDetail);
375
+ $shipment->setShipmentStatus(Osf_Synnex_Model_Source::SHIPMENT_STATUS_ONHOLD);
376
+ try{
377
+ $shipment->save();
378
+ } catch (Exception $e){
379
+ Mage::log('Synnex: Response Error: unknown error order on hold', null, $this->logFile);
380
+ }
381
+ }
382
+
383
+ return;
384
+ }
385
+
386
+ /**
387
+ * The retry the orders gave known errors
388
+ */
389
+ public function retry()
390
+ {
391
+ $jobs = Mage::getModel('synnex/queue')->getCollection();
392
+ foreach ($jobs as $job) {
393
+ Mage::log('Synnex: Retry: ' . $job->getOrderId(), null, $this->logFile);
394
+ $order = Mage::getModel('sales/order')->load($job->getOrderId());
395
+ $xmlResponse = Mage::helper('synnex/connect')->sendXMLRequest($job->getOrderXml());
396
+ $response = Mage::getModel('synnex/order')->processOrderResponse($xmlResponse, $order, $job->getOrderXml());
397
+ }
398
+
399
+ return;
400
+ }
401
+
402
+ }
403
+
404
+ /* Filename: Order.php */
405
+ /* Location: app/code/local/Osf/Synnex/Model/Order.php */
app/code/community/Osf/Synnex/Model/Queue.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Queue Model
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Model_Queue extends Mage_Core_Model_Abstract
18
+ {
19
+ protected function _construct()
20
+ {
21
+ $this->_init('synnex/queue');
22
+ }
23
+
24
+ /**
25
+ * Add conditions to the collection to load by field and value
26
+ * @param string
27
+ * @param string
28
+ * @return object
29
+ */
30
+ public function loadByField($field, $fieldValue)
31
+ {
32
+ $collection = $this->getCollection()
33
+ ->addFieldToFilter($field, $fieldValue);
34
+ return $collection->getFirstItem();
35
+ }
36
+ }
37
+
38
+ /* Filename: Queue.php */
39
+ /* Location: app/code/local/Osf/Synnex/Model/Queue.php */
app/code/community/Osf/Synnex/Model/Resource/Queue.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Queue Model
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Model_Resource_Queue extends Mage_Core_Model_Resource_Db_Abstract
18
+ {
19
+ protected function _construct()
20
+ {
21
+ $this->_init('synnex/queue', 'queue_id');
22
+ }
23
+ }
24
+
25
+ /* Filename: Queue.php */
26
+ /* Location: app/code/local/Osf/Synnex/Model/Queue.php */
app/code/community/Osf/Synnex/Model/Resource/Queue/Collection.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Queue Model
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Model_Resource_Queue_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
18
+ {
19
+ protected function _construct()
20
+ {
21
+ $this->_init('synnex/queue');
22
+ }
23
+ }
24
+
25
+ /* Filename: Collection.php */
26
+ /* Location: app/code/local/Osf/Synnex/Model/Queue/Collection.php */
app/code/community/Osf/Synnex/Model/Shipping/Notice.php ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Shipping Notice Model
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Model_Shipping_Notice extends Mage_Core_Model_Abstract
18
+ {
19
+ protected $logFile = 'synnex.log';
20
+
21
+ /**
22
+ * Gets the shipping files from Synnex FTP
23
+ *
24
+ */
25
+ public function processNotices()
26
+ {
27
+ // get the ship notices from the var/synnex/shipping folder
28
+ $shippingNotices = Mage::helper('synnex/connect')->getShippingFiles();
29
+ if($shippingNotices === false){
30
+ return $this;
31
+ }
32
+ foreach ($shippingNotices as $notice) {
33
+ // Process each ship notice
34
+ $noticeConfirm = $this->processNotice($notice);
35
+
36
+ // Delete the ship notice file
37
+ if($noticeConfirm === true){
38
+ unlink($notice);
39
+ }
40
+ }
41
+
42
+ return $this;
43
+ }
44
+
45
+ /**
46
+ * Processes the shipping notice from synnex
47
+ *
48
+ * @param string $notice
49
+ * @return bool
50
+ */
51
+ public function processNotice($notice)
52
+ {
53
+ Mage::log('Synnex: Ship Notice: The notice ' . $notice, null, $this->logFile);
54
+ if(!file_exists($notice)){
55
+ Mage::log('Synnex: Ship Notice Error: ship notice file not found!', null, $this->logFile);
56
+ return false;
57
+ }
58
+
59
+ $xmlObj = simplexml_load_file($notice);
60
+ $error = false;
61
+ $shipNotice = $xmlObj->ShipNotice3D;
62
+ // get shipment and check if it exists
63
+ $shipment = Mage::getModel('sales/order_shipment')->loadByIncrementId($shipNotice->PONumber);
64
+ if(is_null($shipment->getId())){
65
+ Mage::log('Synnex: Ship Notice does not exits: ' . (string)$shipNotice->PONumber , null, $this->logFile);
66
+ return false;
67
+ }
68
+
69
+ // check if the order was processed before
70
+ if($shipment->getShipmentStatus() == Osf_Synnex_Model_Source::SHIPMENT_STATUS_SHIPPED){
71
+ return false;
72
+ }
73
+
74
+ // get order and check if order exists
75
+ $order = $shipment->getOrder();
76
+ if(is_null($order->getId())){
77
+ Mage::log('Synnex: Processing ship notice: Order does not exist', null, $this->logFile);
78
+ return false;
79
+ }
80
+
81
+ // create the invoice
82
+ $invoice = $this->createInvoice($shipNotice, $order);
83
+ if($invoice === false){
84
+ $error = true;
85
+ }
86
+
87
+ // add tracking number
88
+ $trackNo = Mage::getModel('sales/order_shipment_track')
89
+ ->setNumber($shipNotice->Package->TrackNumber)
90
+ ->setCarrierCode($shipNotice->ShipCode)
91
+ ->setTitle($shipNotice->ShipDescription);
92
+ $shipment->addTrack($trackNo);
93
+
94
+ // set the shipment status to shipped
95
+ $shipment->setShipmentStatus(Osf_Synnex_Model_Source::SHIPMENT_STATUS_SHIPPED);
96
+ $shipment->addComment('The purchase order was shipped');
97
+ // send shipment email
98
+ $shipment->sendEmail(true, '');
99
+ $shipment->setEmailSent(true);
100
+
101
+ // Save the shipment
102
+ try {
103
+ $shipment->save();
104
+ } catch(Exception $e) {
105
+ Mage::log('Synnex: Ship Notice Error:'. $e->getMessage(), null, $this->logFile);
106
+ $error = true;
107
+ }
108
+
109
+ // check to see if there were any errors and send email
110
+ if($error === true){
111
+ // send error email
112
+ $this->sendErrorEmail($shipNotice->InvoiceNumber);
113
+ Mage::log('Synnex: Ship Notice Error: There is an error that holds the finishing of the ship notice processing', null, $this->logFile);
114
+ return false;
115
+ }
116
+
117
+ return true;
118
+ }
119
+
120
+ /**
121
+ * Create and invoice based on the ship notice
122
+ *
123
+ * @param $shipNotice
124
+ * @param $order
125
+ * @return bool
126
+ * @internal param $object
127
+ */
128
+ public function createInvoice($shipNotice, $order)
129
+ {
130
+ // init and build an array with the received sku from the ship notices items
131
+ $shippedSku = array();
132
+ foreach ($shipNotice->Package->Item as $item) {
133
+ $shippedSku[(string)$item->SKU] = (int)$item->ShipQuantity;
134
+ }
135
+
136
+ // get the order items
137
+ $items = $order->getItemsCollection();
138
+ $countOrderItems = count($items);
139
+ $countShipNoticeItems = count($shippedSku);
140
+ // init and build an array for the invoice with the qty and ids
141
+ $qtys = array();
142
+ foreach($items as $orderItem){
143
+ if(in_array($orderItem->getSku(), array_keys($shippedSku))){
144
+ $qtys[$orderItem->getId()] = $shippedSku[$orderItem->getSku()];
145
+ } else {
146
+ $qtys[$orderItem->getId()] = 0;
147
+ }
148
+ }
149
+
150
+ // add the invoiced qtys
151
+ $invoice = Mage::getModel('sales/service_order', $order)->prepareInvoice($qtys);
152
+ $amount = $invoice->getGrandTotal();
153
+ $invoice->register()->pay();
154
+ $invoice->getOrder()->setIsInProcess(true);
155
+
156
+ // adding a comment to the order
157
+ $history = $invoice->getOrder()->addStatusHistoryComment(
158
+ 'Amount of $' . $amount . ' captured automatically.', false
159
+ );
160
+ $history->setIsCustomerNotified(true);
161
+ // save order
162
+ $order->save();
163
+
164
+ // make the invoice transaction
165
+ Mage::getModel('core/resource_transaction')
166
+ ->addObject($invoice)
167
+ ->addObject($invoice->getOrder())
168
+ ->save();
169
+
170
+ // save invoice and send the email to the costumer
171
+ try {
172
+ $invoice->save();
173
+ $invoice->sendEmail(true, '');
174
+ if($countOrderItems == $countShipNoticeItems){
175
+ $order->setStatus(Mage_Sales_Model_Order::STATE_COMPLETE);
176
+ $order->save();
177
+ }
178
+ } catch (Exception $e){
179
+ Mage::log('Synnex: Invoice could not be created:'. $e->getMessage(), null, $this->logFile);
180
+ return false;
181
+ }
182
+
183
+ return true;
184
+ }
185
+
186
+ /**
187
+ * Sending the import error email
188
+ * @param $shipNoticeId
189
+ * @return bool
190
+ */
191
+ public function sendErrorEmail($shipNoticeId)
192
+ {
193
+ $toEmail = Mage::getStoreConfig('trans_email/ident_support/email');
194
+ $toName = Mage::getStoreConfig('trans_email/ident_support/name');
195
+
196
+ // load the template
197
+ $emailTemplate = Mage::getModel('core/email_template')->loadDefault('synnex_import_error');
198
+ $emailTemplate->setSenderName(Mage::getStoreConfig('general/store_information/name'));
199
+ $emailTemplate->setSenderEmail(Mage::getStoreConfig('trans_email/ident_sales/email'));
200
+ $emailTemplate->setTemplateSubject(Mage::helper('core')->__('Canex: Synnex Ship Notice Import Error'));
201
+
202
+ try {
203
+ $emailTemplate->send($toEmail, $toName, array('shipNoticeId'=>$shipNoticeId));
204
+ } catch(Exception $e){
205
+ Mage::log("Synnex: Error send error email: ". $e->getMessage(), null, $this->logFile);
206
+ return false;
207
+ }
208
+
209
+ return true;
210
+ }
211
+
212
+ }
213
+
214
+ /* Filename: Notice.php */
215
+ /* Location: app/code/local/Osf/Synnex/Model/Shipping/Notice.php */
app/code/community/Osf/Synnex/Model/Source.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Source Model
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_Model_Source extends Mage_Core_Model_Abstract
18
+ {
19
+
20
+ const SHIPMENT_STATUS_PENDING = 0;
21
+ const SHIPMENT_STATUS_READY = 1;
22
+ const SHIPMENT_STATUS_SHIPPED = 2;
23
+ const SHIPMENT_STATUS_CANCELED = 3;
24
+ const SHIPMENT_STATUS_ONHOLD = 4;
25
+
26
+ public $statuses;
27
+
28
+ public function _construct()
29
+ {
30
+ $this->statuses = array(
31
+ "Pending",
32
+ "Ready",
33
+ "Shipped",
34
+ "Canceled",
35
+ "Onhold"
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Get the status text based on the status value
41
+ * @param string
42
+ * @return string
43
+ */
44
+ public function getStatusText($status)
45
+ {
46
+ return $this->statuses[$status];
47
+ }
48
+
49
+ }
50
+
51
+ /* Filename: Source.php */
52
+ /* Location: app/code/local/Osf/Synnex/Model/Source.php */
app/code/community/Osf/Synnex/Sql/synnex_setup/install-0.1.0.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+ $table = $installer->getConnection()
7
+ ->newTable($installer->getTable('synnex_queue'))
8
+ ->addColumn('queue_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11, array(
9
+ 'identity' => true,
10
+ 'unsigned' => true,
11
+ 'nullable' => false,
12
+ 'primary' => true,
13
+ )
14
+ )
15
+ ->addColumn('order_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11,array(
16
+ 'nullable' => false
17
+ )
18
+ )
19
+ ->addColumn('po_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11,array(
20
+ 'nullable' => false
21
+ )
22
+ )
23
+ ->addColumn('order_xml', Varien_Db_Ddl_Table::TYPE_TEXT, null,array(
24
+ 'nullable' => false
25
+ )
26
+ )
27
+ ->addColumn('retry', Varien_Db_Ddl_Table::TYPE_INTEGER, 2, array())
28
+ ->addColumn('prev_error', Varien_Db_Ddl_Table::TYPE_TEXT);
29
+
30
+ $installer->getConnection()->createTable($table);
31
+
32
+ //Add new product attributes
33
+ $attr_installer = Mage::getResourceModel('catalog/setup', 'catalog_setup');
34
+
35
+ //Create new Vendor attribute
36
+ $attr_installer->addAttribute('catalog_product', 'osf_product_vendor', array(
37
+ 'type' => 'varchar',
38
+ 'backend' => '',
39
+ 'frontend' => '',
40
+ 'label' => 'Vendor',
41
+ 'input' => 'text',
42
+ 'class' => '',
43
+ 'source' => 'catalog/product_attribute_source_layout',
44
+ 'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
45
+ 'visible' => true,
46
+ 'required' => false,
47
+ 'user_defined' => false,
48
+ 'default' => '',
49
+ 'searchable' => false,
50
+ 'filterable' => false,
51
+ 'comparable' => false,
52
+ 'visible_on_front' => false,
53
+ 'unique' => false,
54
+ 'group' => 'General'
55
+ ));
56
+
57
+ //Create new Cost attribute
58
+ $attr_installer->addAttribute('catalog_product', 'osf_product_cost', array(
59
+ 'type' => 'varchar',
60
+ 'backend' => '',
61
+ 'frontend' => '',
62
+ 'label' => 'Cost',
63
+ 'input' => 'text',
64
+ 'class' => '',
65
+ 'source' => 'catalog/product_attribute_source_layout',
66
+ 'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
67
+ 'visible' => true,
68
+ 'required' => false,
69
+ 'user_defined' => false,
70
+ 'default' => '',
71
+ 'searchable' => false,
72
+ 'filterable' => false,
73
+ 'comparable' => false,
74
+ 'visible_on_front' => false,
75
+ 'unique' => false,
76
+ 'group' => 'General'
77
+ ));
78
+
79
+ $installer->endSetup();
app/code/community/Osf/Synnex/controllers/IndexController.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ * Index controller
11
+ *
12
+ * @category Osf Synnex
13
+ * @package Osf_Synnex
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Synnex_IndexController extends Mage_Core_Controller_Front_Action
18
+ {
19
+ /**
20
+ * The default index action
21
+ *
22
+ * @return string
23
+ *
24
+ */
25
+ // public function indexAction()
26
+ // {
27
+ // return;
28
+ // }
29
+
30
+ /**
31
+ * The Action that starts the import of products from Synnex
32
+ *
33
+ * @return null
34
+ *
35
+ */
36
+ public function startImportAction()
37
+ {
38
+ return Mage::getModel('synnex/import')->processData();
39
+ }
40
+
41
+ /**
42
+ * Retry to send purchase ordersCreate Shipment
43
+ *
44
+ * @return null
45
+ *
46
+ */
47
+ public function retryCronAction()
48
+ {
49
+ return Mage::getModel('synnex/order')->retry();
50
+ }
51
+
52
+ /**
53
+ * Check for ship notices to finalize the purchase order
54
+ *
55
+ * @return null
56
+ *
57
+ */
58
+ public function shipNoticeCronAction()
59
+ {
60
+ return Mage::getModel('synnex/shipping_notice')->processNotices();
61
+ }
62
+ }
63
+
64
+ /* Filename: IndexController.php */
65
+ /* Location: ../app/code/local/Osf/Synnex/controllers/IndexController.php */
app/code/community/Osf/Synnex/etc/config.xml ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ -->
11
+ <config>
12
+ <modules>
13
+ <Osf_Synnex>
14
+ <version>1.0.0</version>
15
+ </Osf_Synnex>
16
+ </modules>
17
+ <global>
18
+ <blocks>
19
+ <synnex>
20
+ <class>Osf_Synnex_Block</class>
21
+ </synnex>
22
+ <adminhtml>
23
+ <rewrite>
24
+ <sales_order_view_tab_shipments>Osf_Synnex_Block_Adminhtml_Shipments</sales_order_view_tab_shipments>
25
+ <sales_shipment_grid>Osf_Synnex_Block_Adminhtml_Shipments_Grid</sales_shipment_grid>
26
+ </rewrite>
27
+ </adminhtml>
28
+ </blocks>
29
+ <models>
30
+ <synnex>
31
+ <class>Osf_Synnex_Model</class>
32
+ <resourceModel>synnex_resource</resourceModel>
33
+ </synnex>
34
+ <synnex_resource>
35
+ <class>Osf_Synnex_Model_Resource</class>
36
+ <entities>
37
+ <queue>
38
+ <table>synnex_queue</table>
39
+ </queue>
40
+ </entities>
41
+ </synnex_resource>
42
+ </models>
43
+ <resources>
44
+ <synnex_setup>
45
+ <setup>
46
+ <module>Osf_Synnex</module>
47
+ </setup>
48
+ <connection>
49
+ <use>core_setup</use>
50
+ </connection>
51
+ </synnex_setup>
52
+ <synnex_read>
53
+ <connection>
54
+ <use>core_read</use>
55
+ </connection>
56
+ </synnex_read>
57
+ <synnex_write>
58
+ <connection>
59
+ <use>core_write</use>
60
+ </connection>
61
+ </synnex_write>
62
+ </resources>
63
+ <helpers>
64
+ <synnex>
65
+ <class>Osf_Synnex_Helper</class>
66
+ </synnex>
67
+ </helpers>
68
+ <blocks>
69
+ <synnex>
70
+ <class>Osf_Synnex_Block</class>
71
+ </synnex>
72
+ </blocks>
73
+ <events>
74
+ <sales_order_save_after>
75
+ <observers>
76
+ <Osf_Synnex_Model_Observer>
77
+ <class>Osf_Synnex_Model_Observer</class>
78
+ <method>processOrder</method>
79
+ </Osf_Synnex_Model_Observer>
80
+ </observers>
81
+ </sales_order_save_after>
82
+ </events>
83
+ <template>
84
+ <email>
85
+ <synnex_import_error module="synnex">
86
+ <label>Synnex Import Ship notice error</label>
87
+ <file>synnex/synnex_import_error.html</file>
88
+ <type>html</type>
89
+ </synnex_import_error>
90
+ </email>
91
+ </template>
92
+ </global>
93
+ <crontab>
94
+ <jobs>
95
+ <synnex_notice>
96
+ <schedule>
97
+ <cron_expr>0 */2 * * *</cron_expr>
98
+ </schedule>
99
+ <run>
100
+ <model>synnex/shipping_notice::processNotices</model>
101
+ </run>
102
+ </synnex_notice>
103
+ <synnex_process>
104
+ <schedule>
105
+ <cron_expr>0 2 * * *</cron_expr>
106
+ </schedule>
107
+ <run>
108
+ <model>synnex/import::processData</model>
109
+ </run>
110
+ </synnex_process>
111
+ <synnex_retry>
112
+ <schedule>
113
+ <cron_expr>0 */2 * * *</cron_expr>
114
+ </schedule>
115
+ <run>
116
+ <model>synnex/order::retry</model>
117
+ </run>
118
+ </synnex_retry>
119
+ </jobs>
120
+ </crontab>
121
+ <frontend>
122
+ <routers>
123
+ <synnex>
124
+ <use>standard</use>
125
+ <args>
126
+ <module>Osf_Synnex</module>
127
+ <frontName>synnex</frontName>
128
+ </args>
129
+ </synnex>
130
+ </routers>
131
+ </frontend>
132
+ <adminhtml>
133
+ <acl>
134
+ <resources>
135
+ <all>
136
+ <title>Allow Everything</title>
137
+ </all>
138
+ <admin>
139
+ <children>
140
+ <synnex module="synnex">
141
+ <title>Synnex Configuration</title>
142
+ <sort_order>11</sort_order>
143
+ </synnex>
144
+ <system>
145
+ <children>
146
+ <config>
147
+ <children>
148
+ <synnex translate="title">
149
+ <title>Synnex Configuration</title>
150
+ <sort_order>80</sort_order>
151
+ </synnex>
152
+ </children>
153
+ </config>
154
+ </children>
155
+ </system>
156
+ </children>
157
+ </admin>
158
+ </resources>
159
+ </acl>
160
+ </adminhtml>
161
+ </config>
app/code/community/Osf/Synnex/etc/system.xml ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ * Osf Global Services
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the EULA
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ *
10
+ -->
11
+ <config>
12
+ <tabs>
13
+ <osftab translate="label" module="synnex">
14
+ <label>Osf</label>
15
+ <sort_order>80</sort_order>
16
+ </osftab>
17
+ </tabs>
18
+ <sections>
19
+ <synnex module="synnex">
20
+ <label>Synnex Configuration</label>
21
+ <tab>osftab</tab>
22
+ <frontend_type>text</frontend_type>
23
+ <sort_order>350</sort_order>
24
+ <show_in_default>1</show_in_default>
25
+ <show_in_website>1</show_in_website>
26
+ <show_in_store>1</show_in_store>
27
+ <groups>
28
+ <synnex translate="label">
29
+ <label>Synnex Account Configuration</label>
30
+ <frontend_type>text</frontend_type>
31
+ <sort_order>11</sort_order>
32
+ <show_in_default>1</show_in_default>
33
+ <show_in_website>1</show_in_website>
34
+ <show_in_store>1</show_in_store>
35
+ <fields>
36
+ <synnex_account_number translate="label">
37
+ <label>Synnex Account Number</label>
38
+ <frontend_type>text</frontend_type>
39
+ <sort_order>11</sort_order>
40
+ <show_in_default>1</show_in_default>
41
+ <show_in_website>1</show_in_website>
42
+ <show_in_store>1</show_in_store>
43
+ <comment><![CDATA[ The account number received from Synnex ]]></comment>
44
+ </synnex_account_number>
45
+ </fields>
46
+ </synnex>
47
+ <ftplogin translate="label">
48
+ <label>Ftp Configuration</label>
49
+ <frontend_type>text</frontend_type>
50
+ <sort_order>11</sort_order>
51
+ <show_in_default>1</show_in_default>
52
+ <show_in_website>1</show_in_website>
53
+ <show_in_store>1</show_in_store>
54
+ <fields>
55
+ <ftp_host translate="label">
56
+ <label>Ftp Server</label>
57
+ <frontend_type>text</frontend_type>
58
+ <sort_order>11</sort_order>
59
+ <show_in_default>1</show_in_default>
60
+ <show_in_website>1</show_in_website>
61
+ <show_in_store>1</show_in_store>
62
+ <comment><![CDATA[<b>Ex: </b> example.com OR ftp.example.com ]]></comment>
63
+ </ftp_host>
64
+ <ftp_user translate="label">
65
+ <label>Ftp Username</label>
66
+ <frontend_type>text</frontend_type>
67
+ <sort_order>12</sort_order>
68
+ <show_in_default>1</show_in_default>
69
+ <show_in_website>1</show_in_website>
70
+ <show_in_store>1</show_in_store>
71
+ <comment><![CDATA[The FTP username used to connect to Synnex ftp]]></comment>
72
+ </ftp_user>
73
+ <ftp_password translate="label">
74
+ <label>Ftp Password</label>
75
+ <frontend_type>password</frontend_type>
76
+ <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
77
+ <sort_order>13</sort_order>
78
+ <show_in_default>1</show_in_default>
79
+ <show_in_website>1</show_in_website>
80
+ <show_in_store>1</show_in_store>
81
+ <comment><![CDATA[The FTP password used to connect to Synnex ftp]]></comment>
82
+ </ftp_password>
83
+ <ftp_prod_file translate="label">
84
+ <label>Ftp Products Filename</label>
85
+ <frontend_type>text</frontend_type>
86
+ <sort_order>13</sort_order>
87
+ <show_in_default>1</show_in_default>
88
+ <show_in_website>1</show_in_website>
89
+ <show_in_store>1</show_in_store>
90
+ <comment><![CDATA[The products file from the server]]></comment>
91
+ </ftp_prod_file>
92
+ </fields>
93
+ </ftplogin>
94
+ <xmllogin translate="label">
95
+ <label>XML Configuration</label>
96
+ <frontend_type>text</frontend_type>
97
+ <sort_order>12</sort_order>
98
+ <show_in_default>1</show_in_default>
99
+ <show_in_website>1</show_in_website>
100
+ <show_in_store>1</show_in_store>
101
+ <fields>
102
+ <xml_endpoint translate="label">
103
+ <label>XML Endpoint</label>
104
+ <frontend_type>text</frontend_type>
105
+ <sort_order>11</sort_order>
106
+ <show_in_default>1</show_in_default>
107
+ <show_in_website>1</show_in_website>
108
+ <show_in_store>1</show_in_store>
109
+ <comment><![CDATA[<b>Ex: </b> example.com ]]></comment>
110
+ </xml_endpoint>
111
+ <xml_username translate="label">
112
+ <label>Xml Username</label>
113
+ <frontend_type>text</frontend_type>
114
+ <sort_order>12</sort_order>
115
+ <show_in_default>1</show_in_default>
116
+ <show_in_website>1</show_in_website>
117
+ <show_in_store>1</show_in_store>
118
+ <comment><![CDATA[The username used to connect to Synnex endpoint]]></comment>
119
+ </xml_username>
120
+ <xml_password translate="label">
121
+ <label>Xml Password</label>
122
+ <frontend_type>password</frontend_type>
123
+ <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
124
+ <sort_order>13</sort_order>
125
+ <show_in_default>1</show_in_default>
126
+ <show_in_website>1</show_in_website>
127
+ <show_in_store>1</show_in_store>
128
+ <comment><![CDATA[The password used to connect to Synnex endpoint]]></comment>
129
+ </xml_password>
130
+ </fields>
131
+ </xmllogin>
132
+ <synnex_import translate="label">
133
+ <label>Synnex Import Configuration</label>
134
+ <frontend_type>text</frontend_type>
135
+ <sort_order>50</sort_order>
136
+ <show_in_default>1</show_in_default>
137
+ <show_in_website>1</show_in_website>
138
+ <show_in_store>1</show_in_store>
139
+ <fields>
140
+ <upload_file translate="label tooltip comment">
141
+ <label>Upload Category Map File</label>
142
+ <comment>Upload the file with the category map. For more infomation about this configuration go to user guide section "How does category map work?"</comment>
143
+ <frontend_type>file</frontend_type>
144
+ <backend_model>adminhtml/system_config_backend_file</backend_model>
145
+ <upload_dir config="system/filesystem/media" scope_info="1">admin-config-uploads</upload_dir>
146
+ <base_url type="media" scope_info="1">admin-config-uploads</base_url>
147
+ <sort_order>1</sort_order>
148
+ <show_in_default>1</show_in_default>
149
+ <show_in_website>1</show_in_website>
150
+ <show_in_store>1</show_in_store>
151
+ </upload_file>
152
+ <import_conditions translate="label">
153
+ <label>Import Conditions</label>
154
+ <frontend_type>textarea</frontend_type>
155
+ <sort_order>20</sort_order>
156
+ <show_in_default>1</show_in_default>
157
+ <show_in_website>1</show_in_website>
158
+ <show_in_store>1</show_in_store>
159
+ <comment><![CDATA[The conditions for import, delimeted by ";" and space between the elements of the condition Available operands "=","!=",">","<". Ex: "column number" = "value". For more information about this configuration go to user guide section 3.1 xml table ]]></comment>
160
+ </import_conditions>
161
+ </fields>
162
+ </synnex_import>
163
+ <synnex_manual translate="label">
164
+ <label>Operations Manual Start</label>
165
+ <frontend_type>text</frontend_type>
166
+ <sort_order>60</sort_order>
167
+ <show_in_default>1</show_in_default>
168
+ <show_in_website>1</show_in_website>
169
+ <show_in_store>1</show_in_store>
170
+ <fields>
171
+ <import_start translate="label comment">
172
+ <label>Import Products</label>
173
+ <comment>You can manualy start the import, the import is automaticaly started at 2am.</comment>
174
+ <frontend_type>button</frontend_type>
175
+ <frontend_model>synnex/adminhtml_system_config_button_import</frontend_model>
176
+ <sort_order>10</sort_order>
177
+ <show_in_default>1</show_in_default>
178
+ <show_in_website>1</show_in_website>
179
+ <show_in_store>1</show_in_store>
180
+ </import_start>
181
+ <ship_notice_start translate="label comment">
182
+ <label>Import Ship Notices</label>
183
+ <comment>You can manualy start the retrival of the shipnotices, the retrival is automaticaly started every 2 hours.</comment>
184
+ <frontend_type>button</frontend_type>
185
+ <frontend_model>synnex/adminhtml_system_config_button_notice</frontend_model>
186
+ <sort_order>20</sort_order>
187
+ <show_in_default>1</show_in_default>
188
+ <show_in_website>1</show_in_website>
189
+ <show_in_store>1</show_in_store>
190
+ </ship_notice_start>
191
+ </fields>
192
+ </synnex_manual>
193
+ </groups>
194
+ </synnex>
195
+ </sections>
196
+ </config>
app/code/community/Osf/Synnex/lib/magmi/conf/ColumnMappingItemProcessor.conf ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ [ColumnMappingItemProcessor]
2
+ CMAP:columnlist = "Attribute Set,UPC,Brand,Name,Descriptor,Size,Vendor Code,Price,Weight"
3
+ CMAP:Attribute%20Set = "attribute_set"
4
+ CMAP:UPC = "upc"
5
+ CMAP:Brand = "manufacturer"
6
+ CMAP:Name = "name"
7
+ CMAP:Descriptor = "description"
8
+ CMAP:Size = "size"
9
+ CMAP:Vendor%20Code = "sku"
10
+ CMAP:Price = "price"
11
+ CMAP:Weight = "weight"
app/code/community/Osf/Synnex/lib/magmi/conf/DefaultValuesItemProcessor.conf ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ [DefaultValuesItemProcessor]
2
+ DEFAULT:columnlist = "type"
3
+ DEFAULT:type = "simple"
app/code/community/Osf/Synnex/lib/magmi/conf/ItemIndexer.conf ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ [ItemIndexer]
2
+ OTFI:urlending = ".html"
3
+ OTFI:usecatinurl = "1"
app/code/community/Osf/Synnex/lib/magmi/conf/Magmi_ReindexingPlugin.conf ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ [Magmi_ReindexingPlugin]
2
+ REINDEX:indexes = "catalog_product_attribute,catalog_product_price,catalog_product_flat,catalog_category_flat,cataloginventory_stock,catalogsearch_fulltext,tag_summary"
3
+ REINDEX:phpcli = "php"
app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/CategoryImporter.conf ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ [CategoryImporter]
2
+ CAT:baseroot =
3
+ CAT:lastonly = "0"
4
+ CAT:urlending = ".html"
5
+ CAT:treesep = "/"
app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/ColumnMappingItemProcessor.conf ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ [ColumnMappingItemProcessor]
2
+ CMAP:columnlist = "Attribute Set,UPC,Brand,Name,Descriptor,Size,Vendor Code,Price,Weight"
3
+ CMAP:Attribute%20Set = "attribute_set"
4
+ CMAP:UPC = "upc"
5
+ CMAP:Brand = "manufacturer"
6
+ CMAP:Name = "name"
7
+ CMAP:Descriptor = "description"
8
+ CMAP:Size = "size"
9
+ CMAP:Vendor%20Code = "sku"
10
+ CMAP:Price = "price"
11
+ CMAP:Weight = "weight"
app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/DefaultValuesItemProcessor.conf ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ [DefaultValuesItemProcessor]
2
+ DEFAULT:columnlist = "type"
3
+ DEFAULT:type = "simple"
app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/ItemIndexer.conf ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ [ItemIndexer]
2
+ OTFI:urlending = ".html"
3
+ OTFI:usecatinurl = "1"
app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/Magmi_CSVDataSource.conf ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ [Magmi_CSVDataSource]
2
+ CSV:importmode = "local"
3
+ CSV:basedir = "var/import"
4
+ CSV:filename = "C:\xampp\htdocs\exten\var\import/test.csv"
5
+ CSV:remoteurl =
6
+ CSV:remotecookie =
7
+ CSV:remoteuser =
8
+ CSV:remotepass =
9
+ CSV:separator =
10
+ CSV:enclosure =
11
+ CSV:headerline =
app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/Magmi_ReindexingPlugin.conf ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ [Magmi_ReindexingPlugin]
2
+ REINDEX:indexes = "catalog_product_attribute,catalog_product_price,catalog_product_flat,catalog_category_flat,cataloginventory_stock,catalogsearch_fulltext,tag_summary"
3
+ REINDEX:phpcli = "php"
app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/ValueReplacerItemProcessor.conf ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ [ValueReplacerItemProcessor]
2
+ VREP:columnlist = "manufacturer,name,description,size,sku,attribute_set,mpn"
3
+ VREP:manufacturer = "{{ trim({item.manufacturer}) }}"
4
+ VREP:name = "{{ trim({item.name}) }}"
5
+ VREP:description = "{{ trim({item.description}) }}"
6
+ VREP:size = "{{ trim({item.size}) }}"
7
+ VREP:sku = "{{ trim({item.sku}) }}"
8
+ VREP:attribute_set = "{{ trim({item.attribute_set}) }}"
9
+ VREP:mpn = "{item.sku}"
app/code/community/Osf/Synnex/lib/magmi/conf/Synnex/plugins.conf ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ [PLUGINS_DATASOURCES]
2
+ class = "Magmi_CSVDataSource"
3
+ [PLUGINS_GENERAL]
4
+ classes = "Magmi_OptimizerPlugin"
5
+ [PLUGINS_ITEMPROCESSORS]
6
+ classes = "CategoryImporter,ItemIndexer"
app/code/community/Osf/Synnex/lib/magmi/conf/ValueReplacerItemProcessor.conf ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ [ValueReplacerItemProcessor]
2
+ VREP:columnlist = "manufacturer,name,description,size,sku,attribute_set,mpn"
3
+ VREP:manufacturer = "{{ trim({item.manufacturer}) }}"
4
+ VREP:name = "{{ trim({item.name}) }}"
5
+ VREP:description = "{{ trim({item.description}) }}"
6
+ VREP:size = "{{ trim({item.size}) }}"
7
+ VREP:sku = "{{ trim({item.sku}) }}"
8
+ VREP:attribute_set = "{{ trim({item.attribute_set}) }}"
9
+ VREP:mpn = "{item.sku}"
app/code/community/Osf/Synnex/lib/magmi/conf/magmi.ini ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [DATABASE]
2
+ connectivity = "net"
3
+ host = "localhost"
4
+ port = "3306"
5
+ unix_socket =
6
+ dbname = "exten"
7
+ user = "root"
8
+ password =
9
+ table_prefix =
10
+ [MAGENTO]
11
+ version = "1.7.x"
12
+ basedir = "../../"
13
+ [GLOBAL]
14
+ step = "0.5"
15
+ multiselect_sep = ","
16
+ dirmask = "755"
17
+ filemask = "644"
app/code/community/Osf/Synnex/lib/magmi/conf/magmi.ini.default ADDED
File without changes
app/code/community/Osf/Synnex/lib/magmi/conf/plugins.conf ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ [PLUGINS_DATASOURCES]
2
+ class = "Magmi_CSVDataSource"
3
+ [PLUGINS_GENERAL]
4
+ classes = "Magmi_ImportUrlPlugin,Magmi_OptimizerPlugin,Magmi_ReindexingPlugin"
5
+ [PLUGINS_ITEMPROCESSORS]
6
+ classes = "ItemIndexer,ColumnMappingItemProcessor,DefaultValuesItemProcessor,ValueReplacerItemProcessor"
app/code/community/Osf/Synnex/lib/magmi/engines/magmi_productimportengine.php ADDED
@@ -0,0 +1,1924 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * MAGENTO MASS IMPORTER CLASS
5
+ *
6
+ * version : 1.7.8
7
+ * author : S.BRACQUEMONT aka dweeves
8
+ * updated : 2010-10-09
9
+ *
10
+ */
11
+
12
+ /* use external file for db helper */
13
+ require_once ("magmi_engine.php");
14
+ require_once ("magmi_valueparser.php");
15
+
16
+ /**
17
+ *
18
+ *
19
+ * Magmi Product Import engine class
20
+ * This class handle product import
21
+ *
22
+ * @author dweeves
23
+ *
24
+ */
25
+ class Magmi_ProductImportEngine extends Magmi_Engine
26
+ {
27
+ //attribute info cache
28
+ public $attrinfo = array();
29
+ //attribute info by type
30
+ public $attrbytype = array();
31
+ //attribute set cache
32
+ public $attribute_sets = array();
33
+ //product entity type
34
+ public $prod_etype;
35
+ //default attribute set id
36
+ public $default_asid;
37
+ //store id cache
38
+ public $sidcache = array();
39
+ //default mode
40
+ public $mode = "update";
41
+ //cache for column names that are not attributes
42
+ private $_notattribs = array();
43
+ //list of attribute handlers
44
+ private $_attributehandlers;
45
+ //current import row
46
+ private $_current_row;
47
+ //option id cache for select/multiselect
48
+ private $_optidcache = null;
49
+ //current item ids
50
+ private $_curitemids = array("sku"=>null);
51
+ //default store list to impact
52
+ private $_dstore = array();
53
+ //same flag if current import line is referencing same item than the previous one
54
+ private $_same;
55
+ //current product id
56
+ private $_currentpid;
57
+ //extra attributes to create
58
+ private $_extra_attrs;
59
+ //current import profile
60
+ private $_profile;
61
+ //Store ids cache for website scoped attributes
62
+ private $_sid_wsscope = array();
63
+ //Store ids cache for store scope attributes
64
+ private $_sid_sscope = array();
65
+ //magento product table columns list
66
+ private $_prodcols = array();
67
+ //magento stock related table columns list
68
+ private $_stockcols = array();
69
+ //stats
70
+ private $_skustats = array();
71
+
72
+
73
+ /**
74
+ * constructor
75
+ *
76
+ * @param string $conffile
77
+ * : configuration .ini filename
78
+ */
79
+ public function __construct()
80
+ {
81
+ parent::__construct();
82
+ $this->setBuiltinPluginClasses("itemprocessors",
83
+ dirname(dirname(__FILE__)) .
84
+ "/plugins/inc/magmi_defaultattributehandler.php::Magmi_DefaultAttributeItemProcessor");
85
+ }
86
+
87
+ public function getSkuStats()
88
+ {
89
+ return $this->_skustats;
90
+ }
91
+
92
+ /**
93
+ * (non-PHPdoc)
94
+ *
95
+ * @see Magmi_Engine::getEngineInfo()
96
+ */
97
+ public function getEngineInfo()
98
+ {
99
+ return array("name"=>"Magmi Product Import Engine","version"=>"1.8.2","author"=>"dweeves");
100
+ }
101
+
102
+ /**
103
+ * load properties
104
+ *
105
+ * @param string $conf
106
+ * : configuration .ini filename
107
+ */
108
+ public function initProdType()
109
+ {
110
+ $tname = $this->tablename("eav_entity_type");
111
+ $this->prod_etype = $this->selectone("SELECT entity_type_id FROM $tname WHERE entity_type_code=?",
112
+ "catalog_product", "entity_type_id");
113
+ $this->default_asid = $this->getAttributeSetId('Default');
114
+ }
115
+
116
+ public function getPluginFamilies()
117
+ {
118
+ return array("datasources","general","itemprocessors");
119
+ }
120
+
121
+ public function registerAttributeHandler($ahinst, $attdeflist)
122
+ {
123
+ foreach ($attdeflist as $attdef)
124
+ {
125
+ $ad = explode(":", $attdef);
126
+ if (count($ad) != 2)
127
+ {
128
+ $this->log("Invalid registration string ($attdef) :" . get_class($ahinst), "warning");
129
+ }
130
+ else
131
+ {
132
+ $this->_attributehandlers[$attdef] = $ahinst;
133
+ }
134
+ }
135
+ }
136
+
137
+ /**
138
+ *
139
+ * Return list of store codes that share the same website than the stores passed as parameter
140
+ *
141
+ * @param string $scodes
142
+ * comma separated list of store view codes
143
+ */
144
+ public function getStoreIdsForWebsiteScope($scodes)
145
+ {
146
+ if (!isset($this->_sid_wsscope[$scodes]))
147
+ {
148
+ $this->_sid_wsscope[$scodes] = array();
149
+ $wscarr = csl2arr($scodes);
150
+ $qcolstr = $this->arr2values($wscarr);
151
+ $cs = $this->tablename("core_store");
152
+ $sql = "SELECT csdep.store_id FROM $cs as csmain
153
+ JOIN $cs as csdep ON csdep.website_id=csmain.website_id
154
+ WHERE csmain.code IN ($qcolstr) ";
155
+ $sidrows = $this->selectAll($sql, $wscarr);
156
+ foreach ($sidrows as $sidrow)
157
+ {
158
+ $this->_sid_wsscope[$scodes][] = $sidrow["store_id"];
159
+ }
160
+ }
161
+ return $this->_sid_wsscope[$scodes];
162
+ }
163
+
164
+ /**
165
+ * Returns the list of store ids corresponding to the store view codes
166
+ * @param unknown $scodes
167
+ * @return multitype:
168
+ */
169
+ public function getStoreIdsForStoreScope($scodes)
170
+ {
171
+ if (!isset($this->_sid_sscope[$scodes]))
172
+ {
173
+ $this->_sid_sscope[$scodes] = array();
174
+ $scarr = csl2arr($scodes);
175
+ $qcolstr = $this->arr2values($scarr);
176
+ $cs = $this->tablename("core_store");
177
+ $sql = "SELECT csmain.store_id from $cs as csmain WHERE csmain.code IN ($qcolstr)";
178
+ $sidrows = $this->selectAll($sql, $scarr);
179
+ foreach ($sidrows as $sidrow)
180
+ {
181
+ $this->_sid_sscope[$scodes][] = $sidrow["store_id"];
182
+ }
183
+ }
184
+ return $this->_sid_sscope[$scodes];
185
+ }
186
+
187
+ /**
188
+ * Returns Magento current data for given item
189
+ * @param unknown $item : item to get magento data from
190
+ * @param unknown $params : item metadata
191
+ * @param string $cols : columns list to return (if not set, all items column list)
192
+ * @return Ambigous <multitype:, multitype:unknown >
193
+ */
194
+ public function getMagentoData($item, $params, $cols = null)
195
+ {
196
+ // out data
197
+ $out = array();
198
+ // if no specific columns set, use all item keys as base
199
+ if ($cols == null)
200
+ {
201
+ $cols = array_keys($item);
202
+ }
203
+ $this->initAttrInfos($cols);
204
+ // cross with defined attributes
205
+ $attrkeys = array_intersect($cols, array_keys($this->attrinfo));
206
+ // Create several maps:
207
+ // 1 per backend type => 1 request per backend type
208
+ // 1 to retrieve attribute code from attribute id (avoid a join since we already have the map)
209
+ $bta = array();
210
+ $idcodemap = array();
211
+
212
+ // Handle atribute retrieval
213
+
214
+ foreach ($attrkeys as $k)
215
+ {
216
+ $attrdata = $this->attrinfo[$k];
217
+ $bt = "" . $attrdata["backend_type"];
218
+ if ($bt != "static")
219
+ {
220
+ $attid = $attrdata["attribute_id"];
221
+ if (!isset($bta[$bt]))
222
+ {
223
+ $bta[$bt] = array();
224
+ }
225
+ $bta[$bt][] = $attid;
226
+ $idcodemap[$attid] = $k;
227
+ }
228
+ }
229
+
230
+ // Peform SQL "by type"
231
+ foreach (array_keys($bta) as $bt)
232
+ {
233
+ $cpet = $this->tablename("catalog_product_entity_$bt");
234
+ $storeids = $this->getItemStoreIds($item);
235
+ $sid = $storeids[0];
236
+ $sql = "SELECT attribute_id,value FROM $cpet WHERE entity_id=? AND store_id=? AND attribute_id IN (" .
237
+ $this->arr2values($bta[$bt]) . ")";
238
+ $tdata = $this->selectAll($sql, array_merge(array($params["product_id"],$sid), $bta[$bt]));
239
+ foreach ($tdata as $row)
240
+ {
241
+ $out[$idcodemap[$row["attribute_id"]]] = $row["value"];
242
+ }
243
+ unset($tdata);
244
+ }
245
+
246
+ // Check for qty attributes
247
+ $scols = array_intersect($cols, $this->getStockCols());
248
+ $sql = "SELECT " . implode(",", $scols) . " FROM " . $this->tablename("cataloginventory_stock_item") .
249
+ " WHERE product_id=?";
250
+ $tdata = $this->selectAll($sql, array($params["product_id"]));
251
+ if (count($tdata) > 0)
252
+ {
253
+ $out = array_merge($out, $tdata[0]);
254
+ }
255
+
256
+ unset($idcodemap);
257
+ unset($bta);
258
+ return $out;
259
+ }
260
+
261
+ /**
262
+ * returns mode
263
+ */
264
+ public function getMode()
265
+ {
266
+ return $this->mode;
267
+ }
268
+
269
+ /**
270
+ * Adds an extra attribute to process
271
+ * Useful for some plugins if generating attribute values that are not in initial scanned list
272
+ * @param unknown $attr attribute code
273
+ */
274
+ public function addExtraAttribute($attr)
275
+ {
276
+ $attinfo = $this->attrinfo[$attr];
277
+ $this->_extra_attrs[$attinfo["backend_type"]]["data"][] = $attinfo;
278
+ }
279
+
280
+ /**
281
+ * Returns the list of magento base product table columns
282
+ * @return multitype:
283
+ */
284
+ public function getProdCols()
285
+ {
286
+ if (count($this->_prodcols) == 0)
287
+ {
288
+ $sql = 'DESCRIBE ' . $this->tablename('catalog_product_entity');
289
+ $rows = $this->selectAll($sql);
290
+ foreach ($rows as $row)
291
+ {
292
+ $this->_prodcols[] = $row['Field'];
293
+ }
294
+ }
295
+ return $this->_prodcols;
296
+ }
297
+
298
+ /**
299
+ * Returns the list of magento product item stock info table columns
300
+ * @return multitype:
301
+ */
302
+ public function getStockCols()
303
+ {
304
+ if (count($this->_stockcols) == 0)
305
+ {
306
+ $sql = 'DESCRIBE ' . $this->tablename('cataloginventory_stock_item');
307
+ $rows = $this->selectAll($sql);
308
+ foreach ($rows as $row)
309
+ {
310
+ $this->_stockcols[] = $row['Field'];
311
+ }
312
+ }
313
+ return $this->_stockcols;
314
+ }
315
+
316
+ /**
317
+ * Initialize attribute infos to be used during import
318
+ *
319
+ * @param array $cols
320
+ * : array of attribute names
321
+ */
322
+ public function checkRequired($cols)
323
+ {
324
+ $eav_attr = $this->tablename("eav_attribute");
325
+ $sql = "SELECT attribute_code FROM $eav_attr WHERE is_required=1
326
+ AND frontend_input!='' AND frontend_label!='' AND entity_type_id=?";
327
+ $required = $this->selectAll($sql, $this->prod_etype);
328
+ $reqcols = array();
329
+ foreach ($required as $line)
330
+ {
331
+ $reqcols[] = $line["attribute_code"];
332
+ }
333
+ $required = array_diff($reqcols, $cols);
334
+ return $required;
335
+ }
336
+
337
+ /**
338
+ *
339
+ * gets attribute metadata from DB and put it in attribute metadata caches
340
+ *
341
+ * @param array $cols
342
+ * list of attribute codes to get metadata from
343
+ * if in this list, some values are not attribute code, no metadata will be cached.
344
+ */
345
+ public function initAttrInfos($cols)
346
+ {
347
+ if ($this->prod_etype == null)
348
+ {
349
+ // Find product entity type
350
+ $tname = $this->tablename("eav_entity_type");
351
+ $this->prod_etype = $this->selectone("SELECT entity_type_id FROM $tname WHERE entity_type_code=?",
352
+ "catalog_product", "entity_type_id");
353
+ }
354
+
355
+ // remove from candidates, those which we already know are not attributes
356
+ $candidates = array_diff($cols, $this->_notattribs);
357
+ // remove from candidates already known attributes
358
+ $candidates = array_diff($candidates, array_keys($this->attrinfo));
359
+ // now we have a count of "unknown columns" that are potential attributes
360
+ $toscan = array_values($candidates);
361
+ if (count($toscan) > 0)
362
+ {
363
+ // create statement parameter string ?,?,?.....
364
+ $qcolstr = $this->arr2values($toscan);
365
+
366
+ $tname = $this->tablename("eav_attribute");
367
+ if ($this->getMagentoVersion() != "1.3.x")
368
+ {
369
+ $extra = $this->tablename("catalog_eav_attribute");
370
+ // SQL for selecting attribute properties for all wanted attributes
371
+ $sql = "SELECT `$tname`.*,$extra.is_global,$extra.apply_to FROM `$tname`
372
+ LEFT JOIN $extra ON $tname.attribute_id=$extra.attribute_id
373
+ WHERE ($tname.attribute_code IN ($qcolstr)) AND (entity_type_id=?)";
374
+ }
375
+ else
376
+ {
377
+ $sql = "SELECT `$tname`.* FROM `$tname` WHERE ($tname.attribute_code IN ($qcolstr)) AND (entity_type_id=?)";
378
+ }
379
+ $toscan[] = $this->prod_etype;
380
+ $result = $this->selectAll($sql, $toscan);
381
+
382
+ $attrinfs = array();
383
+ // create an attribute code based array for the wanted columns
384
+ foreach ($result as $r)
385
+ {
386
+ $attrinfs[$r["attribute_code"]] = $r;
387
+ }
388
+ unset($result);
389
+
390
+ // create a backend_type based array for the wanted columns
391
+ // this will greatly help for optimizing inserts when creating attributes
392
+ // since eav_ model for attributes has one table per backend type
393
+ // skip already in attrinfo
394
+ foreach ($attrinfs as $k => $a)
395
+ {
396
+ if (!in_array($k, array_keys($this->attrinfo)))
397
+ {
398
+ $bt = $a["backend_type"];
399
+ if (!isset($this->attrbytype[$bt]))
400
+ {
401
+ $this->attrbytype[$bt] = array("data"=>array());
402
+ }
403
+ $this->attrbytype[$bt]["data"][] = $a;
404
+ $this->attrinfo[$k] = $a;
405
+ }
406
+ }
407
+ // now add a fast index in the attrbytype array to store id list in a comma separated form
408
+ foreach ($this->attrbytype as $bt => $test)
409
+ {
410
+ $idlist = array();
411
+ foreach ($test["data"] as $it)
412
+ {
413
+ $idlist[] = $it["attribute_id"];
414
+ }
415
+ $this->attrbytype[$bt]["ids"] = implode(",", $idlist);
416
+ }
417
+ // Important Bugfix, array_merge_recurvise to merge 2 dimenstional arrays.
418
+ $this->_notattribs = array_diff($cols, array_keys($this->attrinfo));
419
+ }
420
+ /*
421
+ * now we have 2 index arrays 1. $this->attrinfo which has the following structure: key : attribute_code value : attribute_properties 2. $this->attrbytype which has the following structure: key : attribute backend type value : array of : data => array of attribute_properties ,one for each attribute that match the backend type ids => list of attribute ids of the backend type
422
+ */
423
+ }
424
+
425
+ /**
426
+ * retrieves attribute metadata
427
+ *
428
+ * @param string $attcode
429
+ * attribute code
430
+ * @param boolean $lookup
431
+ * if set, this will try to get info from DB otherwise will get from cache and may return null if not cached
432
+ * @return array attribute metadata info
433
+ */
434
+ public function getAttrInfo($attcode, $lookup = true)
435
+ {
436
+ $attrinf = isset($this->attrinfo[$attcode]) ? $this->attrinfo[$attcode] : null;
437
+ if ($attrinf == null && $lookup)
438
+ {
439
+ $this->initAttrInfos(array($attcode));
440
+ }
441
+ if (isset($this->attrinfo[$attcode]))
442
+ {
443
+ $attrinf = $this->attrinfo[$attcode];
444
+ }
445
+ return $attrinf;
446
+ }
447
+
448
+ /**
449
+ * retrieves attribute set id for a given attribute set name
450
+ *
451
+ * @param string $asname
452
+ * : attribute set name
453
+ */
454
+ public function getAttributeSetId($asname)
455
+ {
456
+ if (!isset($this->attribute_sets[$asname]))
457
+ {
458
+ $tname = $this->tablename("eav_attribute_set");
459
+ $asid = $this->selectone(
460
+ "SELECT attribute_set_id FROM $tname WHERE attribute_set_name=? AND entity_type_id=?",
461
+ array($asname,$this->prod_etype), 'attribute_set_id');
462
+ $this->attribute_sets[$asname] = $asid;
463
+ }
464
+ return $this->attribute_sets[$asname];
465
+ }
466
+
467
+ /**
468
+ * Retrieves product id for a given sku
469
+ *
470
+ * @param string $sku
471
+ * : sku of product to get id for
472
+ */
473
+ public function getProductIds($sku)
474
+ {
475
+ $tname = $this->tablename("catalog_product_entity");
476
+ $result = $this->selectAll(
477
+ "SELECT sku,entity_id as pid,attribute_set_id as asid,type_id as type FROM $tname WHERE sku=?", $sku);
478
+ if (count($result) > 0)
479
+ {
480
+ $pids = $result[0];
481
+ $pids["__new"] = false;
482
+ return $pids;
483
+ }
484
+ else
485
+ {
486
+ return false;
487
+ }
488
+ }
489
+
490
+ /**
491
+ * creates a product in magento database
492
+ *
493
+ * @param array $item:
494
+ * product attributes as array with key:attribute name,value:attribute value
495
+ * @param int $asid
496
+ * : attribute set id for values
497
+ * @return : product id for newly created product
498
+ */
499
+ public function createProduct($item, $asid)
500
+ {
501
+ // force item type if not exists
502
+ if (!isset($item["type"]))
503
+ {
504
+ $item["type"] = "simple";
505
+ }
506
+ $tname = $this->tablename('catalog_product_entity');
507
+ $item['type_id'] = $item['type'];
508
+ $item['attribute_set_id'] = $asid;
509
+ $item['entity_type_id'] = $this->prod_etype;
510
+ $item['created_at'] = strftime("%Y-%m-%d %H:%M:%S");
511
+ $item['updated_at'] = strftime("%Y-%m-%d %H:%M:%S");
512
+ $columns = array_intersect(array_keys($item), $this->getProdCols());
513
+ $values = $this->filterkvarr($item, $columns);
514
+ $sql = "INSERT INTO `$tname` (" . implode(",", $columns) . ") VALUES (" . $this->arr2values($columns) . ")";
515
+ $lastid = $this->insert($sql, array_values($values));
516
+ return $lastid;
517
+ }
518
+
519
+ /**
520
+ * Updateds product update time
521
+ *
522
+ * @param unknown_type $pid
523
+ * : entity_id of product
524
+ */
525
+ public function touchProduct($pid)
526
+ {
527
+ $tname = $this->tablename('catalog_product_entity');
528
+ $this->update("UPDATE $tname SET updated_at=? WHERE entity_id=?", array(strftime("%Y-%m-%d %H:%M:%S"),$pid));
529
+ }
530
+
531
+ /**
532
+ * Get Option id for select attributes based on value
533
+ *
534
+ * @param int $attid
535
+ * : attribute id to find option id from value
536
+ * @param mixed $optval
537
+ * : value to get option id for
538
+ * @return : array of lines (should be as much as values found),"opvd"=>option_id for value on store 0,"opvs" option id for value on current store
539
+ */
540
+ public function getOptionsFromValues($attid, $store_id, $optvals)
541
+ {
542
+ $ovstr = substr(str_repeat("?,", count($optvals)), 0, -1);
543
+ $t1 = $this->tablename('eav_attribute_option');
544
+ $t2 = $this->tablename('eav_attribute_option_value');
545
+ $sql = "SELECT optvals.option_id as opvs,optvals.value FROM $t2 as optvals";
546
+ $sql .= " JOIN $t1 as opt ON opt.option_id=optvals.option_id AND opt.attribute_id=?";
547
+ $sql .= " WHERE optvals.store_id=? AND BINARY optvals.value IN ($ovstr)";
548
+ return $this->selectAll($sql, array_merge(array($attid,$store_id), $optvals));
549
+ }
550
+
551
+ /* create a new option entry for an attribute */
552
+ public function createOption($attid)
553
+ {
554
+ $t = $this->tablename('eav_attribute_option');
555
+ $optid = $this->insert("INSERT INTO $t (attribute_id) VALUES (?)", $attid);
556
+ return $optid;
557
+ }
558
+
559
+ /**
560
+ * Creates a new option value for an option entry for a store
561
+ *
562
+ * @param int $optid
563
+ * : option entry id
564
+ * @param int $store_id
565
+ * : store id to add value for
566
+ * @param mixed $optval
567
+ * : new option value to add
568
+ * @return : option id for new created value
569
+ */
570
+ public function createOptionValue($optid, $store_id, $optval)
571
+ {
572
+ $t = $this->tablename('eav_attribute_option_value');
573
+ $optval_id = $this->insert("INSERT INTO $t (option_id,store_id,value) VALUES (?,?,?)",
574
+ array($optid,$store_id,$optval));
575
+ return $optval_id;
576
+ }
577
+
578
+ /**
579
+ * Returns option ids for a given store for a set of values (for select/multiselect attributes)
580
+ * - Create new entries if values do not exist
581
+ *
582
+ * @param unknown $attid
583
+ * attribute id
584
+ * @param unknown $storeid
585
+ * store id
586
+ * @param unknown $values
587
+ * value to create options for
588
+ * @return Ambigous <multitype:, unknown>
589
+ */
590
+ public function getOptionIds($attid, $storeid, $values)
591
+ {
592
+ $optids = array();
593
+ $svalues = array();
594
+ $avalues = array();
595
+
596
+ // Checking if we want to "translate" values existing in admin
597
+ foreach ($values as $val)
598
+ {
599
+ // if we have a reference value in admin
600
+ if (preg_match("|^(.*)::\[(.*)\]$|", $val, $matches))
601
+ {
602
+ // add translated value in store value array
603
+ $svalues[] = $matches[2];
604
+ // add admin value in admin value array
605
+ $avalues[] = $matches[1];
606
+ }
607
+ else
608
+ {
609
+ // if no translation, add values in both admin & current store array
610
+ $svalues[] = $val;
611
+ $avalues[] = $val;
612
+ }
613
+ }
614
+
615
+ // get Existing options for admin values & current attribute (store = 0)
616
+ $existing = $this->getOptionsFromValues($attid, 0, $avalues);
617
+ $exvals = array();
618
+ foreach ($existing as $optdesc)
619
+ {
620
+ $exvals[] = $optdesc["value"];
621
+ }
622
+ // new option values are option values that are in "admin" option values array & not in existing admin option values array
623
+ $new = array_merge(array_diff($avalues, $exvals));
624
+
625
+ // if we are on admin option values
626
+ if ($storeid == 0)
627
+ {
628
+ // for each new option value to create in admin
629
+ foreach ($new as $nval)
630
+ {
631
+ // create a new option with value
632
+ $row = array("opvs"=>$this->createOption($attid),"value"=>$nval);
633
+ // add an option value for current store
634
+ $this->createOptionValue($row["opvs"], $storeid, $nval);
635
+ // add this value to existing pool
636
+ $existing[] = $row;
637
+ }
638
+ // put this value in the option id cache
639
+ $this->cacheOptIds($attid, $existing);
640
+ }
641
+ else
642
+ // we are on a specific store , not admin
643
+ {
644
+ // get cached option ids for current attribute
645
+ $brows = $this->getCachedOptIds($attid);
646
+ // if none already exist
647
+ if (count($brows) == 0)
648
+ {
649
+ // try to find options matching given values (admin)
650
+ $existing = $this->getOptionsFromValues($attid, 0, $avalues);
651
+ // find non existing ones
652
+ $new = array_merge(array_diff($avalues, $exvals));
653
+ // for each new option value to create in admin
654
+ foreach ($new as $nval)
655
+ {
656
+ // create option
657
+ $row = array("opvs"=>$this->createOption($attid),"value"=>$nval);
658
+ // create option value for current store
659
+ $this->createOptionValue($row["opvs"], $storeid, $nval);
660
+ // add to exisiting pool
661
+ $existing[] = $row;
662
+ }
663
+ // add existing pool to cache
664
+ $this->cacheOptIds($attid, $existing);
665
+ // return cached option ids
666
+ $brows = $this->getCachedOptIds($attid);
667
+ }
668
+ // for all existing
669
+ foreach ($existing as $ex)
670
+ {
671
+ // remove from processing array
672
+ array_shift($brows);
673
+ }
674
+ // for all new
675
+ $cnew = count($new);
676
+ for ($i = 0; $i < $cnew; $i++)
677
+ {
678
+ $row = $brows[$i];
679
+ // if we don't have option
680
+ if (!isset($row["opvs"]))
681
+ {
682
+ // create option
683
+ $row["opvs"] = $this->createOption($attid);
684
+ // create option
685
+ $this->createOptionValue($row["opvs"], 0, $new[$i]);
686
+ }
687
+ // create option value for store
688
+ $this->createOptionValue($row["opvs"], $storeid, $new[$i]);
689
+ // add to existing pool
690
+ $existing[] = $row;
691
+ }
692
+ }
693
+
694
+ $optids = array();
695
+ // now all options are in existing & values are created
696
+ // builf and array with all option ids
697
+ foreach ($existing as $row)
698
+ {
699
+ $optids[] = $row["opvs"];
700
+ }
701
+ // remove existing
702
+ unset($existing);
703
+ // remove existing values
704
+ unset($exvals);
705
+ // remove temp array
706
+ unset($brows);
707
+ // return option ids
708
+ return $optids;
709
+ }
710
+
711
+ /**
712
+ * Adds a new option definition row in the cache
713
+ *
714
+ * @param unknown $attid
715
+ * attribute id
716
+ * @param unknown $row
717
+ * option definition
718
+ */
719
+ public function cacheOptIds($attid, $row)
720
+ {
721
+ $this->_optidcache[$attid] = $row;
722
+ }
723
+
724
+ /**
725
+ * return cached option definition rows for a given attribute id
726
+ *
727
+ * @param unknown $attid
728
+ * attribute id
729
+ * @return NULL or option definition rows found
730
+ */
731
+ public function getCachedOptIds($attid)
732
+ {
733
+ if (isset($this->_optidcache[$attid]))
734
+ {
735
+ return $this->_optidcache[$attid];
736
+ }
737
+ else
738
+ {
739
+ return null;
740
+ }
741
+ }
742
+
743
+ /**
744
+ * returns tax class id for a given tax class value
745
+ *
746
+ * @param $tcvalue :
747
+ * tax class value
748
+ */
749
+ public function getTaxClassId($tcvalue)
750
+ {
751
+ // allow for ids in tax_class_id column , extending support
752
+ if (is_numeric($tcvalue))
753
+ {
754
+ $txid = $tcvalue;
755
+ }
756
+ else
757
+ {
758
+ $t = $this->tablename('tax_class');
759
+ $txid = $this->selectone("SELECT class_id FROM $t WHERE class_name=?", array($tcvalue), "class_id");
760
+ }
761
+ // bugfix for tax class id, if not found set it to none
762
+ if (!isset($txid))
763
+ {
764
+ $txid = 0;
765
+ }
766
+ return $txid;
767
+ }
768
+
769
+ /**
770
+ * parses a calculated value with tokens like {{ }} or {}
771
+ *
772
+ * @param unknown $pvalue
773
+ * parsing value
774
+ * @param unknown $item
775
+ * item for resolving {item.xxx} tokens
776
+ * @param unknown $params
777
+ * params for resolving {meta.xxx} tokens
778
+ * @return string resolved value
779
+ */
780
+ public function parseCalculatedValue($pvalue, $item, $params)
781
+ {
782
+ $pvalue = Magmi_ValueParser::parseValue($pvalue, array("item"=>$item,"meta"=>$params));
783
+ return $pvalue;
784
+ }
785
+
786
+ /**
787
+ * Return affected store ids for a given item given an attribute scope
788
+ *
789
+ * @param array $item
790
+ * : item to get store for scope
791
+ * @param string $scope
792
+ * : scope to get stores from.
793
+ */
794
+ public function getItemStoreIds($item, $scope = 0)
795
+ {
796
+ if (!isset($item['store']))
797
+ {
798
+ $item['store'] = "admin";
799
+ }
800
+ switch ($scope)
801
+ {
802
+ // global scope
803
+ case 1:
804
+ $bstore_ids = $this->getStoreIdsForStoreScope("admin");
805
+ break;
806
+ // store scope
807
+ case 0:
808
+ $bstore_ids = $this->getStoreIdsForStoreScope($item["store"]);
809
+ break;
810
+ // website scope
811
+ case 2:
812
+ $bstore_ids = $this->getStoreIdsForWebsiteScope($item["store"]);
813
+ break;
814
+ }
815
+
816
+ $itemstores = array_unique(array_merge($this->_dstore, $bstore_ids));
817
+ sort($itemstores);
818
+ return $itemstores;
819
+ }
820
+
821
+ /**
822
+ * Create product attribute from values for a given product id
823
+ *
824
+ * @param $pid :
825
+ * product id to create attribute values for
826
+ * @param $item :
827
+ * attribute values in an array indexed by attribute_code
828
+ */
829
+ public function createAttributes($pid, &$item, $attmap, $isnew, $itemids)
830
+ {
831
+ /**
832
+ * get all store ids
833
+ */
834
+ $this->_extra_attrs = array();
835
+ /* now is the interesring part */
836
+ /* iterate on attribute backend type index */
837
+ foreach ($attmap as $tp => $a)
838
+ {
839
+ /* for static types, do not insert into attribute tables */
840
+ if ($tp == "static")
841
+ {
842
+ continue;
843
+ }
844
+
845
+ // table name for backend type data
846
+ $cpet = $this->tablename("catalog_product_entity_$tp");
847
+ // data table for inserts
848
+ $data = array();
849
+ // inserts to perform on backend type eav
850
+ $inserts = array();
851
+ // deletes to perform on backend type eav
852
+ $deletes = array();
853
+
854
+ // use reflection to find special handlers
855
+ $typehandler = "handle" . ucfirst($tp) . "Attribute";
856
+ // iterate on all attribute descriptions for the given backend type
857
+ foreach ($a["data"] as $attrdesc)
858
+ {
859
+ // check item type is compatible with attribute apply_to
860
+ if ($attrdesc["apply_to"] != null &&
861
+ strpos($attrdesc["apply_to"], strtolower($itemids["type"])) === false)
862
+ {
863
+ // do not handle attribute if it does not apply to the product type
864
+ continue;
865
+ }
866
+ // get attribute id
867
+ $attid = $attrdesc["attribute_id"];
868
+ // get attribute value in the item to insert based on code
869
+ $atthandler = "handle" . ucfirst($attrdesc["attribute_code"]) . "Attribute";
870
+ $attrcode = $attrdesc["attribute_code"];
871
+ // if the attribute code is no more in item (plugins may have come into the way), continue
872
+ if (!in_array($attrcode, array_keys($item)))
873
+ {
874
+ continue;
875
+ }
876
+ // get the item value
877
+ $ivalue = $item[$attrcode];
878
+ // get item store id for the current attribute
879
+ $store_ids = $this->getItemStoreIds($item, $attrdesc["is_global"]);
880
+
881
+ // do not handle empty generic int values in create mode
882
+ if ($ivalue == "" && $this->mode != "update" && $tp == "int")
883
+ {
884
+ continue;
885
+ }
886
+ // for all store ids
887
+ foreach ($store_ids as $store_id)
888
+ {
889
+
890
+ // base output value to be inserted = base source value
891
+ $ovalue = $ivalue;
892
+ // check for attribute handlers for current attribute
893
+ foreach ($this->_attributehandlers as $match => $ah)
894
+ {
895
+ $matchinfo = explode(":", $match);
896
+ $mtype = $matchinfo[0];
897
+ $mtest = $matchinfo[1];
898
+ unset($matchinfo);
899
+ unset($hvalue);
900
+ if (preg_match("/$mtest/", $attrdesc[$mtype]))
901
+ {
902
+ // if there is a specific handler for attribute, use it
903
+ if (method_exists($ah, $atthandler))
904
+ {
905
+ $hvalue = $ah->$atthandler($pid, $item, $store_id, $attrcode, $attrdesc, $ivalue);
906
+ }
907
+ else
908
+ // use generic type attribute
909
+ if (method_exists($ah, $typehandler))
910
+ {
911
+ $hvalue = $ah->$typehandler($pid, $item, $store_id, $attrcode, $attrdesc, $ivalue);
912
+ }
913
+ // if handlers returned a value that is not "__MAGMI_UNHANDLED__" , we have our output value
914
+ if (isset($hvalue) && $hvalue != "__MAGMI_UNHANDLED__")
915
+ {
916
+ $ovalue = $hvalue;
917
+ break;
918
+ }
919
+ }
920
+ }
921
+ // if __MAGMI_UNHANDLED__ ,don't insert anything
922
+ if ($ovalue == "__MAGMI_UNHANDLED__")
923
+ {
924
+ $ovalue = false;
925
+ }
926
+
927
+ // force null value => to delete
928
+ if ($ovalue == "__NULL__")
929
+ {
930
+ $ovalue = "__MAGMI_DELETE__";
931
+ }
932
+
933
+ // if handled value is a "DELETE"
934
+ if ($ovalue == "__MAGMI_DELETE__")
935
+ {
936
+ $deletes[] = $attid;
937
+ // do not handle value in insert
938
+ $ovalue = null;
939
+ }
940
+ // if we have something to do with this value
941
+ if ($ovalue !== false && $ovalue != null)
942
+ {
943
+
944
+ $data[] = $this->prod_etype;
945
+ $data[] = $attid;
946
+ $data[] = $store_id;
947
+ $data[] = $pid;
948
+ $data[] = $ovalue;
949
+ $insstr = "(?,?,?,?,?)";
950
+ $inserts[] = $insstr;
951
+ }
952
+
953
+ // if one of the store in the list is admin
954
+ if ($store_id == 0)
955
+ {
956
+ $sids = $store_ids;
957
+ // remove all values bound to the other stores for this attribute,so that they default to "use admin value"
958
+ array_shift($sids);
959
+ if (count($sids) > 0)
960
+ {
961
+ $sidlist = implode(",", $sids);
962
+ $ddata = array($this->prod_etype,$attid,$pid);
963
+ $sql = "DELETE FROM $cpet WHERE entity_type_id=? AND attribute_id=? AND store_id IN ($sidlist) AND entity_id=?";
964
+ $this->delete($sql, $ddata);
965
+ unset($ddata);
966
+ }
967
+ unset($sids);
968
+ break;
969
+ }
970
+ }
971
+ }
972
+ //if we have values to insert or update
973
+ if (!empty($inserts))
974
+ {
975
+ // now perform insert for all values of the the current backend type in one
976
+ // single insert
977
+ $sql = "INSERT INTO $cpet
978
+ (`entity_type_id`, `attribute_id`, `store_id`, `entity_id`, `value`)
979
+ VALUES ";
980
+ $sql .= implode(",", $inserts);
981
+ // this one taken from mysql log analysis of magento import
982
+ // smart one :)
983
+ $sql .= " ON DUPLICATE KEY UPDATE `value`=VALUES(`value`)";
984
+ $this->insert($sql, $data);
985
+ }
986
+
987
+ //if we have values to delete
988
+ if (!empty($deletes))
989
+ {
990
+ $sidlist = implode(",", $store_ids);
991
+ $attidlist = implode(",", $deletes);
992
+ $sql = "DELETE FROM $cpet WHERE entity_type_id=? AND attribute_id IN ($attidlist) AND store_id IN ($sidlist) AND entity_id=?";
993
+ $this->delete($sql, array($this->prod_etype,$pid));
994
+ }
995
+ //if no values inserted or deleted on a new item, we have a problem
996
+ if (empty($deletes) && empty($inserts) && $isnew)
997
+ {
998
+ //in fact we have a problem if we have a really new item
999
+ if (!$this->_same)
1000
+ {
1001
+ $this->log("No $tp Attributes created for sku " . $item["sku"], "warning");
1002
+ }
1003
+ }
1004
+ //memory release
1005
+ unset($store_ids);
1006
+ unset($data);
1007
+ unset($inserts);
1008
+ unset($deletes);
1009
+ }
1010
+ //if new attributes are to be processed, return them
1011
+ return $this->_extra_attrs;
1012
+ }
1013
+
1014
+ /**
1015
+ * update product stock
1016
+ *
1017
+ * @param int $pid
1018
+ * : product id
1019
+ * @param array $item
1020
+ * : attribute values for product indexed by attribute_code
1021
+ */
1022
+ public function updateStock($pid, $item, $isnew)
1023
+ {
1024
+ $scols = $this->getStockCols();
1025
+ // ake only stock columns that are in item
1026
+ $itstockcols = array_intersect(array_keys($item), $scols);
1027
+ // o stock columns set, item exists, no stock update needed.
1028
+ if (count($itstockcols) == 0 && !$isnew)
1029
+ {
1030
+ return;
1031
+ }
1032
+ $csit = $this->tablename("cataloginventory_stock_item");
1033
+ $css = $this->tablename("cataloginventory_stock_status");
1034
+ // alculate is_in_stock flag
1035
+ if (isset($item["qty"]))
1036
+ {
1037
+ if (!isset($item["manage_stock"]))
1038
+ {
1039
+ $item["manage_stock"] = 1;
1040
+ $item["use_config_manage_stock"] = 0;
1041
+ }
1042
+
1043
+ $mqty = (isset($item["min_qty"]) ? $item["min_qty"] : 0);
1044
+ $is_in_stock = isset($item["is_in_stock"]) ? $item["is_in_stock"] : ($item["qty"] > $mqty ? 1 : 0);
1045
+ $item["is_in_stock"] = $is_in_stock;
1046
+ }
1047
+ // ake only stock columns that are in item after item update
1048
+ $common = array_intersect(array_keys($item), $scols);
1049
+
1050
+ // reate stock item line if needed
1051
+ $stock_id = (isset($item["stock_id"]) ? $item["stock_id"] : 1);
1052
+ $sql = "INSERT IGNORE INTO `$csit` (product_id,stock_id) VALUES (?,?)";
1053
+ $this->insert($sql, array($pid,$stock_id));
1054
+
1055
+ if (count($common) > 0)
1056
+ {
1057
+ $cols = $this->arr2columns($common);
1058
+ $stockvals = $this->filterkvarr($item, $common);
1059
+
1060
+ // ill with values
1061
+ $svstr = $this->arr2update($stockvals);
1062
+ if (isset($item["qty"]) && $item["qty"] != "")
1063
+ {
1064
+ $relqty = NULL;
1065
+
1066
+ // if magmi_qty_absolute flag is not set, then use standard "relative" qty parsing.
1067
+ if (!isset($item["magmi_qty_absolute"]) || $item["magmi_qty_absolute"] == 0)
1068
+ {
1069
+ // test for relative qty
1070
+ if ($item["qty"][0] == "+" || $item["qty"][0] == "-")
1071
+ {
1072
+ $relqty = getRelative($item["qty"]);
1073
+ }
1074
+ }
1075
+ // if relative qty
1076
+ if ($relqty != NULL)
1077
+ {
1078
+ // update UPDATE statement value affectation
1079
+ $svstr = preg_replace("/(^|,)qty=\?/", "$1qty=qty$relqty?", $svstr);
1080
+ $stockvals["qty"] = $item["qty"];
1081
+ $svstr = str_replace("is_in_stock=?", "is_in_stock=(qty>min_qty)", $svstr);
1082
+ unset($stockvals["is_in_stock"]);
1083
+ }
1084
+ }
1085
+ $sql = "UPDATE `$csit` SET $svstr WHERE product_id=? AND stock_id=?";
1086
+ $this->update($sql, array_merge(array_values($stockvals), array($pid,$stock_id)));
1087
+ }
1088
+
1089
+ $data = array();
1090
+ $wsids = $this->getItemWebsites($item);
1091
+ $csscols = array("website_id","product_id","stock_id","qty","stock_status");
1092
+ $cssvals = $this->filterkvarr($item, $csscols);
1093
+ $stock_id = (isset($cssvals["stock_id"]) ? $cssvals["stock_id"] : 1);
1094
+ $stock_status = (isset($cssvals["stock_status"]) ? $cssvals["stock_status"] : 1);
1095
+ // new auto synchro on lat inserted stock item values for stock status.
1096
+ // also works for multiple stock ids.
1097
+
1098
+ // [start] exanto.de - this does not work inside a DB transaction bc cataloginventory_stock_item is not written yet on fresh imports
1099
+ /*
1100
+ * :ORG: $sql="INSERT INTO `$css` SELECT csit.product_id,ws.website_id,cis.stock_id,csit.qty,? as stock_status FROM `$csit` as csit JOIN ".$this->tablename("core_website")." as ws ON ws.website_id IN (".$this->arr2values($wsids).") JOIN ".$this->tablename("cataloginventory_stock")." as cis ON cis.stock_id=? WHERE product_id=? ON DUPLICATE KEY UPDATE stock_status=VALUES(`stock_status`),qty=VALUES(`qty`)";
1101
+ */
1102
+ // Fixed version
1103
+ $cpe = $this->tablename("catalog_product_entity");
1104
+ // Fix , $stockvals is already a mix between item keys & stock table keys.
1105
+ $qty = isset($stockvals['qty']) ? $stockvals['qty'] : 0;
1106
+ if (!$qty)
1107
+ {
1108
+ $qty = 0;
1109
+ }
1110
+ $sql = "INSERT INTO `$css` SELECT '$pid' as product_id,ws.website_id,cis.stock_id,'$qty' as qty,? as stock_status
1111
+ FROM `$cpe` as cpe
1112
+ JOIN " .
1113
+ $this->tablename("core_website") . " as ws ON ws.website_id IN (" . $this->arr2values($wsids) . ")
1114
+ JOIN " .
1115
+ $this->tablename("cataloginventory_stock") . " as cis ON cis.stock_id=?
1116
+ WHERE cpe.entity_id=?
1117
+ ON DUPLICATE KEY UPDATE stock_status=VALUES(`stock_status`),qty=VALUES(`qty`)";
1118
+ // [ end ] exanto.de - this does not work inside a DB transaction bc cataloginventory_stock_item is not written yet on fresh imports
1119
+
1120
+ $data[] = $stock_status;
1121
+ $data = array_merge($data, $wsids);
1122
+ $data[] = $stock_id;
1123
+ $data[] = $pid;
1124
+ $this->insert($sql, $data);
1125
+ unset($data);
1126
+ }
1127
+
1128
+ /**
1129
+ * assign categories for a given product id from values
1130
+ * categories should already be created & csv values should be as the ones
1131
+ * given in the magento export (ie: comma separated ids, minus 1,2)
1132
+ *
1133
+ * @param int $pid
1134
+ * : product id
1135
+ * @param array $item
1136
+ * : attribute values for product indexed by attribute_code
1137
+ */
1138
+ public function assignCategories($pid, $item)
1139
+ {
1140
+ $cce = $this->tablename("catalog_category_entity");
1141
+ $ccpt = $this->tablename("catalog_category_product");
1142
+ // andle assignment reset
1143
+ if (!isset($item["category_reset"]) || $item["category_reset"] == 1)
1144
+ {
1145
+ $sql = "DELETE $ccpt.*
1146
+ FROM $ccpt
1147
+ JOIN $cce ON $cce.entity_id=$ccpt.category_id
1148
+ WHERE product_id=?";
1149
+ $this->delete($sql, $pid);
1150
+ }
1151
+
1152
+ $inserts = array();
1153
+ $data = array();
1154
+ $cdata = array();
1155
+ $ddata = array();
1156
+ $cpos = array();
1157
+ $catids = csl2arr($item["category_ids"]);
1158
+
1159
+ // find positive category assignments
1160
+
1161
+ foreach ($catids as $catdef)
1162
+ {
1163
+ $a = explode("::", $catdef);
1164
+ $catid = $a[0];
1165
+ $catpos = (count($a) > 1 ? $a[1] : "0");
1166
+ $rel = getRelative($catid);
1167
+ if ($rel == "-")
1168
+ {
1169
+ $ddata[] = $catid;
1170
+ }
1171
+ else
1172
+ {
1173
+ $cdata[$catid] = $catpos;
1174
+ }
1175
+ }
1176
+
1177
+ // get all "real ids"
1178
+ if (count($cdata) > 0)
1179
+ {
1180
+ $scatids = array_keys($cdata);
1181
+ $rcatids = $this->selectAll(
1182
+ "SELECT cce.entity_id as id FROM $cce as cce WHERE cce.entity_id IN (" . $this->arr2values($scatids) .
1183
+ ")", $scatids);
1184
+ $vcatids = array();
1185
+ foreach ($rcatids as $rcatrow)
1186
+ {
1187
+ $vcatids[] = $rcatrow['id'];
1188
+ }
1189
+ // now get the diff
1190
+ $diff = array_diff(array_keys($cdata), $vcatids);
1191
+ $cdiff = count($diff);
1192
+ // if there are some, warning
1193
+ if ($cdiff > 0)
1194
+ {
1195
+ $this->log('Invalid category ids found for sku ' . $item['sku'] . ":" . implode(",", $diff), "warning");
1196
+ // remove invalid category entries
1197
+ for ($i = 0; $i < $cdiff; $i++)
1198
+ {
1199
+ unset($cdata[$diff[$i]]);
1200
+ }
1201
+ }
1202
+
1203
+ // ow we have verified ids
1204
+ foreach ($cdata as $catid => $catpos)
1205
+ {
1206
+ $inserts[] = "(?,?,?)";
1207
+ $data[] = $catid;
1208
+ $data[] = $pid;
1209
+ $data[] = $catpos;
1210
+ }
1211
+ }
1212
+
1213
+ // eform deletion of removed category affectation
1214
+ if (count($ddata) > 0)
1215
+ {
1216
+ $sql = "DELETE FROM $ccpt WHERE category_id IN (" . $this->arr2values($ddata) . ") AND product_id=?";
1217
+ $ddata[] = $pid;
1218
+ $this->delete($sql, $ddata);
1219
+ unset($ddata);
1220
+ }
1221
+
1222
+ // reate new category assignment for products, if multi store with repeated ids
1223
+ // gnore duplicates
1224
+ if (count($inserts) > 0)
1225
+ {
1226
+ $sql = "INSERT INTO $ccpt (`category_id`,`product_id`,`position`)
1227
+ VALUES ";
1228
+ $sql .= implode(",", $inserts);
1229
+ $sql .= " ON DUPLICATE KEY UPDATE position=VALUES(`position`)";
1230
+ $this->insert($sql, $data);
1231
+ unset($data);
1232
+ }
1233
+ unset($deletes);
1234
+ unset($inserts);
1235
+ }
1236
+
1237
+ public function getItemWebsites($item, $default = false)
1238
+ {
1239
+ // support for websites column if set
1240
+ if (!empty($item["websites"]))
1241
+ {
1242
+ if (!isset($this->_wsids[$item["websites"]]))
1243
+ {
1244
+ $this->_wsids[$item["websites"]] = array();
1245
+
1246
+ $cws = $this->tablename("core_website");
1247
+ $wscodes = csl2arr($item["websites"]);
1248
+ $qcolstr = $this->arr2values($wscodes);
1249
+ $rows = $this->selectAll("SELECT website_id FROM $cws WHERE code IN ($qcolstr)", $wscodes);
1250
+ foreach ($rows as $row)
1251
+ {
1252
+ $this->_wsids[$item["websites"]][] = $row['website_id'];
1253
+ }
1254
+ }
1255
+ return $this->_wsids[$item["websites"]];
1256
+ }
1257
+
1258
+ if (!isset($item['store']))
1259
+ {
1260
+ $item['store'] = "admin";
1261
+ }
1262
+ $k = $item["store"];
1263
+
1264
+ if (!isset($this->_wsids[$k]))
1265
+ {
1266
+ $this->_wsids[$k] = array();
1267
+ $cs = $this->tablename("core_store");
1268
+ if (trim($k) != "admin")
1269
+ {
1270
+ $scodes = csl2arr($k);
1271
+ $qcolstr = $this->arr2values($scodes);
1272
+ $rows = $this->selectAll(
1273
+ "SELECT website_id FROM $cs WHERE code IN ($qcolstr) AND store_id!=0 GROUP BY website_id", $scodes);
1274
+ }
1275
+ else
1276
+ {
1277
+ $rows = $this->selectAll("SELECT website_id FROM $cs WHERE store_id!=0 GROUP BY website_id ");
1278
+ }
1279
+ foreach ($rows as $row)
1280
+ {
1281
+ $this->_wsids[$k][] = $row['website_id'];
1282
+ }
1283
+ }
1284
+ return $this->_wsids[$k];
1285
+ }
1286
+
1287
+ /**
1288
+ * set website of product if not exists
1289
+ *
1290
+ * @param int $pid
1291
+ * : product id
1292
+ * @param array $item
1293
+ * : attribute values for product indexed by attribute_code
1294
+ */
1295
+ public function updateWebSites($pid, $item)
1296
+ {
1297
+ $wsids = $this->getItemWebsites($item);
1298
+ $qcolstr = $this->arr2values($wsids);
1299
+ $cpst = $this->tablename("catalog_product_website");
1300
+ $cws = $this->tablename("core_website");
1301
+ // associate product with all websites in a single multi insert (use ignore to avoid duplicates)
1302
+ $ddata = array($pid);
1303
+ $sql = "DELETE FROM `$cpst` WHERE product_id=?";
1304
+ $this->delete($sql, $ddata);
1305
+ $sql = "INSERT INTO `$cpst` (`product_id`, `website_id`) SELECT ?,website_id FROM $cws WHERE website_id IN ($qcolstr)";
1306
+ $this->insert($sql, array_merge(array($pid), $wsids));
1307
+ }
1308
+
1309
+ public function clearOptCache()
1310
+ {
1311
+ unset($this->_optidcache);
1312
+ $this->_optidcache = array();
1313
+ }
1314
+
1315
+ public function onNewSku($sku, $existing)
1316
+ {
1317
+ $this->clearOptCache();
1318
+ // only assign values to store 0 by default in create mode for new sku
1319
+ // for store related options
1320
+ if (!$existing)
1321
+ {
1322
+ $this->_dstore = array(0);
1323
+ }
1324
+ else
1325
+ {
1326
+ $this->_dstore = array();
1327
+ }
1328
+ $this->_same = false;
1329
+ }
1330
+
1331
+ public function onSameSku($sku)
1332
+ {
1333
+ unset($this->_dstore);
1334
+ $this->_dstore = array();
1335
+ $this->_same = true;
1336
+ }
1337
+
1338
+ public function currentItemExists()
1339
+ {
1340
+ return $this->_curitemids["__new"] == false;
1341
+ }
1342
+
1343
+ public function getItemIds($item)
1344
+ {
1345
+ $sku = $item["sku"];
1346
+ if (strcmp($sku, $this->_curitemids["sku"]) != 0)
1347
+ {
1348
+ // try to find item ids in db
1349
+ $cids = $this->getProductIds($sku);
1350
+ if ($cids !== false)
1351
+ {
1352
+ // if found use it
1353
+ $this->_curitemids = $cids;
1354
+ }
1355
+ else
1356
+ {
1357
+ // only sku & attribute set id from datasource otherwise.
1358
+ $this->_curitemids = array("pid"=>null,"sku"=>$sku,
1359
+ "asid"=>isset($item["attribute_set"]) ? $this->getAttributeSetId($item["attribute_set"]) : $this->default_asid,
1360
+ "type"=>isset($item["type"]) ? $item["type"] : "simple","__new"=>true);
1361
+ }
1362
+ // do not reset values for existing if non admin
1363
+ $this->onNewSku($sku, ($cids !== false));
1364
+ unset($cids);
1365
+ }
1366
+ else
1367
+ {
1368
+ $this->onSameSku($sku);
1369
+ }
1370
+ return $this->_curitemids;
1371
+ }
1372
+
1373
+ public function handleIgnore(&$item)
1374
+ {
1375
+ // filter __MAGMI_IGNORE__ COLUMNS
1376
+ foreach ($item as $k => $v)
1377
+ {
1378
+ if ($v === "__MAGMI_IGNORE__")
1379
+ {
1380
+ unset($item[$k]);
1381
+ }
1382
+ }
1383
+ }
1384
+
1385
+ public function findItemStores($pid)
1386
+ {
1387
+ $sql = "SELECT cs.code FROM " . $this->tablename("catalog_product_website") . " AS cpw" . " JOIN " .
1388
+ $this->tablename("core_store") . " as cs ON cs.website_id=cpw.website_id" . " WHERE cpw.product_id=?";
1389
+ $result = $this->selectAll($sql, array($pid));
1390
+ $scodes = array();
1391
+ foreach ($result as $row)
1392
+ {
1393
+ $scodes[] = $row["code"];
1394
+ }
1395
+ return implode(",", $scodes);
1396
+ }
1397
+
1398
+ public function checkItemStores($scodes)
1399
+ {
1400
+ if ($scodes == "admin")
1401
+ {
1402
+ return $scodes;
1403
+ }
1404
+
1405
+ $scarr = explode(",", $scodes);
1406
+ trimarray($scarr);
1407
+ $rscode = array();
1408
+ $sql = "SELECT code FROM " . $this->tablename("core_store") . " WHERE code IN (" . $this->arr2values($scarr) .
1409
+ ")";
1410
+ $result = $this->selectAll($sql, $scarr);
1411
+ $rscodes = array();
1412
+ foreach ($result as $row)
1413
+ {
1414
+ $rscodes[] = $row["code"];
1415
+ }
1416
+ $diff = array_diff($scarr, $rscodes);
1417
+ $out = "";
1418
+ if (count($diff) > 0)
1419
+ {
1420
+ $out = "Invalid store code(s) found:" . implode(",", $diff);
1421
+ }
1422
+ if ($out != "")
1423
+ {
1424
+ if (count($rscodes) == 0)
1425
+ {
1426
+ $out .= ", NO VALID STORE FOUND";
1427
+ }
1428
+ $this->log($out, "warning");
1429
+ }
1430
+
1431
+ return implode(",", $rscodes);
1432
+ }
1433
+
1434
+ public function checkstore(&$item, $pid, $isnew)
1435
+ {
1436
+ // we have store column set , just check
1437
+ if (isset($item["store"]) && trim($item["store"]) != "")
1438
+ {
1439
+ $scodes = $this->checkItemStores($item["store"]);
1440
+ }
1441
+ else
1442
+ {
1443
+ $scodes = "admin";
1444
+ }
1445
+ if ($scodes == "")
1446
+ {
1447
+ return false;
1448
+ }
1449
+ $item["store"] = $scodes;
1450
+ return true;
1451
+ }
1452
+
1453
+ /**
1454
+ * full import workflow for item
1455
+ *
1456
+ * @param array $item
1457
+ * : attribute values for product indexed by attribute_code
1458
+ */
1459
+ public function importItem($item)
1460
+ {
1461
+ $this->handleIgnore($item);
1462
+ if (Magmi_StateManager::getState() == "canceled")
1463
+ {
1464
+ throw new Exception("MAGMI_RUN_CANCELED");
1465
+ }
1466
+ // first step
1467
+
1468
+ if (!$this->callPlugins("itemprocessors", "processItemBeforeId", $item))
1469
+ {
1470
+ return false;
1471
+ }
1472
+
1473
+ // check if sku has been reset
1474
+ if (!isset($item["sku"]) || trim($item["sku"]) == '')
1475
+ {
1476
+ $this->log('No sku info found for record #' . $this->_current_row, "error");
1477
+ return false;
1478
+ }
1479
+ // handle "computed" ignored columns
1480
+ $this->handleIgnore($item);
1481
+ // get Item identifiers in magento
1482
+ $itemids = $this->getItemIds($item);
1483
+
1484
+ // extract product id & attribute set id
1485
+ $pid = $itemids["pid"];
1486
+ $asid = $itemids["asid"];
1487
+
1488
+ $isnew = false;
1489
+ if (isset($pid) && $this->mode == "xcreate")
1490
+ {
1491
+ $this->log("skipping existing sku:{$item["sku"]} - xcreate mode set", "skip");
1492
+ return false;
1493
+ }
1494
+
1495
+ if (!isset($pid))
1496
+ {
1497
+
1498
+ if ($this->mode !== 'update')
1499
+ {
1500
+ if (!isset($asid))
1501
+ {
1502
+ $this->log("cannot create product sku:{$item["sku"]}, no attribute_set defined", "error");
1503
+ return false;
1504
+ }
1505
+ $pid = $this->createProduct($item, $asid);
1506
+ $this->_curitemids["pid"] = $pid;
1507
+ $isnew = true;
1508
+ }
1509
+ else
1510
+ {
1511
+ // mode is update, do nothing
1512
+ $this->log("skipping unknown sku:{$item["sku"]} - update mode set", "skip");
1513
+ return false;
1514
+ }
1515
+ }
1516
+ else
1517
+ {
1518
+ $this->updateProduct($item, $pid);
1519
+ }
1520
+
1521
+ try
1522
+ {
1523
+ $basemeta = array("product_id"=>$pid,"new"=>$isnew,"same"=>$this->_same,"asid"=>$asid);
1524
+ $fullmeta = array_merge($basemeta, $itemids);
1525
+ if (!$this->callPlugins("itemprocessors", "processItemAfterId", $item, $fullmeta))
1526
+ {
1527
+ return false;
1528
+ }
1529
+
1530
+ if (count($item) == 0)
1531
+ {
1532
+ return true;
1533
+ }
1534
+ // handle "computed" ignored columns from afterImport
1535
+ $this->handleIgnore($item);
1536
+
1537
+ if (!$this->checkstore($item, $pid, $isnew))
1538
+ {
1539
+ $this->log("invalid store value, skipping item sku:" . $item["sku"]);
1540
+ return false;
1541
+ }
1542
+ // if column list has been modified by callback, update attribute info cache.
1543
+ $this->initAttrInfos(array_keys($item));
1544
+ // create new ones
1545
+ $attrmap = $this->attrbytype;
1546
+ do
1547
+ {
1548
+ $attrmap = $this->createAttributes($pid, $item, $attrmap, $isnew, $itemids);
1549
+ }
1550
+ while (count($attrmap) > 0);
1551
+
1552
+ if (!testempty($item, "category_ids") || (isset($item["category_reset"]) && $item["category_reset"] == 1))
1553
+ {
1554
+ // assign categories
1555
+ $this->assignCategories($pid, $item);
1556
+ }
1557
+
1558
+ // update websites if column is set
1559
+ if (isset($item["websites"]) || $isnew)
1560
+ {
1561
+ $this->updateWebSites($pid, $item);
1562
+ }
1563
+
1564
+ if (!$this->_same)
1565
+ {
1566
+ // update stock
1567
+ $this->updateStock($pid, $item, $isnew);
1568
+ }
1569
+
1570
+ $this->touchProduct($pid);
1571
+ // ok,we're done
1572
+ if (!$this->callPlugins("itemprocessors", "processItemAfterImport", $item, $fullmeta))
1573
+ {
1574
+ return false;
1575
+ }
1576
+ }
1577
+ catch (Exception $e)
1578
+ {
1579
+ $this->callPlugins(array("itemprocessors"), "processItemException", $item, array("exception"=>$e));
1580
+ $this->logException($e);
1581
+ throw $e;
1582
+ }
1583
+ // return true;
1584
+ return $pid;
1585
+ }
1586
+
1587
+ public function getProperties()
1588
+ {
1589
+ return $this->_props;
1590
+ }
1591
+
1592
+ /**
1593
+ * count lines of csv file
1594
+ *
1595
+ * @param string $csvfile
1596
+ * filename
1597
+ */
1598
+ public function lookup()
1599
+ {
1600
+ $t0 = microtime(true);
1601
+ $this->log("Performing Datasouce Lookup...", "startup");
1602
+
1603
+ $count = $this->datasource->getRecordsCount();
1604
+ $t1 = microtime(true);
1605
+ $time = $t1 - $t0;
1606
+ $this->log("$count:$time", "lookup");
1607
+ $this->log("Found $count records, took $time sec", "startup");
1608
+
1609
+ return $count;
1610
+ }
1611
+
1612
+ public function updateProduct($item, $pid)
1613
+ {
1614
+ $tname = $this->tablename('catalog_product_entity');
1615
+ if (isset($item['type']))
1616
+ {
1617
+ $item['type_id'] = $item['type'];
1618
+ }
1619
+ $item['entity_type_id'] = $this->prod_etype;
1620
+ $item['updated_at'] = strftime("%Y-%m-%d %H:%M:%S");
1621
+ $columns = array_intersect(array_keys($item), $this->getProdCols());
1622
+ $values = $this->filterkvarr($item, $columns);
1623
+
1624
+ $sql = "UPDATE `$tname` SET " . $this->arr2update($values) . " WHERE entity_id=?";
1625
+
1626
+ $this->update($sql, array_merge(array_values($values), array($pid)));
1627
+ }
1628
+
1629
+ public function getProductEntityType()
1630
+ {
1631
+ return $this->prod_etype;
1632
+ }
1633
+
1634
+ public function getCurrentRow()
1635
+ {
1636
+ return $this->_current_row;
1637
+ }
1638
+
1639
+ public function setCurrentRow($cnum)
1640
+ {
1641
+ $this->_current_row = $cnum;
1642
+ }
1643
+
1644
+ public function isLastItem($item)
1645
+ {
1646
+ return isset($item["__MAGMI_LAST__"]);
1647
+ }
1648
+
1649
+ public function setLastItem(&$item)
1650
+ {
1651
+ $item["__MAGMI_LAST__"] = 1;
1652
+ }
1653
+
1654
+ public function engineInit($params)
1655
+ {
1656
+ $this->_profile = $this->getParam($params, "profile", "default");
1657
+ // create an instance of local magento directory handler
1658
+ // this instance will autoregister in factory
1659
+ $mdh = new LocalMagentoDirHandler(Magmi_Config::getInstance()->getMagentoDir());
1660
+ $this->_timecounter->initTimingCats(array("global","line"));
1661
+ $this->initPlugins($this->_profile);
1662
+ $this->mode = $this->getParam($params, "mode", "update");
1663
+ }
1664
+
1665
+ public function reportStats($nrow, &$tstart, &$tdiff, &$lastdbtime, &$lastrec)
1666
+ {
1667
+ $tend = microtime(true);
1668
+ $this->log($nrow . " - " . ($tend - $tstart) . " - " . ($tend - $tdiff), "itime");
1669
+ $this->log(
1670
+ $this->_nreq . " - " . ($this->_indbtime) . " - " . ($this->_indbtime - $lastdbtime) . " - " .
1671
+ ($this->_nreq - $lastrec), "dbtime");
1672
+ $lastrec = $this->_nreq;
1673
+ $lastdbtime = $this->_indbtime;
1674
+ $tdiff = microtime(true);
1675
+ }
1676
+
1677
+ public function initImport($params)
1678
+ {
1679
+ $this->log("MAGMI by dweeves - version:" . Magmi_Version::$version, "title");
1680
+ $this->log("Import Profile:$this->_profile", "startup");
1681
+ $this->log("Import Mode:$this->mode", "startup");
1682
+ $this->log("step:" . $this->getProp("GLOBAL", "step", 0.5) . "%", "step");
1683
+ // intialize store id cache
1684
+ $this->connectToMagento();
1685
+ try
1686
+ {
1687
+ $this->initProdType();
1688
+ $this->createPlugins($this->_profile, $params);
1689
+ $this->_registerPluginLoopCallback("processItemAfterId", "onPluginProcessedItemAfterId");
1690
+ $this->callPlugins("datasources,itemprocessors", "startImport");
1691
+ $this->resetSkuStats();
1692
+ }
1693
+ catch (Exception $e)
1694
+ {
1695
+ $this->disconnectFromMagento();
1696
+ }
1697
+ }
1698
+
1699
+ public function onPluginProcessedItemAfterId($plinst, &$item, $plresult)
1700
+ {
1701
+ $this->handleIgnore($item);
1702
+ }
1703
+
1704
+ /**
1705
+ * Breaks item processing , but validates partial import
1706
+ * This is useful for complex plugins that would assur
1707
+ *
1708
+ * @param array $item
1709
+ * , item to break process on
1710
+ * @param array $params
1711
+ * , processing parameters (item metadata)
1712
+ * @param
1713
+ * bool touch , sets product update time if true (default)
1714
+ */
1715
+ public function breakItemProcessing(&$item, $params, $touch = true)
1716
+ {
1717
+ // setting empty item to break standard processing
1718
+ $item = array();
1719
+ if ($touch && isset($params["product_id"]))
1720
+ {
1721
+ $this->touchProduct($params["product_id"]);
1722
+ }
1723
+ }
1724
+
1725
+ public function exitImport()
1726
+ {
1727
+ $this->callPlugins("datasources,general,itemprocessors", "endImport");
1728
+ $this->callPlugins("datasources,general,itemprocessors", "afterImport");
1729
+ $this->disconnectFromMagento();
1730
+ }
1731
+
1732
+ public function updateSkuStats($res)
1733
+ {
1734
+ if (!$this->_same)
1735
+ {
1736
+ $this->_skustats["nsku"]++;
1737
+ if ($res["ok"])
1738
+ {
1739
+ $this->_skustats["ok"]++;
1740
+ }
1741
+ else
1742
+ {
1743
+ $this->_skustats["ko"]++;
1744
+ }
1745
+ }
1746
+ }
1747
+
1748
+ public function getDataSource()
1749
+ {
1750
+ return $this->getPluginInstance("datasources");
1751
+ }
1752
+
1753
+ public function processDataSourceLine($item, $rstep, &$tstart, &$tdiff, &$lastdbtime, &$lastrec)
1754
+ {
1755
+ // counter
1756
+ $res = array("ok"=>0,"last"=>0);
1757
+ $canceled = false;
1758
+ $this->_current_row++;
1759
+ if ($this->_current_row % $rstep == 0)
1760
+ {
1761
+ $this->reportStats($this->_current_row, $tstart, $tdiff, $lastdbtime, $lastrec);
1762
+ }
1763
+ try
1764
+ {
1765
+ if (is_array($item) && count($item) > 0)
1766
+ {
1767
+ // import item
1768
+ $this->beginTransaction();
1769
+ $importedok = $this->importItem($item);
1770
+ if ($importedok)
1771
+ {
1772
+ $res["_product_id"] = $importedok;
1773
+ $res["ok"] = true;
1774
+ $this->commitTransaction();
1775
+ }
1776
+ else
1777
+ {
1778
+ $res["ok"] = false;
1779
+ $res["_exception_message"] = $item[ 'sku' ] . ': ' . $e->getMessage();
1780
+ $this->rollbackTransaction();
1781
+ }
1782
+ }
1783
+ else
1784
+ {
1785
+ $this->log("ERROR - RECORD #$this->_current_row - INVALID RECORD", "error");
1786
+ }
1787
+ // intermediary measurement
1788
+ }
1789
+ catch (Exception $e)
1790
+ {
1791
+ $this->rollbackTransaction();
1792
+ $res["ok"] = false;
1793
+ $this->logException($e, "ERROR ON RECORD #$this->_current_row");
1794
+ if ($e->getMessage() == "MAGMI_RUN_CANCELED")
1795
+ {
1796
+ $canceled = true;
1797
+ }
1798
+ }
1799
+ if ($this->isLastItem($item) || $canceled)
1800
+ {
1801
+ unset($item);
1802
+ $res["last"] = 1;
1803
+ }
1804
+
1805
+ unset($item);
1806
+ $this->updateSkuStats($res);
1807
+
1808
+ return $res;
1809
+ }
1810
+
1811
+ public function resetSkuStats()
1812
+ {
1813
+ $this->_skustats = array("nsku"=>0,"ok"=>0,"ko"=>0);
1814
+ }
1815
+
1816
+ public function engineRun($params, $forcebuiltin = array())
1817
+ {
1818
+ $this->log("Import Profile:$this->_profile", "startup");
1819
+ $this->log("Import Mode:$this->mode", "startup");
1820
+ $this->log("step:" . $this->getProp("GLOBAL", "step", 0.5) . "%", "step");
1821
+ $this->createPlugins($this->_profile, $params);
1822
+ $this->datasource = $this->getDataSource();
1823
+ $this->callPlugins("datasources,general", "beforeImport");
1824
+ $nitems = $this->lookup();
1825
+ Magmi_StateManager::setState("running");
1826
+ // if some rows found
1827
+ if ($nitems > 0)
1828
+ {
1829
+ // initializing product type early (in case of db update on startImport)
1830
+ $this->initProdType();
1831
+ $this->resetSkuStats();
1832
+ // intialize store id cache
1833
+ $this->callPlugins("datasources,itemprocessors", "startImport");
1834
+ // initializing item processors
1835
+ $cols = $this->datasource->getColumnNames();
1836
+ $this->log(count($cols), "columns");
1837
+ $this->callPlugins("itemprocessors", "processColumnList", $cols);
1838
+ if (count($cols) < 2)
1839
+ {
1840
+ $this->log("Invalid input data , not enough columns found,check datasource parameters", "error");
1841
+ $this->log("Import Ended", "end");
1842
+ Magmi_StateManager::setState("idle");
1843
+ return;
1844
+ }
1845
+ $this->log("Ajusted processed columns:" . count($cols), "startup");
1846
+ // initialize attribute infos & indexes from column names
1847
+ if ($this->mode != "update")
1848
+ {
1849
+ $this->checkRequired($cols);
1850
+ }
1851
+ $this->initAttrInfos(array_values($cols));
1852
+ // counter
1853
+ $this->_current_row = 0;
1854
+ // start time
1855
+ $tstart = microtime(true);
1856
+ // differential
1857
+ $tdiff = $tstart;
1858
+ // intermediary report step
1859
+ $this->initDbqStats();
1860
+ $pstep = $this->getProp("GLOBAL", "step", 0.5);
1861
+ $rstep = ceil(($nitems * $pstep) / 100);
1862
+ // read each line
1863
+ $lastrec = 0;
1864
+ $lastdbtime = 0;
1865
+ while (($item = $this->datasource->getNextRecord()) !== false)
1866
+ {
1867
+ $this->_timecounter->initTimingCats(array("line"));
1868
+ $res = $this->processDataSourceLine($item, $rstep, $tstart, $tdiff, $lastdbtime, $lastrec);
1869
+ // break on "forced" last
1870
+ if ($res["last"] == 1)
1871
+ {
1872
+ $this->log("last item encountered", "info");
1873
+ break;
1874
+ }
1875
+ }
1876
+ $this->callPlugins("datasources,general,itemprocessors", "endImport");
1877
+ $this->reportStats($this->_current_row, $tstart, $tdiff, $lastdbtime, $lastrec);
1878
+ $this->log("Skus imported OK:" . $this->_skustats["ok"] . "/" . $this->_skustats["nsku"], "info");
1879
+ if ($this->_skustats["ko"] > 0)
1880
+ {
1881
+ $this->log("Skus imported KO:" . $this->_skustats["ko"] . "/" . $this->_skustats["nsku"], "warning");
1882
+ }
1883
+ }
1884
+ else
1885
+ {
1886
+ $this->log("No Records returned by datasource", "warning");
1887
+ }
1888
+ $this->callPlugins("datasources,general,itemprocessors", "afterImport");
1889
+ $this->log("Import Ended", "end");
1890
+ Magmi_StateManager::setState("idle");
1891
+
1892
+ $timers = $this->_timecounter->getTimers();
1893
+ $f = fopen(Magmi_StateManager::getStateDir() . "/timings.txt", "w");
1894
+ foreach ($timers as $cat => $info)
1895
+ {
1896
+ $rep = "\nTIMING CATEGORY:$cat\n--------------------------------";
1897
+ foreach ($info as $phase => $pinfo)
1898
+ {
1899
+ $rep .= "\nPhase:$phase\n";
1900
+ foreach ($pinfo as $plugin => $data)
1901
+ {
1902
+ $rdur = round($data["dur"], 4);
1903
+ if ($rdur > 0)
1904
+ {
1905
+ $rep .= "- Class:$plugin :$rdur ";
1906
+ }
1907
+ }
1908
+ }
1909
+ fwrite($f, $rep);
1910
+ }
1911
+ fclose($f);
1912
+ }
1913
+
1914
+ public function onEngineException($e)
1915
+ {
1916
+ if (isset($this->datasource))
1917
+ {
1918
+ $this->datasource->onException($e);
1919
+ }
1920
+ $this->log("Import Ended", "end");
1921
+
1922
+ Magmi_StateManager::setState("idle");
1923
+ }
1924
+ }
app/code/community/Osf/Synnex/lib/magmi/engines/magmi_utilityengine.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * MAGENTO MASS IMPORTER CLASS
5
+ *
6
+ * version : 0.6
7
+ * author : S.BRACQUEMONT aka dweeves
8
+ * updated : 2010-10-09
9
+ *
10
+ */
11
+
12
+ /* use external file for db helper */
13
+ require_once ("magmi_engine.php");
14
+ require_once ("magmi_pluginhelper.php");
15
+
16
+ /* Magmi ProductImporter is now a Magmi_Engine instance */
17
+ class Magmi_UtilityEngine extends Magmi_Engine
18
+ {
19
+
20
+ /**
21
+ * constructor
22
+ *
23
+ * @param string $conffile
24
+ * : configuration .ini filename
25
+ */
26
+ public function __construct()
27
+ {
28
+ parent::__construct();
29
+ }
30
+
31
+ public function getEnabledPluginClasses($profile)
32
+ {
33
+ $clist = Magmi_PluginHelper::getInstance("main")->getPluginsInfo("utilities", "class");
34
+ return $clist;
35
+ }
36
+
37
+ public function getEngineInfo()
38
+ {
39
+ return array("name"=>"Magmi Utilities Engine","version"=>"1.0.1","author"=>"dweeves");
40
+ }
41
+
42
+ /**
43
+ * load properties
44
+ *
45
+ * @param string $conf
46
+ * : configuration .ini filename
47
+ */
48
+ public function getPluginFamilies()
49
+ {
50
+ return array("utilities");
51
+ }
52
+
53
+ public function engineInit($params)
54
+ {
55
+ $this->initPlugins(null);
56
+ }
57
+
58
+ public function engineRun($params)
59
+ {
60
+ $this->log("Magento Mass Importer by dweeves - version:" . Magmi_Version::$version, "title");
61
+ // initialize db connectivity
62
+ Magmi_StateManager::setState("running");
63
+ // force only one class to run
64
+ $this->_pluginclasses = array("utilities"=>array($params["pluginclass"]));
65
+
66
+ $this->createPlugins("__utilities__", $params);
67
+ foreach ($this->_activeplugins["utilities"] as $pinst)
68
+ {
69
+ try
70
+ {
71
+ $pinst->runUtility();
72
+ }
73
+ catch (Exception $e)
74
+ {
75
+ $this->logException($e);
76
+ }
77
+ }
78
+
79
+ Magmi_StateManager::setState("idle");
80
+ }
81
+
82
+ public function onEngineException($e)
83
+ {
84
+ Magmi_StateManager::setState("idle");
85
+ }
86
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/dbhelper.class.php ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *
4
+ * This class is a Database Operation Helper based on PDO library
5
+ * It provides shortcuts for common DB CRUD operations and some advanced templated requests operations
6
+ * @author dweeves
7
+ *
8
+ */
9
+ include_once ("timecounter.php");
10
+
11
+ class DBHelper
12
+ {
13
+ protected $_db;
14
+ protected $_debug;
15
+ protected $_laststmt;
16
+ protected $_use_stmt_cache = true;
17
+ protected $_nreq;
18
+ protected $_indbtime;
19
+ protected $_intrans = false;
20
+ protected $prepared = array();
21
+ protected $_timecounter = null;
22
+ protected $_tcats;
23
+
24
+ public function __construct()
25
+ {
26
+ $this->_timecounter = new TimeCounter(get_class($this));
27
+ $this->_tcats = "db";
28
+ $this->_timecounter->initTimingCats(array($this->_tcats));
29
+ $this->_timecounter->addCounter("requests");
30
+ }
31
+
32
+ /**
33
+ * Intializes database connection
34
+ *
35
+ * @param string $host
36
+ * : hostname
37
+ * @param string $dbname
38
+ * : database name
39
+ * @param string $user
40
+ * : username
41
+ * @param string $pass
42
+ * : password
43
+ * @param bool $debug
44
+ * : debug mode
45
+ */
46
+ public function initDb($host, $dbname, $user, $pass, $port = 3306, $socket = "/tmp/mysql.sock", $conntype = "net", $debug = false)
47
+ {
48
+ // intialize connection with PDO
49
+ // fix by Mr Lei for UTF8 special chars
50
+ if ($conntype == "net")
51
+ {
52
+ $pdostr = "mysql:host=$host;port=$port;dbname=$dbname;charset=utf8";
53
+ }
54
+ else
55
+ {
56
+ $pdostr = "mysql:unix_socket=$socket;dbname=$dbname;charset=utf8";
57
+ }
58
+
59
+ $this->_db = new PDO($pdostr, $user, $pass, array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8"));
60
+ // use exception error mode
61
+ $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
62
+ $this->_db->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL);
63
+ $this->_db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
64
+
65
+ // set database debug mode to trace if necessary
66
+ $this->_debug = $debug;
67
+ $this->prepared = array();
68
+ }
69
+
70
+ /**
71
+ * store output in some debug file
72
+ *
73
+ * @param unknown_type $data
74
+ */
75
+ public function logdebug($data)
76
+ {
77
+ if ($this->_debug)
78
+ {
79
+ $f = fopen($this->_debugfile, "a");
80
+ fwrite($f, microtime());
81
+ fwrite($f, $data);
82
+ fwrite($f, "\n");
83
+ fclose($f);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Sets or unsets the usage of internal prepared statement cache for reuse
89
+ *
90
+ * @param boolean $uc
91
+ * true:use cache,false:do not use cache
92
+ */
93
+ public function usestmtcache($uc)
94
+ {
95
+ $this->_use_stmt_cache = $uc;
96
+ }
97
+
98
+ /**
99
+ * releases database connection
100
+ */
101
+ public function exitDb()
102
+ {
103
+ // clear PDO resource
104
+ $this->_db = NULL;
105
+ }
106
+
107
+ /**
108
+ * Helper method to try to guess mysql socket based on some tricky phpinfo analysis
109
+ *
110
+ * @throws Exception if something got wrong during detection
111
+ */
112
+ public static function getMysqlSocket()
113
+ {
114
+ $mysqlsock = "";
115
+ $old_track = ini_set('track_errors', '1');
116
+ try
117
+ {
118
+ $mysqlsock = ini_get("mysql.default_socket");
119
+
120
+ if (isset($mysqlsock) && !@file_exists($mysqlsock))
121
+ {
122
+ if (error_get_last() !== null)
123
+ {
124
+ throw new Exception();
125
+ }
126
+ ob_start();
127
+ phpinfo();
128
+ $data = ob_get_contents();
129
+ ob_end_clean();
130
+ $cap = preg_match("/MYSQL_SOCKET.*?<td .*?>(.*?)<\/td>/msi", $data, $matches);
131
+ if ($cap)
132
+ {
133
+ $mysqlsock = $matches[1];
134
+ }
135
+ }
136
+ if (isset($mysqlsock) && !@file_exists($mysqlsock))
137
+ {
138
+ $mysqlsock = "";
139
+ }
140
+ }
141
+ catch (Exception $e)
142
+ {}
143
+ if (error_get_last() !== null)
144
+ {
145
+ $mysqlsock = false;
146
+ }
147
+ ini_set('track_errors', $old_track);
148
+ return $mysqlsock;
149
+ }
150
+
151
+ /**
152
+ * Initializes database requests stats counters
153
+ */
154
+ public function initDbqStats()
155
+ {
156
+ $this->_nreq = 0;
157
+ $this->_indbtime = 0;
158
+ }
159
+
160
+ /**
161
+ * Returns database statistics
162
+ *
163
+ * @param unknown_type $nbreq
164
+ */
165
+ public function collectDbqStats(&$nbreq)
166
+ {
167
+ return $this->_nreq;
168
+ }
169
+
170
+ /**
171
+ * cache sorting comparison method
172
+ *
173
+ * @param unknown_type $a
174
+ * @param unknown_type $b
175
+ */
176
+ public function cachesort($a, $b)
177
+ {
178
+ return $b[1] - $a[1];
179
+ }
180
+
181
+ /**
182
+ * Garbages statement cache if above 500 , removes less used statements
183
+ */
184
+ public function garbageStmtCache()
185
+ {
186
+ if (count($this->prepared) >= 500)
187
+ {
188
+ uasort($this->prepared, array($this,"cachesort"));
189
+ array_splice($this->prepared, 350, count($this->prepared));
190
+ }
191
+ }
192
+
193
+ /**
194
+ * executes an sql statement
195
+ *
196
+ * @param string $sql
197
+ * : sql statement (may include ? placeholders or named variables)
198
+ * @param array $params
199
+ * : parameters to replace placeholders (can be null)
200
+ * @param boolean $close
201
+ * : auto close cursor after statement execution (defaults to true)
202
+ * @return PDOStatement : statement for further processing if needed
203
+ */
204
+ public function exec_stmt($sql, $params = null, $close = true)
205
+ {
206
+ $this->_nreq++;
207
+ $this->_timecounter->initTime("indb", null, $this->_tcats);
208
+ $this->_timecounter->incCounter("requests");
209
+ $t0 = microtime(true);
210
+ if ($this->_use_stmt_cache && strpos($sql, "'") == false)
211
+ {
212
+ // if sql not in statement cache
213
+ if (!isset($this->prepared[$sql]))
214
+ {
215
+ $this->garbageStmtCache();
216
+ // create new prepared statement
217
+ $stmt = $this->_db->prepare($sql);
218
+ // cache prepare statement
219
+ $this->prepared[$sql] = array($stmt,1);
220
+ }
221
+ else
222
+ {
223
+ // get from statement cache
224
+ $this->prepared[$sql][1]++;
225
+ $stmt = $this->prepared[$sql][0];
226
+ }
227
+ }
228
+ else
229
+ {
230
+ // create new prepared statement
231
+ $stmt = $this->_db->prepare($sql);
232
+ }
233
+ $this->_laststmt = $stmt;
234
+ if ($params != null)
235
+ {
236
+ if (!$this->is_assoc($params))
237
+ {
238
+ $params = is_array($params) ? $params : array($params);
239
+ $stmt->execute($params);
240
+ }
241
+ else
242
+ {
243
+ foreach ($params as $pname => $pval)
244
+ {
245
+ if (count(explode(":", $pname)) == 1)
246
+ {
247
+ $val = strval($pval);
248
+ $stmt->bindValue(":$pname", $val);
249
+ }
250
+ }
251
+ $stmt->execute();
252
+ }
253
+ }
254
+ else
255
+ {
256
+
257
+ $stmt->execute();
258
+ }
259
+ if ($close)
260
+ {
261
+ $stmt->closeCursor();
262
+ }
263
+ $this->_timecounter->exitTime("indb", null, $this->_tcats);
264
+ $t1 = microtime(true);
265
+ $this->_indbtime += $t1 - $t0;
266
+ $this->logdebug("$sql\n" . print_r($params, true));
267
+ unset($params);
268
+ return $stmt;
269
+ }
270
+
271
+ /**
272
+ * Perform a delete statement, sql should be "DELETE"
273
+ *
274
+ * @param string $sql
275
+ * : DELETE statement sql (placeholders allowed)
276
+ * @param array $params
277
+ * : placeholder replacements (can be null)
278
+ */
279
+ public function delete($sql, $params = null)
280
+ {
281
+ $this->exec_stmt($sql, $params);
282
+ }
283
+
284
+ /**
285
+ * Performs an update statement
286
+ *
287
+ * @param string $sql
288
+ * UPDATE statement sql (placeholder allowed)
289
+ * @param array $params
290
+ * parameter values if placeholders in SQL
291
+ */
292
+ public function update($sql, $params = null)
293
+ {
294
+ $this->exec_stmt($sql, $params);
295
+ }
296
+
297
+ /**
298
+ * Perform an insert , sql should be "INSERT"
299
+ *
300
+ * @param string $sql
301
+ * :INSERT statement SQL (placeholders allowed)
302
+ * @param array $params
303
+ * : placeholder replacements (can be null)
304
+ * @return mixed : last inserted id
305
+ */
306
+ public function insert($sql, $params = null)
307
+ {
308
+ $this->exec_stmt($sql, $params);
309
+ $liid = $this->_db->lastInsertId();
310
+ return $liid;
311
+ }
312
+
313
+ /**
314
+ * Perform a select ,sql should be "SELECT"
315
+ *
316
+ * @param string $sql
317
+ * :SELECT statement SQL (placeholders allowed)
318
+ * @param array $params
319
+ * : placeholder replacements (can be null)
320
+ * @return PDOStatement : statement instance for further processing
321
+ */
322
+ public function select($sql, $params = null)
323
+ {
324
+ return $this->exec_stmt($sql, $params, false);
325
+ }
326
+
327
+ /**
328
+ * Selects one unique value from one single row
329
+ *
330
+ * @param $sql :
331
+ * SELECT statement SQL (placeholders allowed)
332
+ * @param $params :placeholder
333
+ * replacements (can be null)
334
+ * @param $col :
335
+ * column value to retrieve
336
+ * @return mixed : null if not result , wanted column value if match
337
+ */
338
+ public function selectone($sql, $params, $col)
339
+ {
340
+ $stmt = $this->select($sql, $params);
341
+ $this->_timecounter->initTime("indb", null, $this->_tcats);
342
+ $t0 = microtime(true);
343
+
344
+ $r = $stmt->fetch(PDO::FETCH_ASSOC);
345
+ $stmt->closeCursor();
346
+ $this->_timecounter->exitTime("indb", null, $this->_tcats);
347
+
348
+ $t1 = microtime(true);
349
+
350
+ $this->_indbtime += $t1 - $t0;
351
+ $v = (is_array($r) ? $r[$col] : null);
352
+ unset($r);
353
+ return $v;
354
+ }
355
+
356
+ /**
357
+ * Selects all values from a statement into a php array
358
+ *
359
+ * @param unknown_type $sql
360
+ * sql select to execute
361
+ * @param unknown_type $params
362
+ * placeholder replacements (can be null)
363
+ */
364
+ public function selectAll($sql, $params = null)
365
+ {
366
+ $stmt = $this->select($sql, $params);
367
+ $this->_timecounter->initTime("indb", null, $this->_tcats);
368
+
369
+ $t0 = microtime(true);
370
+
371
+ $r = $stmt->fetchAll(PDO::FETCH_ASSOC);
372
+ $stmt->closeCursor();
373
+ $this->_timecounter->exitTime("indb", null, $this->_tcats);
374
+
375
+ $t1 = microtime(true);
376
+ $this->_indbtime += $t1 - $t0;
377
+ return $r;
378
+ }
379
+
380
+ /**
381
+ * test if value exists (test should be compatible with unique select)
382
+ *
383
+ * @param $sql :
384
+ * SELECT statement SQL (placeholders allowed)
385
+ * @param $params :placeholder
386
+ * replacements (can be null)
387
+ * @param $col :
388
+ * column value to retrieve
389
+ * @return boolean : true if value found, false otherwise
390
+ */
391
+ public function testexists($sql, $params, $col)
392
+ {
393
+ return $this->selectone($sql, $params, $col) != null;
394
+ }
395
+
396
+ /**
397
+ * Quote array values in order to be used as parameters (handy if array used directly in explode in a IN condition)
398
+ *
399
+ * @param array $arr
400
+ * array of values to be quoted
401
+ */
402
+ public function quotearr($arr)
403
+ {
404
+ $arrout = array();
405
+ foreach ($arr as $v)
406
+ {
407
+ $arrout[] = $this->_db->quote($v);
408
+ }
409
+ return $arrout;
410
+ }
411
+
412
+ /**
413
+ * transforms an array in a comma separated list of enclosed column names for request
414
+ *
415
+ * @param array $arr
416
+ * list of names to enclose
417
+ */
418
+ public function arr2columns($arr)
419
+ {
420
+ $arrout = array();
421
+ foreach ($arr as $cname)
422
+ {
423
+ $arrout[] = "`" . $cname . "`";
424
+ }
425
+ $colstr = implode(",", $arrout);
426
+ unset($arrout);
427
+ return $colstr;
428
+ }
429
+
430
+ /**
431
+ * transform an array of values into equivalent comma separated list of unnamed placeholders.
432
+ *
433
+ * @param array $arr
434
+ */
435
+ public function arr2values($arr)
436
+ {
437
+ $str = substr(str_repeat("?,", count($arr)), 0, -1);
438
+ return $str;
439
+ }
440
+
441
+ /**
442
+ * transform a list of values into static select to use it as SQL static resultset
443
+ *
444
+ * @param array $arr
445
+ * list of values to use as SQL dataset
446
+ * @param string $cname
447
+ * sql column name to use to represent dataset
448
+ */
449
+ public function arr2select($arr, $cname = "id")
450
+ {
451
+ $rpt = str_repeat("? AS $cname UNION SELECT ", count($arr));
452
+ $subsel = substr($rpt, 0, -1 * strlen(" UNION SELECT "));
453
+ return "(SELECT $subsel)";
454
+ }
455
+
456
+ /**
457
+ * transform associative array into CASE sub statement
458
+ */
459
+ public function arr2case($arr, $casevar)
460
+ {
461
+ $sql = "(CASE ";
462
+ foreach ($arr as $k => $v)
463
+ {
464
+ $sql .= "WHEN $casevar='$k' THEN '$v'\n";
465
+ }
466
+ $sql .= "END)";
467
+ return $sql;
468
+ }
469
+
470
+ /**
471
+ * transform a associative array into a list of update prepared placeholders
472
+ *
473
+ * @param array $arr
474
+ * associative array to prepare for update , array keys used as column to update
475
+ */
476
+ public function arr2update($arr)
477
+ {
478
+ $arrout = array();
479
+ foreach ($arr as $k => $v)
480
+ {
481
+ $arrout[] = "$k=?";
482
+ }
483
+ $updstr = implode(",", $arrout);
484
+ unset($arrout);
485
+ return $updstr;
486
+ }
487
+
488
+ /**
489
+ * Filters a key value array over a list of keys , replacing __NULL__ magic value with true null
490
+ *
491
+ * @param unknown_type $kvarr
492
+ * @param unknown_type $keys
493
+ */
494
+ public function filterkvarr($kvarr, $keys)
495
+ {
496
+ $out = array();
497
+ foreach ($keys as $k)
498
+ {
499
+ $out[$k] = (isset($kvarr[$k]) && $kvarr[$k] != '__NULL__') ? $kvarr[$k] : null;
500
+ }
501
+ return $out;
502
+ }
503
+
504
+ /**
505
+ * begins a transaction
506
+ */
507
+ public function beginTransaction()
508
+ {
509
+ $this->_db->beginTransaction();
510
+ $this->_intrans = true;
511
+ $this->logdebug("-- TRANSACTION BEGIN --");
512
+ }
513
+
514
+ /**
515
+ * commits the current transaction
516
+ */
517
+ public function commitTransaction()
518
+ {
519
+ $this->_db->commit();
520
+ $this->_intrans = false;
521
+ $this->logdebug("-- TRANSACTION COMMIT --");
522
+ }
523
+
524
+ /**
525
+ * rollback the current transaction
526
+ */
527
+ public function rollbackTransaction()
528
+ {
529
+ if ($this->_intrans)
530
+ {
531
+ $this->_db->rollBack();
532
+ $this->_intrans = false;
533
+ $this->logdebug("-- TRANSACTION ROLLBACK --");
534
+ }
535
+ }
536
+
537
+ /**
538
+ * Sets debug management
539
+ *
540
+ * @param bool $debug
541
+ * debug flag
542
+ * @param string $debugfname
543
+ * debug file name to use
544
+ */
545
+ public function setDebug($debug, $debugfname)
546
+ {
547
+ $this->_debug = $debug;
548
+ $this->_debugfile = $debugfname;
549
+ }
550
+
551
+ /**
552
+ * Replaces named params in a descriptive parameterized request
553
+ * Descriptive parameterized request have parameters defined as
554
+ * [namespace:name/label/default value] , this parameters may represent table names or any request parameter
555
+ * namespace is optional, as label & default value
556
+ * - NameSpaces:
557
+ * tn : tablename, this namespace ensures replacement of given name with defined DB prefix so, parameterized request can use generic names to define their ops
558
+ *
559
+ * @param unknown_type $stmt
560
+ * @param unknown_type $rparams
561
+ */
562
+ public function replaceParams(&$stmt, &$rparams)
563
+ {
564
+ $params = array();
565
+ $hasp = preg_match_all('|\[\[(.*?)\]\]|msi', $stmt, $matches);
566
+ if ($hasp)
567
+ {
568
+ $pdefs = $matches[0];
569
+ $params = $matches[1];
570
+ }
571
+ $cparams = count($params);
572
+ for ($i = 0; $i < $cparams; $i++)
573
+ {
574
+ $param = $params[$i];
575
+ $pdef = $pdefs[$i];
576
+ $pinfo = explode("/", $param);
577
+ $pname = $pinfo[0];
578
+ $epar = explode(":", $pname);
579
+ if (count($epar) > 1)
580
+ {
581
+ $stmt = str_replace($pdef, $rparams[$pname], $stmt);
582
+ }
583
+ else
584
+ {
585
+ $stmt = str_replace($pdef, ":$pname", $stmt);
586
+ }
587
+ }
588
+ for ($i = 0; $i < $cparams; $i++)
589
+ {
590
+ $param = $params[$i];
591
+ $pinfo = explode("/", $param);
592
+ $pname = $pinfo[0];
593
+ $epar = explode(":", $pname);
594
+ if (count($epar) > 1)
595
+ {
596
+ unset($rparams[$pname]);
597
+ }
598
+ }
599
+ }
600
+
601
+ /**
602
+ * Checks wether an array is associative
603
+ *
604
+ * @param mixed $var
605
+ * array or variable to test
606
+ */
607
+ public function is_assoc($var)
608
+ {
609
+ return is_array($var) && array_keys($var) !== range(0, sizeof($var) - 1);
610
+ }
611
+
612
+ /**
613
+ * This method handled mutiple statements in a single SQL (PDO cannot do it by itself)
614
+ * Statements have to be separed by ; & line return.
615
+ *
616
+ * @param string $sql
617
+ * multiple statements
618
+ * @param array $params
619
+ * values to use for parameter placeholder, in case of named parameters,array has to have array keys aligned with parameter names
620
+ */
621
+ public function multipleParamRequests($sql, $params, $return = false)
622
+ {
623
+ // ensure windows/mac compatibility for user made requests
624
+ $sql = str_replace("\r\n", "\n", $sql);
625
+ $sqllines = explode("--", $sql);
626
+ foreach ($sqllines as $sqlline)
627
+ {
628
+ if ($sqlline != "")
629
+ {
630
+ $subs = explode(";\n", "--" . $sqlline);
631
+ foreach ($subs as $sub)
632
+ {
633
+
634
+ if (trim($sub) != "" && substr($sub, 0, 2) != "--")
635
+ {
636
+ $stmts[] = $sub;
637
+ }
638
+ }
639
+ }
640
+ }
641
+ $results = array();
642
+ foreach ($stmts as $stmt)
643
+ {
644
+ $zparams = $params;
645
+ $this->replaceParams($stmt, $zparams);
646
+ if ($return)
647
+ {
648
+ if (substr(trim($stmt), 0, 6) == "SELECT")
649
+ {
650
+ $results[$stmt] = $this->selectAll($stmt, $zparams);
651
+ continue;
652
+ }
653
+ }
654
+ $this->exec_stmt($stmt, $zparams);
655
+ }
656
+ if ($return)
657
+ {
658
+ return $results;
659
+ }
660
+ }
661
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/fshelper.php ADDED
@@ -0,0 +1,478 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once ('remotefilegetter.php');
3
+
4
+ /**
5
+ * Class FSHelper
6
+ *
7
+ * File System Helper
8
+ * Gives several utility methods for filesystem testing
9
+ *
10
+ * @author dweeves
11
+ *
12
+ */
13
+ class FSHelper
14
+ {
15
+
16
+ /**
17
+ * Checks if a directory has write rights
18
+ *
19
+ * @param string $dir
20
+ * directory to test
21
+ * @return boolean wether directory is writable
22
+ */
23
+ public static function isDirWritable($dir)
24
+ {
25
+ // try to create a new file
26
+ $test = @fopen("$dir/__testwr__", "w");
27
+ if ($test == false)
28
+ {
29
+ return false;
30
+ }
31
+ else
32
+ {
33
+ // if succeeded, remove test file
34
+ fclose($test);
35
+ unlink("$dir/__testwr__");
36
+ }
37
+ return true;
38
+ }
39
+
40
+ /**
41
+ * Tries to find a suitable way to execute processes
42
+ *
43
+ * @return string NULL method to execute process
44
+ */
45
+ public static function getExecMode()
46
+ {
47
+ $is_disabled = array();
48
+ // Check for php disabled functions
49
+ $disabled = explode(',', ini_get('disable_functions'));
50
+ foreach ($disabled as $disableFunction)
51
+ {
52
+ $is_disabled[] = trim($disableFunction);
53
+ }
54
+ // try the following if not disabled,return first non disabled
55
+ foreach (array("popen","shell_exec") as $func)
56
+ {
57
+ if (!in_array($func, $is_disabled))
58
+ {
59
+ return $func;
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Factory for magento directory handle
68
+ *
69
+ * @author dweeves
70
+ *
71
+ */
72
+ class MagentoDirHandlerFactory
73
+ {
74
+ protected $_handlers = array();
75
+ protected static $_instance;
76
+
77
+ public function __construct()
78
+ {}
79
+
80
+ /**
81
+ * Singleton getInstance method
82
+ *
83
+ * @return MagentoDirHandlerFactory
84
+ */
85
+ public static function getInstance()
86
+ {
87
+ if (!isset(self::$_instance))
88
+ {
89
+ self::$_instance = new MagentoDirHandlerFactory();
90
+ }
91
+ return self::$_instance;
92
+ }
93
+
94
+ /**
95
+ * Registers a new object to handle magento directory
96
+ *
97
+ * @param unknown $obj
98
+ */
99
+ public function registerHandler($obj)
100
+ {
101
+ $cls = get_class($obj);
102
+ if (!isset($this->_handlers[$cls]))
103
+ {
104
+ $this->_handlers[$cls] = $obj;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Return a handler for a given url
110
+ *
111
+ * @param unknown $url
112
+ * @return unknown
113
+ */
114
+ public function getHandler($url)
115
+ {
116
+ // Iterates on declared handlers , return first matching url
117
+ foreach ($this->_handlers as $cls => $handler)
118
+ {
119
+ if ($handler->canHandle($url))
120
+ {
121
+ return $handler;
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Magento Directory Handler
129
+ *
130
+ * Provides methods for filesystem operations & command execution
131
+ * Mother abstract class to be derived either for local operation or remote (for performing operations on remote systems)
132
+ *
133
+ * @author dweeves
134
+ *
135
+ */
136
+ abstract class MagentoDirHandler
137
+ {
138
+ protected $_magdir;
139
+ protected $_lasterror;
140
+ protected $_exec_mode;
141
+
142
+ /**
143
+ * Constructor from a magento directory url
144
+ *
145
+ * @param unknown $magurl
146
+ * magento base directory url
147
+ */
148
+ public function __construct($magurl)
149
+ {
150
+ $this->_magdir = $magurl;
151
+ $this->_lasterror = array();
152
+ $this->_exec_mode = FSHelper::getExecMode();
153
+ }
154
+
155
+ /**
156
+ * Returns magento directory
157
+ *
158
+ * @return string
159
+ */
160
+ public function getMagentoDir()
161
+ {
162
+ return $this->_magdir;
163
+ }
164
+
165
+ /**
166
+ * Returns available execution mode
167
+ *
168
+ * @return Ambigous <string, NULL>
169
+ */
170
+ public function getexecmode()
171
+ {
172
+ return $this->_exec_mode;
173
+ }
174
+
175
+ /**
176
+ * Wether current handler is compatible with given url
177
+ *
178
+ * @param unknown $url
179
+ */
180
+ public abstract function canhandle($url);
181
+
182
+ /**
183
+ * File exists
184
+ *
185
+ * @param unknown $filepath
186
+ */
187
+ public abstract function file_exists($filepath);
188
+
189
+ /**
190
+ * Mkdir
191
+ *
192
+ * @param unknown $path
193
+ * @param string $mask
194
+ * @param string $rec
195
+ */
196
+ public abstract function mkdir($path, $mask = null, $rec = false);
197
+
198
+ /**
199
+ * File Copy
200
+ *
201
+ * @param unknown $srcpath
202
+ * @param unknown $destpath
203
+ */
204
+ public abstract function copy($srcpath, $destpath);
205
+
206
+ /**
207
+ * File Deletion
208
+ *
209
+ * @param unknown $path
210
+ */
211
+ public abstract function unlink($path);
212
+
213
+ /**
214
+ * Chmod
215
+ *
216
+ * @param unknown $path
217
+ * @param unknown $mask
218
+ */
219
+ public abstract function chmod($path, $mask);
220
+
221
+ /**
222
+ * Check if we can execute processes
223
+ *
224
+ * @return boolean
225
+ */
226
+ public function isExecEnabled()
227
+ {
228
+ return $this->_exec_mode != null;
229
+ }
230
+
231
+ /**
232
+ * Executes a process
233
+ *
234
+ * @param unknown $cmd
235
+ * @param unknown $params
236
+ * @param string $workingdir
237
+ */
238
+ public abstract function exec_cmd($cmd, $params, $workingdir = null);
239
+ }
240
+
241
+ /**
242
+ * Local Magento Dir Handler.
243
+ *
244
+ * Handle Magento related filesystem operations for a given local directory
245
+ *
246
+ * @author dweeves
247
+ *
248
+ */
249
+ class LocalMagentoDirHandler extends MagentoDirHandler
250
+ {
251
+ protected $_rfgid;
252
+
253
+ /**
254
+ * Constructor
255
+ *
256
+ * @param unknown $magdir
257
+ */
258
+ public function __construct($magdir)
259
+ {
260
+ parent::__construct($magdir);
261
+ // Registers itself in the factory
262
+ MagentoDirHandlerFactory::getInstance()->registerHandler($this);
263
+ $this->_rfgid = "default";
264
+ }
265
+
266
+ /**
267
+ * Can Handle any non remote urls
268
+ *
269
+ * @param unknown $url
270
+ * @return boolean
271
+ */
272
+ public function canHandle($url)
273
+ {
274
+ return (preg_match("|^.*?://.*$|", $url) == false);
275
+ }
276
+
277
+ /**
278
+ * Cleans a bit input filename, ensures filename will be located under magento directory if not already
279
+ *
280
+ * @see MagentoDirHandler::file_exists()
281
+ */
282
+ public function file_exists($filename)
283
+ {
284
+ $mp = str_replace("//", "/", $this->_magdir . "/" . str_replace($this->_magdir, '', $filename));
285
+
286
+ return file_exists($mp);
287
+ }
288
+
289
+ /**
290
+ * Specific, set remote operation credentials for local file download
291
+ */
292
+ public function setRemoteCredentials($user, $passwd)
293
+ {
294
+ $fginst = RemoteFileGetterFactory::getFGInstance($this->_rfgid);
295
+ $fginst->setCredentials($user, $passwd);
296
+ }
297
+
298
+ /**
299
+ * Handles a remote file getter id
300
+ *
301
+ * @param unknown $rfgid
302
+ */
303
+ public function setRemoteGetterId($rfgid)
304
+ {
305
+ $this->_rfgid = $rfgid;
306
+ }
307
+
308
+ /**
309
+ * ensures dirname will be located under magento directory if not already
310
+ *
311
+ * @see MagentoDirHandler::mkdir()
312
+ */
313
+ public function mkdir($path, $mask = null, $rec = false)
314
+ {
315
+ $mp = str_replace("//", "/", $this->_magdir . "/" . str_replace($this->_magdir, '', $path));
316
+
317
+ if ($mask == null)
318
+ {
319
+ $mask = octdec('755');
320
+ }
321
+ $ok = @mkdir($mp, $mask, $rec);
322
+ if (!$ok)
323
+ {
324
+ $this->_lasterror = error_get_last();
325
+ }
326
+ return $ok;
327
+ }
328
+
329
+ /**
330
+ * ensures path will be located under magento directory if not already
331
+ *
332
+ * @see MagentoDirHandler::chmod()
333
+ */
334
+ public function chmod($path, $mask)
335
+ {
336
+ $mp = str_replace("//", "/", $this->_magdir . "/" . str_replace($this->_magdir, '', $path));
337
+
338
+ if ($mask == null)
339
+ {
340
+ $mask = octdec('755');
341
+ }
342
+ $ok = @chmod($mp, $mask);
343
+ if (!$ok)
344
+ {
345
+ $this->_lasterror = error_get_last();
346
+ }
347
+ return $ok;
348
+ }
349
+
350
+ /**
351
+ * Returns last error
352
+ *
353
+ * @return Ambigous <multitype:, multitype:string multitype: >
354
+ */
355
+ public function getLastError()
356
+ {
357
+ return $this->_lasterror;
358
+ }
359
+
360
+ /**
361
+ * ensures filename will be located under magento directory if not already
362
+ *
363
+ * @see MagentoDirHandler::unlink()
364
+ */
365
+ public function unlink($path)
366
+ {
367
+ $mp = str_replace("//", "/", $this->_magdir . "/" . str_replace($this->_magdir, '', $path));
368
+ return @unlink($mp);
369
+ }
370
+
371
+ /**
372
+ * Download a file into local filesystem
373
+ * ensures local filename will be located under magento directory if not already
374
+ *
375
+ * @param unknown $remoteurl
376
+ * @param unknown $destpath
377
+ * @return unknown
378
+ */
379
+ public function copyFromRemote($remoteurl, $destpath)
380
+ {
381
+ $rfg = RemoteFileGetterFactory::getFGInstance($this->_rfgid);
382
+ $mp = str_replace("//", "/", $this->_magdir . "/" . str_replace($this->_magdir, '', $destpath));
383
+ $ok = $rfg->copyRemoteFile($remoteurl, $mp);
384
+ if (!$ok)
385
+ {
386
+ $this->_lasterror = $rfg->getErrors();
387
+ }
388
+ return $ok;
389
+ }
390
+
391
+ /**
392
+ * ensures filename will be located under magento directory if not already
393
+ *
394
+ * @see MagentoDirHandler::copy()
395
+ */
396
+ public function copy($srcpath, $destpath)
397
+ {
398
+ $result = false;
399
+ $destpath = str_replace("//", "/", $this->_magdir . "/" . str_replace($this->_magdir, '', $destpath));
400
+ if (preg_match('|^.*?://.*$|', $srcpath))
401
+ {
402
+ $result = $this->copyFromRemote($srcpath, $destpath);
403
+ }
404
+ else
405
+ {
406
+
407
+ $result = @copy($srcpath, $destpath);
408
+ if (!$result)
409
+ {
410
+ $this->_lasterror = error_get_last();
411
+ }
412
+ }
413
+ return $result;
414
+ }
415
+
416
+ /**
417
+ * execute command, performs some execution directory check
418
+ * uses available command execution method
419
+ *
420
+ * @see MagentoDirHandler::exec_cmd()
421
+ */
422
+ public function exec_cmd($cmd, $params, $working_dir = null)
423
+ {
424
+ $full_cmd = $cmd . " " . $params;
425
+ $curdir = false;
426
+ $precmd = "";
427
+ // If a working directory has been specified, switch to it
428
+ // before running the requested command
429
+ if (!empty($working_dir))
430
+ {
431
+ $curdir = getcwd();
432
+ $wdir = realpath($working_dir);
433
+ // get current directory
434
+ if ($curdir != $wdir && $wdir !== false)
435
+ {
436
+ // trying to change using chdir
437
+ if (!@chdir($wdir))
438
+ {
439
+ // if no success, use cd from shell
440
+ $precmd = "cd $wdir && ";
441
+ }
442
+ }
443
+ }
444
+ $full_cmd = $precmd . $full_cmd;
445
+ // Handle Execution
446
+ $emode = $this->getexecmode();
447
+ switch ($emode)
448
+ {
449
+ case "popen":
450
+ $x = popen($full_cmd, "r");
451
+ $out = "";
452
+ while (!feof($x))
453
+ {
454
+ $data = fread($x, 1024);
455
+ $out .= $data;
456
+ usleep(100000);
457
+ }
458
+ fclose($x);
459
+ break;
460
+ case "shell_exec":
461
+ $out = shell_exec($full_cmd);
462
+ break;
463
+ }
464
+
465
+ // restore old directory if changed
466
+ if ($curdir)
467
+ {
468
+ @chdir($curdir);
469
+ }
470
+
471
+ if ($out == null)
472
+ {
473
+ $this->_lasterror = array("type"=>" execution error","message"=>error_get_last());
474
+ return false;
475
+ }
476
+ return $out;
477
+ }
478
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/license.txt ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (C) 2012 by Dweeves (S.BRACQUEMONT)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
app/code/community/Osf/Synnex/lib/magmi/inc/magmi_config.php ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once ("properties.php");
3
+
4
+ /**
5
+ * Directory based configuration object
6
+ * Uses a property file
7
+ *
8
+ * @author dweeves
9
+ *
10
+ */
11
+ class DirbasedConfig extends Properties
12
+ {
13
+ protected $_basedir = null;
14
+ protected $_confname = null;
15
+
16
+ public function __construct($basedir, $confname)
17
+ {
18
+ $this->_basedir = $basedir;
19
+ $this->_confname = $basedir . DIRSEP . $confname;
20
+ }
21
+
22
+ public function get($secname, $pname, $default = null)
23
+ {
24
+ if (!isset($this->_props))
25
+ {
26
+ $this->load();
27
+ }
28
+ return parent::get($secname, $pname, $default);
29
+ }
30
+
31
+ public function getConfFile()
32
+ {
33
+ return $this->_confname;
34
+ }
35
+
36
+ public function getLastSaved($fmt)
37
+ {
38
+ return strftime($fmt, filemtime($this->_confname));
39
+ }
40
+
41
+ public function load($name = null)
42
+ {
43
+ if ($name == null)
44
+ {
45
+ $name = $this->_confname;
46
+ }
47
+
48
+ if (!file_exists($name))
49
+ {
50
+ $this->save();
51
+ }
52
+ parent::load($name);
53
+ }
54
+
55
+ public function save($arr = null)
56
+ {
57
+ if ($arr != null)
58
+ {
59
+ $this->setPropsFromFlatArray($arr);
60
+ }
61
+ return parent::save($this->_confname);
62
+ }
63
+
64
+ public function saveTo($arr, $newdir)
65
+ {
66
+ if (!file_exists($newdir))
67
+ {
68
+ mkdir($newdir, Magmi_Config::getInstance()->getDirMask());
69
+ }
70
+ $val = parent::save($newdir . DIRSEP . basename($this->_confname));
71
+ $this->_basedir = $newdir;
72
+ $this->_confname = $newdir . DIRSEP . basename($this->_confname);
73
+ return $val;
74
+ }
75
+
76
+ public function getConfDir()
77
+ {
78
+ return $this->_basedir;
79
+ }
80
+ }
81
+
82
+ class ProfileBasedConfig extends DirbasedConfig
83
+ {
84
+ private static $_script = __FILE__;
85
+ protected $_profile = null;
86
+
87
+ public function getProfileDir()
88
+ {
89
+ $subdir = ($this->_profile == "default" ? "" : DIRSEP . $this->_profile);
90
+ $confdir = dirname(dirname(__FILE__)) . DIRSEP . "conf$subdir";
91
+ if (!file_exists($confdir))
92
+ {
93
+ @mkdir($confdir, Magmi_Config::getInstance()->getDirMask());
94
+ }
95
+ return realpath($confdir);
96
+ }
97
+
98
+ public function __construct($fname, $profile = null)
99
+ {
100
+ $this->_profile = $profile;
101
+ parent::__construct($this->getProfileDir(), $fname);
102
+ }
103
+
104
+ public function getProfile()
105
+ {
106
+ return $this->_profile;
107
+ }
108
+ }
109
+
110
+ class Magmi_Config extends DirbasedConfig
111
+ {
112
+ private static $_instance = null;
113
+ private $_defaultconfigname = null;
114
+ public static $conffile = null;
115
+
116
+ public function getConfDir()
117
+ {
118
+ $confdir = realpath(dirname(dirname(__FILE__)) . DIRSEP . "conf");
119
+ return $confdir;
120
+ }
121
+
122
+ public function __construct()
123
+ {
124
+ parent::__construct($this->getConfDir(), "magmi.ini");
125
+ }
126
+
127
+ public function getDirMask()
128
+ {
129
+ return octdec($this->get("GLOBAL", "dirmask", "755"));
130
+ }
131
+
132
+ public function getFileMask()
133
+ {
134
+ return octdec($this->get("GLOBAL", "filemask", "644"));
135
+ }
136
+
137
+ public function getMagentoDir()
138
+ {
139
+ $bd = $this->get("MAGENTO", "basedir");
140
+ $dp = $bd[0] == "." ? dirname(__FILE__) . "/" . $bd : $bd;
141
+ return realpath($dp);
142
+ }
143
+
144
+ public static function getInstance()
145
+ {
146
+ if (self::$_instance == null)
147
+ {
148
+ self::$_instance = new Magmi_Config();
149
+ }
150
+ return self::$_instance;
151
+ }
152
+
153
+ public function isDefault()
154
+ {
155
+ return !file_exists($this->_confname);
156
+ }
157
+
158
+ public function load($name = null)
159
+ {
160
+ $conf = (!$this->isDefault()) ? $this->_confname : $this->_confname . ".default";
161
+ parent::load($conf);
162
+ $alt = false;
163
+ if ($this->hasSection('USE_ALTERNATE'))
164
+ {
165
+ $this->_confname = $this->get("USE_ALTERNATE", "file");
166
+ $alt = true;
167
+ }
168
+ parent::load($this->_confname);
169
+ if ($alt)
170
+ {
171
+ $this->set("USE_ALTERNATE", "file", $this->_confname);
172
+ }
173
+ // Migration from 0.6.17
174
+ if ($this->hasSection("PLUGINS_DATASOURCES"))
175
+ {
176
+ $pluginsconf = new DirbasedConfig($this->getConfDir(), "plugins.conf");
177
+ $arr = array("PLUGINS_DATASOURCES"=>$this->getSection("PLUGINS_DATASOURCES"),
178
+ "PLUGINS_GENERAL"=>$this->getSection("PLUGINS_GENERAL"),
179
+ "PLUGINS_ITEMPROCESSORS"=>$this->getSection("PLUGINS_ITEMPROCESSORS"));
180
+ $pluginsconf->setProps($arr);
181
+ $pluginsconf->save();
182
+ $this->removeSection("PLUGINS_DATASOURCES");
183
+ $this->removeSection("PLUGINS_GENERAL");
184
+ $this->removeSection("PLUGINS_ITEMPROCESSORS");
185
+ $this->save();
186
+ }
187
+ // Migration step (to percent) , 0.7beta4
188
+ if ($this->get("GLOBAL", "step", 0) == 0 || floatval($this->get("GLOBAL", "step", 0.5)) > 20)
189
+ {
190
+ $this->set("GLOBAL", "step", 0.5);
191
+ $this->save();
192
+ }
193
+ return $this;
194
+ }
195
+
196
+ public function save($arr = null)
197
+ {
198
+ if (isset($arr["USE_ALTERNATE:file"]))
199
+ {
200
+ $this->_confname = $arr["USE_ALTERNATE:file"];
201
+ unset($arr["USE_ALTERNATE:file"]);
202
+ }
203
+ if ($arr !== null)
204
+ {
205
+ foreach ($arr as $k => $v)
206
+ {
207
+ if (!preg_match("/\w+:\w+/", $k))
208
+ {
209
+ unset($arr[$k]);
210
+ }
211
+ }
212
+ }
213
+ return parent::save($arr);
214
+ }
215
+
216
+ public function getProfileList()
217
+ {
218
+ $proflist = array();
219
+ $candidates = scandir($this->getConfDir());
220
+ foreach ($candidates as $candidate)
221
+ {
222
+ if (is_dir($this->getConfDir() . DIRSEP . $candidate) && $candidate[0] != "." &&
223
+ substr($candidate, 0, 2) != "__")
224
+ {
225
+ $proflist[] = $candidate;
226
+ }
227
+ }
228
+ return $proflist;
229
+ }
230
+ }
231
+
232
+ class EnabledPlugins_Config extends ProfileBasedConfig
233
+ {
234
+
235
+ public function __construct($profile = "default")
236
+ {
237
+ parent::__construct("plugins.conf", $profile);
238
+ }
239
+
240
+ public function getEnabledPluginFamilies($typelist)
241
+ {
242
+ $btlist = array();
243
+ if (!is_array($typelist))
244
+ {
245
+ $typelist = explode(",", $typelist);
246
+ }
247
+ foreach ($typelist as $pfamily)
248
+ {
249
+ $btlist[$pfamily] = $this->getEnabledPluginClasses($pfamily);
250
+ }
251
+ return $btlist;
252
+ }
253
+
254
+ public function getEnabledPluginClasses($type)
255
+ {
256
+ $type = strtoupper($type);
257
+ $cslist = $this->get("PLUGINS_$type", "classes");
258
+ if ($cslist == null)
259
+ {
260
+ $cslist = $this->get("PLUGINS_$type", "class");
261
+ $epc = ($cslist == null ? array() : array($cslist));
262
+ }
263
+ else
264
+ {
265
+ $epc = ($cslist == "" ? array() : explode(",", $cslist));
266
+ }
267
+ return $epc;
268
+ }
269
+
270
+ public function isPluginEnabled($type, $pclass)
271
+ {
272
+ return in_array($pclass, $this->getEnabledPluginClasses($type));
273
+ }
274
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/magmi_defs.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ define("MAGMI_BASEDIR", dirname(dirname(__FILE__)));
3
+ define("MAGMI_INCDIR", MAGMI_BASEDIR . "/inc");
4
+ define("MAGMI_INTEGRATION_INCDIR", MAGMI_BASEDIR . "/integration/inc");
5
+ define("MAGMI_ENGINE_DIR", MAGMI_BASEDIR . "/engines");
6
+ set_include_path(
7
+ ini_get("include_path") . PATH_SEPARATOR . MAGMI_INCDIR . PATH_SEPARATOR . MAGMI_INTEGRATION_INCDIR . PATH_SEPARATOR .
8
+ MAGMI_ENGINE_DIR);
9
+ $dtz = date_default_timezone_get();
10
+ if ($dtz == "")
11
+ {
12
+ date_default_timezone_set("UTC");
13
+ }
14
+ require_once ('magmi_loggers.php');
app/code/community/Osf/Synnex/lib/magmi/inc/magmi_engine.php ADDED
@@ -0,0 +1,559 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once ("dbhelper.class.php");
3
+ require_once ("magmi_config.php");
4
+ require_once ("magmi_version.php");
5
+ require_once ("magmi_utils.php");
6
+ require_once ("magmi_statemanager.php");
7
+ require_once ("magmi_pluginhelper.php");
8
+
9
+ /**
10
+ * This class is the mother class for magmi engines
11
+ * A magmi engine is a class that performs operations on DB
12
+ *
13
+ * @author dweeves
14
+ *
15
+ */
16
+ abstract class Magmi_Engine extends DbHelper
17
+ {
18
+ protected $_conf;
19
+ protected $_initialized = false;
20
+ protected $_exceptions = array();
21
+ public $tprefix;
22
+ protected $_connected;
23
+ protected $_activeplugins;
24
+ protected $_pluginclasses;
25
+ protected $_builtinplugins = array();
26
+ protected $_ploop_callbacks = array();
27
+ private $_excid = 0;
28
+ public $logger = null;
29
+ protected $_timingcats = array();
30
+
31
+ /**
32
+ * Engine Metadata Table access
33
+ */
34
+ public function getEngineInfo()
35
+ {
36
+ return array("name"=>"Generic Magmi Engine","version"=>"1.1","author"=>"dweeves");
37
+ }
38
+
39
+ /**
40
+ * Constructor
41
+ */
42
+ public function __construct()
43
+ {
44
+ parent::__construct();
45
+ // force PHP internal encoding as UTF 8
46
+ mb_internal_encoding("UTF-8");
47
+ }
48
+
49
+ /**
50
+ * Engine initialization @param params : key/value array of initialization parameters
51
+ */
52
+ public final function initialize($params = array())
53
+ {
54
+ try
55
+ {
56
+ // Retrieving master config file
57
+ $this->_conf = Magmi_Config::getInstance();
58
+ $this->_conf->load();
59
+ // Intializing members
60
+ $this->tprefix = $this->_conf->get("DATABASE", "table_prefix");
61
+ $this->_excid = 0;
62
+ $this->_initialized = true;
63
+ $this->_exceptions = array();
64
+ }
65
+ catch (Exception $e)
66
+ {
67
+ die("Error initializing Engine:{$this->_conf->getConfigFilename()} \n" . $e->getMessage());
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Returns magento directory
73
+ */
74
+ public function getMagentoDir()
75
+ {
76
+ return $this->_conf->getMagentoDir();
77
+ }
78
+
79
+ /**
80
+ * returns magento version
81
+ */
82
+ public function getMagentoVersion()
83
+ {
84
+ return $this->_conf->get("MAGENTO", "version");
85
+ }
86
+
87
+ /**
88
+ * Plugin loop callback registration
89
+ */
90
+ protected function _registerPluginLoopCallback($cbtype, $cb)
91
+ {
92
+ $this->_ploop_callbacks[$cbtype] = $cb;
93
+ }
94
+
95
+ /**
96
+ * Plugin loop callback deregistration
97
+ */
98
+ protected function _unregisterPluginLoopCallback($cbtype)
99
+ {
100
+ unset($this->_ploop_callbacks[$cbtype]);
101
+ }
102
+
103
+ /**
104
+ * Generic implementation of plugin families, empty for this mother class
105
+ */
106
+ public function getPluginFamilies()
107
+ {
108
+ return array();
109
+ }
110
+
111
+ /**
112
+ * return the list of enabled plugin classes for a given profile @param $profile : profile name to check
113
+ */
114
+ public function getEnabledPluginClasses($profile)
115
+ {
116
+ $enabledplugins = new EnabledPlugins_Config($profile);
117
+ $enabledplugins->load();
118
+ return $enabledplugins->getEnabledPluginFamilies($this->getPluginFamilies());
119
+ }
120
+
121
+ /**
122
+ * initializes Plugin instances for a given profile @param $profile : profile to initialize plugins for , defaults to null (Default Profile)
123
+ */
124
+ public function initPlugins($profile = null)
125
+ {
126
+ // reset _active plugins in case of Engine reuse
127
+ $this->_activeplugins = array();
128
+ $this->_pluginclasses = $this->getEnabledPluginClasses($profile);
129
+ }
130
+
131
+ /**
132
+ * Returns a list of class names for "Builtin" plugins
133
+ */
134
+ public function getBuiltinPluginClasses()
135
+ {
136
+ $bplarr = array();
137
+
138
+ foreach ($this->_builtinplugins as $pfamily => $pdef)
139
+ {
140
+ $plinfo = explode("::", $pdef);
141
+ $pfile = $plinfo[0];
142
+ $pclass = $plinfo[1];
143
+ require_once ($pfile);
144
+ if (!isset($bplarr[$pfamily]))
145
+ {
146
+ $bplarr[$pfamily] = array();
147
+ }
148
+ $bplarr[$pfamily][] = $pclass;
149
+ }
150
+ return $bplarr;
151
+ }
152
+
153
+ /**
154
+ * Return the list of enabled plugin classes
155
+ */
156
+ public function getPluginClasses()
157
+ {
158
+ return $this->_pluginclasses;
159
+ }
160
+
161
+ /*
162
+ * Return the list of active plugin instances for a given plugin family @param $family : plugin family to get instances from, defaults to null (all plugins)
163
+ */
164
+ public function getPluginInstances($family = null)
165
+ {
166
+ $pil = null;
167
+ // if no family set, return all active plugins
168
+ if ($family == null)
169
+ {
170
+ $pil = $this->_activeplugins();
171
+ }
172
+ else
173
+ // filter active plugins by family
174
+ {
175
+ $pil = (isset($this->_activeplugins[$family]) ? $this->_activeplugins[$family] : array());
176
+ }
177
+ return $pil;
178
+ }
179
+
180
+ /*
181
+ * Force Builtin plugin classes list with a list of classes for a given plugin family @param $family : family of builtin plugins to set @param $pclasses : array of plugin class names to set as buitin for this engine
182
+ */
183
+ public function setBuiltinPluginClasses($pfamily, $pclasses)
184
+ {
185
+ $this->_builtinplugins[$pfamily] = $pclasses;
186
+ }
187
+
188
+ /*
189
+ * Plugin sorting callback for call order in the same execution step. Sorts by filename
190
+ */
191
+ public function sortPlugins($p1, $p2)
192
+ {
193
+ $m1 = $p1->getPluginMeta();
194
+ if ($m1 == null)
195
+ {
196
+ return 1;
197
+ }
198
+ $m2 = $p2->getPluginMeta();
199
+ if ($m2 == null)
200
+ {
201
+ return -1;
202
+ }
203
+ return strcmp($m1["file"], $m2["file"]);
204
+ }
205
+
206
+ /*
207
+ * Create plugin instances for a given profile @param $profile : profile name to create plugins for @param $params : configuration parameters for the profile (all plugins)
208
+ */
209
+ public function createPlugins($profile, $params)
210
+ {
211
+ // Get Plugin Helper instance
212
+ $plhelper = Magmi_PluginHelper::getInstance($profile);
213
+ // Merge Builtin Plugin classes with current plugin classes
214
+ $this->_pluginclasses = array_merge_recursive($this->_pluginclasses, $this->getBuiltinPluginClasses());
215
+ // Iterate on plugin classes by family
216
+ foreach ($this->_pluginclasses as $pfamily => $pclasses)
217
+ {
218
+ // If family name starts with *
219
+ if ($pfamily[0] == "*")
220
+ {
221
+ // use the real family name (after *)
222
+ $this->_pluginclasses[substr($pfamily, 1)] = $pclasses;
223
+ // clear the * pseudo family
224
+ unset($this->_pluginclasses[$pfamily]);
225
+ }
226
+ }
227
+
228
+ // Iterate on final plugin classes list
229
+ foreach ($this->_pluginclasses as $pfamily => $pclasses)
230
+ {
231
+ // If there is no active plugins in the current family
232
+ if (!isset($this->_activeplugins[$pfamily]))
233
+ {
234
+ // initialize active plugins for plugin family
235
+ $this->_activeplugins[$pfamily] = array();
236
+ }
237
+ // For all plugin classes in current family
238
+ foreach ($pclasses as $pclass)
239
+ {
240
+ // Create a new instance of plugin with parameters
241
+ // Add it to the list of active plugins in the current family
242
+ $this->_activeplugins[$pfamily][] = $plhelper->createInstance($pfamily, $pclass, $params, $this);
243
+ }
244
+ // Sort family plugins with plugin sorting callback
245
+ usort($this->_activeplugins[$pfamily], array(&$this,"sortPlugins"));
246
+ }
247
+ }
248
+
249
+ /*
250
+ * Retrieve all active plugins instances for a give plugin class name
251
+ */
252
+ public function getPluginInstanceByClassName($pfamily, $pclassname)
253
+ {
254
+ $inst = null;
255
+ if (isset($this->_activeplugins[$pfamily]))
256
+ {
257
+ foreach ($this->_activeplugins[$pfamily] as $pinstance)
258
+ {
259
+ if (get_class($pinstance) == $pclassname)
260
+ {
261
+ $inst = $pinstance;
262
+ break;
263
+ }
264
+ }
265
+ }
266
+ return $inst;
267
+ }
268
+
269
+ /*
270
+ * Get a plugin instance in a family based on it's execution order
271
+ */
272
+ public function getPluginInstance($family, $order = -1)
273
+ {
274
+ if ($order < 0)
275
+ {
276
+ $order += count($this->_activeplugins[$family]);
277
+ }
278
+ return $this->_activeplugins[$family][$order];
279
+ }
280
+
281
+ /*
282
+ * Plugin call generic callback for engine @param $types : plugin types to call @param $callback : processing step to call @param $data : (reference) , data to pass to plugin processing @param $params : extra parameters for processing step @param $break : flag to stop calling chain at first plugin returning false (defaults to true)
283
+ */
284
+ public function callPlugins($types, $callback, &$data = null, $params = null, $break = true)
285
+ {
286
+ $result = true;
287
+
288
+ // If plugin type list is not an array , process it as string
289
+ if (!is_array($types))
290
+ {
291
+ // If plugin is not wildcard , build array of types based on comma separated string
292
+ if ($types != "*")
293
+ {
294
+ $types = explode(",", $types);
295
+ }
296
+ else
297
+ {
298
+ $types = array_keys($this->_activeplugins);
299
+ }
300
+ }
301
+
302
+ // Timing initialization (global processing step)
303
+ $this->_timecounter->initTime($callback, get_class($this));
304
+
305
+ // Iterate on plugin types (families)
306
+ foreach ($types as $ptype)
307
+ {
308
+ // If there is at least one active plugin in this family
309
+ if (isset($this->_activeplugins[$ptype]))
310
+ {
311
+ // For all instances in the family
312
+ foreach ($this->_activeplugins[$ptype] as $pinst)
313
+ {
314
+ // If the plugin has a hook for the defined processing step
315
+ if (method_exists($pinst, $callback))
316
+ {
317
+ // Timing initialization for current plugin in processing step
318
+ $this->_timecounter->initTime($callback, get_class($pinst));
319
+ // Perform plugin call
320
+ // either with or without parameters,or parameters & data
321
+ // store execution result
322
+ $callres = ($data == null ? ($params == null ? $pinst->$callback() : $pinst->$callback($params)) : $pinst->$callback(
323
+ $data, $params));
324
+ // End Timing for current plugin in current step
325
+ $this->_timecounter->exitTime($callback, get_class($pinst));
326
+ // if plugin call result is false with data set
327
+ if ($callres === false && $data != null)
328
+ {
329
+ // final result is false
330
+ $result = false;
331
+ }
332
+ // If there is a register callback for the plugin processing loop
333
+ if (isset($this->_ploop_callbacks[$callback]))
334
+ {
335
+ $cb = $this->_ploop_callbacks[$callback];
336
+ // Call the plugin processing loop callback , time it
337
+ $this->_timecounter->initTime($callback, get_class($pinst));
338
+ $this->$cb($pinst, $data, $result);
339
+ $this->_timecounter->exitTime($callback, get_class($pinst));
340
+ }
341
+ // if last result plugin is false & break flag
342
+ if ($result === false && $break)
343
+ {
344
+ // End timing
345
+ $this->_timecounter->exitTime($callback, get_class($this));
346
+ // return false
347
+ return $result;
348
+ }
349
+ }
350
+ }
351
+ }
352
+ }
353
+ // Nothing broke, end timing
354
+ $this->_timecounter->exitTime($callback, get_class($this));
355
+ // Return plugin call result
356
+ return $result;
357
+ }
358
+
359
+ public function getParam($params, $pname, $default = null)
360
+ {
361
+ return isset($params[$pname]) ? $params[$pname] : $default;
362
+ }
363
+
364
+ public function setLogger($logger)
365
+ {
366
+ $this->logger = $logger;
367
+ }
368
+
369
+ /**
370
+ * logging function
371
+ *
372
+ * @param string $data
373
+ * : string to log
374
+ * @param string $type
375
+ * : log type
376
+ */
377
+ public function microDateTime()
378
+ {
379
+ list($microSec,$timeStamp) = explode(" ", microtime());
380
+ return date('Y-m-d h:i:', $timeStamp) . (date('s', $timeStamp) + $microSec);
381
+ }
382
+
383
+ public function log($data, $type = "default", $logger = null)
384
+ {
385
+ $usedlogger = ($logger == null ? $this->logger : $logger);
386
+ if (isset($usedlogger))
387
+ {
388
+ $usedlogger->log($data, $type);
389
+ }
390
+ }
391
+
392
+ public function logException($e, $data = "", $logger = null)
393
+ {
394
+ $this->trace($e, $data);
395
+ $this->log($this->_excid . ":" . $e->getMessage() . " - " . $data, "error", $logger);
396
+ }
397
+
398
+ public function getExceptionTrace($tk, &$traces)
399
+ {
400
+ $this->_excid++;
401
+ $trstr = "";
402
+ foreach ($traces as $trace)
403
+ {
404
+ if (isset($trace["file"]))
405
+ {
406
+ $fname = str_replace(dirname(dirname(__FILE__)), "", $trace["file"]);
407
+ $trstr .= $fname . ":" . (isset($trace["line"]) ? $trace["line"] : "?") . " - ";
408
+ if (isset($trace["class"]))
409
+ {
410
+ $trstr .= $trace["class"] . "->";
411
+ if (isset($trace["function"]))
412
+ {
413
+ $trstr .= $trace["function"];
414
+ }
415
+ $trstr .= "\n----------------------------------------\n";
416
+ if (isset($trace["args"]))
417
+ {
418
+ $trstr .= print_r($trace["args"], true);
419
+ }
420
+ $trstr .= "\n";
421
+ }
422
+ }
423
+ }
424
+ if (!isset($this->_exceptions[$tk]))
425
+ {
426
+ $this->_exceptions[$tk] = array(0,$this->_excid);
427
+ }
428
+ $this->_exceptions[$tk][0]++;
429
+ $trstr = "************************************\n$tk\n*************************************\n$trstr";
430
+ return array($trstr,$this->_exceptions[$tk][0] == 1,$this->_exceptions[$tk][1]);
431
+ }
432
+
433
+ public function trace($e, $data = "")
434
+ {
435
+ $traces = $e->getTrace();
436
+ $tk = $e->getMessage();
437
+ $traceinfo = $this->getExceptionTrace($tk, $traces);
438
+ $f = fopen(Magmi_StateManager::getTraceFile(), "a");
439
+ fwrite($f, "---- TRACE : $this->_excid -----\n");
440
+ fwrite($f, "---- DATE : " . date('Y-m-d H:i:s') . " ------\n");
441
+ try
442
+ {
443
+ if ($traceinfo[1] == true)
444
+ {
445
+ fwrite($f, $traceinfo[0]);
446
+ fwrite($f, "+++++++++++++++++++++++++++++\nCONTEXT DUMP\n+++++++++++++++++++++++++++++\n");
447
+ fwrite($f, print_r($this, true));
448
+ fwrite($f, "\n+++++++++++++++++++++++++++++\nEND CONTEXT DUMP\n+++++++++++++++++++++++++++++\n");
449
+ }
450
+ else
451
+ {
452
+ $tnum = $traceinfo[2];
453
+ fwrite($f, "Duplicated exception - same trace as TRACE : $tnum\n");
454
+ }
455
+ }
456
+ catch (Exception $te)
457
+ {
458
+ fwrite($f, "Exception occured during trace:" . $te->getMessage());
459
+ }
460
+ fwrite($f, "---- ENDTRACE : $this->_excid -----\n");
461
+ fclose($f);
462
+ }
463
+
464
+ /**
465
+ * Engine run method
466
+ *
467
+ * @param array $params
468
+ * - run parameters
469
+ */
470
+ public final function run($params = array())
471
+ {
472
+ try
473
+ {
474
+ $f = fopen(Magmi_StateManager::getTraceFile(), "w");
475
+ fclose($f);
476
+ $enginf = $this->getEngineInfo();
477
+ $this->log("MAGMI by dweeves - version:" . Magmi_Version::$version, "title");
478
+ $this->log("Running {$enginf["name"]} v${enginf["version"]} by ${enginf["author"]}", "startup");
479
+ if (!$this->_initialized)
480
+ {
481
+ $this->initialize($params);
482
+ }
483
+ $this->connectToMagento();
484
+ $this->engineInit($params);
485
+ $this->engineRun($params);
486
+ $this->disconnectFromMagento();
487
+ }
488
+ catch (Exception $e)
489
+ {
490
+ $this->disconnectFromMagento();
491
+
492
+ $this->handleException($e);
493
+ }
494
+ }
495
+
496
+ public function handleException($e)
497
+ {
498
+ $this->logException($e);
499
+ if (method_exists($this, "onEngineException"))
500
+ {
501
+ $this->onEngineException($e);
502
+ }
503
+ }
504
+
505
+ /**
506
+ * shortcut method for configuration properties get
507
+ */
508
+ public function getProp($sec, $val, $default = null)
509
+ {
510
+ return $this->_conf->get($sec, $val, $default);
511
+ }
512
+
513
+ /**
514
+ * Initialize Connection with Magento Database
515
+ */
516
+ public function connectToMagento()
517
+ {
518
+ // et database infos from properties
519
+ if (!$this->_connected)
520
+ {
521
+ $host = $this->getProp("DATABASE", "host", "localhost");
522
+ $dbname = $this->getProp("DATABASE", "dbname", "magento");
523
+ $user = $this->getProp("DATABASE", "user");
524
+ $pass = $this->getProp("DATABASE", "password");
525
+ $debug = $this->getProp("DATABASE", "debug");
526
+ $conn = $this->getProp("DATABASE", "connectivity", "net");
527
+ $port = $this->getProp("DATABASE", "port", "3306");
528
+ $socket = $this->getProp("DATABASE", "unix_socket");
529
+ $this->initDb($host, $dbname, $user, $pass, $port, $socket, $conn, $debug);
530
+ // suggested by pastanislas
531
+ $this->_db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
532
+ }
533
+ }
534
+ /*
535
+ * Disconnect Magento db
536
+ */
537
+ public function disconnectFromMagento()
538
+ {
539
+ if ($this->_connected)
540
+ {
541
+ $this->exitDb();
542
+ }
543
+ }
544
+
545
+ /**
546
+ * returns prefixed table name
547
+ *
548
+ * @param string $magname
549
+ * : magento base table name
550
+ */
551
+ public function tablename($magname)
552
+ {
553
+ return $this->tprefix != "" ? $this->tprefix . "$magname" : $magname;
554
+ }
555
+
556
+ public abstract function engineInit($params);
557
+
558
+ public abstract function engineRun($params);
559
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/magmi_loggers.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Magmi_Logger
4
+ {
5
+
6
+ public abstract function log($data, $type = null);
7
+ }
8
+
9
+ class FileLogger extends Magmi_Logger
10
+ {
11
+ protected $_fname;
12
+
13
+ public function __construct($fname = null)
14
+ {
15
+ if ($fname == null)
16
+ {
17
+ $fname = Magmi_StateManager::getProgressFile(true);
18
+ }
19
+ $this->_fname = $fname;
20
+ $f = fopen($this->_fname, "w");
21
+ if ($f == false)
22
+ {
23
+ throw new Exception("CANNOT WRITE PROGRESS FILE ");
24
+ }
25
+ fclose($f);
26
+ }
27
+
28
+ public function log($data, $type = null)
29
+ {
30
+ $f = fopen($this->_fname, "a");
31
+ if ($f == false)
32
+ {
33
+ throw new Exception("CANNOT WRITE PROGRESS FILE ");
34
+ }
35
+ $data = preg_replace("/(\r|\n|\r\n)/", "<br>", $data);
36
+ if ($type == null)
37
+ {
38
+ $type = "default";
39
+ }
40
+ fwrite($f, "$type:$data\n");
41
+ fclose($f);
42
+ }
43
+ }
44
+
45
+ class EchoLogger extends Magmi_Logger
46
+ {
47
+
48
+ public function log($data, $type = null)
49
+ {
50
+ if ($type != null)
51
+ {
52
+ $info = explode(";", $type);
53
+ $type = $info[0];
54
+ }
55
+ else
56
+ {
57
+ $type = "default";
58
+ }
59
+ echo ('<p class="logentry log_' . $type . '">' . $data . "</p>");
60
+ }
61
+ }
62
+
63
+ class CLILogger extends Magmi_Logger
64
+ {
65
+
66
+ public function log($data, $type = null)
67
+ {
68
+ echo ("$type:$data\n");
69
+ }
70
+ }
71
+ ?>
app/code/community/Osf/Synnex/lib/magmi/inc/magmi_mixin.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Magmi_Mixin
4
+ {
5
+ protected $_callers;
6
+
7
+ public function bind($caller)
8
+ {
9
+ $this->_callers[] = $caller;
10
+ $this->_callers = array_unique($this->_callers);
11
+ }
12
+
13
+ public function unbind($caller)
14
+ {
15
+ $ks = array_keys($this->_callers, $caller);
16
+ if (count($ks) > 0)
17
+ {
18
+ foreach ($ks as $k)
19
+ {
20
+ unset($this->_callers[$k]);
21
+ }
22
+ }
23
+ }
24
+
25
+ public function __call($data, $arg)
26
+ {
27
+ if (substr($data, 0, 8) == "_caller_")
28
+ {
29
+ $data = substr($data, 8);
30
+ }
31
+ $ccallers = count($this->_callers);
32
+ for ($i = 0; $i < $ccallers; $i++)
33
+ {
34
+ if (method_exists($this->_callers[$i], $data))
35
+ {
36
+ return call_user_func_array(array($this->_callers[$i],$data), $arg);
37
+ }
38
+ else
39
+ {
40
+ die("Invalid Method Call: $data - Not found in Caller");
41
+ }
42
+ }
43
+ }
44
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/magmi_pluginhelper.php ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once ("magmi_config.php");
3
+ require_once ("fshelper.php");
4
+
5
+ class Magmi_PluginHelper
6
+ {
7
+ static $_plugins_cache = array();
8
+ static $_instances = array();
9
+ public $base_dir;
10
+ public $plugin_dir;
11
+ protected $_profile;
12
+ protected $_plmeta = array("datasources"=>array("Magmi_Datasource","*/*"),
13
+ "itemprocessors"=>array("Magmi_ItemProcessor","*/*"),"general"=>array("Magmi_GeneralImportPlugin","*/*"),
14
+ "utilities"=>array("Magmi_UtilityPlugin","utilities"));
15
+
16
+ public function __construct($profile = null)
17
+ {
18
+ $this->_profile = $profile;
19
+ $this->base_dir = dirname(__FILE__);
20
+ $this->plugin_dir = realpath(dirname(dirname(__FILE__)) . DIRSEP . "plugins");
21
+ // set include path to inclue plugins inc & base dir
22
+ set_include_path(
23
+ ini_get("include_path") . PATH_SEPARATOR . "$this->plugin_dir/inc" . PATH_SEPARATOR . "$this->base_dir");
24
+ // add base classes in context
25
+ require_once ("magmi_item_processor.php");
26
+ require_once ("magmi_datasource.php");
27
+ require_once ("magmi_generalimport_plugin.php");
28
+ require_once ("magmi_utility_plugin.php");
29
+ }
30
+
31
+ public static function getInstance($profile = null)
32
+ {
33
+ $key = ($profile == null ? "default" : $profile);
34
+ if (!isset(self::$_instances[$key]))
35
+ {
36
+ self::$_instances[$key] = new Magmi_PluginHelper($profile);
37
+ }
38
+ return self::$_instances[$key];
39
+ }
40
+
41
+ public static function fnsort($f1, $f2)
42
+ {
43
+ return strcmp(basename($f1), basename($f2));
44
+ }
45
+
46
+ public function initPluginInfos($baseclass, $basedir = "*/*")
47
+ {
48
+ $candidates = glob("$this->plugin_dir/$basedir/*/*.php");
49
+ usort($candidates, array("Magmi_PluginHelper","fnsort"));
50
+ $pluginclasses = array();
51
+ foreach ($candidates as $pcfile)
52
+ {
53
+ $dirname = dirname(substr($pcfile, strlen($this->plugin_dir)));
54
+ if (substr(basename($dirname), 0, 2) != '__')
55
+ {
56
+ $content = file_get_contents($pcfile);
57
+ if (preg_match_all("/class\s+(.*?)\s+extends\s+$baseclass/mi", $content, $matches, PREG_SET_ORDER))
58
+ {
59
+ require_once ($pcfile);
60
+ foreach ($matches as $match)
61
+ {
62
+ $pluginclasses[] = array("class"=>$match[1],"dir"=>$dirname,"file"=>basename($pcfile));
63
+ }
64
+ }
65
+ }
66
+ }
67
+ return $pluginclasses;
68
+ }
69
+
70
+ public function getPluginClasses($pltypes)
71
+ {
72
+ return self::getPluginsInfo($pltypes, "class");
73
+ }
74
+
75
+ public function getPluginsInfo($pltypes, $filter = null)
76
+ {
77
+ if (self::$_plugins_cache == null)
78
+ {
79
+ self::scanPlugins($pltypes);
80
+ }
81
+
82
+ if (isset($filter))
83
+ {
84
+ $out = array();
85
+ foreach (self::$_plugins_cache as $k => $arr)
86
+ {
87
+ if (!isset($out[$k]))
88
+ {
89
+ $out[$k] = array();
90
+ }
91
+ foreach ($arr as $desc)
92
+ {
93
+ $out[$k][] = $desc[$filter];
94
+ }
95
+ }
96
+ $plugins = $out;
97
+ }
98
+ else
99
+ {
100
+ $plugins = self::$_plugins_cache;
101
+ }
102
+ return $plugins;
103
+ }
104
+
105
+ public function scanPlugins($pltypes)
106
+ {
107
+ if (!is_array($pltypes))
108
+ {
109
+ $pltypes = array($pltypes);
110
+ }
111
+ foreach ($pltypes as $pltype)
112
+ {
113
+ if (!isset(self::$_plugins_cache[$pltype]))
114
+ {
115
+ self::$_plugins_cache[$pltype] = self::initPluginInfos($this->_plmeta[$pltype][0],
116
+ $this->_plmeta[$pltype][1]);
117
+ }
118
+ }
119
+ }
120
+
121
+ public function createInstance($ptype, $pclass, $params = null, $mmi = null)
122
+ {
123
+ if (!isset(self::$_plugins_cache[$ptype]))
124
+ {
125
+ self::scanPlugins($ptype);
126
+ }
127
+ $plinst = new $pclass();
128
+
129
+ $plinst->pluginInit($mmi, $this->getPluginMeta($plinst), $params, ($mmi != null), $this->_profile);
130
+ return $plinst;
131
+ }
132
+
133
+ public function getPluginDir($pinst)
134
+ {
135
+ $mt = $this->getPluginMeta($pinst);
136
+ return $mt["dir"];
137
+ }
138
+
139
+ public function getPluginMeta($pinst)
140
+ {
141
+ if (self::$_plugins_cache == null)
142
+ {
143
+ self::scanPlugins();
144
+ }
145
+
146
+ foreach (self::$_plugins_cache as $t => $l)
147
+ {
148
+ foreach ($l as $pdesc)
149
+ {
150
+ if ($pdesc["class"] == get_class($pinst))
151
+ {
152
+ $out = $pdesc;
153
+ $out["dir"] = $this->plugin_dir . $pdesc["dir"];
154
+ return $out;
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ public function installPluginPackage($pkgname)
161
+ {
162
+ $zip = new ZipArchive();
163
+ $res = $zip->open($pkgname);
164
+ if ($res === TRUE)
165
+ {
166
+ $zip->extractTo($this->plugin_dir);
167
+ $zip->close();
168
+ return array("plugin_install"=>"OK");
169
+ }
170
+ else
171
+ {
172
+ return array("plugin_install"=>"ERROR","ERROR"=>"Invalid Plugin Package Archive");
173
+ }
174
+ $packages = glob("$this->plugin_dir/*");
175
+ foreach ($packages as $pdir)
176
+ {
177
+ if (file_exists($pdir . DIRSEP . "obsolete.txt"))
178
+ {
179
+ $content = file_get_contents($pdir . DIRSEP . "obsolete.txt");
180
+ $obsolete = explode("\n", $content);
181
+ foreach ($obsolete as $todelete)
182
+ {
183
+ if ($todelete != "")
184
+ {
185
+ @unlink($pdir . DIRSEP . $todelete);
186
+ }
187
+ }
188
+ unlink($pdir . DIRSEP . "obsolete.txt");
189
+ }
190
+ }
191
+ }
192
+
193
+ public function removePlugin($pgpath)
194
+ {
195
+ unlink($pgpath);
196
+ }
197
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/magmi_statemanager.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if (!defined("DIRSEP"))
3
+ {
4
+ define("DIRSEP", DIRECTORY_SEPARATOR);
5
+ }
6
+
7
+ class Magmi_StateManager
8
+ {
9
+ private static $_statefile = null;
10
+ private static $_script = __FILE__;
11
+ private static $_state = "idle";
12
+
13
+ public static function getStateFile()
14
+ {
15
+ return self::getStateDir() . DIRSEP . "magmistate";
16
+ }
17
+
18
+ public static function getTraceFile()
19
+ {
20
+ return self::getStateDir() . DIRSEP . "trace.txt";
21
+ }
22
+
23
+ public static function getStateDir()
24
+ {
25
+ return dirname(dirname(self::$_script)) . DIRSEP . "state";
26
+ }
27
+
28
+ public static function getProgressFile($full = false)
29
+ {
30
+ $fullname = self::getStateDir() . DIRSEP . "progress.txt";
31
+ $pfname = ($full ? $fullname : "progress.txt");
32
+ return $pfname;
33
+ }
34
+
35
+ public static function setState($state, $force = false)
36
+ {
37
+ if (self::$_state == $state && !$force)
38
+ {
39
+ return;
40
+ }
41
+
42
+ self::$_state = $state;
43
+ $f = fopen(self::getStateFile(), "w");
44
+ fwrite($f, self::$_state);
45
+ fclose($f);
46
+ @chmod(self::getStateFile(), 0664);
47
+ if ($state == "running")
48
+ {
49
+ $f = fopen(self::getTraceFile(), "w");
50
+ fclose($f);
51
+ @chmod(self::getTraceFile(), 0664);
52
+ }
53
+ }
54
+
55
+ public static function getState($cached = false)
56
+ {
57
+ if (!$cached)
58
+ {
59
+ if (!file_exists(self::getStateFile()))
60
+ {
61
+ self::setState("idle", true);
62
+ }
63
+ $state = file_get_contents(self::getStateFile());
64
+ }
65
+ else
66
+ {
67
+ $state = self::$_state;
68
+ }
69
+ return $state;
70
+ }
71
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/magmi_utils.php ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // utilities function
3
+
4
+ // return null for empty string
5
+ function nullifempty($val)
6
+ {
7
+ return (isset($val) ? (trim($val) == "" ? null : $val) : null);
8
+ }
9
+ // return false for empty string
10
+ function falseifempty($val)
11
+ {
12
+ return (isset($val) ? (strlen($val) == 0 ? false : $val) : false);
13
+ }
14
+ // test for empty string
15
+ function testempty($arr, $val)
16
+ {
17
+ return !isset($arr[$val]) || strlen(trim($arr[$val])) == 0;
18
+ }
19
+
20
+ // place a DELETE maker for empty values
21
+ function deleteifempty($val)
22
+ {
23
+ return (isset($val) ? (trim($val) == "" ? "__MAGMI_DELETE__" : $val) : "__MAGMI_DELETE__");
24
+ }
25
+
26
+ // convert to array & trims a comma separated list
27
+ function csl2arr($cslarr, $sep = ",")
28
+ {
29
+ $arr = explode($sep, $cslarr);
30
+ $carr = count($arr);
31
+ for ($i = 0; $i < $carr; $i++)
32
+ {
33
+ $arr[$i] = trim($arr[$i]);
34
+ }
35
+ return $arr;
36
+ }
37
+
38
+ // trim a list of array values
39
+ function trimarray(&$arr)
40
+ {
41
+ $carr = count($arr);
42
+ for ($i = 0; $i < $carr; $i++)
43
+ {
44
+ $arr[$i] = trim($arr[$i]);
45
+ }
46
+ }
47
+
48
+ // Relative value detection (prepend of + or -)
49
+ function getRelative(&$val)
50
+ {
51
+ $dir = "+";
52
+ if ($val[0] == "-")
53
+ {
54
+ $val = substr($val, 1);
55
+ $dir = "-";
56
+ }
57
+ else
58
+ if ($val[0] == "+")
59
+ {
60
+ $val = substr($val, 1);
61
+ }
62
+ return $dir;
63
+ }
64
+
65
+ // Check if we have a remote path
66
+ function is_remote_path($path)
67
+ {
68
+ $parsed = parse_url($path);
69
+ return isset($parsed['host']);
70
+ }
71
+
72
+ // Returns absolute path for a file with a base path
73
+ // if $resolve is set to true,return associated realpath
74
+ function abspath($path, $basepath = "", $resolve = true)
75
+ {
76
+ if ($basepath == "")
77
+ {
78
+ $basepath = dirname(dirname(__FILE__));
79
+ }
80
+ $cpath = str_replace('//', '/', $basepath . "/" . $path);
81
+ if ($resolve && !is_remote_path($cpath))
82
+ {
83
+ $abs = realpath($cpath);
84
+ }
85
+ else
86
+ {
87
+ $inparts = explode("/", $cpath);
88
+ $outparts = array();
89
+ $cinparts = count($inparts);
90
+ for ($i = 0; $i < $cinparts; $i++)
91
+ {
92
+ if ($inparts[$i] == '..')
93
+ {
94
+ array_pop($outparts);
95
+ }
96
+ else
97
+ if ($inparts[$i] != '.')
98
+ {
99
+ $outparts[] = $inparts[$i];
100
+ }
101
+ }
102
+ $abs = implode("/", $outparts);
103
+ }
104
+ return $abs;
105
+ }
106
+
107
+ function truepath($path)
108
+ {
109
+ $opath = $path;
110
+ // whether $path is unix or not
111
+ $unipath = strlen($path) == 0 || $path{0} != '/';
112
+ // attempts to detect if path is relative in which case, add cwd
113
+ if (strpos($path, ':') === false && $unipath)
114
+ $path = getcwd() . DIRECTORY_SEPARATOR . $path;
115
+ // resolve path parts (single dot, double dot and double delimiters)
116
+ $path = str_replace(array('/','\\'), DIRECTORY_SEPARATOR, $path);
117
+ $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
118
+ $absolutes = array();
119
+ foreach ($parts as $part)
120
+ {
121
+ if ('.' == $part)
122
+ continue;
123
+ if ('..' == $part)
124
+ {
125
+ array_pop($absolutes);
126
+ }
127
+ else
128
+ {
129
+ $absolutes[] = $part;
130
+ }
131
+ }
132
+ $path = implode(DIRECTORY_SEPARATOR, $absolutes);
133
+ // resolve any symlinks
134
+ if (file_exists($path) && linkinfo($path) > 0)
135
+ {
136
+ $path = readlink($path);
137
+ }
138
+ // put initial separator that could have been lost
139
+ $path = !$unipath ? '/' . $path : $path;
140
+ return $path;
141
+ }
142
+
143
+ // Test for absolute path using OS detection
144
+ function isabspath($path)
145
+ {
146
+ return ($path[0] == "." || (substr(PHP_OS, 0, 3) == "WIN" && strlen($path) > 1) ? $path[1] == ":" : $path[0] == "/");
147
+ }
148
+
149
+ /*
150
+ * Slugger class, for producing valid url from strings
151
+ */
152
+ class Slugger
153
+ {
154
+ // Mapping array for intl accented chars
155
+ protected static $_translit = array('Š'=>'S','š'=>'s','Ð'=>'Dj','Ž'=>'Z','ž'=>'z','À'=>'A','Á'=>'A','Â'=>'A',
156
+ 'Ã'=>'A','Ä'=>'A','Å'=>'A','Æ'=>'A','Ç'=>'C','È'=>'E','É'=>'E','Ê'=>'E','Ë'=>'E','Ì'=>'I','Í'=>'I','Î'=>'I',
157
+ 'Ï'=>'I','Ñ'=>'N','Ò'=>'O','Ó'=>'O','Ô'=>'O','Õ'=>'O','Ö'=>'O','Ø'=>'O','Ù'=>'U','Ú'=>'U','Û'=>'U','Ü'=>'U',
158
+ 'Ý'=>'Y','Þ'=>'B','ß'=>'Ss','à'=>'a','á'=>'a','â'=>'a','ã'=>'a','ä'=>'a','å'=>'a','æ'=>'a','ç'=>'c','è'=>'e',
159
+ 'é'=>'e','ê'=>'e','ë'=>'e','ì'=>'i','í'=>'i','î'=>'i','ï'=>'i','ð'=>'o','ñ'=>'n','ò'=>'o','ó'=>'o','ô'=>'o',
160
+ 'õ'=>'o','ö'=>'o','ø'=>'o','ù'=>'u','ú'=>'u','û'=>'u','ý'=>'y','ý'=>'y','þ'=>'b','ÿ'=>'y','ƒ'=>'f','Č'=>'C',
161
+ 'č'=>'c','Ľ'=>'L','ľ'=>'l','Ĺ'=>'L','Ť'=>'T','ť'=>'t','Ň'=>'N','ň'=>'n','Ŕ'=>'R','ŕ'=>'r','Ř'=>'R','ř'=>'r',
162
+ 'Ő'=>'O','ő'=>'o','Ű'=>'U','ű'=>'u','ü'=>'u');
163
+
164
+ // Stripping accents
165
+ public static function stripAccents($text)
166
+ {
167
+ return strtr($text, self::$_translit);
168
+ }
169
+ // Slugging function
170
+ public static function slug($str, $allowslash = false)
171
+ {
172
+ $str = strtolower(self::stripAccents(trim($str)));
173
+ $rerep = $allowslash ? '[^a-z0-9-/]' : '[^a-z0-9-]';
174
+ $str = preg_replace("|$rerep|", '-', $str);
175
+ $str = preg_replace('|-+|', "-", $str);
176
+ $str = preg_replace('|-$|', "", $str);
177
+ return $str;
178
+ }
179
+ }
180
+
app/code/community/Osf/Synnex/lib/magmi/inc/magmi_valueparser.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Magmi_ValueParser
4
+ {
5
+
6
+ public static function parseValue($pvalue, $dictarray)
7
+ {
8
+ $matches = array();
9
+ $rep = "";
10
+ $renc = "<-XMagmi_Enc->";
11
+
12
+ foreach ($dictarray as $key => $vals)
13
+ {
14
+ $ik = array_keys($vals);
15
+ // replace base values
16
+ while (preg_match("|\{$key\.(.*?)\}|", $pvalue, $matches))
17
+ {
18
+ foreach ($matches as $match)
19
+ {
20
+ if ($match != $matches[0])
21
+ {
22
+ if (in_array($match, $ik))
23
+ {
24
+ $rep = $renc . $dictarray[$key][$match] . $renc;
25
+ }
26
+ else
27
+ {
28
+ $rep = "";
29
+ }
30
+ $pvalue = str_replace($matches[0], $rep, $pvalue);
31
+ }
32
+ }
33
+ unset($matches);
34
+ }
35
+ }
36
+
37
+ // replacing expr values
38
+ while (preg_match("|\{\{\s*(.*?)\s*\}\}|", $pvalue, $matches))
39
+ {
40
+ foreach ($matches as $match)
41
+ {
42
+ if ($match != $matches[0])
43
+ {
44
+ $code = trim($match);
45
+ $code = str_replace($renc, '"', $code);
46
+
47
+ $rep = eval("return ($code);");
48
+ // escape potential "{{xxx}}" values in interpreted target
49
+ // so that they won't be reparsed in next round
50
+ $rep = preg_replace("|\{\{\s*(.*?)\s*\}\}|", "____$1____", $rep);
51
+ $pvalue = str_replace($matches[0], $rep, $pvalue);
52
+ }
53
+ }
54
+ }
55
+
56
+ // unescape matches
57
+ $pvalue = preg_replace("|____(.*?)____|", '{{$1}}', $pvalue);
58
+ // single replaced values
59
+ $pvalue = str_replace($renc, '', $pvalue);
60
+ return "" . $pvalue;
61
+ }
62
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/magmi_version.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+ class Magmi_Version
3
+ {
4
+ public static $version="0.7.19a";
5
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/properties.php ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if (!defined("DIRSEP"))
3
+ {
4
+ define("DIRSEP", DIRECTORY_SEPARATOR);
5
+ }
6
+
7
+ class FileNotFoundException extends Exception
8
+ {
9
+ }
10
+
11
+ class InvalidPropertiesException extends Exception
12
+ {
13
+ }
14
+
15
+ class Properties
16
+ {
17
+ protected $_props;
18
+ public $inifile;
19
+ protected $_specialchars = array('"'=>":DQUOTE:","'"=>":SQUOTE:",'\\t'=>"TAB");
20
+
21
+ public function __construct()
22
+ {
23
+ $this->inifile = null;
24
+ $this->_props = array();
25
+ }
26
+
27
+ public function setPropsFromFlatArray($flatarr)
28
+ {
29
+ $this->_props = $this->getIniStruct($flatarr);
30
+ }
31
+
32
+ public function setProps($proparr)
33
+ {
34
+ $this->_props = $proparr;
35
+ }
36
+
37
+ public function load($file)
38
+ {
39
+ if (!file_exists($file))
40
+ {
41
+ return;
42
+ // throw new FileNotFoundException();
43
+ }
44
+ try
45
+ {
46
+ $this->inifile = $file;
47
+ $this->_props = parse_ini_file($this->inifile, true);
48
+ foreach ($this->_props as $sec => $data)
49
+ {
50
+ foreach ($data as $k => $v)
51
+ {
52
+ foreach ($this->_specialchars as $spch => $alias)
53
+ {
54
+ $newv = str_replace($alias, $spch, $v);
55
+ if ($newv != $v)
56
+ {
57
+ break;
58
+ }
59
+ }
60
+ $this->_props[$sec][$k] = $newv;
61
+ }
62
+ }
63
+ }
64
+ catch (Exception $e)
65
+ {
66
+ throw new InvalidPropertiesException();
67
+ }
68
+ }
69
+
70
+ public function getIniStruct($arr)
71
+ {
72
+ $conf = array();
73
+ foreach ($arr as $k => $v)
74
+ {
75
+ list($section,$value) = explode(":", $k, 2);
76
+ if (!isset($conf[$section]))
77
+ {
78
+ $conf[$section] = array();
79
+ }
80
+ $conf[$section][$value] = $v;
81
+ }
82
+ return $conf;
83
+ }
84
+
85
+ public function save($fname = null)
86
+ {
87
+ if ($fname == null)
88
+ {
89
+ $fname == $this->inifile;
90
+ }
91
+ return $this->write_ini_file($this->_props, $fname, true);
92
+ }
93
+
94
+ public function esc($str)
95
+ {
96
+ foreach ($this->_specialchars as $spch => $alias)
97
+ {
98
+ $str = str_replace($spch, $alias, $str);
99
+ }
100
+ return $str;
101
+ }
102
+
103
+ public function write_ini_file($assoc_arr, $path, $has_sections = FALSE)
104
+ {
105
+ $content = "";
106
+ if (count($assoc_arr) > 0)
107
+ {
108
+ if ($has_sections)
109
+ {
110
+ foreach ($assoc_arr as $key => $elem)
111
+ {
112
+ $content .= "[" . $key . "]\n";
113
+ foreach ($elem as $key2 => $elem2)
114
+ {
115
+ if (is_array($elem2))
116
+ {
117
+ $celem2 = count($elem2);
118
+ for ($i = 0; $i < $celem2; $i++)
119
+ {
120
+ $content .= $key2 . "[] = \"" . $this->esc($elem2[$i]) . "\"\n";
121
+ }
122
+ }
123
+ else
124
+ if ($elem2 == "")
125
+ $content .= $key2 . " = \n";
126
+ else
127
+ $content .= $key2 . " = \"" . $this->esc($elem2) . "\"\n";
128
+ }
129
+ }
130
+ }
131
+ else
132
+ {
133
+ foreach ($assoc_arr as $key => $elem)
134
+ {
135
+ if (is_array($elem))
136
+ {
137
+ $celem = count($elem);
138
+ for ($i = 0; $i < $celem; $i++)
139
+ {
140
+ $content .= $key2 . "[] = \"" . $this->esc($elem[$i]) . "\"\n";
141
+ }
142
+ }
143
+ else
144
+ if ($elem == "")
145
+ $content .= $key2 . " = \n";
146
+ else
147
+ $content .= $key2 . " = \"" . $this->esc($elem) . "\"\n";
148
+ }
149
+ }
150
+ }
151
+
152
+ if (!$handle = fopen($path, 'w'))
153
+ {
154
+ return false;
155
+ }
156
+ if (!fwrite($handle, $content))
157
+ {
158
+ return false;
159
+ }
160
+ @chmod($path, 0664);
161
+ fclose($handle);
162
+ return true;
163
+ }
164
+
165
+ public function set($secname, $pname, $val)
166
+ {
167
+ $this->_props[$secname][$pname] = $val;
168
+ }
169
+
170
+ /**
171
+ * retrieve property value with default if not found
172
+ *
173
+ * @param string $secname
174
+ * section name
175
+ * @param string $pname
176
+ * property name
177
+ * @param string $default
178
+ * default value if not found (null if not set)
179
+ * @return string value if found or default if not found
180
+ */
181
+ public function get($secname, $pname, $default = null)
182
+ {
183
+ if (isset($this->_props[$secname]) && isset($this->_props[$secname][$pname]))
184
+ {
185
+ $v = $this->_props[$secname][$pname];
186
+ return $v;
187
+ }
188
+ else
189
+ {
190
+ return $default;
191
+ }
192
+ }
193
+
194
+ public function getsection($secname)
195
+ {
196
+ if (isset($this->_props[$secname]))
197
+ {
198
+ return $this->_props[$secname];
199
+ }
200
+ else
201
+ {
202
+ return array();
203
+ }
204
+ }
205
+
206
+ public function hasSection($secname)
207
+ {
208
+ return isset($this->_props[$secname]);
209
+ }
210
+
211
+ public function removeSection($secname)
212
+ {
213
+ if ($this->hasSection($secname))
214
+ {
215
+ unset($this->_props[$secname]);
216
+ }
217
+ }
218
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/remotefilegetter.php ADDED
@@ -0,0 +1,381 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class RemoteFileGetter
4
+ {
5
+ protected $_errors;
6
+ protected $_user;
7
+ protected $_password;
8
+ protected $_logger = null;
9
+
10
+ public function setLogger($logger)
11
+ {
12
+ $this->_logger = $logger;
13
+ }
14
+
15
+ public function log($data)
16
+ {
17
+ if ($this->_logger != null)
18
+ {
19
+ $this->_logger->log($data);
20
+ }
21
+ }
22
+
23
+ public abstract function urlExists($url);
24
+
25
+ public abstract function copyRemoteFile($url, $dest);
26
+
27
+ // using credentials
28
+ public function setCredentials($user = null, $passwd = null)
29
+ {
30
+ $this->_user = $user;
31
+ $this->_password = $passwd;
32
+ }
33
+
34
+ public function getErrors()
35
+ {
36
+ return $this->_errors;
37
+ }
38
+ }
39
+
40
+ class CURL_RemoteFileGetter extends RemoteFileGetter
41
+ {
42
+ protected $_cookie;
43
+ protected $_lookup_opts;
44
+ protected $_dl_opts;
45
+ protected $_lookup;
46
+ protected $_protocol;
47
+ protected $_creds;
48
+ protected $_opts;
49
+ protected $_user;
50
+ protected $_password;
51
+
52
+ public function __construct()
53
+ {
54
+ $this->_opts=array(
55
+ 'http'=>array('lookup'=>$this->initBaseOptions('http', 'lookup'),
56
+ 'dl'=>$this->initBaseOptions('http', 'dl')),
57
+ 'https'=>array('lookup'=>$this->initBaseOptions('https', 'lookup'),
58
+ 'dl'=>$this->initBaseOptions('https', 'dl')),
59
+ 'ftp'=>array('dl'=>$this->initBaseOptions('ftp','dl'))
60
+ );
61
+ }
62
+
63
+ public function initBaseOptions($protocol,$mode)
64
+ {
65
+ $curlopts=array();
66
+ switch ($protocol)
67
+ {
68
+ case 'http':
69
+ case 'https':
70
+ switch($mode)
71
+ {
72
+ case 'lookup':
73
+ $curlopts=array(
74
+ // we want the response
75
+ CURLOPT_RETURNTRANSFER=>true,
76
+ // we want the headers
77
+ CURLOPT_HEADER=>true,
78
+ // we don't want the body
79
+ CURLOPT_NOBODY=>true,
80
+ // follow redirects
81
+ CURLOPT_FOLLOWLOCATION=>true,
82
+ // some stats on target
83
+ CURLOPT_FILETIME=>true);
84
+ break;
85
+ case 'dl':
86
+ $curlopts=array(
87
+ // force get
88
+ CURLOPT_HTTPGET=>true,
89
+ // no header
90
+ CURLOPT_HEADER=>false,
91
+ // we want body
92
+ CURLOPT_NOBODY=>false,
93
+ // redirect
94
+ CURLOPT_FOLLOWLOCATION=>true,
95
+ // handle 100 continue
96
+ CURLOPT_HTTPHEADER=>array('Expect:'),
97
+ // we don't want the response as we will store it in a file
98
+ CURLOPT_RETURNTRANSFER=>false,
99
+ //use binary
100
+ CURLOPT_BINARYTRANSFER=>true
101
+ );
102
+ break;
103
+ default:
104
+ break;
105
+ }
106
+ break;
107
+ /*
108
+ * Initializing for ftp
109
+ */
110
+ case 'ftp':
111
+ $curlopts = array(
112
+ //longer timeouts for big files
113
+ CURLOPT_TIMEOUT =>300,
114
+ //use binary
115
+ CURLOPT_BINARYTRANSFER=>true,
116
+ CURLOPT_FOLLOWLOCATION=> 1,
117
+ //Better compatibility with some FTP Servers
118
+ CURLOPT_FTP_USE_EPSV=>0,
119
+ //no need to return anything, we'll have a file pointer
120
+ CURLOPT_RETURNTRANSFER=>0);
121
+ break;
122
+ }
123
+ return $curlopts;
124
+ }
125
+
126
+ public function setAuthOptions($context,&$opts,$user=null,$pass=null)
127
+ {
128
+ $creds="";
129
+ if ($user == null)
130
+ {
131
+ $user=$this->_user;
132
+ $pass=$this->_password;
133
+ }
134
+
135
+ if($user)
136
+ {
137
+ $creds=$user.":";
138
+ }
139
+
140
+ if($pass)
141
+ {
142
+ $creds.=$pass;
143
+ }
144
+
145
+ if (!is_null($creds) && $creds != "" && !isset($opts[CURLOPT_USERPWD]))
146
+ {
147
+ if (substr($context['scheme'], 0, 4) == "http")
148
+ {
149
+ $opts[CURLOPT_HTTPAUTH] = CURLAUTH_ANY;
150
+ $opts[CURLOPT_UNRESTRICTED_AUTH] = true;
151
+ }
152
+ $opts[CURLOPT_USERPWD] = "$creds";
153
+ }
154
+ }
155
+
156
+ /*
157
+ * Creating a CURL context with adequate options from an URL For a given URL host/port/user , the same context is reused for optimizing performance
158
+ */
159
+ public function createContext($url)
160
+ {
161
+ // handle spaces in url
162
+ $curl_url = str_replace(" ", "%20", $url);
163
+ // parsing url components
164
+ $comps = parse_url($url);
165
+ if ($comps == false || !isset($this->_opts[$comps['scheme']]))
166
+ {
167
+ throw new Exception("Unsupported URL : $url");
168
+ }
169
+
170
+ // create a curl context
171
+ $ch = curl_init();
172
+ $opts=$this->_opts[$comps['scheme']];
173
+ $ctx=array("curlhandle"=>$ch,"opts"=>$opts,"scheme"=>$comps['scheme']);
174
+
175
+ /*
176
+ * Inline user/pass if in url
177
+ */
178
+ if (isset($comps['user']))
179
+ {
180
+ $ctx["creds"]=array($comps['user'],$comps['password']);
181
+ }
182
+ return $ctx;
183
+ }
184
+
185
+ public function destroyContext($context)
186
+ {
187
+ curl_close($context["curlhandle"]);
188
+ }
189
+
190
+
191
+ public function urlExists($remoteurl)
192
+ {
193
+ $context = $this->createContext($remoteurl);
194
+ // assume existing urls
195
+ if (!isset($context["opts"]["lookup"]))
196
+ {
197
+ return true;
198
+ }
199
+ $ch=$context["curlhandle"];
200
+ $opts=$context["opts"]["lookup"];
201
+ $this->setAuthOptions($context,$opts);
202
+ // optimized lookup through curl
203
+ curl_setopt_array($ch, $opts);
204
+
205
+
206
+ /* Get the HTML or whatever is linked in $url. */
207
+ $response = curl_exec($ch);
208
+ if ($context['scheme'] == "http" || $context['scheme'] == "https")
209
+ {
210
+ /* Check for 404 (file not found). */
211
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
212
+ $exists = ($httpCode < 400);
213
+ /* retry on error */
214
+
215
+ if ($httpCode == 503 or $httpCode == 403)
216
+ {
217
+ /* wait for a half second */
218
+ usleep(500000);
219
+ $response = curl_exec($ch);
220
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
221
+ $exists = ($httpCode < 400);
222
+ }
223
+ }
224
+ $this->destroyContext($context);
225
+ return $exists;
226
+ }
227
+
228
+ // using credentials
229
+ public function setCredentials($user = null, $passwd = null)
230
+ {
231
+ $this->_user = $user;
232
+ $this->_password = $passwd;
233
+ }
234
+
235
+ // using cookie
236
+ public function setCookie($cookie = null)
237
+ {
238
+ $this->_cookie = $cookie;
239
+ }
240
+
241
+ public function copyRemoteFile($url, $dest)
242
+ {
243
+ $result = false;
244
+ $this->_errors=array();
245
+ try
246
+ {
247
+ $result = $this->getRemoteFile($url, $dest, $this->_cookie);
248
+ }
249
+ catch (Exception $e)
250
+ {
251
+ $this->_errors = array("type"=>"source error","message"=>$e->getMessage(),"exception"=>$e);
252
+ }
253
+ return $result;
254
+ }
255
+
256
+ public function setURLOptions($url, &$optab)
257
+ {
258
+ $optab[CURLOPT_URL] = $url;
259
+ }
260
+
261
+
262
+ public function getRemoteFile($url, $dest, $authmode = null, $cookies = null)
263
+ {
264
+ $context = $this->createContext($url);
265
+ $ch=$context['curlhandle'];
266
+ $dl_opts = $context['opts']['dl'];
267
+ $outname = $dest;
268
+
269
+ if ($cookies)
270
+ {
271
+
272
+ if (substr($url, 0, 4) == "http")
273
+ {
274
+ $dl_opts[CURLOPT_COOKIE] = $cookies;
275
+ }
276
+ }
277
+
278
+ $fp = fopen($outname, "w");
279
+ if ($fp == false)
280
+ {
281
+ $this->destroyContext($context);
282
+ throw new Exception("Cannot write file:$outname");
283
+ }
284
+ $dl_opts[CURLOPT_FILE] = $fp;
285
+ $this->setURLOptions($url, $dl_opts);
286
+ $this->setAuthOptions($context,$dl_opts);
287
+
288
+
289
+ // Download the file , force expect to nothing to avoid buffer save problem
290
+ curl_setopt_array($ch, $dl_opts);
291
+ $inf = curl_getinfo($ch);
292
+ if (!curl_exec($ch))
293
+ {
294
+ if (curl_error($ch) != "")
295
+ {
296
+ $err = "Cannot fetch $url :" . curl_error($ch);
297
+ }
298
+ else
299
+ {
300
+ $err = "CURL Error downloading $url";
301
+ }
302
+ $this->destroyContext($context);
303
+ fclose($fp);
304
+ unlink($dest);
305
+ throw new Exception($err);
306
+ }
307
+ else
308
+ {
309
+ $proto=$context['scheme'];
310
+ if($proto=='http' || $proto=='https')
311
+ {
312
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
313
+ $ok = ($httpCode < 400);
314
+ if(!$ok)
315
+ {
316
+ fclose($fp);
317
+ @unlink($outname);
318
+ throw new Exception('Cannot fetch URL :'.$url);
319
+ }
320
+ }
321
+ }
322
+
323
+ fclose($fp);
324
+ $this->destroyContext($context);
325
+
326
+ return true;
327
+ }
328
+ }
329
+
330
+ class URLFopen_RemoteFileGetter extends RemoteFileGetter
331
+ {
332
+
333
+ public function urlExists($url)
334
+ {
335
+ $fname = $url;
336
+ $h = @fopen($fname, "r");
337
+ if ($h !== false)
338
+ {
339
+ $exists = true;
340
+ fclose($h);
341
+ }
342
+ unset($h);
343
+ }
344
+
345
+ public function copyRemoteFile($url, $dest)
346
+ {
347
+ if (!$this->urlExists($url))
348
+ {
349
+ $this->_errors = array("type"=>"target error","message"=>"URL $url is unreachable");
350
+ return false;
351
+ }
352
+
353
+ $ok = @copy($url, $dest);
354
+ if (!$ok)
355
+ {
356
+ $this->_errors = error_get_last();
357
+ }
358
+ return $ok;
359
+ }
360
+ }
361
+
362
+ class RemoteFileGetterFactory
363
+ {
364
+ private static $__fginsts = array();
365
+
366
+ public static function getFGInstance($id = "default")
367
+ {
368
+ if (!isset(self::$__fginsts[$id]))
369
+ {
370
+ if (function_exists("curl_init"))
371
+ {
372
+ self::$__fginsts[$id] = new CURL_RemoteFileGetter();
373
+ }
374
+ else
375
+ {
376
+ self::$__fginsts[$id] = new URLFopen_RemoteFileGetter();
377
+ }
378
+ }
379
+ return self::$__fginsts[$id];
380
+ }
381
+ }
app/code/community/Osf/Synnex/lib/magmi/inc/timecounter.php ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Time counter class
5
+ * This class provides a way to measure :
6
+ *
7
+ * - time
8
+ * - counters
9
+ *
10
+ * store results into many categories for many timing aspects
11
+ *
12
+ * It is based on 2 levels :
13
+ * - sources : labels under which timing categories will be stored, it is a label, most of the time, a method name
14
+ * - categories : for each source, you can store many infos like "inDB","processing" and so on....
15
+ *
16
+ * time measure can be divided into phases, so you can measure different subparts of your processing like "initialization","lookup" aso....
17
+ * many counters can also be defined at the same category leve
18
+ * At the end, the timecounter will give this kind of results
19
+ *
20
+ * + cat1
21
+ * +timers
22
+ * + phase1 => time for phase 1 of cat1 of source 1
23
+ * + phase2 => time for phase 2 of cat1 of source 1
24
+ * +counters
25
+ * + counter1 => value of counter 1 of cat 1 of source 1
26
+ * + cat2
27
+ *
28
+ * Sources, categories & phases could be added dynamically, each time a new phase/counter/category or source is
29
+ * declared trough calls to addCounter, initTime,exitTime that does not exist yet, a container is created for it.
30
+ *
31
+ * sources are tags. categories are more like containers
32
+ */
33
+ class TimeCounter
34
+ {
35
+ protected $_timingcats = array();
36
+ protected $_defaultsrc = "";
37
+ protected $_timingcontext = array();
38
+
39
+ /**
40
+ * Constructor
41
+ *
42
+ * @param string $defaultsrc
43
+ * : default timing source
44
+ */
45
+ public function __construct($defaultsrc = "*")
46
+ {
47
+ $this->_defaultsrc = $defaultsrc;
48
+ }
49
+
50
+ /**
51
+ * Initializes default timing categories to use
52
+ *
53
+ * @param unknown $tcats
54
+ * array of timing categories
55
+ */
56
+ public function initTimingCats($tcats)
57
+ {
58
+ foreach ($tcats as $tcat)
59
+ {
60
+ $this->_timingcats[$tcat] = array("_counters"=>array(),"_timers"=>array());
61
+ }
62
+ }
63
+
64
+ /**
65
+ * returns the content for a given timing category name
66
+ *
67
+ * @param string $cat
68
+ * : timing category name
69
+ * @return array informations for given category
70
+ */
71
+ public function getTimingCategory($cat)
72
+ {
73
+ return $this->_timingcats[$cat];
74
+ }
75
+
76
+ /**
77
+ * return all timers
78
+ *
79
+ * @return all timers info by category
80
+ */
81
+ public function getTimers()
82
+ {
83
+ $timers = array();
84
+ foreach ($this->_timingcats as $cname => $info)
85
+ {
86
+ $timers[$cname] = $info['_timers'];
87
+ }
88
+ return $timers;
89
+ }
90
+
91
+ /**
92
+ * return all counters
93
+ *
94
+ * @return all counters info by category
95
+ */
96
+ public function getCounters()
97
+ {
98
+ $counters = array();
99
+ foreach ($this->_timingcats as $cname => $info)
100
+ {
101
+ $counters[$cname] = $info['_counters'];
102
+ }
103
+ return $counters;
104
+ }
105
+
106
+ /**
107
+ * creates a new counter
108
+ *
109
+ * @param string $cname
110
+ * : counter name
111
+ * @param string $tcats
112
+ * : array of category names to add counter to, if null => all categories
113
+ */
114
+ public function addCounter($cname, $tcats = null)
115
+ {
116
+ if ($tcats == null)
117
+ {
118
+ $tcats = array_keys($this->_timingcats);
119
+ }
120
+
121
+ foreach ($tcats as $tcat)
122
+ {
123
+ if (!isset($this->_timingcats[$tcat]))
124
+ {
125
+ $this->_timingcats[$tcat] = array("_counters"=>array(),"_timers"=>array());
126
+ }
127
+ $this->_timingcats[$tcat]["_counters"][$cname] = 0;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * initializes a new counter
133
+ *
134
+ * @param string $cname
135
+ * : counter name
136
+ * @param string $tcats
137
+ * : array of category names to initialize counter for, if null => all categories
138
+ */
139
+ public function initCounter($cname, $tcats = null)
140
+ {
141
+ if ($tcats == null)
142
+ {
143
+ $tcats = array_keys($this->_timingcats);
144
+ }
145
+ foreach ($tcats as $tcat)
146
+ {
147
+ $this->_timingcats[$tcat]["_counters"][$cname] = 0;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * increments a counter
153
+ *
154
+ * @param string $cname
155
+ * : counter name
156
+ * @param string $tcats
157
+ * : array of category names to initialize counter for, if null => all categories
158
+ */
159
+ public function incCounter($cname, $tcats = null)
160
+ {
161
+ if ($tcats == null)
162
+ {
163
+ $tcats = array_keys($this->_timingcats);
164
+ }
165
+ foreach ($tcats as $tcat)
166
+ {
167
+ if (!isset($this->_timingcats[$tcat]["_counters"][$cname]))
168
+ {
169
+ $this->_timingcats[$tcat]["_counters"][$cname] = 0;
170
+ }
171
+ $this->_timingcats[$tcat]["_counters"][$cname]++;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Initializes a timer
177
+ *
178
+ * @param string $phase
179
+ * : timer phase to initialize
180
+ * @param string $src
181
+ * : source tag
182
+ * @param string $tcat
183
+ * : timing category to initialize timer for
184
+ */
185
+ public function initTime($phase = "global", $src = null, $tcat = null)
186
+ {
187
+ if (isset($src))
188
+ {
189
+ array_push($this->_timingcontext, $src);
190
+ $this->_timingcontext = array_values(array_unique($this->_timingcontext));
191
+ }
192
+ if (count($this->_timingcontext) == 0)
193
+ return;
194
+
195
+ if (!isset($tcat))
196
+ {
197
+ $tcats = $this->_timingcats;
198
+ }
199
+ else
200
+ {
201
+ $tcats = array($tcat=>$this->_timingcats[$tcat]);
202
+ }
203
+ $t = microtime(true);
204
+
205
+ foreach ($tcats as $tcat => $dummy)
206
+ {
207
+ if (!isset($this->_timingcats[$tcat]["_timers"][$phase]))
208
+ {
209
+ $this->_timingcats[$tcat]["_timers"][$phase] = array();
210
+ }
211
+ $ctxc = count($this->_timingcontext);
212
+ for ($i = 0; $i < $ctxc; $i++)
213
+ {
214
+ $src = $this->_timingcontext[$i];
215
+ if (!isset($this->_timingcats[$tcat]["_timers"][$phase][$src]))
216
+ {
217
+ $this->_timingcats[$tcat]["_timers"][$phase][$src] = array("init"=>$t,"dur"=>0);
218
+ }
219
+ $this->_timingcats[$tcat]["_timers"][$phase][$src]["start"] = $t;
220
+ }
221
+ }
222
+ }
223
+
224
+ /**
225
+ * closes a timer for a given phase on a given category for a given source
226
+ *
227
+ * @param unknown $phase
228
+ * : time phase to exit
229
+ * @param string $src
230
+ * : source tag
231
+ * @param string $tcat
232
+ * : timing category
233
+ */
234
+ public function exitTime($phase, $src = null, $tcat = null)
235
+ {
236
+ $targets = $this->_timingcontext;
237
+ if (count($targets) == 0)
238
+ {
239
+ return;
240
+ }
241
+ if (isset($src) && in_array($src, $this->_timingcontext))
242
+ {
243
+ $targets = array($src);
244
+ }
245
+
246
+ $ctargets = count($targets);
247
+
248
+ if ($ctargets == 0)
249
+ return;
250
+ if ($tcat == null)
251
+ {
252
+ $tcats = $this->_timingcats;
253
+ }
254
+ else
255
+ {
256
+ $tcats = array($tcat=>$this->_timingcats[$tcat]);
257
+ }
258
+ $end = microtime(true);
259
+ foreach ($tcats as $tcat => $phasetimes)
260
+ {
261
+ for ($i = 0; $i < $ctargets; $i++)
262
+ {
263
+ $src = $targets[$i];
264
+ if (isset($this->_timingcats[$tcat]["_timers"][$phase][$src]))
265
+ {
266
+ $this->_timingcats[$tcat]["_timers"][$phase][$src]["end"] = $end;
267
+ $this->_timingcats[$tcat]["_timers"][$phase][$src]["dur"] += $end -
268
+ $this->_timingcats[$tcat]["_timers"][$phase][$src]["start"];
269
+ }
270
+ else
271
+ {
272
+ echo "Invalid timing source : $src";
273
+ }
274
+ }
275
+ }
276
+ $this->_timingcontext = array_diff($this->_timingcontext, $targets);
277
+ }
278
+ }
app/code/community/Osf/Synnex/lib/magmi/integration/inc/magmi_datapump.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once ("properties.php");
3
+
4
+ class Magmi_DataPumpFactory
5
+ {
6
+ protected static $_factoryprops = null;
7
+
8
+ static function getDataPumpInstance($pumptype)
9
+ {
10
+ if (self::$_factoryprops == null)
11
+ {
12
+ self::$_factoryprops = new Properties();
13
+ self::$_factoryprops->load(dirname(__FILE__) . DIRSEP . "pumpfactory.ini");
14
+ }
15
+ $pumpinfo = self::$_factoryprops->get("DATAPUMPS", $pumptype, "");
16
+ $arr = explode("::", $pumpinfo);
17
+ if (count($arr) == 2)
18
+ {
19
+ $pumpfile = $arr[0];
20
+ $pumpclass = $arr[1];
21
+
22
+ try
23
+ {
24
+ require_once (dirname(__FILE__) . DIRSEP . "$pumpfile.php");
25
+ $pumpinst = new $pumpclass();
26
+ }
27
+ catch (Exception $e)
28
+ {
29
+ $pumpinst = null;
30
+ }
31
+ }
32
+ else
33
+ {
34
+ echo "Invalid Pump Type";
35
+ }
36
+ return $pumpinst;
37
+ }
38
+ }
app/code/community/Osf/Synnex/lib/magmi/integration/inc/magmi_datapumpdatasource.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This is a fake datasource for datapump, it just does nothing ;)
5
+ * @author dweeves
6
+ *
7
+ */
8
+ class Magmi_DatapumpDS extends Magmi_Datasource
9
+ {
10
+
11
+ public function getPluginInfo()
12
+ {
13
+ return array("name"=>"DataPump Datasource","author"=>"Dweeves","version"=>"1.0.0");
14
+ }
15
+ }
app/code/community/Osf/Synnex/lib/magmi/integration/inc/productimport_datapump.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once ("magmi_productimportengine.php");
3
+
4
+ class Magmi_ProductImport_DataPump
5
+ {
6
+ protected $_engine = null;
7
+ protected $_params = array();
8
+ protected $_logger = null;
9
+ protected $_importcolumns = array();
10
+ protected $_defaultvalues = array();
11
+ protected $_stats;
12
+ protected $_crow;
13
+ protected $_rstep = 100;
14
+ protected $_mdpatched = false;
15
+
16
+ public function __construct()
17
+ {
18
+ $this->_engine = new Magmi_ProductImportEngine();
19
+ $this->_engine->setBuiltinPluginClasses("*datasources",
20
+ dirname(__FILE__) . DIRSEP . "magmi_datapumpdatasource.php::Magmi_DatapumpDS");
21
+
22
+ $this->_stats["tstart"] = microtime(true);
23
+ // differential
24
+ $this->_stats["tdiff"] = $this->_stats["tstart"];
25
+ }
26
+
27
+ public function setReportingStep($rstep)
28
+ {
29
+ $this->_rstep = $rstep;
30
+ }
31
+
32
+ public function beginImportSession($profile, $mode, $logger = null)
33
+ {
34
+ $this->_engine->setLogger($logger);
35
+ $this->_engine->initialize();
36
+ $this->_params = array("profile"=>$profile,"mode"=>$mode);
37
+ $this->_engine->engineInit($this->_params);
38
+ $this->_engine->initImport($this->_params);
39
+ // intermediary report step
40
+ $this->_engine->initDbqStats();
41
+ $pstep = $this->_engine->getProp("GLOBAL", "step", 0.5);
42
+ // read each line
43
+ $this->_stats["lastrec"] = 0;
44
+ $this->_stats["lastdbtime"] = 0;
45
+ $this->crow = 0;
46
+ }
47
+
48
+ public function getEngine()
49
+ {
50
+ return $this->_engine;
51
+ }
52
+
53
+ public function setDefaultValues($dv = array())
54
+ {
55
+ $this->_defaultvalues = $dv;
56
+ }
57
+
58
+ public function ingest($item = array())
59
+ {
60
+ $item = array_merge($this->_defaultvalues, $item);
61
+ $diff = array_diff(array_keys($item), $this->_importcolumns);
62
+ if (count($diff) > 0)
63
+ {
64
+ $this->_importcolumns = array_keys($item);
65
+ // process columns
66
+ $this->_engine->callPlugins("itemprocessors", "processColumnList", $this->_importcolumns);
67
+ $this->_engine->initAttrInfos($this->_importcolumns);
68
+ }
69
+ $res = $this->_engine->processDataSourceLine($item, $this->_rstep, $this->_stats["tstart"],
70
+ $this->_stats["tdiff"], $this->_stats["lastdbtime"], $this->stats["lastrec"]);
71
+ return $res;
72
+ }
73
+
74
+ public function endImportSession()
75
+ {
76
+ $this->_engine->reportStats($this->_engine->getCurrentRow(), $this->_stats["tstart"], $this->_stats["tdiff"],
77
+ $this->_stats["lastdbtime"], $this->stats["lastrec"]);
78
+ $skustats = $this->_engine->getSkuStats();
79
+ $this->_engine->log("Skus imported OK:" . $skustats["ok"] . "/" . $skustats["nsku"], "info");
80
+ if ($skustats["ko"] > 0)
81
+ {
82
+ $this->_engine->log("Skus imported KO:" . $skustats["ko"] . "/" . $skustats["nsku"], "warning");
83
+ }
84
+
85
+ $this->_engine->exitImport();
86
+ }
87
+ }
app/code/community/Osf/Synnex/lib/magmi/integration/inc/pumpfactory.ini ADDED
@@ -0,0 +1,2 @@
 
 
1
+ [DATAPUMPS]
2
+ productimport=productimport_datapump::Magmi_ProductImport_Datapump
app/code/community/Osf/Synnex/lib/magmi/plugins/base/general/optimizer/magmi_optimizer_plugin.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Magmi_OptimizerPlugin extends Magmi_GeneralImportPlugin
4
+ {
5
+
6
+ public function getPluginInfo()
7
+ {
8
+ return array("name"=>"Magmi Optimizer","author"=>"Dweeves","version"=>"1.0.5",
9
+ "url"=>$this->pluginDocUrl("Magmi_Optimizer"));
10
+ }
11
+
12
+ public function beforeImport()
13
+ {
14
+ $tbls = array("eav_attribute_option_value"=>array("value","MAGMI_EAOV_OPTIMIZATION_IDX"),
15
+ "catalog_product_entity_media_gallery"=>array("value","MAGMI_CPEM_OPTIMIZATION_IDX"),
16
+ "catalog_category_entity_varchar"=>array("value","MAGMI_CCEV_OPTIMIZATION_IDX"),
17
+ "eav_attribute"=>array("attribute_code","MAGMI_EA_CODE_OPTIMIZATION_IDX"));
18
+ $this->log("Optimizing magmi", "info");
19
+ foreach ($tbls as $tblname => $idxinfo)
20
+ {
21
+ try
22
+ {
23
+ $t = $this->tablename($tblname);
24
+ $this->log("Adding index {$idxinfo[1]} on $t", "info");
25
+ $sql = "ALTER TABLE $t ADD INDEX {$idxinfo[1]} (`{$idxinfo[0]}`)";
26
+ $this->exec_stmt($sql);
27
+ }
28
+ catch (Exception $e)
29
+ {
30
+ // ignore exception
31
+ $this->log("Already optmized!", "info");
32
+ }
33
+ }
34
+ return true;
35
+ }
36
+
37
+ public function initialize($params)
38
+ {}
39
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/base/general/reindex/magmi_reindexing_plugin.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Magmi_ReindexingPlugin extends Magmi_GeneralImportPlugin
4
+ {
5
+ protected $_reindex;
6
+ protected $_indexlist = "catalog_product_attribute,catalog_product_price,catalog_product_flat,catalog_category_flat,catalog_category_product,cataloginventory_stock,catalog_url,catalogsearch_fulltext,tag_summary";
7
+ protected $_mdh;
8
+
9
+ public function getPluginInfo()
10
+ {
11
+ return array("name"=>"Magmi Magento Reindexer","author"=>"Dweeves","version"=>"1.0.3a",
12
+ "url"=>$this->pluginDocUrl("Magmi_Magento_Reindexer"));
13
+ }
14
+
15
+ public function afterImport()
16
+ {
17
+ $this->fixFlat();
18
+ $this->log("running indexer", "info");
19
+ $this->updateIndexes();
20
+ return true;
21
+ }
22
+
23
+ public function OptimEav()
24
+ {
25
+ $tables = array("catalog_product_entity_varchar","catalog_product_entity_int","catalog_product_entity_text",
26
+ "catalog_product_entity_decimal","catalog_product_entity_datetime","catalog_product_entity_media_gallery",
27
+ "catalog_product_entity_tier_price");
28
+
29
+ $cpe = $this->tablename('catalog_product_entity');
30
+ $this->log("Optmizing EAV Tables...", "info");
31
+ foreach ($tables as $t)
32
+ {
33
+ $this->log("Optmizing $t....", "info");
34
+ $sql = "DELETE ta.* FROM " . $this->tablename($t) . " as ta
35
+ LEFT JOIN $cpe as cpe on cpe.entity_id=ta.entity_id
36
+ WHERE ta.store_id=0 AND cpe.entity_id IS NULL";
37
+ $this->delete($sql);
38
+ $this->log("$t optimized", "info");
39
+ }
40
+ }
41
+
42
+ public function fixFlat()
43
+ {
44
+ $this->log("Cleaning flat tables before reindex...", "info");
45
+ $stmt = $this->exec_stmt("SHOW TABLES LIKE '" . $this->tablename('catalog_product_flat') . "%'", NULL, false);
46
+ while ($row = $stmt->fetch(PDO::FETCH_NUM))
47
+ {
48
+ $tname = $row[0];
49
+ // removing records in flat tables that are no more linked to entries in catalog_product_entity table
50
+ // for some reasons, this seem to happen
51
+ $sql = "DELETE cpf.* FROM $tname as cpf
52
+ LEFT JOIN " . $this->tablename('catalog_product_entity') . " as cpe ON cpe.entity_id=cpf.entity_id
53
+ WHERE cpe.entity_id IS NULL";
54
+ $this->delete($sql);
55
+ }
56
+ }
57
+
58
+ public function getPluginParamNames()
59
+ {
60
+ return array("REINDEX:indexes","REINDEX:phpcli");
61
+ }
62
+
63
+ public function getIndexList()
64
+ {
65
+ return $this->_indexlist;
66
+ }
67
+
68
+ public function updateIndexes()
69
+ {
70
+ // make sure we are not in session
71
+ if (session_id() !== "")
72
+ {
73
+ session_write_close();
74
+ }
75
+ $cl = $this->getParam("REINDEX:phpcli") . " shell/indexer.php";
76
+ $idxlstr = $this->getParam("REINDEX:indexes", "");
77
+ $idxlist = explode(",", $idxlstr);
78
+ if (count($idxlist) == 0)
79
+ {
80
+ $this->log("No indexes selected , skipping reindexing...", "warning");
81
+ return true;
82
+ }
83
+ foreach ($idxlist as $idx)
84
+ {
85
+ $tstart = microtime(true);
86
+ $this->log("Reindexing $idx....", "info");
87
+
88
+ // Execute Reindex command, and specify that it should be ran from Magento directory
89
+ $out = $this->_mdh->exec_cmd($cl, "--reindex $idx", $this->_mdh->getMagentoDir());
90
+ $this->log($out, "info");
91
+ $tend = microtime(true);
92
+ $this->log("done in " . round($tend - $tstart, 2) . " secs", "info");
93
+ if (Magmi_StateManager::getState() == "canceled")
94
+ {
95
+ exit();
96
+ }
97
+ flush();
98
+ }
99
+ }
100
+
101
+ public function isRunnable()
102
+ {
103
+ return array(FSHelper::getExecMode() != null,"");
104
+ }
105
+
106
+ public function initialize($params)
107
+ {
108
+ $magdir = Magmi_Config::getInstance()->getMagentoDir();
109
+ $this->_mdh = MagentoDirHandlerFactory::getInstance()->getHandler($magdir);
110
+ $this->log("Using execution mode :" . $this->_mdh->getexecmode(), "startup");
111
+ }
112
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/base/general/reindex/options_panel.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="plugin_description">This plugin calls magento reindex script
3
+ via calling php cli. please ensure security configuration enable
4
+ "shell_exec()" calls from php</div>
5
+
6
+ <div class="formline">
7
+ <span class="label">PHP CLI command</span> <span class="value"><input
8
+ type="text" name="REINDEX:phpcli"
9
+ value="<?php echo $this->getParam("REINDEX:phpcli","php")?>"></input></span>
10
+ </div>
11
+ <hr />
12
+ <input type="hidden" name="REINDEX:indexes" id="indexes"
13
+ value="<?php echo $this->getParam("REINDEX:indexes")?>"></input>
14
+ <div>
15
+ <a name="REINDEX:config"></a> <span>Indexing:</span><a
16
+ href="#REINDEX:config" onclick="fcheck(1);">All</a>&nbsp;<a
17
+ href="#REINDEX:config" onclick="fcheck(0)">None</a>
18
+ <ul>
19
+ <?php
20
+ $idxarr = explode(",", $this->_plugin->getIndexList());
21
+ $indexes = explode(",", $this->getParam("REINDEX:indexes"));
22
+ foreach ($idxarr as $indexname)
23
+ {
24
+ ?>
25
+ <li><input type="checkbox" name="<?php echo $indexname?>"
26
+ class="_magindex" <?php if(in_array($indexname,$indexes)){?>
27
+ checked=checked <?php }?>><?php echo $indexname?></li>
28
+ <?php }?>
29
+ </ul>
30
+ </div>
31
+ <script type="text/javascript">
32
+ getIndexes=function()
33
+ {
34
+ var outs=[];
35
+ $$('._magindex').each(function(it){if(it.checked){outs.push(it.name)}});
36
+ return outs.join(",");
37
+ };
38
+
39
+
40
+
41
+ fcheck=function(t)
42
+ {
43
+ $$('._magindex').each(function(it){it.checked=t});
44
+ updateIndexes();
45
+
46
+ }
47
+
48
+ updateIndexes=function()
49
+ {
50
+ $('indexes').value=getIndexes();
51
+ }
52
+
53
+ $$('._magindex').each(function(it){it.observe('click',updateIndexes)});
54
+ </script>
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/columnmapper/000_columnmapper.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class SampleItemProcessor
5
+ * @author dweeves
6
+ *
7
+ * This class is a sample for item processing
8
+ */
9
+ class ColumnMappingItemProcessor extends Magmi_ItemProcessor
10
+ {
11
+ protected $_dcols = array();
12
+
13
+ public function getPluginInfo()
14
+ {
15
+ return array("name"=>"Column mapper","author"=>"Dweeves","version"=>"0.0.3b",
16
+ "url"=>$this->pluginDocUrl("Column_mapper"));
17
+ }
18
+
19
+ /**
20
+ * you can add/remove columns for the item passed since it is passed by reference
21
+ *
22
+ * @param Magmi_Engine $mmi
23
+ * : reference to magmi engine(convenient to perform database operations)
24
+ * @param unknown_type $item
25
+ * : modifiable reference to item before import
26
+ * the $item is a key/value array with column names as keys and values as read from csv file.
27
+ * @return bool :
28
+ * true if you want the item to be imported after your custom processing
29
+ * false if you want to skip item import after your processing
30
+ */
31
+ public function processColumnList(&$cols, $params = null)
32
+ {
33
+ $icols = $cols;
34
+ $ocols = array();
35
+ $scols = array();
36
+ foreach ($icols as $cname)
37
+ {
38
+ if (isset($this->_dcols[$cname]))
39
+ {
40
+ $mlist = array_unique(explode(",", $this->_dcols[$cname]));
41
+ $ncol = array_shift($mlist);
42
+ $ocols[] = $ncol;
43
+ if ($ncol != $cname)
44
+ {
45
+ $this->log("Replacing Column $cname by $ncol", "startup");
46
+ }
47
+ if (count($mlist) > 0)
48
+ {
49
+ $scols = array_merge($scols, $mlist);
50
+ $this->log("Replicating Column $cname to " . implode(",", $mlist), "startup");
51
+ }
52
+ }
53
+ else
54
+ {
55
+ $ocols[] = $cname;
56
+ }
57
+ }
58
+ $ocols = array_unique(array_merge($ocols, $scols));
59
+ $cols = $ocols;
60
+ return true;
61
+ }
62
+
63
+ public function processItemBeforeId(&$item, $params = null)
64
+ {
65
+ foreach ($this->_dcols as $oname => $mnames)
66
+ {
67
+ if (isset($item[$oname]))
68
+ {
69
+ $mapped = explode(",", $mnames);
70
+ foreach ($mapped as $mname)
71
+ {
72
+ $mnane = trim($mname);
73
+ $item[$mname] = $item[$oname];
74
+ }
75
+ if (!in_array($oname, $mapped))
76
+ {
77
+ unset($item[$oname]);
78
+ }
79
+ }
80
+ }
81
+ return true;
82
+ }
83
+
84
+ public function initialize($params)
85
+ {
86
+ foreach ($params as $k => $v)
87
+ {
88
+ if (preg_match_all("/^CMAP:(.*)$/", $k, $m) && $k != "CMAP:columnlist")
89
+ {
90
+ $colname = rawurldecode($m[1][0]);
91
+ $this->_dcols[$colname] = $params[$k];
92
+ }
93
+ }
94
+ }
95
+
96
+ public function getPluginParams($params)
97
+ {
98
+ $pp = array();
99
+ foreach ($params as $k => $v)
100
+ {
101
+ if (preg_match("/^CMAP:.*$/", $k))
102
+ {
103
+ $pp[$k] = $v;
104
+ }
105
+ }
106
+ return $pp;
107
+ }
108
+
109
+ static public function getCategory()
110
+ {
111
+ return "Input Data Preprocessing";
112
+ }
113
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/columnmapper/options_panel.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="plugin_description">
2
+ This plugin enables to change column names from datasource before they
3
+ are handled by magmi. enter columns to set new name for in mapped
4
+ column list field, separated by commas (,) when leaving the field, new
5
+ fields will be inserted for filling new column names. <b>You can put
6
+ several values (comma separated) in the mapped column names,doing so ,
7
+ the column mapper will replicate values of column to map to all mapped
8
+ columns !!!</b>
9
+ </div>
10
+ <?php $clist=$this->fixListParam($this->getParam("CMAP:columnlist"))?>
11
+ <div>
12
+ <ul class="formline">
13
+ <li class="label">Mapped columns list</li>
14
+ <li class="value"><input type="text" id="CMAP:columnlist"
15
+ name="CMAP:columnlist" size="80" value="<?php echo $clist?>"
16
+ onblur="cmap_mf.buildparamlist()"></input></li>
17
+ </ul>
18
+ <div id="CMAP:columnsetup"></div>
19
+ </div>
20
+ <script type="text/javascript">
21
+ var cm_vals=<?php echo tdarray_to_js($this,'CMAP:columnlist','CMAP')?>;
22
+ var cm_linetpl='<ul class="formline"><li class="label">New name for col {fieldname}</li><li class="value"><input type="text" name="CMAP:{fieldname.enc}" value="{value}"></input></li></ul>';
23
+ cmap_mf=new magmi_multifield('CMAP:columnlist','CMAP:columnsetup',cm_linetpl,cm_vals);
24
+ cmap_mf.buildparamlist();
25
+ </script>
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/defaultvalues/00_default_values.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class SampleItemProcessor
5
+ * @author dweeves
6
+ *
7
+ * This class is a sample for item processing
8
+ */
9
+ class DefaultValuesItemProcessor extends Magmi_ItemProcessor
10
+ {
11
+ protected $_dset = array();
12
+ protected $_dcols = array();
13
+
14
+ public function getPluginInfo()
15
+ {
16
+ return array("name"=>"Default Values setter","author"=>"Dweeves","version"=>"0.0.5",
17
+ "url"=>$this->pluginDocUrl("Default_Values_setter"));
18
+ }
19
+
20
+ /**
21
+ * you can add/remove columns for the item passed since it is passed by reference
22
+ *
23
+ * @param Magmi_Engine $mmi
24
+ * : reference to magmi engine instance (convenient to perform database operations)
25
+ * @param unknown_type $item
26
+ * : modifiable reference to item before import
27
+ * the $item is a key/value array with column names as keys and values as read from csv file.
28
+ * @return bool :
29
+ * true if you want the item to be imported after your custom processing
30
+ * false if you want to skip item import after your processing
31
+ */
32
+ public function processItemBeforeId(&$item, $params = null)
33
+ {
34
+ foreach ($this->_dcols as $col)
35
+ {
36
+ $item[$col] = $this->_dset[$col];
37
+ }
38
+ return true;
39
+ }
40
+
41
+ public function processItemAfterId(&$item, $params = null)
42
+ {
43
+ return true;
44
+ }
45
+
46
+ /*
47
+ * public function processItemException(&$item,$params=null) { }
48
+ */
49
+ public function initialize($params)
50
+ {
51
+ foreach ($params as $k => $v)
52
+ {
53
+ if (preg_match_all("/^DEFAULT:(.*)$/", $k, $m) && $k != "DEFAULT:columnlist")
54
+ {
55
+ $this->_dset[$m[1][0]] = $params[$k];
56
+ }
57
+ }
58
+ }
59
+
60
+ public function getPluginParams($params)
61
+ {
62
+ $pp = array();
63
+ foreach ($params as $k => $v)
64
+ {
65
+ if (preg_match("/^DEFAULT:.*$/", $k))
66
+ {
67
+ $pp[$k] = $v;
68
+ }
69
+ }
70
+ return $pp;
71
+ }
72
+
73
+ public function processColumnList(&$cols, $params = null)
74
+ {
75
+ $dcols = array_diff(array_keys($this->_dset), array_intersect($cols, array_keys($this->_dset)));
76
+ foreach ($dcols as $col)
77
+ {
78
+ if (!empty($this->_dset[$col]))
79
+ {
80
+ $cols[] = $col;
81
+ $this->_dcols[] = $col;
82
+ }
83
+ }
84
+ $this->log("Adding Columns " . implode(",", $dcols), "startup");
85
+
86
+ return true;
87
+ }
88
+
89
+ static public function getCategory()
90
+ {
91
+ return "Input Data Preprocessing";
92
+ }
93
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/defaultvalues/options_panel.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php $clist=$this->fixListParam($this->getParam("DEFAULT:columnlist"))?>
2
+ <div class="plugin_description">This plugin enables to set some default
3
+ item values if not found in input source. enter columns to set default
4
+ value for in default attribute list field, separated by commas (,) when
5
+ leaving the field, new fields will be inserted for filling default
6
+ values.</div>
7
+ <div>
8
+ <ul class="formline">
9
+ <li class="label">Default attribute list</li>
10
+ <li class="value"><input type="text" id="DEFAULT:columnlist"
11
+ name="DEFAULT:columnlist" size="80" value="<?php echo $clist?>"
12
+ onblur="default_mf.buildparamlist()"></input></li>
13
+ </ul>
14
+ <div style="position: relative">
15
+ <div id="DEFAULT:columnsetup"></div>
16
+ </div>
17
+ </div>
18
+ <script type="text/javascript">
19
+ var df_vals=<?php echo tdarray_to_js($this,'DEFAULT:columnlist','DEFAULT')?>;
20
+ var df_linetpl='<ul class="formline"><li class="label">Default {fieldname}</li><li class="value"><input type="text" name="DEFAULT:{fieldname.enc}" value="{value}"></input></li></ul>';
21
+ default_mf=new magmi_multifield('DEFAULT:columnlist','DEFAULT:columnsetup',df_linetpl,df_vals);
22
+ default_mf.buildparamlist();
23
+ </script>
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/02_genericmapper.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class SampleItemProcessor
5
+ * @author dweeves
6
+ *
7
+ * This class is a sample for item processing
8
+ */
9
+ class GenericMapperProcessor extends Magmi_ItemProcessor
10
+ {
11
+ protected $_mapping;
12
+
13
+ public function getPluginInfo()
14
+ {
15
+ return array("name"=>"Generic mapper","author"=>"Dweeves","version"=>"0.0.6a",
16
+ "url"=>$this->pluginDocUrl("Generic_mapper"));
17
+ }
18
+
19
+ /**
20
+ * you can add/remove columns for the item passed since it is passed by reference
21
+ *
22
+ * @param Magmi_Engine $mmi
23
+ * : reference to magmi engine instance (convenient to perform database operations)
24
+ * @param unknown_type $item
25
+ * : modifiable reference to item before import
26
+ * the $item is a key/value array with column names as keys and values as read from csv file.
27
+ * @return bool :
28
+ * true if you want the item to be imported after your custom processing
29
+ * false if you want to skip item import after your processing
30
+ */
31
+ public function processItemBeforeId(&$item, $params = null)
32
+ {
33
+ foreach (array_keys($item) as $k)
34
+ {
35
+ $mapped = false;
36
+ if (isset($this->_mapping["$k.csv"]))
37
+ {
38
+ $mpd = $this->_mapping["$k.csv"]["DIRECT"];
39
+ if (isset($mpd[$item[$k]]))
40
+ {
41
+ $item[$k] = $mpd[$item[$k]];
42
+ $mapped = true;
43
+ }
44
+ else
45
+ {
46
+ $mpr = $this->_mapping["$k.csv"]["RE"];
47
+ foreach ($mpr as $re => $value)
48
+ {
49
+ if (preg_match("|$re|msi", $item[$k]))
50
+ {
51
+ $item[$k] = preg_replace("|$re|", $value, $item[$k]);
52
+ $mapped = true;
53
+ break;
54
+ }
55
+ }
56
+ }
57
+ }
58
+ // f not found,try common mappings
59
+ if (!$mapped)
60
+ {
61
+ $mpd = $this->_mapping["__common__.csv"]["DIRECT"];
62
+ if (isset($mpd[$item[$k]]))
63
+ {
64
+ $item[$k] = $mpd[$item[$k]];
65
+ }
66
+ }
67
+ }
68
+ return true;
69
+ }
70
+
71
+ public function initialize($params)
72
+ {
73
+ $this->_mapping = array();
74
+
75
+ $dlist = glob(dirname(__file__) . "/mappings/default/*.csv");
76
+ if ($dlist == false)
77
+ {
78
+ $dlist = array();
79
+ $this->log("No default mapping found", "warning");
80
+ }
81
+ $slist = glob(dirname(__file__) . "/mappings/*.csv");
82
+ if ($slist == false)
83
+ {
84
+ $slist = array();
85
+ $this->log("No custom mapping found", "startup");
86
+ }
87
+ $flist = array_merge($dlist, $slist);
88
+ foreach ($flist as $fname)
89
+ {
90
+ $idx = basename($fname);
91
+ if (!isset($this->_mapping[$idx]))
92
+ {
93
+ $this->_mapping[$idx] = array("DIRECT"=>array(),"RE"=>array());
94
+ }
95
+ $mf = fopen("$fname", "r");
96
+ while (($data = fgetcsv($mf, 1000, ",")) !== FALSE)
97
+ {
98
+ if (substr($data[0], 0, 4) == "_RE:")
99
+ {
100
+ $target = "RE";
101
+ $key = substr($data[0], 4);
102
+ }
103
+ else
104
+ {
105
+ $target = "DIRECT";
106
+ $key = $data[0];
107
+ }
108
+ $this->_mapping[$idx][$target][$key] = $data[1];
109
+ }
110
+ }
111
+ }
112
+
113
+ static public function getCategory()
114
+ {
115
+ return "Input Data Preprocessing";
116
+ }
117
+ }
118
+
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/__common__.csv ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ "Yes",1
2
+ "No",0
3
+ "yes",1
4
+ "no",0
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/backorders.csv ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ "No Backorders","0"
2
+ "Allow Qty Below 0","1"
3
+ "Allow Qty Below 0 and Notify Customer","2"
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/country_of_manufacture.csv ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Afghanistan","AF"
2
+ "Åland Islands","AX"
3
+ "Albania","AL"
4
+ "Algeria","DZ"
5
+ "American Samoa","AS"
6
+ "Andorra","AD"
7
+ "Angola","AO"
8
+ "Anguilla","AI"
9
+ "Antarctica","AQ"
10
+ "Antigua and Barbuda","AG"
11
+ "Argentina","AR"
12
+ "Armenia","AM"
13
+ "Aruba","AW"
14
+ "Australia","AU"
15
+ "Austria","AT"
16
+ "Azerbaijan","AZ"
17
+ "Bahamas","BS"
18
+ "Bahrain","BH"
19
+ "Bangladesh","BD"
20
+ "Barbados","BB"
21
+ "Belarus","BY"
22
+ "Belgium","BE"
23
+ "Belize","BZ"
24
+ "Benin","BJ"
25
+ "Bermuda","BM"
26
+ "Bhutan","BT"
27
+ "Bolivia","BO"
28
+ "Bosnia and Herzegovina","BA"
29
+ "Botswana","BW"
30
+ "Bouvet Island","BV"
31
+ "Brazil","BR"
32
+ "British Indian Ocean Territory","IO"
33
+ "British Virgin Islands","VG"
34
+ "Brunei","BN"
35
+ "Bulgaria","BG"
36
+ "Burkina Faso","BF"
37
+ "Burundi","BI"
38
+ "Cambodia","KH"
39
+ "Cameroon","CM"
40
+ "Canada","CA"
41
+ "Cape Verde","CV"
42
+ "Cayman Islands","KY"
43
+ "Central African Republic","CF"
44
+ "Chad","TD"
45
+ "Chile","CL"
46
+ "China","CN"
47
+ "Christmas Island","CX"
48
+ "Cocos [Keeling] Islands","CC"
49
+ "Colombia","CO"
50
+ "Comoros","KM"
51
+ "Congo - Brazzaville","CG"
52
+ "Congo - Kinshasa","CD"
53
+ "Cook Islands","CK"
54
+ "Costa Rica","CR"
55
+ "Côte d’Ivoire","CI"
56
+ "Croatia","HR"
57
+ "Cuba","CU"
58
+ "Cyprus","CY"
59
+ "Czech Republic","CZ"
60
+ "Denmark","DK"
61
+ "Djibouti","DJ"
62
+ "Dominica","DM"
63
+ "Dominican Republic","DO"
64
+ "Ecuador","EC"
65
+ "Egypt","EG"
66
+ "El Salvador","SV"
67
+ "Equatorial Guinea","GQ"
68
+ "Eritrea","ER"
69
+ "Estonia","EE"
70
+ "Ethiopia","ET"
71
+ "Falkland Islands","FK"
72
+ "Faroe Islands","FO"
73
+ "Fiji","FJ"
74
+ "Finland","FI"
75
+ "France","FR"
76
+ "French Guiana","GF"
77
+ "French Polynesia","PF"
78
+ "French Southern Territories","TF"
79
+ "Gabon","GA"
80
+ "Gambia","GM"
81
+ "Georgia","GE"
82
+ "Germany","DE"
83
+ "Ghana","GH"
84
+ "Gibraltar","GI"
85
+ "Greece","GR"
86
+ "Greenland","GL"
87
+ "Grenada","GD"
88
+ "Guadeloupe","GP"
89
+ "Guam","GU"
90
+ "Guatemala","GT"
91
+ "Guernsey","GG"
92
+ "Guinea","GN"
93
+ "Guinea-Bissau","GW"
94
+ "Guyana","GY"
95
+ "Haiti","HT"
96
+ "Heard Island and McDonald Islands","HM"
97
+ "Honduras","HN"
98
+ "Hong Kong SAR China","HK"
99
+ "Hungary","HU"
100
+ "Iceland","IS"
101
+ "India","IN"
102
+ "Indonesia","ID"
103
+ "Iran","IR"
104
+ "Iraq","IQ"
105
+ "Ireland","IE"
106
+ "Isle of Man","IM"
107
+ "Israel","IL"
108
+ "Italy","IT"
109
+ "Jamaica","JM"
110
+ "Japan","JP"
111
+ "Jersey","JE"
112
+ "Jordan","JO"
113
+ "Kazakhstan","KZ"
114
+ "Kenya","KE"
115
+ "Kiribati","KI"
116
+ "Kuwait","KW"
117
+ "Kyrgyzstan","KG"
118
+ "Laos","LA"
119
+ "Latvia","LV"
120
+ "Lebanon","LB"
121
+ "Lesotho","LS"
122
+ "Liberia","LR"
123
+ "Libya","LY"
124
+ "Liechtenstein","LI"
125
+ "Lithuania","LT"
126
+ "Luxembourg","LU"
127
+ "Macau SAR China","MO"
128
+ "Macedonia","MK"
129
+ "Madagascar","MG"
130
+ "Malawi","MW"
131
+ "Malaysia","MY"
132
+ "Maldives","MV"
133
+ "Mali","ML"
134
+ "Malta","MT"
135
+ "Marshall Islands","MH"
136
+ "Martinique","MQ"
137
+ "Mauritania","MR"
138
+ "Mauritius","MU"
139
+ "Mayotte","YT"
140
+ "Mexico","MX"
141
+ "Micronesia","FM"
142
+ "Moldova","MD"
143
+ "Monaco","MC"
144
+ "Mongolia","MN"
145
+ "Montenegro","ME"
146
+ "Montserrat","MS"
147
+ "Morocco","MA"
148
+ "Mozambique","MZ"
149
+ "Myanmar [Burma]","MM"
150
+ "Namibia","NA"
151
+ "Nauru","NR"
152
+ "Nepal","NP"
153
+ "Netherlands","NL"
154
+ "Netherlands Antilles","AN"
155
+ "New Caledonia","NC"
156
+ "New Zealand","NZ"
157
+ "Nicaragua","NI"
158
+ "Niger","NE"
159
+ "Nigeria","NG"
160
+ "Niue","NU"
161
+ "Norfolk Island","NF"
162
+ "Northern Mariana Islands","MP"
163
+ "North Korea","KP"
164
+ "Norway","NO"
165
+ "Oman","OM"
166
+ "Pakistan","PK"
167
+ "Palau","PW"
168
+ "Palestinian Territories","PS"
169
+ "Panama","PA"
170
+ "Papua New Guinea","PG"
171
+ "Paraguay","PY"
172
+ "Peru","PE"
173
+ "Philippines","PH"
174
+ "Pitcairn Islands","PN"
175
+ "Poland","PL"
176
+ "Portugal","PT"
177
+ "Puerto Rico","PR"
178
+ "Qatar","QA"
179
+ "Réunion","RE"
180
+ "Romania","RO"
181
+ "Russia","RU"
182
+ "Rwanda","RW"
183
+ "Saint Barthélemy","BL"
184
+ "Saint Helena","SH"
185
+ "Saint Kitts and Nevis","KN"
186
+ "Saint Lucia","LC"
187
+ "Saint Martin","MF"
188
+ "Saint Pierre and Miquelon","PM"
189
+ "Saint Vincent and the Grenadines","VC"
190
+ "Samoa","WS"
191
+ "San Marino","SM"
192
+ "São Tomé and Príncipe","ST"
193
+ "Saudi Arabia","SA"
194
+ "Senegal","SN"
195
+ "Serbia","RS"
196
+ "Seychelles","SC"
197
+ "Sierra Leone","SL"
198
+ "Singapore","SG"
199
+ "Slovakia","SK"
200
+ "Slovenia","SI"
201
+ "Solomon Islands","SB"
202
+ "Somalia","SO"
203
+ "South Africa","ZA"
204
+ "South Georgia and the South Sandwich Islands","GS"
205
+ "South Korea","KR"
206
+ "Spain","ES"
207
+ "Sri Lanka","LK"
208
+ "Sudan","SD"
209
+ "Suriname","SR"
210
+ "Svalbard and Jan Mayen","SJ"
211
+ "Swaziland","SZ"
212
+ "Sweden","SE"
213
+ "Switzerland","CH"
214
+ "Syria","SY"
215
+ "Taiwan","TW"
216
+ "Tajikistan","TJ"
217
+ "Tanzania","TZ"
218
+ "Thailand","TH"
219
+ "Timor-Leste","TL"
220
+ "Togo","TG"
221
+ "Tokelau","TK"
222
+ "Tonga","TO"
223
+ "Trinidad and Tobago","TT"
224
+ "Tunisia","TN"
225
+ "Turkey","TR"
226
+ "Turkmenistan","TM"
227
+ "Turks and Caicos Islands","TC"
228
+ "Tuvalu","TV"
229
+ "Uganda","UG"
230
+ "Ukraine","UA"
231
+ "United Arab Emirates","AE"
232
+ "United Kingdom","GB"
233
+ "United States","US"
234
+ "Uruguay","UY"
235
+ "U.S. Minor Outlying Islands","UM"
236
+ "U.S. Virgin Islands","VI"
237
+ "Uzbekistan","UZ"
238
+ "Vanuatu","VU"
239
+ "Vatican City","VA"
240
+ "Venezuela","VE"
241
+ "Vietnam","VN"
242
+ "Wallis and Futuna","WF"
243
+ "Western Sahara","EH"
244
+ "Yemen","YE"
245
+ "Zambia","ZM"
246
+ "Zimbabwe","ZW"
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/msrp_display_actual_price_type.csv ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ "In Cart","2"
2
+ "Before Order Confirmation","3"
3
+ "On Gesture","1"
4
+ "Use config","0"
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/msrp_enabled.csv ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ "No",0
2
+ "Yes",1
3
+ "Use config",2
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/options_container.csv ADDED
@@ -0,0 +1,2 @@
 
 
1
+ "Product Info Column","container1"
2
+ "Block after Info Column","container2"
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/page_layout.csv ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ "No layout updates",""
2
+ "CMS Page","cms_one_column"
3
+ "Product Description Page","two_columns_left"
4
+ "1 column","one_column"
5
+ "2 columns with right bar","two_columns_right"
6
+ "3 columns","three_columns"
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/status.csv ADDED
@@ -0,0 +1,2 @@
 
 
1
+ "Enabled",1
2
+ "Disabled",2
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/tax_class_id.csv ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ "None","0"
2
+ "default","1"
3
+ "Taxable Goods","2"
4
+ "Shipping","4"
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/visibility.csv ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ "Not Visible Individually",1
2
+ "Catalog",2
3
+ "Search",3
4
+ "Catalog, Search",4
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/genericmapper/options_panel.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <div class="plugin_description">
2
+ This plugin provides value mapping for csv values before they are
3
+ handled by magmi typically visibility &amp; page_layout need mapping
4
+ for magmi to process them correctly. to change the mappings accordingly
5
+ to your config,please edit the csv files located at :
6
+ <pre>[magmi_dir]/plugins/itemprocessors/genericmapper/mappings</pre>
7
+ the csv files have the following names [column_to_map].csv
8
+ </div>
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/importlimiter/01_importlimiter.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ImportLimiter extends Magmi_ItemProcessor
4
+ {
5
+ protected $_recranges;
6
+ protected $_rmax = -1;
7
+ protected $_filters;
8
+ protected $_col_filter = NULL;
9
+
10
+ public function getPluginInfo()
11
+ {
12
+ return array("name"=>"Magmi Import Limiter","author"=>"Dweeves","version"=>"0.0.6",
13
+ "url"=>$this->pluginDocUrl("Magmi_Import_Limiter"));
14
+ }
15
+
16
+ public function filtermatch($item, $fltdef)
17
+ {
18
+ $negate = 0;
19
+ $field = $fltdef[0];
20
+ $match = false;
21
+ if ($field[0] == "!")
22
+ {
23
+ $field = substr($field, 1);
24
+ $negate = 1;
25
+ }
26
+ $re = $fltdef[1];
27
+ if (in_array($field, array_keys($item)))
28
+ {
29
+ $v = $item[$field];
30
+ $match = preg_match("|$re|", $v);
31
+ if ($negate)
32
+ {
33
+ $match = !$match;
34
+ }
35
+ if ($match)
36
+ {
37
+ $this->log("skipping sku {$item['sku']} => Filter '$field::$re'", "info");
38
+ }
39
+ }
40
+ return $match;
41
+ }
42
+
43
+ public function processItemBeforeId(&$item, $params = null)
44
+ {
45
+ $crow = $this->getCurrentRow();
46
+ $ok = (count($this->_recranges) == 0);
47
+
48
+ if (!$ok)
49
+ {
50
+ if ($this->_rmax > -1 && $crow == $this->_rmax)
51
+ {
52
+ $this->setLastItem($item);
53
+ }
54
+ foreach ($this->_recranges as $rr)
55
+ {
56
+ $ok = ($crow >= $rr[0] && ($crow <= $rr[1] || $rr[1] == -1));
57
+ if ($ok)
58
+ {
59
+ break;
60
+ }
61
+ }
62
+ }
63
+
64
+ if ($ok)
65
+ {
66
+ foreach ($this->_filters as $fltdef)
67
+ {
68
+ // negative filters
69
+ $ok = $ok && (!$this->filtermatch($item, $fltdef));
70
+ if (!$ok)
71
+ {
72
+ break;
73
+ }
74
+ }
75
+ }
76
+ else
77
+ {
78
+ $this->log("Filtered row $crow not in range " . $this->getParam("LIMITER:ranges", ""));
79
+ }
80
+
81
+ return $ok;
82
+ }
83
+
84
+ public function parseFilters($fltstr)
85
+ {
86
+ $this->_filters = array();
87
+ if ($fltstr == "")
88
+ {
89
+ return;
90
+ }
91
+ $fltlist = explode(";;", $fltstr);
92
+ foreach ($fltlist as $fltdef)
93
+ {
94
+ $fltinf = explode("::", $fltdef);
95
+ $this->_filters[] = $fltinf;
96
+ }
97
+ }
98
+
99
+ public function parseRanges($rangestr)
100
+ {
101
+ $this->_recranges = array();
102
+ if ($rangestr == "")
103
+ {
104
+ return;
105
+ }
106
+ $rangelist = explode(",", $rangestr);
107
+ foreach ($rangelist as $rdef)
108
+ {
109
+ $rlist = explode("-", $rdef);
110
+ if ($rlist[0] == "")
111
+ {
112
+ $rlist[0] = -1;
113
+ }
114
+ else
115
+ {
116
+ $rmin = $rlist[0];
117
+ }
118
+ if (count($rlist) > 1)
119
+ {
120
+ if ($rlist[1] == "")
121
+ {
122
+ $rlist[1] = -1;
123
+ }
124
+ else
125
+ {
126
+ $rmax = $rlist[1];
127
+ if ($rmax > $this->_rmax && $this->_rmax != -1)
128
+ {
129
+ $this->_rmax = $rmax;
130
+ }
131
+ }
132
+ }
133
+ else
134
+ {
135
+ $rmax = $rmin;
136
+ }
137
+ $this->_recranges[] = array($rmin,$rmax);
138
+ }
139
+ }
140
+
141
+ public function processColumnList(&$cols, $params = null)
142
+ {
143
+ if (count($this->_col_filter) > 0)
144
+ {
145
+ $this->log("limiting columns to :" . implode(",", $this->_col_filter), "startup");
146
+ $cols = $this->_col_filter;
147
+ }
148
+ }
149
+
150
+ public function initialize($params)
151
+ {
152
+ $this->parseRanges($this->getParam("LIMITER:ranges", ""));
153
+ $this->parseFilters($this->getParam("LIMITER:filters", ""));
154
+ $this->_col_filter = explode(",", $this->getParam("LIMITER:col_filter"));
155
+ return true;
156
+ }
157
+
158
+ public function getPluginParamNames()
159
+ {
160
+ return array('LIMITER:ranges','LIMITER:filters','LIMITER:col_filter');
161
+ }
162
+
163
+ static public function getCategory()
164
+ {
165
+ return "Input Data Preprocessing";
166
+ }
167
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/importlimiter/options_panel.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="plugin_description">This plugin is made to limit magmi
2
+ import to selected record ranges or matching values. ranges are ranges
3
+ of rows to be imported filters are regexps or strings that if matched
4
+ will exclude record from import</div>
5
+
6
+ <div class="ifield">
7
+ <span class="">Column filter:</span><input type="text"
8
+ name="LIMITER:col_filter" size="80"
9
+ value="<?php echo $this->getParam("LIMITER:col_filter")?>"></input>
10
+ <div class="fieldhelp"></div>
11
+ <div class="fieldinfo">
12
+ This field defines what columns should be imported
13
+ <div class="fieldsyntax" style="display: none">
14
+ <pre>
15
+ You should put column names, comma separated ie : sku,qty
16
+ </pre>
17
+ </div>
18
+ </div>
19
+ </div>
20
+
21
+ <div class="ifield">
22
+ <span class="">Limiter ranges:</span><input type="text"
23
+ name="LIMITER:ranges" size="80"
24
+ value="<?php echo $this->getParam("LIMITER:ranges")?>"></input>
25
+ <div class="fieldhelp"></div>
26
+ <div class="fieldinfo">
27
+ This field defines what lines should be imported
28
+ <div class="fieldsyntax" style="display: none">
29
+ <pre>
30
+ 1-100 : for the first 100 records of csv
31
+ 100- : for all records after 100 (including 100th)
32
+ 1-10,40-50,67,78,89 : for records 1 to 10,40 to 50 , 67 , 78 &amp; 89
33
+ </pre>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ <div class="ifield">
38
+
39
+ <span class="">Limiter filters:</span><input type="text"
40
+ name="LIMITER:filters" size="80"
41
+ value="<?php echo $this->getParam("LIMITER:filters")?>"></input>
42
+ <div class="fieldhelp"></div>
43
+ <div class="fieldinfo">
44
+ This field defines what content should not be imported with a regexp
45
+ like syntax.
46
+ <div class="fieldsyntax" style="display: none">
47
+ <pre>
48
+ sku::00.* : exclude all skus that begin with 00
49
+ !name::.*blue.* : exclude all items with name not blue (see the ! before the "name" field to negate the filter)
50
+ sku:00.*;;!name::.*blue.* : exclude all items with skus that begin with 00 which name does not contain blue
51
+ </pre>
52
+ </div>
53
+ </div>
54
+ </div>
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/productdeleter/options_panel.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <div class="plugin_description">This plugins should be used to delete
2
+ existing products</div>
3
+ <ul class="formline">
4
+ <li><input type="checkbox" name="PDEL:delsimples"
5
+ <?php if($this->getParam("PDEL:delsimples",false)==true){?>
6
+ checked="checked" <?php }?>>Delete children products</li>
7
+
8
+ </ul>
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/productdeleter/productdeleter.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ProductDeleter extends Magmi_ItemProcessor
4
+ {
5
+
6
+ public function getPluginInfo()
7
+ {
8
+ return array("name"=>"Product Deleter","author"=>"Dweeves","version"=>"0.0.2",
9
+ "url"=>$this->pluginDocUrl("Product_Deleter"));
10
+ }
11
+
12
+ public function getPluginParamNames()
13
+ {
14
+ return array("PDEL:delsimples");
15
+ }
16
+
17
+ public function removeFromFlat($pid)
18
+ {
19
+ $this->log("Cleaning flat tables before reindex...", "info");
20
+ $stmt = $this->exec_stmt("SHOW TABLES LIKE '" . $this->tablename('catalog_product_flat') . "%'", NULL, false);
21
+ while ($row = $stmt->fetch(PDO::FETCH_NUM))
22
+ {
23
+ $tname = $row[0];
24
+ // removing records in flat tables that are no more linked to entries in catalog_product_entity table
25
+ // for some reasons, this seem to happen
26
+ $sql = "DELETE cpf.* FROM $tname as cpf
27
+ WHERE cpf.entity_id=?";
28
+ $this->delete($sql, $pid);
29
+ }
30
+ }
31
+
32
+ public function processItemAfterId(&$item, $params = null)
33
+ {
34
+
35
+ // get item ids, since we are before id
36
+ $pid = $params["product_id"];
37
+ if (isset($item["magmi:delete"]) && $item["magmi:delete"] == 1)
38
+ {
39
+ $this->log("DELETING SKU '" . $item["sku"] . "' =>" . $pid, "info");
40
+ // delete simple products if flag set
41
+ if ($this->getParam("PDEL:delsimples", false) == true)
42
+ {
43
+ $childrensel = "SELECT entity_id FROM " . $this->tablename("catalog_product_entity") . " as cpe
44
+ JOIN " . $this->tablename("catalog_product_super_link") .
45
+ " as cpl ON cpl.parent_id=? AND cpe.entity_id=cpl.product_id";
46
+ $sql = "DELETE cpe.* FROM " . $this->tablename("catalog_product_entity") .
47
+ " cpe WHERE cpe.entity_id IN (SELECT s1.entity_id FROM ($childrensel) as s1)";
48
+
49
+ $this->delete($sql, $pid);
50
+ }
51
+ // delete from indexes table if store is set
52
+ $this->removeFromFlat($pid);
53
+
54
+ // delete product (this cascades for all eav & relations)
55
+ $sql = "DELETE FROM " . $this->tablename("catalog_product_entity") . " WHERE entity_id=?";
56
+ $this->delete($sql, $pid);
57
+ $this->log($sql, "info");
58
+ $item = array();
59
+ }
60
+ }
61
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/skufinder/001_skufinder.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class SkuFinderItemProcessor extends Magmi_ItemProcessor
4
+ {
5
+ private $_compchecked = FALSE;
6
+
7
+ public function getPluginInfo()
8
+ {
9
+ return array("name"=>"SKU Finder","author"=>"Dweeves","version"=>"0.0.2",
10
+ "url"=>$this->pluginDocUrl("SKU_Finder"));
11
+ }
12
+
13
+ public function processItemBeforeId(&$item, $params = null)
14
+ {
15
+ $matchfield = trim($this->getParam("SKUF:matchfield"));
16
+ // protection from tricky testers ;)
17
+ if ($matchfield == "sku")
18
+ {
19
+ return true;
20
+ }
21
+ $attinfo = $this->getAttrInfo($matchfield);
22
+ if ($this->_compchecked == FALSE)
23
+ {
24
+ // Checking attribute compatibility with sku matching
25
+ if ($attinfo == NULL)
26
+ {
27
+ $this->log("$matchfield is not a valid attribute", "error");
28
+ $item["__MAGMI_LAST__"] = 1;
29
+ return false;
30
+ }
31
+ if ($attinfo["is_unique"] == 0 || $attinfo["is_global"] == 0)
32
+ {
33
+ $this->log("sku matching attribute $matchfield must be unique & global scope");
34
+ $item["__MAGMI_LAST__"] = 1;
35
+ return false;
36
+ }
37
+ if ($attinfo["backend_type"] == "static")
38
+ {
39
+ $this->log("$matchfield is " . $attinfo["backend_type"] . ", it cannot be used as sku matching field.",
40
+ "error");
41
+ $item["__MAGMI_LAST__"] = 1;
42
+ return false;
43
+ }
44
+ if ($attinfo["frontend_input"] == "select" || $attinfo["frontend_input"] == "multiselect")
45
+ {
46
+ $this->log(
47
+ "$matchfield is " . $attinfo["frontend_input"] . ", it cannot be used as sku matching field.",
48
+ "error");
49
+ $item["__MAGMI_LAST__"] = 1;
50
+ return false;
51
+ }
52
+ $this->_compchecked = true;
53
+ }
54
+
55
+ // no item data for selected matching field, skipping
56
+ if (!isset($item[$matchfield]) && trim($item["matchfield"]) !== '')
57
+ {
58
+ $this->log("No value for $matchfield in datasource", "error");
59
+ return false;
60
+ }
61
+ // now find sku
62
+ $cpebt = $this->tablebname("catalog_product_entity_" . $attinfo["backend_type"]);
63
+ $sql = "SELECT sku FROM " . $this->tablename("catalog_product_entity") . " as cpe JOIN
64
+ $cpebt as cpebt ON cpebt.value=? AND cpebt.attribute_id=? AND cpebt.entity_id=cpe.entity_id";
65
+ $stmt = $this->select($sql, array($item[$matchfield],$attinfo["attribute_id"]));
66
+ $n = 0;
67
+ while ($result = $stmt->fetch())
68
+ {
69
+ // if more than one result, cannot match single sku
70
+ if ($n > 1)
71
+ {
72
+ $this->log("Several skus match $matchfield value : " . $item[$matchfield], "error");
73
+ return false;
74
+ }
75
+ else
76
+ {
77
+ $item["sku"] = $result["sku"];
78
+ }
79
+ $n++;
80
+ }
81
+ // if no item found, warning & skip
82
+ if ($n == 0)
83
+ {
84
+ $this->log("No sku found matching $matchfield value : " . $item[$matchfield], "warning");
85
+ return false;
86
+ }
87
+ // found a single sku ! item sku is in place, continue with processor chain
88
+ return true;
89
+ }
90
+
91
+ static public function getCategory()
92
+ {
93
+ return "Input Data Preprocessing";
94
+ }
95
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/base/itemprocessors/skufinder/options_panel.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <div class="plugin_description">
2
+ This plugins can be used in <b>update</b> mode to find sku from custom
3
+ column in datasource. this column <b>MUST</b> be an attribute code
4
+ </div>
5
+ <ul class="formline">
6
+ <li class="label"><span>sku find attribute code</span></li>
7
+ <li class="value"><input type="text" name="SKUF:matchfield"
8
+ value="<?php $this->getParam("SKUF:matchfield","")?>"></li>
9
+
10
+ </ul>
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/categories/categoryimport.php ADDED
@@ -0,0 +1,464 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class CategoryImporter extends Magmi_ItemProcessor
4
+ {
5
+ protected $_idcache = array();
6
+ protected $_catattr = array();
7
+ protected $_cattrinfos = array();
8
+ protected $_catroots = array();
9
+ protected $_catrootw = array();
10
+ protected $_cat_eid = null;
11
+ protected $_tsep;
12
+ // tricky escaped separator that matches slugging separator
13
+ protected $_escapedtsep = "---";
14
+
15
+ public function initialize($params)
16
+ {
17
+ $this->initCats();
18
+ $this->_cattrinfos = array("varchar"=>array("name"=>array(),"url_key"=>array(),"url_path"=>array()),
19
+ "int"=>array("is_active"=>array(),"is_anchor"=>array(),"include_in_menu"=>array()));
20
+ foreach ($this->_cattrinfos as $catype => $attrlist)
21
+ {
22
+ foreach (array_keys($attrlist) as $catatt)
23
+ {
24
+ $this->_cattrinfos[$catype][$catatt] = $this->getCatAttributeInfos($catatt);
25
+ }
26
+ }
27
+ $this->_tsep = $this->getParam("CAT:treesep", "/");
28
+ }
29
+
30
+ public function initCats()
31
+ {
32
+ // zioigor - 20110426 missing call to tablename method for table_prfix
33
+ $t = $this->tablename("catalog_category_entity");
34
+ $csg = $this->tablename("core_store_group");
35
+ $cs = $this->tablename("core_store");
36
+ $ccev = $t . "_varchar";
37
+ $ea = $this->tablename("eav_attribute");
38
+ $result = $this->selectAll(
39
+ "SELECT cs.store_id,csg.website_id,cce.entity_type_id,cce.path,ccev.value as name
40
+ FROM $cs as cs
41
+ JOIN $csg as csg on csg.group_id=cs.group_id
42
+ JOIN $t as cce ON cce.entity_id=csg.root_category_id
43
+ JOIN $ea as ea ON ea.attribute_code='name' AND ea.entity_type_id=cce.entity_type_id
44
+ JOIN $ccev as ccev ON ccev.attribute_id=ea.attribute_id AND ccev.entity_id=cce.entity_id
45
+ ");
46
+ foreach ($result as $row)
47
+ {
48
+ $rootinfo = array("path"=>$row["path"],"etid"=>$row["entity_type_id"],"name"=>$row["name"],
49
+ "rootarr"=>explode("/", $row["path"]));
50
+ $this->_catroots[$row["store_id"]] = $rootinfo;
51
+ $this->_catrootw[$row["website_id"]][] = $row["store_id"];
52
+ if ($this->_cat_eid == null)
53
+ {
54
+ $this->_cat_eid = $row["entity_type_id"];
55
+ }
56
+ }
57
+ }
58
+
59
+ public function getCatAttributeInfos($attcode)
60
+ {
61
+ $t = $this->tablename("eav_attribute");
62
+ $sql = "SELECT * FROM $t WHERE entity_type_id=? AND attribute_code=?";
63
+ $info = $this->selectAll($sql, array($this->_cat_eid,$attcode));
64
+ return $info[0];
65
+ }
66
+
67
+ public function getCache($cdef, $bp)
68
+ {
69
+ $ck = "$bp::$cdef";
70
+ return $this->_idcache[$ck];
71
+ }
72
+
73
+ public function isInCache($cdef, $bp)
74
+ {
75
+ $ck = "$bp::$cdef";
76
+ return isset($this->_idcache[$ck]);
77
+ }
78
+
79
+ public function putInCache($cdef, $bp, $idarr)
80
+ {
81
+ $ck = "$bp::$cdef";
82
+ $this->_idcache[$ck] = $idarr;
83
+ }
84
+
85
+ public function getPluginInfo()
86
+ {
87
+ return array("name"=>"On the fly category creator/importer","author"=>"Dweeves","version"=>"0.2.4",
88
+ "url"=>$this->pluginDocUrl("On_the_fly_category_creator/importer"));
89
+ }
90
+
91
+ public function getExistingCategory($parentpath, $cattr)
92
+ {
93
+ $cet = $this->tablename("catalog_category_entity");
94
+ $cetv = $this->tablename("catalog_category_entity_varchar");
95
+ $parentid = array_pop($parentpath);
96
+ $sql = "SELECT cet.entity_id FROM $cet as cet
97
+ JOIN $cetv as cetv ON cetv.entity_id=cet.entity_id AND cetv.attribute_id=? AND cetv.value=?
98
+ WHERE cet.parent_id=? ";
99
+ $catid = $this->selectone($sql,
100
+ array($this->_cattrinfos["varchar"]["name"]["attribute_id"],$cattr["name"],$parentid), "entity_id");
101
+ return $catid;
102
+ }
103
+
104
+ public function getCategoryId($parentpath, $cattrs)
105
+ {
106
+ $cattrs["name"] = str_replace($this->_escapedtsep, $this->_tsep, $cattrs["name"]);
107
+ // get exisiting cat id
108
+ $catid = $this->getExistingCategory($parentpath, $cattrs);
109
+ // if found , return it
110
+ if ($catid != null)
111
+ {
112
+ return $catid;
113
+ }
114
+ // otherwise, get new category values from parent & siblings
115
+ $cet = $this->tablename("catalog_category_entity");
116
+ $path = implode("/", $parentpath);
117
+ $parentid = array_pop($parentpath);
118
+ // get child info using parent data
119
+ $sql = "SELECT cce.entity_type_id,cce.attribute_set_id,cce.level+1 as level,COALESCE(MAX(eac.position),0)+1 as position
120
+ FROM $cet as cce
121
+ LEFT JOIN $cet as eac ON eac.parent_id=cce.entity_id
122
+ WHERE cce.entity_id=?
123
+ GROUP BY eac.parent_id";
124
+ $info = $this->selectAll($sql, $parentid);
125
+ $info = $info[0];
126
+ // insert new category
127
+ $sql = "INSERT INTO $cet (entity_type_id,attribute_set_id,parent_id,position,level,path,children_count) VALUES (?,?,?,?,?,?,?)";
128
+ // insert empty path until we get category id
129
+ $data = array($info["entity_type_id"],$info["attribute_set_id"],$parentid,$info["position"],$info["level"],"",0);
130
+ // insert in db,get cat id
131
+ $catid = $this->insert($sql, $data);
132
+
133
+ unset($data);
134
+ // set category path with inserted category id
135
+ $sql = "UPDATE $cet SET path=?,created_at=NOW(),updated_at=NOW() WHERE entity_id=?";
136
+ $data = array("$path/$catid",$catid);
137
+ $this->update($sql, $data);
138
+ unset($data);
139
+ // set category attributes
140
+ foreach ($this->_cattrinfos as $tp => $attinfo)
141
+ {
142
+ $inserts = array();
143
+ $data = array();
144
+ $tb = $this->tablename("catalog_category_entity_$tp");
145
+
146
+ foreach ($attinfo as $attrcode => $attdata)
147
+ {
148
+ if (isset($attdata["attribute_id"]))
149
+ {
150
+ $inserts[] = "(?,?,?,?,?)";
151
+ $data[] = $info["entity_type_id"];
152
+ $data[] = $attdata["attribute_id"];
153
+ $data[] = 0; // store id 0 for categories
154
+ $data[] = $catid;
155
+ $data[] = $cattrs[$attrcode];
156
+ }
157
+ }
158
+
159
+ $sql = "INSERT INTO $tb (entity_type_id,attribute_id,store_id,entity_id,value) VALUES " .
160
+ implode(",", $inserts) . " ON DUPLICATE KEY UPDATE value=VALUES(`value`)";
161
+ $this->insert($sql, $data);
162
+ unset($data);
163
+ unset($inserts);
164
+ }
165
+ return $catid;
166
+ }
167
+
168
+ public function extractCatAttrs(&$catdef)
169
+ {
170
+ $cdefs = explode($this->_tsep, $catdef);
171
+ $odefs = array();
172
+ $clist = array();
173
+ foreach ($cdefs as $cdef)
174
+ {
175
+
176
+ $attrs = array();
177
+ $parts = explode("::", $cdef);
178
+ $cp = count($parts);
179
+ $cname = trim($parts[0]);
180
+ $odefs[] = $cname;
181
+ $attrs = array("name"=>$cname,"is_active"=>($cp > 1) ? $parts[1] : 1,"is_anchor"=>($cp > 2) ? $parts[2] : 1,
182
+ "include_in_menu"=>$cp > 3 ? $parts[3] : 1,"url_key"=>Slugger::slug($cname),
183
+ "url_path"=>Slugger::slug(implode("/", $odefs), true) . $this->getParam("CAT:urlending", ".html"));
184
+ $clist[] = $attrs;
185
+ }
186
+ $catdef = implode($this->_tsep, $odefs);
187
+ return $clist;
188
+ }
189
+
190
+ public function getCategoryIdsFromDef($pcatdef, $srdefs)
191
+ {
192
+ $srp = "%RP:base%";
193
+ foreach (array_keys($srdefs) as $tsrp)
194
+ {
195
+ // check which root we have
196
+ if (substr($pcatdef, 0, strlen($tsrp)) == $tsrp)
197
+ {
198
+ $srp = $tsrp;
199
+ break;
200
+ }
201
+ }
202
+ // remove explicit root
203
+ $pcatdef = str_replace($srp . $this->_tsep, "", $pcatdef);
204
+ $zcatparts = explode($this->_tsep, $pcatdef);
205
+ // cleaning parts (trimming, removing empty)
206
+ $pcatparts = array();
207
+ $czcatparts = count($zcatparts);
208
+ for ($i = 0; $i < $czcatparts; $i++)
209
+ {
210
+ $cp = trim($zcatparts[$i]);
211
+ if ($cp != "")
212
+ {
213
+ $pcatparts[] = $cp;
214
+ }
215
+ }
216
+ $catparts = array();
217
+ $catpos = array();
218
+ // build a position table to restore after cat ids will be created
219
+ foreach ($pcatparts as $cp)
220
+ {
221
+ $a = explode("::", $cp);
222
+ $catparts[] = $a[0];
223
+ $catpos[] = (count($a) > 1 ? $a[1] : "0");
224
+ // remove position to build catpart array
225
+ }
226
+
227
+ // build a position free category def
228
+ $catdef = implode($this->_tsep, $catparts);
229
+
230
+ // if full def is in cache, use it
231
+ if ($this->isInCache($catdef, $srp))
232
+ {
233
+ $catids = $this->getCache($catdef, $srp);
234
+ }
235
+ else
236
+ {
237
+ // category ids
238
+ $catids = array();
239
+ $lastcached = array();
240
+
241
+ // path as array , basepath is always "/" separated
242
+ $basearr = explode("/", $srdefs[$srp]["path"]);
243
+ // for each cat tree branch
244
+ $pdef = array();
245
+ foreach ($catparts as $catpart)
246
+ {
247
+ // ignore empty
248
+ if ($catpart == "")
249
+ {
250
+ continue;
251
+ }
252
+ // add it to the current tree level
253
+ $pdef[] = $catpart;
254
+ $ptest = implode($this->_tsep, $pdef);
255
+ // test for tree level in cache
256
+ if ($this->isInCache($ptest, $srp))
257
+ {
258
+ // if yes , set current known cat ids to corresponding cached branch
259
+ $catids = $this->getCache($ptest, $srp);
260
+ // store last cached branch
261
+ $lastcached = $pdef;
262
+ }
263
+ else
264
+ // no more tree info in cache,stop further retrieval, we need to create missing levels
265
+ {
266
+ break;
267
+ }
268
+ }
269
+ // add store tree root to category path
270
+ $curpath = array_merge($basearr, $catids);
271
+ // get categories attributes
272
+ $catattributes = $this->extractCatAttrs($catdef);
273
+ $ccatids = count($catids);
274
+ $ccatparts = count($catparts);
275
+ // iterate on missing levels.
276
+ for ($i = $ccatids; $i < $ccatparts; $i++)
277
+ {
278
+ if ($catparts[$i] == "")
279
+ {
280
+ continue;
281
+ }
282
+ // retrieve category id (by creating it if needed from categories attributes)
283
+ $catid = $this->getCategoryId($curpath, $catattributes[$i]);
284
+ // add newly created level to item category ids
285
+ $catids[] = $catid;
286
+ // add newly created level to current paths
287
+ $curpath[] = $catid;
288
+ // cache newly created levels
289
+ $lastcached[] = $catparts[$i];
290
+ $this->putInCache(implode($this->_tsep, $lastcached), $srp, $catids);
291
+ }
292
+ }
293
+ $ccatparts = count($catparts);
294
+ // added position handling
295
+ for ($i = 0; $i < $ccatparts; $i++)
296
+ {
297
+ $catids[$i] .= "::" . $catpos[$i];
298
+ }
299
+
300
+ return $catids;
301
+ }
302
+
303
+ public function processColumnList(&$cols, $params)
304
+ {
305
+ $cols[] = "category_ids";
306
+ $cols = array_unique($cols);
307
+ return true;
308
+ }
309
+
310
+ public function getStoreRootPaths(&$item)
311
+ {
312
+ $rootpaths = array();
313
+ $sids = $this->getItemStoreIds($item, 2);
314
+ $trimroot = "";
315
+ // remove admin from store ids (no category root on it)
316
+ if ($sids[0] == 0)
317
+ {
318
+ array_shift($sids);
319
+ }
320
+ // only admin store set,use websites store roots
321
+ if (count($sids) == 0)
322
+ {
323
+ $wsids = $this->getItemWebsites($item);
324
+ foreach ($wsids as $wsid)
325
+ {
326
+ $sids = array_merge($sids, $this->_catrootw[$wsid]);
327
+ }
328
+ }
329
+ $rootpaths["__error__"] = array();
330
+ // If using explicit root assignment , identify which root it is
331
+ if (preg_match_all("|\[(.*?)\]|", $item["categories"], $matches))
332
+ {
333
+ $cm1 = count($matches[1]);
334
+ // for each found explicit root
335
+ for ($i = 0; $i < $cm1; $i++)
336
+ {
337
+ // test store matching
338
+ foreach ($sids as $sid)
339
+ {
340
+ $srp = $this->_catroots[$sid];
341
+ $rname = $matches[1][$i];
342
+ $cmatch = (trim($rname) == $srp["name"]);
343
+ // found a store match
344
+ if ($cmatch)
345
+ {
346
+ // set a specific store key
347
+ $k = "%RP:$sid%";
348
+ // store root path definitions
349
+ $rootpaths[$k] = array("path"=>$srp["path"],"rootarr"=>$srp["rootarr"]);
350
+ $trimroot = trim($rname);
351
+ // replace root name with store root key
352
+ $item["categories"] = str_replace($matches[0][$i], $k, $item["categories"]);
353
+ break;
354
+ }
355
+ }
356
+ }
357
+ // now finding unmatched replaces
358
+ }
359
+ if (preg_match_all("|\[(.*?)\]|", $item["categories"], $matches))
360
+ {
361
+ $cm1 = count($matches[1]);
362
+
363
+ for ($i = 0; $i < $cm1; $i++)
364
+ {
365
+ $rootpaths['__error__'] = $matches[1];
366
+ }
367
+ }
368
+ $sids = array_keys($this->_catroots);
369
+ $srp = $this->_catroots[$sids[0]];
370
+ $rootpaths["%RP:base%"] = array("path"=>$srp["path"],"rootarr"=>$srp["rootarr"]);
371
+
372
+ return $rootpaths;
373
+ }
374
+
375
+ public function processEscaping($icats)
376
+ {
377
+ return str_replace("\\" . $this->_tsep, $this->_escapedtsep, $icats);
378
+ }
379
+
380
+ public function processItemAfterId(&$item, $params = null)
381
+ {
382
+ if (isset($item["categories"]))
383
+ {
384
+ // handle escaping
385
+ $icats = $this->processEscaping($item["categories"]);
386
+ // first apply category root on each category
387
+
388
+ $root = $this->getParam("CAT:baseroot", "");
389
+ if ($root != "")
390
+ {
391
+ $catlist = explode(";;", $icats);
392
+ $ccatlist = count($catlist);
393
+ for ($i = 0; $i < $ccatlist; $i++)
394
+ {
395
+ if (trim($catlist[$i]) != "")
396
+ {
397
+ $catlist[$i] = $root . $this->_tsep . $catlist[$i];
398
+ }
399
+ }
400
+ // recompose rooted categories
401
+ $item["categories"] = implode(";;", $catlist);
402
+ }
403
+ // get store root category paths, this may modify categories !!!!!
404
+ $rootpaths = $this->getStoreRootPaths($item);
405
+
406
+ // process escaping at the end
407
+ $icats = $this->processEscaping($item["categories"]);
408
+
409
+ if (count($rootpaths["__error__"]) > 0)
410
+ {
411
+ $this->log("Cannot find site root with names : " . implode(",", $rootpaths["__error__"]), "error");
412
+ return false;
413
+ }
414
+ // unset error if empty
415
+ unset($rootpaths["__error__"]);
416
+ // categories may have been changed , use escaping
417
+ $catlist = explode(";;", $icats);
418
+ $catids = array();
419
+ foreach ($catlist as $catdef)
420
+ {
421
+ $cdef = $this->getCategoryIdsFromDef($catdef, $rootpaths);
422
+ if ($this->getParam("CAT:lastonly", 0) == 1)
423
+ {
424
+ $cdef = array($cdef[count($cdef) - 1]);
425
+ }
426
+ $catids = array_unique(array_merge($catids, $cdef));
427
+ }
428
+
429
+ // assign to category roots
430
+ if ($this->getParam("CAT:lastonly", 0) == 0)
431
+ {
432
+ foreach (array_values($rootpaths) as $ra)
433
+ {
434
+ $id = array_pop($ra["rootarr"]);
435
+ $catids[] = $id;
436
+ }
437
+ }
438
+ $catids = array_unique($catids);
439
+ $item["category_ids"] = implode(",", $catids);
440
+ }
441
+ return true;
442
+ }
443
+
444
+ public function getPluginParamNames()
445
+ {
446
+ return array('CAT:baseroot','CAT:lastonly','CAT:urlending','CAT:treesep');
447
+ }
448
+
449
+ public function afterImport()
450
+ {
451
+ $this->log("Updating Category children count....", "info");
452
+ // automatically update all children_count for catalog categories
453
+ $cce = $this->tablename("catalog_category_entity");
454
+ $sql = "UPDATE $cce as cce
455
+ LEFT JOIN
456
+ (SELECT s1.entity_id as cid, COALESCE( COUNT( s2.entity_id ) , 0 ) AS cnt
457
+ FROM $cce AS s1
458
+ LEFT JOIN $cce AS s2 ON s2.parent_id = s1.entity_id
459
+ GROUP BY s1.entity_id) as sq ON sq.cid=cce.entity_id
460
+ SET cce.children_count=sq.cnt";
461
+ $this->update($sql);
462
+ return true;
463
+ }
464
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/categories/options_panel.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="plugin_description">
2
+ This plugin let you create categories on the fly. it enable creation or
3
+ use of full category tree. multiple categories supported :)
4
+ <p>
5
+ the column (in csv or mapped) should be <b>categories</b>
6
+ </p>
7
+ <div class="fieldinfo">
8
+ <p>"light" syntax for the values:
9
+ level1_category/level2_category/level3_category,level1_category2/level2_category2</p>
10
+ <p>"verbose" syntax for the values:</p>
11
+ <p>all category levels separated by configurable tree separator ,
12
+ defaulting to /</p>
13
+ <p>in each level you can put [category name]:[x]:[y]:[z] (each of x,y
14
+ or z being optional) with</p>
15
+ <ul>
16
+ <li>x: 0 or 1 , is_active</li>
17
+ <li>y: 0 or 1 , is_anchor</li>
18
+ <li>x: 0 or 1 , include_in_menu</li>
19
+ </ul>
20
+ </div>
21
+ </div>
22
+ <div class="formline">
23
+ <?php $lastonly=$this->getParam("CAT:lastonly",0)?>
24
+ <span>Assign product to :</span><select name="CAT:lastonly">
25
+ <option value="0" <?php if($lastonly==0){?> selected="selected"
26
+ <?php }?>>all categories in tree</option>
27
+ <option value="1" <?php if($lastonly==1){?> selected="selected"
28
+ <?php }?>>last category of each branch</option>
29
+ </select>
30
+ <div class="fieldinfo">When checked, this options will assign product
31
+ only to the categories that are located at the last level of the
32
+ defined trees</div>
33
+ </div>
34
+ <div class="formline">
35
+ <span class="label">Tree level separator:</span> <span class="value"><input
36
+ type="text" name="CAT:treesep" maxlength=3 size=3
37
+ " value="<?php echo $this->getParam("CAT:treesep","/")?>"></input></span>
38
+ </div>
39
+ <div class="formline">
40
+ <span>base category tree:</span><input type="text" name="CAT:baseroot"
41
+ size="80" value="<?php echo $this->getParam("CAT:baseroot","")?>"></input>
42
+ <div class="fieldinfo">
43
+ this enable you to import the categories prepending a base root tree
44
+ to the values found in csv (use same syntax as described above)<br />
45
+ <b>IMPORTANT , use tree level separator defined above in case of
46
+ multilevel base tree</b>
47
+ </div>
48
+ </div>
49
+ <div class="formline">
50
+ <span>url ending:</span><input type="text" name="CAT:urlending"
51
+ size="80"
52
+ value="<?php echo $this->getParam("CAT:urlending",".html")?>"></input>
53
+ <div class="fieldinfo">Choose what url ending to put on category page
54
+ (defaults to .html)</div>
55
+ </div>
56
+
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/customoptions/options_panel.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <div class="plugin_description">This plugin let you import custom
2
+ options for products</div>
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/customoptions/pablo_customoptions.php ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class SampleItemProcessor
5
+ * @author dweeves
6
+ *
7
+ * This class is a sample for item processing
8
+ */
9
+ class CustomOptionsItemProcessor extends Magmi_ItemProcessor
10
+ {
11
+ private $_containerMap = array("Product Info Column"=>"container1","Block after Info Column"=>"container2");
12
+ protected $_optids = array();
13
+ protected $_opttypeids = array();
14
+ protected $_multivals = array('drop_down','multiple','radio','checkbox');
15
+
16
+ public function getPluginInfo()
17
+ {
18
+ return array("name"=>"Custom Options","author"=>"Pablo & Dweeves","version"=>"0.0.7a",
19
+ "url"=>$this->pluginDocUrl("Custom_Options"));
20
+ }
21
+
22
+ public function processItemBeforeId(&$item, $params = null)
23
+ {
24
+ return true;
25
+ }
26
+
27
+ public function getOptId($field)
28
+ {
29
+ return isset($this->_optids[$field]) ? $this->_optids[$field] : null;
30
+ }
31
+
32
+ public function setOptId($field, $val)
33
+ {
34
+ $this->_optids[$field] = $val;
35
+ }
36
+
37
+ public function getOptTypeIds($field)
38
+ {
39
+ return isset($this->_opttypeids[$field]) ? $this->_opttypeids[$field] : null;
40
+ }
41
+
42
+ public function setOptTypeIds($field, $arr)
43
+ {
44
+ $this->_opttypeids[$field] = $arr;
45
+ }
46
+
47
+ public function createOption($pid, $sids, $opt)
48
+ {
49
+ $t1 = $this->tablename('catalog_product_option');
50
+ $t2 = $this->tablename('catalog_product_option_title');
51
+ $t3 = $this->tablename('catalog_product_option_price');
52
+ $values = array($pid,$opt['type'],$opt['is_require'],$opt['sort_order'],$opt['sku']);
53
+ $f = "product_id, type, is_require,sort_order,sku";
54
+ $i = "?,?,?,?,?";
55
+
56
+ foreach (array("max_characters","file_extension","image_size_x","image_size_y") as $extra)
57
+ {
58
+ if (isset($opt[$extra]))
59
+ {
60
+ $values[] = $opt[$extra];
61
+ $i .= ",?";
62
+ $f .= ",$extra";
63
+ }
64
+ }
65
+
66
+ $optionId = $this->getOptId($opt['__field']);
67
+ if (!isset($optionId))
68
+ {
69
+ $sql = "INSERT INTO $t1 ($f) VALUES ($i)";
70
+ $optionId = $this->insert($sql, $values);
71
+ $this->setOptId($opt['__field'], $optionId);
72
+ }
73
+ $tvals = array();
74
+ $tins = array();
75
+ $pvals = array();
76
+ $pins = array();
77
+
78
+ foreach ($sids as $sid)
79
+ {
80
+ $tins[] = "(?,?,?)";
81
+ $tvals[] = $optionId;
82
+ $tvals[] = $sid;
83
+ $tvals[] = $opt["title"];
84
+ // price inserts only if option can have price
85
+ if (isset($opt['price']))
86
+ {
87
+ $pins[] = "(?,?,?,?)";
88
+ $pvals[] = $optionId;
89
+ $pvals[] = $sid;
90
+ $pvals[] = $opt["price"];
91
+ $pvals[] = $opt["price_type"];
92
+ }
93
+ // here we set admin values, so no more value needed since all other stores will share it
94
+ if ($sid == 0)
95
+ {
96
+ break;
97
+ }
98
+ }
99
+
100
+ $sql = "INSERT IGNORE INTO $t2 (option_id, store_id, title) VALUES " . implode(",", $tins);
101
+ $this->insert($sql, $tvals);
102
+
103
+ if (count($pins) > 0)
104
+ {
105
+ $sql = "INSERT IGNORE INTO $t3 (option_id, store_id, price, price_type) VALUES " . implode(",", $pins);
106
+ $this->insert($sql, $pvals);
107
+ }
108
+ return $optionId;
109
+ }
110
+
111
+ public function createOptionValues($field, $sids, $valarr)
112
+ {
113
+ if (!isset($valarr) || count($valarr) == 0)
114
+ {
115
+ return;
116
+ }
117
+ $t4 = $this->tablename('catalog_product_option_type_value');
118
+ $t5 = $this->tablename('catalog_product_option_type_title');
119
+ $t6 = $this->tablename('catalog_product_option_type_price');
120
+
121
+ $ttvals = array();
122
+ $ttins = array();
123
+ $tpvals = array();
124
+ $tpins = array();
125
+ $optid = $this->getOptId($field);
126
+
127
+ $optionTypeIds = $this->getOptTypeIds($field);
128
+ $optionTypeId = null;
129
+ $cvalarr = count($valarr);
130
+ for ($i = 0; $i < $cvalarr; $i++)
131
+ {
132
+ $val = $valarr[$i];
133
+ if ($i < count($optionTypeIds))
134
+ {
135
+ $optionTypeId = $optionTypeIds[$i];
136
+ }
137
+ else
138
+ {
139
+ $sql = "INSERT INTO $t4
140
+ (option_id, sku, sort_order)
141
+ VALUES (?, ?, ?)";
142
+ $optionTypeId = $this->insert($sql, array($optid,$val["sku"],$val["sort_order"]));
143
+ $optionTypeIds[] = $optionTypeId;
144
+ }
145
+ foreach ($sids as $sid)
146
+ {
147
+ $ttins[] = "(?,?,?)";
148
+ $ttvals[] = $optionTypeId;
149
+ $ttvals[] = $sid;
150
+ $ttvals[] = $val["title"];
151
+ $tpins[] = "(?,?,?,?)";
152
+ $tpvals[] = $optionTypeId;
153
+ $tpvals[] = $sid;
154
+ $tpvals[] = $val["price"];
155
+ $tpvals[] = $val["price_type"];
156
+ // here we set admin values, so no more value needed since all other stores will share it
157
+ if ($sid == 0)
158
+ {
159
+ break;
160
+ }
161
+ }
162
+ }
163
+
164
+ $this->setOptTypeIds($field, $optionTypeIds);
165
+
166
+ $sql = "INSERT IGNORE INTO $t5 (option_type_id, store_id, title) VALUES " . implode(",", $ttins);
167
+ $this->insert($sql, $ttvals);
168
+
169
+ $sql = "INSERT IGNORE INTO $t6 (option_type_id, store_id, price, price_type) VALUES " . implode(",", $tpins);
170
+ $this->insert($sql, $tpvals);
171
+ }
172
+
173
+ public function isMultiValue($type)
174
+ {
175
+ $mv = in_array($type, $this->_multivals);
176
+ return $mv;
177
+ }
178
+
179
+ public function BuildCustomOption($field, $value)
180
+ {
181
+ $fieldParts = explode(":", $field);
182
+ $title = $fieldParts[0];
183
+ $type = $fieldParts[1];
184
+ $is_required = $fieldParts[2];
185
+ $sort_order = isset($fieldParts[3]) ? $fieldParts[3] : 0;
186
+ // @list($title,$type,$is_required,$sort_order) = $fieldParts;
187
+ $title = ucfirst(str_replace('_', ' ', $title));
188
+ $opt = array('__field'=>$field,'is_delete'=>0,'title'=>$title,'previous_group'=>'','previous_type'=>'',
189
+ 'type'=>$type,'is_require'=>$is_required,'sort_order'=>$sort_order,'values'=>array());
190
+
191
+ $values = explode('|', $value);
192
+
193
+ foreach ($values as $v)
194
+ {
195
+ $ovalues = array();
196
+ $parts = explode(':', $v);
197
+ $mv = $this->isMultiValue($type);
198
+ if ($mv)
199
+ {
200
+ if (preg_match("|\[(.*)\]|", $parts[0], $matches))
201
+ {
202
+ $opt['title'] = $matches[1];
203
+ array_shift($parts);
204
+ }
205
+ $ovalues["title"] = ($parts[0] != '' ? $parts[0] : $title);
206
+ }
207
+ else
208
+ {
209
+ $opt['title'] = ($parts[0] != '' ? $parts[0] : $title);
210
+ }
211
+
212
+ $c = count($parts);
213
+ $price_type = ($c > 1) ? ($parts[1] != '' ? $parts[1] : 'fixed') : 'fixed';
214
+ $price = ($c > 2) ? $parts[2] : 0;
215
+ $sku = ($c > 3) ? $parts[3] : '';
216
+ $sort_order = ($c > 4) ? $parts[4] : 0;
217
+
218
+ switch ($type)
219
+ {
220
+
221
+ case 'file':
222
+ if ($c > 5)
223
+ {
224
+ $opt['file_extension'] = $parts[5];
225
+ }
226
+ if ($c > 6)
227
+ {
228
+ $opt['image_size_x'] = $parts[6];
229
+ }
230
+ if ($c > 6)
231
+ {
232
+ $opt['image_size_y'] = $parts[7];
233
+ }
234
+ $opt['sku'] = $sku;
235
+ $opt['price'] = $price;
236
+ break;
237
+ case 'field':
238
+ case 'area':
239
+ $opt['max_characters'] = $sort_order;
240
+ case 'date':
241
+ case 'date_time':
242
+ case 'time':
243
+ $opt['price_type'] = $price_type;
244
+ $opt['price'] = $price;
245
+ $opt['sku'] = $sku;
246
+ break;
247
+ /* NO BREAK */
248
+ case 'drop_down':
249
+ case 'radio':
250
+ case 'checkbox':
251
+ case 'multiple':
252
+ default:
253
+ $ovalues["price_type"] = $price_type;
254
+ $ovalues["price"] = $price;
255
+ $opt['sku'] = '';
256
+ $ovalues["sku"] = $sku;
257
+ $ovalues["sort_order"] = $sort_order;
258
+ $opt['values'][] = $ovalues;
259
+ }
260
+ }
261
+ return $opt;
262
+ }
263
+
264
+ public function processItemAfterId(&$item, $params = null)
265
+ {
266
+ $hasOptions = 0;
267
+ $requiredOptions = 0;
268
+ $custom_options = array();
269
+ $itemCopy = $item;
270
+
271
+ foreach ($itemCopy as $field => $value)
272
+ {
273
+ $fieldParts = explode(':', $field);
274
+ if (count($fieldParts) > 2)
275
+ {
276
+ if (strlen($value) > 0)
277
+ {
278
+ $custom_options[] = $this->BuildCustomOption($field, $value);
279
+ }
280
+ unset($item[$field]);
281
+ }
282
+ unset($fieldParts);
283
+ }
284
+
285
+ // create new custom options
286
+ if (count($custom_options) > 0)
287
+ {
288
+ $pid = $params['product_id'];
289
+ $tname = $this->tablename('catalog_product_entity');
290
+ foreach ($custom_options as $opt)
291
+ {
292
+ if ($opt["is_require"])
293
+ {
294
+ $requiredOptions = 1;
295
+ break;
296
+ }
297
+ }
298
+ $data = array(1,$requiredOptions,$pid);
299
+ // set product has having options
300
+ $sql = "UPDATE `$tname` SET has_options=?,required_options=? WHERE entity_id=?";
301
+ $this->update($sql, $data);
302
+ $t1 = $this->tablename('catalog_product_option');
303
+ // destroy existing options if first time we encounter item
304
+ if (!$params["same"])
305
+ {
306
+ $sql = "DELETE $t1 FROM $t1 WHERE $t1.product_id=$pid";
307
+ $this->delete($sql);
308
+ unset($this->_opttypeids);
309
+ unset($this->_optids);
310
+ $this->_opttypeids = array();
311
+ $this->_optids = array();
312
+ }
313
+ // check options container
314
+ $oc = isset($item['options_container']) ? $item['options_container'] : "container2";
315
+ if (!in_array($oc, array('container1','container2')))
316
+ {
317
+ $item['options_container'] = $this->_containerMap[$oc];
318
+ }
319
+ else
320
+ {
321
+ $item['options_container'] = $oc;
322
+ }
323
+ // fill custom options table
324
+ $sids = $this->getItemStoreIds($item, 0);
325
+ if (!$params["same"])
326
+ {
327
+ $sids = array_unique(array_merge(array(0), $sids));
328
+ }
329
+
330
+ foreach ($custom_options as $option)
331
+ {
332
+ $opt = $this->createOption($pid, $sids, $option);
333
+ $this->createOptionValues($option['__field'], $sids, $option["values"]);
334
+ }
335
+ }
336
+ unset($custom_options);
337
+ return true;
338
+ }
339
+
340
+ /*
341
+ * public function processItemException(&$item,$params=null) { }
342
+ */
343
+ public function getPluginDescription()
344
+ {
345
+ return "This plugins enable to import custom options using specific column syntax";
346
+ }
347
+
348
+ public function initialize($params)
349
+ {
350
+ return true;
351
+ }
352
+
353
+ public function processColumnList(&$cols, $params = null)
354
+ {
355
+ // detect if we have at least one custom option
356
+ $hasopt = false;
357
+ foreach ($cols as $k)
358
+ {
359
+ $hasopt = count(explode(":", $k)) > 1;
360
+ if ($hasopt)
361
+ {
362
+ break;
363
+ }
364
+ }
365
+ // if we have at least one custom option, add options_container if not exist
366
+ if ($hasopt && !in_array('options_container', $cols))
367
+ {
368
+ $cols[] = 'options_container';
369
+ }
370
+ return true;
371
+ }
372
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/downloadable/downloadableprocessor.php ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class DownloadableProcessor extends Magmi_ItemProcessor
4
+ {
5
+ protected $_filePath;
6
+
7
+ public function initialize($params)
8
+ {
9
+ $this->_filePath = $this->getMagentoDir() . DIRSEP . "media" . DIRSEP . "downloadable" . DIRSEP . "files" .
10
+ DIRSEP . "links" . DIRSEP;
11
+ }
12
+
13
+ public function getPluginInfo()
14
+ {
15
+ return array("name"=>"Downloadable products importer","author"=>"Tangkoko SARL","version"=>"1.0.0.1",
16
+ "url"=>"http://store.tangkoko.com/fr/extensions-magento/magmi-downloadable-products-importer-plugin.html");
17
+ }
18
+
19
+ public function processItemBeforeId(&$item, $params = null)
20
+ {
21
+ // if item is not configurable, nothing to do
22
+ if ($item["type"] !== "downloadable")
23
+ {
24
+ return true;
25
+ }
26
+
27
+ return true;
28
+ }
29
+
30
+ public function processItemAfterId(&$item, $params = null)
31
+ {
32
+ // if item is not downloadable, nothing to do
33
+ if ($item["type"] !== "downloadable")
34
+ {
35
+ return true;
36
+ }
37
+
38
+ $filename = $item["sku"] . ".zip";
39
+ $sampleFilename = "sample_" . $item["sku"] . ".zip";
40
+ // donnes à importer dans les liens des produits téléchargeable
41
+ if (isset($item["links"]))
42
+ {
43
+
44
+ $this->log($item["links"], "debug");
45
+ $links = array();
46
+ $str_links = explode(";", $item["links"]);
47
+ foreach ($str_links as $str_link)
48
+ {
49
+ $arr_link = explode(",", $str_link);
50
+ $link = array();
51
+ foreach ($arr_link as $str_link)
52
+ {
53
+ $val = preg_split('/[\s:]+[\s]*/', $str_link, 2);
54
+ $link[$val[0]] = $val[1];
55
+ }
56
+ $links[] = $link;
57
+ }
58
+
59
+ $pid = $params["product_id"];
60
+
61
+ $existingLinks = $this->getExistingLinks($pid);
62
+ $nbupdate = count($existingLinks);
63
+ $nbdiff = count($links) - count($existingLinks);
64
+ $updateLinks = array();
65
+ try
66
+ {
67
+ if ($nbdiff > 0) // update existing links and add new links (more in xml file than database)
68
+ {
69
+ $addLinks = array();
70
+
71
+ for ($j = $nbupdate; $j < count($links); $j++)
72
+ {
73
+ $addLinks[] = $links[$j];
74
+ }
75
+
76
+ // ajoute les nouveaux liens
77
+ $i = 0;
78
+
79
+ foreach ($addLinks as $addLink)
80
+ {
81
+
82
+ if ($addLink["file"] = $this->copyFile($addLink))
83
+ {
84
+ $addLink["link_id"] = $this->addLink($addLink, $pid);
85
+ $addLink["price_id"] = $this->addLinkPrice($addLink);
86
+ $addLink["title_id"] = $this->addLinkTitle($addLink);
87
+ if ($addLink["sample"])
88
+ {
89
+ $addLink["sample"] = $this->copyFile($addLink);
90
+ }
91
+ }
92
+ $i++;
93
+ }
94
+ }
95
+ elseif ($nbdiff < 0)
96
+ { // update existing and delete links (more in database than xml file)
97
+ $nbdiff = $nbdiff * -1; // number to delete
98
+ $nbupdate = count($existingLinks) - $nbdiff; // nb links to update = number of existing links - difference
99
+ $i = 0;
100
+ $deleteLinks = array();
101
+ if (count($existingLinks))
102
+ {
103
+
104
+ $i = 0;
105
+
106
+ $reverse = array_reverse($existingLinks);
107
+ foreach ($reverse as $deletelink)
108
+ {
109
+ if ($i >= $nbdiff)
110
+ break;
111
+ $deleteLinks[] = $deletelink["link_id"];
112
+ $i++;
113
+ }
114
+ $this->deleteLinks($deleteLinks);
115
+ }
116
+ }
117
+
118
+ if (count($existingLinks))
119
+ {
120
+ $i = 0;
121
+ foreach ($existingLinks as $updatelink)
122
+ {
123
+ if ($i >= $nbupdate)
124
+ break;
125
+ $links[$i]["link_id"] = $updatelink["link_id"];
126
+ $updateLinks[] = $links[$i];
127
+ $i++;
128
+ }
129
+ }
130
+
131
+ if (count($updateLinks))
132
+ {
133
+ foreach ($updateLinks as $updateLink)
134
+ {
135
+ if ($updateLink["file"] = $this->copyFile($updateLink, $filename))
136
+ $this->updateLink($updateLink);
137
+ }
138
+ }
139
+ }
140
+ catch (Exception $e)
141
+ {
142
+ die($e->getMessage());
143
+ }
144
+ }
145
+ }
146
+
147
+ public function copyFile($link)
148
+ {
149
+ if (preg_match("|.*?://.*|", $link["file"]))
150
+ {
151
+ $filename = $this->getFilename($link["file"]);
152
+ }
153
+ else
154
+ {
155
+ $filename = basename($link["file"]);
156
+ }
157
+
158
+ if ($filename)
159
+ {
160
+ $destdir = $this->_filePath . $filename[0] . DIRSEP . $filename[1] . DIRSEP;
161
+ $cpfilename = $destdir . $filename;
162
+
163
+ @mkdir($this->_filePath . $filename[0], 0777);
164
+ @mkdir($destdir, 0777);
165
+
166
+ if (preg_match("|.*?://.*|", $link["file"]))
167
+ {
168
+ if (is_file($cpfilename))
169
+ unlink($cpfilename);
170
+ $this->download($link["file"], $cpfilename);
171
+ }
172
+ else
173
+ {
174
+
175
+ if (!@copy($link["file"], $cpfilename))
176
+ {
177
+ unlink($cpfilename);
178
+ @copy($link["file"], $cpfilename);
179
+ }
180
+ }
181
+ }
182
+ else
183
+ {
184
+ $this->log("Le fichier n' pas été trouvé à l'emplacement " . $link["file"], "warning");
185
+ return false;
186
+ }
187
+ return substr($cpfilename, strlen($this->_filePath) - 1);
188
+ }
189
+
190
+ /**
191
+ * download files and return hash key content
192
+ */
193
+ public function download($url, $tmp_path)
194
+ {
195
+ $this->log("Téléchargement " . $url, "warning");
196
+ $ch = curl_init($url);
197
+ $fp = fopen($tmp_path, "w+");
198
+ curl_setopt($ch, CURLOPT_FILE, $fp);
199
+ curl_setopt($ch, CURLOPT_HEADER, 0);
200
+ $this->log("BEGIN Download " . $url, "warning");
201
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
202
+
203
+ if ($httpCode == 404)
204
+ {
205
+ throw new Exception("File " . $url . " not found !");
206
+ }
207
+ curl_exec($ch);
208
+ curl_close($ch);
209
+ $this->log("End Download " . $url, "warning");
210
+ fclose($fp);
211
+ }
212
+
213
+ private function getFilename($url)
214
+ {
215
+ $ch = curl_init();
216
+ curl_setopt($ch, CURLOPT_URL, $url);
217
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
218
+ curl_setopt($ch, CURLOPT_HEADER, 1);
219
+ curl_setopt($ch, CURLOPT_NOBODY, true);
220
+ $header = curl_exec($ch);
221
+ curl_close($ch);
222
+
223
+ return $this->extractCustomHeader('Content-Disposition: attachment; filename=', '\n', $header);
224
+ }
225
+
226
+ private function extractCustomHeader($start, $end, $header)
227
+ {
228
+ $pattern = '/' . $start . '(.*?)' . $end . '/';
229
+ if (preg_match($pattern, $header, $result))
230
+ {
231
+ return $result[1];
232
+ }
233
+ else
234
+ {
235
+ return false;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * retrieve existing product links
241
+ */
242
+ public function getExistingLinks($pid)
243
+ {
244
+ $dl = $this->tablename('downloadable_link');
245
+ $sql = "select * from downloadable_link where product_id = ?";
246
+ $links = $this->selectAll($sql, array($pid));
247
+ return $links;
248
+ }
249
+
250
+ public function deleteLinks($lids)
251
+ {
252
+ $dl = $this->tablename('downloadable_link');
253
+ $sql = "DELETE FROM $dl WHERE link_id in(" . implode(",", $lids) . ")";
254
+ $this->delete($sql);
255
+ }
256
+
257
+ public function updateLink($link)
258
+ {
259
+ $dl = $this->tablename('downloadable_link');
260
+ $dlt = $this->tablename('downloadable_link_title');
261
+
262
+ $sql = "UPDATE $dl as dl
263
+ JOIN $dlt as dlt ON dl.link_id=dlt.link_id AND dlt.store_id=0
264
+ SET link_file = ?,
265
+ title = ?
266
+ WHERE dl.link_id = ?";
267
+
268
+ $this->update($sql, array($link["file"],$link["title"],$link["link_id"]));
269
+ }
270
+
271
+ public function addLink($link, $pid)
272
+ {
273
+ if ($link["is_shareable"] == "config")
274
+ {
275
+ $link["is_shareable"] = 2;
276
+ }
277
+ $dl = $this->tablename('downloadable_link');
278
+ $sql = "INSERT INTO $dl(product_id,sort_order, number_of_downloads,is_shareable, link_file,link_type) VALUES (?,?,?,?,?,?)";
279
+ // insert links
280
+ $data = array($pid,$link["sort_order"],$link["number_of_downloads"],$link["is_shareable"],$link["file"],"file");
281
+ // insert in db
282
+ return $this->insert($sql, $data);
283
+ }
284
+
285
+ public function addLinkPrice($link)
286
+ {
287
+ $dlp = $this->tablename('downloadable_link_price');
288
+ $sql = "INSERT INTO $dlp(link_id,website_id,price) VALUES (?,?,?)";
289
+ // insert links prices
290
+ $data = array($link["link_id"],0,0);
291
+ return $this->insert($sql, $data);
292
+ }
293
+
294
+ public function addLinkTitle($link)
295
+ {
296
+ $dlt = $this->tablename('downloadable_link_title');
297
+ $sql = "INSERT INTO $dlt(link_id,store_id,title) VALUES (?,?,?)";
298
+ // insert links titles
299
+ $data = array($link["link_id"],0,$link["title"]);
300
+ return $this->insert($sql, $data);
301
+ }
302
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/downloadable/options_panel.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <div class="plugin_description">
2
+ This plugin import downloadable products
3
+ <p>
4
+ A column name
5
+ <code>"links"</code>
6
+ must contain links attribute.
7
+ </p>
8
+ </div>
9
+
10
+
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/imageprocessor/imageitattributeemprocessor.php ADDED
@@ -0,0 +1,654 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ImageAttributeItemProcessor extends Magmi_ItemProcessor
4
+ {
5
+ protected $forcename = null;
6
+ protected $magdir = null;
7
+ protected $imgsourcedirs = array();
8
+ protected $errattrs = array();
9
+ protected $_errorimgs = array();
10
+ protected $_lastimage = "";
11
+ protected $_handled_attributes = array();
12
+ protected $_img_baseattrs = array("image","small_image","thumbnail");
13
+ protected $_active = false;
14
+ protected $_newitem;
15
+ protected $_mdh;
16
+ protected $_remoteroot = "";
17
+ protected $debug;
18
+
19
+ public function initialize($params)
20
+ {
21
+ // declare current class as attribute handler
22
+ $this->registerAttributeHandler($this, array("frontend_input:(media_image|gallery)"));
23
+ $this->magdir = Magmi_Config::getInstance()->getMagentoDir();
24
+ $this->_mdh = MagentoDirHandlerFactory::getInstance()->getHandler($this->magdir);
25
+ $this->_mdh->setRemoteGetterId("image");
26
+ // remote root
27
+ if ($this->getParam("IMG:remoteroot", ""))
28
+ {
29
+ if ($this->getParam("IMG:remoteauth", false) == true)
30
+ {
31
+ $this->_mdh->setRemoteCredentials($this->getParam("IMG:remoteuser"), $this->getParam("IMG:remotepass"));
32
+ }
33
+ $this->_remoteroot = $this->getParam("IMG:remoteroot");
34
+ }
35
+ $this->forcename = $this->getParam("IMG:renaming");
36
+ foreach ($params as $k => $v)
37
+ {
38
+ if (preg_match_all("/^IMG_ERR:(.*)$/", $k, $m))
39
+ {
40
+ $this->errattrs[$m[1][0]] = $params[$k];
41
+ }
42
+ }
43
+ $this->debug = $this->getParam("IMG:debug", 0);
44
+ }
45
+
46
+ public function getPluginInfo()
47
+ {
48
+ return array("name"=>"Image attributes processor","author"=>"Dweeves","version"=>"1.0.29",
49
+ "url"=>$this->pluginDocUrl("Image_attributes_processor"));
50
+ }
51
+
52
+ public function isErrorImage($img)
53
+ {
54
+ return isset($this->_errorimgs[$img]);
55
+ }
56
+
57
+ public function cachesort($v1, $v2)
58
+ {
59
+ return $v2 - $v1;
60
+ }
61
+
62
+ public function setErrorImg($img)
63
+ {
64
+ $mxerrcache = $this->getParam("IMG:maxerrorcache", 100);
65
+ // remove limit => 10%
66
+ $removelimit = intval($mxerrcache / 10);
67
+ if (count($this->_errorimgs) > $mxerrcache)
68
+ {
69
+ uasort($this->prepared, array($this,"cachesort"));
70
+ array_splice($this->_errorimgs, $removelimit, count($this->_errorimgs));
71
+ }
72
+ $this->_errorimgs[$img] = microtime(true);
73
+ }
74
+
75
+ // Image removal feature
76
+ public function handleRemoveImages($pid, &$item, $ivalue)
77
+ {
78
+ $gal_attinfo = $this->getAttrInfo("media_gallery");
79
+ $t = $this->tablename('catalog_product_entity_media_gallery');
80
+ $tv = $this->tablename('catalog_product_entity_media_gallery_value');
81
+ $rimgs = explode(";", $ivalue);
82
+ $rivals = array();
83
+ foreach ($rimgs as $rimg)
84
+ {
85
+ $rivals[] = '/' . implode('/', array($rimg[0],$rimg[1],$rimg));
86
+ }
87
+
88
+ $sql = "DELETE $t.* FROM $t
89
+ WHERE $t.entity_id=? AND $t.attribute_id=? AND $t.value IN (" . $this->arr2values($rivals) . ")";
90
+ $this->delete($sql, array_merge(array($pid,$gal_attinfo["attribute_id"]), $rivals));
91
+ }
92
+
93
+ public function handleGalleryTypeAttribute($pid, &$item, $storeid, $attrcode, $attrdesc, $ivalue)
94
+ {
95
+ // do nothing if empty
96
+ if ($ivalue == "")
97
+ {
98
+ return false;
99
+ }
100
+ // use ";" as image separator
101
+ $images = explode(";", $ivalue);
102
+ $imageindex = 0;
103
+ // for each image
104
+ foreach ($images as $imagefile)
105
+ {
106
+ // trim image file in case of spaced split
107
+ $imagefile = trim($imagefile);
108
+ // handle exclude flag explicitely
109
+ $exclude = $this->getExclude($imagefile, false);
110
+ $infolist = explode("::", $imagefile);
111
+ $label = null;
112
+ if (count($infolist) > 1)
113
+ {
114
+ $label = $infolist[1];
115
+ $imagefile = $infolist[0];
116
+ }
117
+ unset($infolist);
118
+ // copy it from source dir to product media dir
119
+ $imagefile = $this->copyImageFile($imagefile, $item,
120
+ array("store"=>$storeid,"attr_code"=>$attrcode,"imageindex"=>$imageindex == 0 ? "" : $imageindex));
121
+ if ($imagefile !== false)
122
+ {
123
+ // add to gallery
124
+ $targetsids = $this->getStoreIdsForStoreScope($item["store"]);
125
+ $vid = $this->addImageToGallery($pid, $storeid, $attrdesc, $imagefile, $targetsids, $label, $exclude);
126
+ }
127
+ $imageindex++;
128
+ }
129
+ unset($images);
130
+ // we don't want to insert after that
131
+ $ovalue = false;
132
+ return $ovalue;
133
+ }
134
+
135
+ public function removeImageFromGallery($pid, $storeid, $attrdesc)
136
+ {
137
+ $t = $this->tablename('catalog_product_entity_media_gallery');
138
+ $tv = $this->tablename('catalog_product_entity_media_gallery_value');
139
+
140
+ $sql = "DELETE $tv.* FROM $tv
141
+ JOIN $t ON $t.value_id=$tv.value_id AND $t.entity_id=? AND $t.attribute_id=?
142
+ WHERE $tv.store_id=?";
143
+ $this->delete($sql, array($pid,$attrdesc["attribute_id"],$storeid));
144
+ }
145
+
146
+ public function getExclude(&$val, $default = true)
147
+ {
148
+ $exclude = $default;
149
+ if ($val[0] == "+" || $val[0] == "-")
150
+ {
151
+ $exclude = $val[0] == "-";
152
+ $val = substr($val, 1);
153
+ }
154
+ return $exclude;
155
+ }
156
+
157
+ public function findImageFile($ivalue)
158
+ {
159
+ // do no try to find remote image
160
+ if (is_remote_path($ivalue))
161
+ {
162
+ return $ivalue;
163
+ }
164
+ // if existing, return it directly
165
+ if (realpath($ivalue))
166
+ {
167
+ return $ivalue;
168
+ }
169
+
170
+ // ok , so it's a relative path
171
+ $imgfile = false;
172
+ $scandirs = explode(";", $this->getParam("IMG:sourcedir"));
173
+ $cscandirs = count($scandirs);
174
+ // iterate on image sourcedirs, trying to resolve file name based on input value and current source dir
175
+ for ($i = 0; $i < $cscandirs && $imgfile === false; $i++)
176
+ {
177
+ $sd = $scandirs[$i];
178
+ // scandir is relative, use mdh
179
+ if ($sd[0] != "/")
180
+ {
181
+ $sd = $this->_mdh->getMagentoDir() . "/" . $sd;
182
+ }
183
+ $imgfile = abspath($ivalue, $sd, true);
184
+ }
185
+ return $imgfile;
186
+ }
187
+
188
+ public function handleImageTypeAttribute($pid, &$item, $storeid, $attrcode, $attrdesc, $ivalue)
189
+ {
190
+ // remove attribute value if empty
191
+ if ($ivalue == "" || $ivalue == "__NULL__")
192
+ {
193
+ $this->removeImageFromGallery($pid, $storeid, $attrdesc);
194
+ return "__MAGMI_DELETE__";
195
+ }
196
+
197
+ // add support for explicit exclude
198
+ $exclude = $this->getExclude($ivalue, true);
199
+ $imagefile = trim($ivalue);
200
+
201
+ // else copy image file
202
+ $imagefile = $this->copyImageFile($imagefile, $item, array("store"=>$storeid,"attr_code"=>$attrcode));
203
+ $ovalue = $imagefile;
204
+ // add to gallery as excluded
205
+ if ($imagefile !== false)
206
+ {
207
+ $label = null;
208
+ if (isset($item[$attrcode . "_label"]))
209
+ {
210
+ $label = $item[$attrcode . "_label"];
211
+ }
212
+ $targetsids = $this->getStoreIdsForStoreScope($item["store"]);
213
+ $vid = $this->addImageToGallery($pid, $storeid, $attrdesc, $imagefile, $targetsids, $label, $exclude,
214
+ $attrdesc["attribute_id"]);
215
+ }
216
+ return $ovalue;
217
+ }
218
+
219
+ public function handleVarcharAttribute($pid, &$item, $storeid, $attrcode, $attrdesc, $ivalue)
220
+ {
221
+ if (trim($ivalue) == "")
222
+ {
223
+ return trim($ivalue);
224
+ }
225
+ // trimming
226
+ $ivalue = trim($ivalue);
227
+ // If not already a remote image & force remote root, set it & set authentication
228
+ if (!is_remote_path($ivalue))
229
+ {
230
+ if ($this->_remoteroot != "")
231
+ {
232
+ $ivalue = $this->_remoteroot . str_replace("//", "/", "/$ivalue");
233
+ }
234
+ }
235
+ if (is_remote_path($ivalue))
236
+ {
237
+ // Amazon images patch , remove SLXXXX part
238
+ if (strpos($ivalue, 'amazon.com/images/I') !== false)
239
+ {
240
+ $pattern = '/\bSL[0-9]+\./i';
241
+ $ivalue = preg_replace($pattern, '', $ivalue);
242
+ }
243
+ }
244
+
245
+ // if it's a gallery
246
+ switch ($attrdesc["frontend_input"])
247
+ {
248
+ case "gallery":
249
+
250
+ $ovalue = $this->handleGalleryTypeAttribute($pid, $item, $storeid, $attrcode, $attrdesc, $ivalue);
251
+ break;
252
+ case "media_image":
253
+ $ovalue = $this->handleImageTypeAttribute($pid, $item, $storeid, $attrcode, $attrdesc, $ivalue);
254
+ break;
255
+ default:
256
+ $ovalue = "__MAGMI_UNHANDLED__";
257
+ }
258
+ return $ovalue;
259
+ }
260
+
261
+ /**
262
+ * imageInGallery
263
+ *
264
+ * @param int $pid
265
+ * : product id to test image existence in gallery
266
+ * @param string $imgname
267
+ * : image file name (relative to /products/media in magento dir)
268
+ * @return bool : if image is already present in gallery for a given product id
269
+ */
270
+ public function getImageId($pid, $attid, $imgname, $refid = null)
271
+ {
272
+ $t = $this->tablename('catalog_product_entity_media_gallery');
273
+
274
+ $sql = "SELECT $t.value_id FROM $t ";
275
+ if ($refid != null)
276
+ {
277
+ $vc = $this->tablename('catalog_product_entity_varchar');
278
+ $sql .= " JOIN $vc ON $t.entity_id=$vc.entity_id AND $t.value=$vc.value AND $vc.attribute_id=?
279
+ WHERE $t.entity_id=?";
280
+ $imgid = $this->selectone($sql, array($refid,$pid), 'value_id');
281
+ }
282
+ else
283
+ {
284
+ $sql .= " WHERE value=? AND entity_id=? AND attribute_id=?";
285
+ $imgid = $this->selectone($sql, array($imgname,$pid,$attid), 'value_id');
286
+ }
287
+
288
+ if ($imgid == null)
289
+ {
290
+ // insert image in media_gallery
291
+ $sql = "INSERT INTO $t
292
+ (attribute_id,entity_id,value)
293
+ VALUES
294
+ (?,?,?)";
295
+
296
+ $imgid = $this->insert($sql, array($attid,$pid,$imgname));
297
+ }
298
+ else
299
+ {
300
+ $sql = "UPDATE $t
301
+ SET value=?
302
+ WHERE value_id=?";
303
+ $this->update($sql, array($imgname,$imgid));
304
+ }
305
+ return $imgid;
306
+ }
307
+
308
+ /**
309
+ * reset product gallery
310
+ *
311
+ * @param int $pid
312
+ * : product id
313
+ */
314
+ public function resetGallery($pid, $storeid, $attid)
315
+ {
316
+ $tgv = $this->tablename('catalog_product_entity_media_gallery_value');
317
+ $tg = $this->tablename('catalog_product_entity_media_gallery');
318
+ $sql = "DELETE emgv,emg FROM `$tgv` as emgv
319
+ JOIN `$tg` AS emg ON emgv.value_id = emg.value_id AND emgv.store_id=?
320
+ WHERE emg.entity_id=? AND emg.attribute_id=?";
321
+ $this->delete($sql, array($storeid,$pid,$attid));
322
+ }
323
+
324
+ /**
325
+ * adds an image to product image gallery only if not already exists
326
+ *
327
+ * @param int $pid
328
+ * : product id to test image existence in gallery
329
+ * @param array $attrdesc
330
+ * : product attribute description
331
+ * @param string $imgname
332
+ * : image file name (relative to /products/media in magento dir)
333
+ */
334
+ public function addImageToGallery($pid, $storeid, $attrdesc, $imgname, $targetsids, $imglabel = null, $excluded = false,
335
+ $refid = null)
336
+ {
337
+ $gal_attinfo = $this->getAttrInfo("media_gallery");
338
+ $tg = $this->tablename('catalog_product_entity_media_gallery');
339
+ $tgv = $this->tablename('catalog_product_entity_media_gallery_value');
340
+ $vid = $this->getImageId($pid, $gal_attinfo["attribute_id"], $imgname, $refid);
341
+ if ($vid != null)
342
+ {
343
+
344
+ // et maximum current position in the product gallery
345
+ $sql = "SELECT MAX( position ) as maxpos
346
+ FROM $tgv AS emgv
347
+ JOIN $tg AS emg ON emg.value_id = emgv.value_id AND emg.entity_id = ?
348
+ WHERE emgv.store_id=?
349
+ GROUP BY emg.entity_id";
350
+ $pos = $this->selectone($sql, array($pid,$storeid), 'maxpos');
351
+ $pos = ($pos == null ? 0 : $pos + 1);
352
+ // nsert new value (ingnore duplicates)
353
+
354
+ $vinserts = array();
355
+ $data = array();
356
+
357
+ foreach ($targetsids as $tsid)
358
+ {
359
+ $vinserts[] = "(?,?,?,?," . ($imglabel == null ? "NULL" : "?") . ")";
360
+ $data = array_merge($data, array($vid,$tsid,$pos,$excluded ? 1 : 0));
361
+ if ($imglabel != null)
362
+ {
363
+ $data[] = $imglabel;
364
+ }
365
+ }
366
+
367
+ if (count($data) > 0)
368
+ {
369
+ $sql = "INSERT INTO $tgv
370
+ (value_id,store_id,position,disabled,label)
371
+ VALUES " . implode(",", $vinserts) . "
372
+ ON DUPLICATE KEY UPDATE label=VALUES(`label`),disabled=VALUES(`disabled`)";
373
+ $this->insert($sql, $data);
374
+ }
375
+ unset($vinserts);
376
+ unset($data);
377
+ }
378
+ }
379
+
380
+ public function parsename($info, $item, $extra)
381
+ {
382
+ $info = $this->parseCalculatedValue($info, $item, $extra);
383
+ return $info;
384
+ }
385
+
386
+ public function getPluginParams($params)
387
+ {
388
+ $pp = array();
389
+ foreach ($params as $k => $v)
390
+ {
391
+ if (preg_match("/^IMG(_ERR)?:.*$/", $k))
392
+ {
393
+ $pp[$k] = $v;
394
+ }
395
+ }
396
+ return $pp;
397
+ }
398
+
399
+ public function fillErrorAttributes(&$item)
400
+ {
401
+ foreach ($this->errattrs as $k => $v)
402
+ {
403
+ $this->addExtraAttribute($k);
404
+ $item[$k] = $v;
405
+ }
406
+ }
407
+
408
+ public function getImagenameComponents($fname, $formula, $extra)
409
+ {
410
+ $matches = array();
411
+ $xname = $fname;
412
+ if (preg_match("|re::(.*)::(.*)|", $formula, $matches))
413
+ {
414
+ $rep = $matches[2];
415
+ $xname = preg_replace("|" . $matches[1] . "|", $rep, $xname);
416
+ $extra['parsed'] = true;
417
+ }
418
+ $xname = basename($xname);
419
+ $m = preg_match("/(.*)\.(jpg|png|gif)$/i", $xname, $matches);
420
+ if ($m)
421
+ {
422
+ $extra["imagename"] = $xname;
423
+ $extra["imagename.ext"] = $matches[2];
424
+ $extra["imagename.noext"] = $matches[1];
425
+ }
426
+ else
427
+ {
428
+ $uid = uniqid("img", true);
429
+ $extra = array_merge($extra, array("imagename"=>"$uid.jpg","imagename.ext"=>"jpg","imagename.noext"=>$uid));
430
+ }
431
+
432
+ return $extra;
433
+ }
434
+
435
+ public function getTargetName($fname, $item, $extra)
436
+ {
437
+ $cname = basename($fname);
438
+ if (isset($this->forcename) && $this->forcename != "")
439
+ {
440
+ $extra = $this->getImagenameComponents($fname, $this->forcename, $extra);
441
+ $pname = ($extra['parsed'] ? $extra['imagename'] : $this->forcename);
442
+ $cname = $this->parsename($pname, $item, $extra);
443
+ }
444
+ $cname = strtolower(preg_replace("/%[0-9][0-9|A-F]/", "_", rawurlencode($cname)));
445
+
446
+ return $cname;
447
+ }
448
+
449
+ public function saveImage($imgfile, $target)
450
+ {
451
+ $result = $this->_mdh->copy($imgfile, $target);
452
+ return $result;
453
+ }
454
+
455
+ /**
456
+ * copy image file from source directory to
457
+ * product media directory
458
+ *
459
+ * @param $imgfile :
460
+ * name of image file name in source directory
461
+ * @return : name of image file name relative to magento catalog media dir,including leading
462
+ * directories made of first char & second char of image file name.
463
+ */
464
+ public function copyImageFile($imgfile, &$item, $extra)
465
+ {
466
+ if ($imgfile == "__NULL__" || $imgfile == null)
467
+ {
468
+ return false;
469
+ }
470
+
471
+ // check for source image in error
472
+ if ($this->isErrorImage($imgfile))
473
+ {
474
+ if ($this->_newitem)
475
+ {
476
+ $this->fillErrorAttributes($item);
477
+ }
478
+ ;
479
+ return false;
480
+ }
481
+
482
+ $source = $this->findImageFile($imgfile);
483
+ if ($source == false)
484
+ {
485
+ $this->log("$imgfile cannot be found in images path", "warning");
486
+ // last image in error,add it to error cache
487
+ $this->setErrorImg($imgfile);
488
+ return false;
489
+ }
490
+ $imgfile = $source;
491
+ $checkexist = ($this->getParam("IMG:existingonly") == "yes");
492
+ $curlh = false;
493
+ $bimgfile = $this->getTargetName($imgfile, $item, $extra);
494
+ // source file exists
495
+ $i1 = $bimgfile[0];
496
+ $i2 = $bimgfile[1];
497
+ // magento image value (relative to media catalog)
498
+ $impath = "/$i1/$i2/$bimgfile";
499
+ // target directory;
500
+ $l2d = "media/catalog/product/$i1/$i2";
501
+ // test for existence
502
+ $targetpath = "$l2d/$bimgfile";
503
+ /* test for same image (without problem) */
504
+ if ($impath == $this->_lastimage)
505
+ {
506
+ return $impath;
507
+ }
508
+ /* test if imagefile comes from export */
509
+ if (!$this->_mdh->file_exists($targetpath) || $this->getParam("IMG:writemode") == "override")
510
+ {
511
+ // if we already had problems with this target,assume we'll get others.
512
+ if ($this->isErrorImage($impath))
513
+ {
514
+ return false;
515
+ }
516
+
517
+ /* try to recursively create target dir */
518
+ if (!$this->_mdh->file_exists($l2d))
519
+ {
520
+
521
+ $tst = $this->_mdh->mkdir($l2d, Magmi_Config::getInstance()->getDirMask(), true);
522
+ if (!$tst)
523
+ {
524
+ // if we had problem creating target directory,add target to error cache
525
+ $errors = $this->_mdh->getLastError();
526
+ $this->log("error creating $l2d: {$errors["type"]},{$errors["message"]}", "warning");
527
+ unset($errors);
528
+ $this->setErrorImg($impath);
529
+ return false;
530
+ }
531
+ }
532
+
533
+ if (!$this->saveImage($imgfile, $targetpath))
534
+ {
535
+ $errors = $this->_mdh->getLastError();
536
+ $this->fillErrorAttributes($item);
537
+ $this->log("error copying $l2d/$bimgfile : {$errors["type"]},{$errors["message"]}", "warning");
538
+ unset($errors);
539
+ $this->setErrorImg($impath);
540
+ return false;
541
+ }
542
+ else
543
+ {
544
+ @$this->_mdh->chmod("$l2d/$bimgfile", Magmi_Config::getInstance()->getFileMask());
545
+ }
546
+ }
547
+ $this->_lastimage = $impath;
548
+ /* return image file name relative to media dir (with leading / ) */
549
+ return $impath;
550
+ }
551
+
552
+ public function updateLabel($attrdesc, $pid, $sids, $label)
553
+ {
554
+ $tg = $this->tablename('catalog_product_entity_media_gallery');
555
+ $tgv = $this->tablename('catalog_product_entity_media_gallery_value');
556
+ $vc = $this->tablename('catalog_product_entity_varchar');
557
+ $sql = "UPDATE $tgv as emgv
558
+ JOIN $tg as emg ON emg.value_id=emgv.value_id AND emg.entity_id=?
559
+ JOIN $vc as ev ON ev.entity_id=emg.entity_id AND ev.value=emg.value and ev.attribute_id=?
560
+ SET label=?
561
+ WHERE emgv.store_id IN (" . implode(",", $sids) . ")";
562
+ $this->update($sql, array($pid,$attrdesc["attribute_id"],$label));
563
+ }
564
+
565
+ public function processItemAfterId(&$item, $params = null)
566
+ {
567
+ if (!$this->_active)
568
+ {
569
+ return true;
570
+ }
571
+ $this->_newitem = $params["new"];
572
+ $pid = $params["product_id"];
573
+ foreach ($this->_img_baseattrs as $attrcode)
574
+ {
575
+ // if only image/small_image/thumbnail label is present (ie: no image field)
576
+ if (isset($item[$attrcode . "_label"]) && !isset($item[$attrcode]))
577
+ {
578
+ // force label update
579
+ $attrdesc = $this->getAttrInfo($attrcode);
580
+ $this->updateLabel($attrdesc, $pid, $this->getItemStoreIds($item, $attr_desc["is_global"]),
581
+ $item[$attrcode . "_label"]);
582
+ unset($attrdesc);
583
+ }
584
+ }
585
+ // Reset media_gallery
586
+ $galreset = !(isset($item["media_gallery_reset"])) || $item["media_gallery_reset"] == 1;
587
+ $forcereset = (isset($item["media_gallery_reset"])) && $item["media_gallery_reset"] == 1;
588
+
589
+ if ((isset($item["media_gallery"]) && $galreset) || $forcereset)
590
+ {
591
+ $gattrdesc = $this->getAttrInfo("media_gallery");
592
+ $sids = $this->getItemStoreIds($item, $gattrdesc["is_global"]);
593
+ foreach ($sids as $sid)
594
+ {
595
+ $this->resetGallery($pid, $sid, $gattrdesc["attribute_id"]);
596
+ }
597
+ }
598
+ if (isset($item["image_remove"]))
599
+ {
600
+ $this->handleRemoveImages($pid, $item, $item["image_remove"]);
601
+ }
602
+ return true;
603
+ }
604
+
605
+ public function processColumnList(&$cols, $params = null)
606
+ {
607
+ // automatically add modified attributes if not found in datasource
608
+
609
+ // automatically add media_gallery for attributes to handle
610
+ $imgattrs = array_intersect(array_merge($this->_img_baseattrs, array('media_gallery')), $cols);
611
+ if (count($imgattrs) > 0)
612
+ {
613
+ $this->_active = true;
614
+ $cols = array_unique(array_merge(array_keys($this->errattrs), $cols, $imgattrs));
615
+ }
616
+ else
617
+ {
618
+ $this->log("no image attributes found in datasource, disabling image processor", "startup");
619
+ }
620
+ return true;
621
+ }
622
+
623
+ // Cleanup gallery from removed images if no more image values are present in any store
624
+ public function endImport()
625
+ {
626
+ if (!$this->_active)
627
+ {
628
+ return;
629
+ }
630
+ $attids = array();
631
+ foreach ($this->_img_baseattrs as $attrcode)
632
+ {
633
+ $inf = $this->getAttrInfo($attrcode);
634
+ if (count($inf) > 0)
635
+ {
636
+ $attids[] = $inf["attribute_id"];
637
+ }
638
+ }
639
+ if (count($attids) > 0)
640
+ {
641
+ $tg = $this->tablename('catalog_product_entity_media_gallery');
642
+ $tgv = $this->tablename('catalog_product_entity_media_gallery_value');
643
+ $sql = "DELETE emg.* FROM $tg as emg
644
+ LEFT JOIN (SELECT emg.value_id,count(emgv.value_id) as cnt FROM $tgv as emgv JOIN $tg as emg ON emg.value_id=emgv.value_id GROUP BY emg.value_id ) as t1 ON t1.value_id=emg.value_id
645
+ WHERE attribute_id IN (" . implode(",", $attids) . ") AND t1.cnt IS NULL";
646
+ $this->delete($sql);
647
+ }
648
+ else
649
+ {
650
+ $this->log("Unexpected problem in image attributes retrieval", "warning");
651
+ }
652
+ unset($attids);
653
+ }
654
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/imageprocessor/options_panel.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="plugin_description">This plugin is the new image importing
2
+ feature of magmi. It enables image renaming from input value with some
3
+ dynamic values coming from input values.</div>
4
+
5
+ <div class="formline">
6
+ <span>Image search path:</span><input type="text" name="IMG:sourcedir"
7
+ size="80"
8
+ value="<?php echo $this->getParam("IMG:sourcedir","media/import")?>"></input>
9
+ <div class="fieldinfo">
10
+ semicolon separated list of search paths for images<br>if relative
11
+ path is used, it means "relative to magento base dir",absolute path is
12
+ used as is
13
+ </div>
14
+ </div>
15
+ <div class="formline ifield">
16
+ <span>Image Renaming:</span><input type="text" name="IMG:renaming"
17
+ size="80" value="<?php echo $this->getParam("IMG:renaming")?>"></input>
18
+ <div class="fieldhelp"></div>
19
+ <div class="fieldinfo">
20
+ Leave blank to keep original image name.
21
+ <div class="fieldsyntax" style="display: none">
22
+ <ul>
23
+ <li>You can use "dynamic variables" to fill this field.</li>
24
+ <li>{item.[some item field]} and
25
+ {magmi.[store|imagename|imagename.noext|imagename.ext|attr_code]}
26
+ are supported.</li>
27
+ <li>{item.sku}.jpg : will create an image with item sku value as
28
+ name and a jpg extension.</li>
29
+ <li>{item.sku}_{magmi.store}.jpg : this is a little trickier.<br />
30
+ if you've got 5 stores,i will create 5 different copies of the
31
+ input image &amp; force the name to be [item sku]_[store id].jpg
32
+ for each copy.
33
+ </li>
34
+ <li>{item.sku}_{magmi.imagename.noext}_{magmi.attr_code}.jpg, will
35
+ create [sku]_[image name without extension]_[column name].jpg
36
+ magento filename.</li>
37
+ </ul>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <div class="formline">
42
+ <span>Image import mode</span>
43
+ <?php $iwm=$this->getParam("IMG:writemode","keep");?>
44
+ <select name="IMG:writemode">
45
+ <option value="keep" <?php if($iwm=="keep"){?> selected="selected"
46
+ <?php }?>>Keep existing images</option>
47
+ <option value="override" <?php if($iwm=="override"){?>
48
+ selected="selected" <?php }?>>Override existing images</option>
49
+ </select> <span>Assign only existing images</span> <select
50
+ name="IMG:existingonly">
51
+ <?php $existonly=$this->getParam("IMG:existingonly","no");?>
52
+ <option value="yes" <?php if($existonly=="yes"){?> selected="selected"
53
+ <?php }?>>Yes</option>
54
+ <option value="no" <?php if($existonly=="no"){?> selected="selected"
55
+ <?php }?>>No</option>
56
+ </select>
57
+ </div>
58
+ <div id="IMG:err_attrsetup">
59
+ <ul class="formline">
60
+ <li class="label">Change attributes on image error</li>
61
+ <li class="value"><input type="text" id="IMG:err_attrlist"
62
+ name="IMG:err_attrlist" size="80"
63
+ value="<?php echo $this->fixListParam($this->getParam("IMG:err_attrlist"))?>"
64
+ onblur="img_mf.buildparamlist()"></input></li>
65
+ </ul>
66
+ </div>
67
+
68
+ <div class="formline">
69
+ <span>Debug mode</span> <select name="IMG:debug">
70
+ <?php $qdd=$this->getParam("IMG:debug","no");?>
71
+ <option value="yes" <?php if($qdd=="yes"){?> selected="selected"
72
+ <?php }?>>Enable</option>
73
+ <option value="no" <?php if($qdd=="no"){?> selected="selected"
74
+ <?php }?>>Disable</option>
75
+ </select>
76
+ </div>
77
+
78
+ <div id="imgremote_details">
79
+ <h3>Remote Images Connection</h3>
80
+
81
+ <ul class="formline">
82
+ <li class="label">Remote Image root</li>
83
+ <li class="value"><input type="text" id="IMG:remoteroot"
84
+ name="IMG:remoteroot" style="width: 500px"
85
+ value="<?php echo $this->getParam("IMG:remoteroot","")?>"></li>
86
+ </ul>
87
+
88
+ <ul class="formline">
89
+ <li class="label">Remote root Authentication</li>
90
+ <li class="value"><input type="checkbox" id="IMG:remoteauth"
91
+ name="IMG:remoteauth"
92
+ <?php if($this->getParam("IMG:remoteauth",false)==true){?>
93
+ checked="checked" <?php }?>>authentication needed</li>
94
+ </ul>
95
+
96
+ <div id="imgremoteauth"
97
+ <?php if($this->getParam("IMG:remoteauth",false)==false){?>
98
+ style="display: none" <?php }?>>
99
+ <ul class="formline">
100
+ <li class="label">User</li>
101
+ <li class="value"><input type="text" name="IMG:remoteuser"
102
+ id="IMG:remoteuser"
103
+ value="<?php echo $this->getParam("IMG:remoteuser","")?>"></li>
104
+
105
+ </ul>
106
+ <ul class="formline">
107
+ <li class="label">Password</li>
108
+ <li class="value"><input type="text" name="IMG:remotepass"
109
+ id="IMG:remotepass"
110
+ value="<?php echo $this->getParam("IMG:remotepass","")?>"></li>
111
+ </ul>
112
+ </div>
113
+ </div>
114
+
115
+ <ul class="formline">
116
+ <li class="label"><span>Pre-download check</span></li>
117
+ <li class="value"><select name="IMG:predlcheck">
118
+ <?php $qdlc=$this->getParam("IMG:predlcheck","yes");?>
119
+ <option value="yes" <?php if($qdlc=="yes"){?> selected="selected"
120
+ <?php }?>>Enable</option>
121
+ <option value="no" <?php if($qdlc=="no"){?> selected="selected"
122
+ <?php }?>>Disable</option>
123
+ </select></li>
124
+ </ul>
125
+
126
+ <script type="text/javascript">
127
+ var img_vals=<?php echo tdarray_to_js($this,'IMG:err_attrlist','IMG_ERR')?>;
128
+ var img_linetpl='<ul class="formline"><li class="label">set {fieldname} as</li><li class="value"><input type="text" name="IMG_ERR:{fieldname.enc}" value="{value}"></input></li></ul>';
129
+ img_mf=new magmi_multifield('IMG:err_attrlist','IMG:err_attrsetup',img_linetpl,img_vals);
130
+ img_mf.buildparamlist();
131
+ $('IMG:remoteauth').observe('click',function()
132
+ {
133
+ if($('IMG:remoteauth').checked)
134
+ {
135
+ $('imgremoteauth').show();
136
+ }
137
+ else
138
+ {
139
+ $('imgremoteauth').hide();
140
+ }
141
+ });
142
+
143
+ </script>
144
+
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/itemindexer/options_panel.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="plugin_description">This plugin fills some magento index
2
+ tables on the fly while importing item.</div>
3
+ <div>
4
+ <ul class="formline">
5
+ <li class="label">URL endings</li>
6
+ <li class="value"><input type="text" name="OTFI:urlending"
7
+ value="<?php echo $this->getParam("OTFI:urlending",".html")?>"></input></li>
8
+ </ul>
9
+ <ul class="formline">
10
+ <li class="label">Use Categories in url</li>
11
+ <li class="value">
12
+ <?php $usecat=$this->getParam("OTFI:usecatinurl",1);?>
13
+ <select name="OTFI:usecatinurl">
14
+ <option value="1" <?php if($usecat==1){?> selected="selected"
15
+ <?php }?>>Yes</option>
16
+ <option value="0" <?php if($usecat==0){?> selected="selected"
17
+ <?php }?>>No</option>
18
+ </select>
19
+ </li>
20
+ </ul>
21
+
22
+ </div>
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/itemindexer/otfindexer.php ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ItemIndexer extends Magmi_ItemProcessor
4
+ {
5
+ protected $_toindex;
6
+ protected $tns;
7
+ protected $visinf;
8
+ protected $catpaths;
9
+
10
+ public function getPluginInfo()
11
+ {
12
+ return array("name"=>"On the fly indexer","author"=>"Dweeves","version"=>"0.1.6",
13
+ "url"=>$this->pluginDocUrl("On_the_fly_indexer"));
14
+ }
15
+
16
+ public function getPluginParamNames()
17
+ {
18
+ return array("OTFI:urlending","OTFI:usecatinurl");
19
+ }
20
+
21
+ public function initialize($params)
22
+ {
23
+ $this->_toindex = null;
24
+ // initialize shortname array for tables
25
+ $this->tns = array("cpe"=>$this->tablename("catalog_product_entity"),
26
+ "cce"=>$this->tablename("catalog_category_entity"),"ccp"=>$this->tablename("catalog_category_product"),
27
+ "cpw"=>$this->tablename("catalog_product_website"),"cs"=>$this->tablename("core_store"),
28
+ "csg"=>$this->tablename("core_store_group"),"cpev"=>$this->tablename("catalog_product_entity_varchar"),
29
+ "cpei"=>$this->tablename("catalog_product_entity_int"),
30
+ "ccev"=>$this->tablename("catalog_category_entity_varchar"),"ea"=>$this->tablename("eav_attribute"),
31
+ "ccpi"=>$this->tablename("catalog_category_product_index"),"curw"=>$this->tablename("core_url_rewrite"));
32
+ $inf = $this->getAttrInfo("visibility");
33
+ if ($inf == null)
34
+ {
35
+ $this->initAttrInfos(array("visibility"));
36
+ $inf = $this->getAttrInfo("visibility");
37
+ }
38
+ $this->visinf = $inf;
39
+ }
40
+
41
+ /**
42
+ * Return item category ids from PID (full tree)
43
+ *
44
+ * @param $baselevel :
45
+ * begin tree from specified category level (defaults 0)
46
+ * @return array of category ids (all mixed branches) for the item
47
+ * *
48
+ */
49
+ public function getItemCategoryIds($pid)
50
+ {
51
+ $sql = "SELECT cce.path FROM {$this->tns["ccp"]} as ccp
52
+ JOIN {$this->tns["cce"]} as cce ON ccp.category_id=cce.entity_id
53
+ WHERE ccp.product_id=?";
54
+ $result = $this->selectAll($sql, $pid);
55
+ $catidlist = array();
56
+ foreach ($result as $row)
57
+ {
58
+ $catidlist = array_merge($catidlist, explode("/", $row["path"]));
59
+ }
60
+ $catidlist = array_unique($catidlist);
61
+ sort($catidlist);
62
+ return $catidlist;
63
+ }
64
+
65
+ /**
66
+ * Return item category paths per store from PID
67
+ * *
68
+ */
69
+ public function getItemCategoryPaths($pid)
70
+ {
71
+ $sql = "SELECT cce.path as cpath,SUBSTR(cce.path,LOCATE('/',cce.path,3)+1) as cshortpath,csg.default_store_id as store_id,cce.entity_id as catid
72
+ FROM {$this->tns["ccp"]} as ccp
73
+ JOIN {$this->tns["cce"]} as cce ON cce.entity_id=ccp.category_id
74
+ JOIN {$this->tns["csg"]} as csg ON csg.root_category_id=SUBSTR(SUBSTRING_INDEX(cce.path,'/',2),LOCATE('/',SUBSTRING_INDEX(cce.path,'/',2))+1)
75
+ WHERE ccp.product_id=?";
76
+ $result = $this->selectAll($sql, $pid);
77
+ $cpaths = array();
78
+ foreach ($result as $row)
79
+ {
80
+ $sid = $row["store_id"];
81
+ if (!isset($cpaths[$sid]))
82
+ {
83
+ $cpaths[$sid] = array();
84
+ }
85
+ $cpaths[$sid][] = $row;
86
+ }
87
+ unset($result);
88
+ return $cpaths;
89
+ }
90
+
91
+ /**
92
+ * Build catalog_category_product_index entry for given pid
93
+ *
94
+ * @param int $pid
95
+ * , product id to create index entry for
96
+ */
97
+ public function buildCatalogCategoryProductIndex($pid)
98
+ {
99
+ $catidlist = $this->getItemCategoryIds($pid);
100
+ if (count($catidlist) == 0)
101
+ {
102
+ return;
103
+ }
104
+ array_shift($catidlist);
105
+ // get all category ids on which the product is affected
106
+
107
+ // let's make a IN placeholder string with that
108
+ $catidin = $this->arr2values($catidlist);
109
+ // first delete lines where last inserted product was
110
+ $sql = "DELETE FROM {$this->tns["ccpi"]} WHERE product_id=?";
111
+ $this->delete($sql, $pid);
112
+ // then add lines for index
113
+ $sqlsel = "INSERT IGNORE INTO {$this->tns["ccpi"]}
114
+ SELECT cce.entity_id as category_id,ccp.product_id,ccp.position,IF(cce.entity_id=ccp.category_id,1,0) as is_parent,csg.default_store_id,cpei.value as visibility
115
+ FROM {$this->tns["ccp"]} as ccp
116
+ JOIN {$this->tns["cpe"]} as cpe ON ccp.product_id=cpe.entity_id
117
+ JOIN {$this->tns["cpei"]} as cpei ON cpei.attribute_id=? AND cpei.entity_id=cpe.entity_id
118
+ JOIN {$this->tns["cce"]} as cce ON cce.entity_id IN ($catidin)
119
+ JOIN {$this->tns["csg"]} as csg ON csg.root_category_id=SUBSTR(SUBSTRING_INDEX(cce.path,'/',2),LOCATE('/',SUBSTRING_INDEX(cce.path,'/',2))+1)
120
+ WHERE ccp.product_id=?
121
+ ORDER by is_parent DESC,csg.default_store_id,cce.entity_id";
122
+ // build data array for request
123
+ $data = array_merge(array($this->visinf["attribute_id"]), $catidlist, array($pid));
124
+ // create index line(s)
125
+ $this->insert($sqlsel, $data);
126
+ }
127
+
128
+ /**
129
+ * Build core_url_rewrite index entry for given pid
130
+ *
131
+ * @param int $pid
132
+ * , product id to create index entry for
133
+ */
134
+ public function buildUrlCatProdRewrite($pid, $purlk)
135
+ {
136
+ $catpathlist = $this->getItemCategoryPaths($pid);
137
+ $data = array();
138
+ $vstr = array();
139
+ // now we have catpaths per store
140
+ $cnames = array();
141
+ foreach ($catpathlist as $storeid => $paths)
142
+ {
143
+ $catids = array();
144
+
145
+ foreach ($paths as $pathinfo)
146
+ {
147
+ $catids = array_unique(array_merge($catids, explode("/", $pathinfo["cshortpath"])));
148
+ }
149
+
150
+ $catin = $this->arr2values($catids);
151
+
152
+ // use tricky double join on eav_attribute to find category related 'name' attribute using 'children' category only attr to distinguish on category entity_id
153
+ $sql = "SELECT cce.entity_id as catid,COALESCE(ccev.value,ccevd.value) as value
154
+ FROM {$this->tns["cce"]} as cce
155
+ JOIN {$this->tns["ea"]} as ea1 ON ea1.attribute_code='children'
156
+ JOIN {$this->tns["ea"]} as ea2 ON ea2.attribute_code ='name' AND ea2.entity_type_id=ea1.entity_type_id
157
+ JOIN {$this->tns["ccev"]} as ccevd ON ccevd.attribute_id=ea2.attribute_id AND ccevd.entity_id=cce.entity_id AND ccevd.store_id=0
158
+ LEFT JOIN {$this->tns["ccev"]} as ccev ON ccev.attribute_id=ea2.attribute_id AND ccev.entity_id=cce.entity_id AND ccev.store_id=?
159
+ WHERE cce.entity_id IN ($catin)
160
+ GROUP BY cce.entity_id";
161
+ $result = $this->selectAll($sql, array_merge(array($storeid), $catids));
162
+
163
+ // iterate on all names
164
+ foreach ($result as $row)
165
+ {
166
+ $catid = $row["catid"];
167
+ $cnames[$catid] = $row["value"];
168
+ }
169
+
170
+ foreach ($paths as $pinfo)
171
+ {
172
+ $sp = $pinfo["cshortpath"];
173
+ $cpids = explode("/", $sp);
174
+ $names = array();
175
+ foreach ($cpids as $cpid)
176
+ {
177
+ if (isset($cnames[$cpid]))
178
+ {
179
+ $names[] = $cnames[$cpid];
180
+ }
181
+ }
182
+ // make string with that
183
+ $namestr = implode("/", $names);
184
+ // build category url key (allow / in slugging)
185
+ $curlk = Slugger::slug($namestr, true);
186
+
187
+ // product + category url entries request
188
+ $catid = $pinfo["catid"];
189
+ $sdata = array($pid,$storeid,$catid,"product/$pid/$catid",
190
+ "catalog/product/view/id/$pid/category/$catid","$curlk/$purlk",1);
191
+ $vstr[] = "(" . $this->arr2values($sdata) . ")";
192
+ $data = array_merge($data, $sdata);
193
+ }
194
+ }
195
+ if (count($vstr) > 0)
196
+ {
197
+ $sqlprodcat = "INSERT IGNORE INTO {$this->tns["curw"]} (product_id,store_id,category_id,id_path,target_path,request_path,is_system) VALUES " .
198
+ implode(",", $vstr);
199
+ $this->insert($sqlprodcat, $data);
200
+ }
201
+ if (count($catpathlist) > 0)
202
+ {
203
+ // now insert category url rewrite
204
+ $this->buildCatUrlRewrite($catpathlist, $cnames);
205
+ }
206
+ unset($data);
207
+ unset($sdata);
208
+ unset($vstr);
209
+ }
210
+
211
+ public function buildCatUrlRewrite($catpathlist, $cnames)
212
+ {
213
+ $vstr = array();
214
+ $data = array();
215
+ foreach ($catpathlist as $storeid => $paths)
216
+ {
217
+ foreach ($paths as $pinfo)
218
+ {
219
+ $sp = $pinfo["cshortpath"];
220
+ $cpids = explode("/", $sp);
221
+ $names = array();
222
+ foreach ($cpids as $cpid)
223
+ {
224
+ if (isset($cnames[$cpid]))
225
+ {
226
+ $names[] = $cnames[$cpid];
227
+ // make string with that
228
+ $namestr = implode("/", $names);
229
+ $urlend = $this->getParam("OTFI:urlending", ".html");
230
+ // build category url key (allow / in slugging)
231
+ $curlk = Slugger::slug($namestr, true) . $urlend;
232
+ $cdata = array($storeid,$cpid,"category/$cpid","catalog/category/view/id/$cpid","$curlk",1);
233
+ $vstr[] = "(" . $this->arr2values($cdata) . ")";
234
+ $data = array_merge($data, $cdata);
235
+ }
236
+ }
237
+ }
238
+ }
239
+ if (count($vstr) > 0)
240
+ {
241
+ $sqlcat = "INSERT INTO {$this->tns["curw"]} (store_id,category_id,id_path,target_path,request_path,is_system) VALUES " .
242
+ implode(",", $vstr) . " ON DUPLICATE KEY UPDATE request_path=VALUES(`request_path`)";
243
+ $this->insert($sqlcat, $data);
244
+ }
245
+ }
246
+
247
+ public function builProductUrlRewrite($pid)
248
+ {
249
+ $sql = "SELECT ea.attribute_code,cpei.value,cpev.attribute_id,cpev.value
250
+ FROM {$this->tns["cpe"]} AS cpe
251
+ JOIN {$this->tns["ea"]} as ea ON ea.attribute_code IN ('url_key','name')
252
+ JOIN {$this->tns["cpev"]} as cpev ON cpev.entity_id=cpe.entity_id AND cpev.attribute_id=ea.attribute_id
253
+ JOIN {$this->tns["cpei"]} as cpei ON cpei.entity_id=cpe.entity_id AND cpei.attribute_id=? AND cpei.value>1
254
+ WHERE cpe.entity_id=?";
255
+ $result = $this->selectAll($sql, array($this->visinf["attribute_id"],$pid));
256
+ // nothing to build, product is not visible,return
257
+ if (count($result) == 0)
258
+ {
259
+ return;
260
+ }
261
+ // see what we get as available product attributes
262
+ foreach ($result as $row)
263
+ {
264
+ if ($row["attribute_code"] == "url_key")
265
+ {
266
+ $pburlk = nullifempty($row["value"]);
267
+ }
268
+ if ($row["attribute_code"] == "name")
269
+ {
270
+ $pname = $row["value"];
271
+ }
272
+ }
273
+ // if we've got an url key use it, otherwise , make a slug from the product name as url key
274
+ $urlend = $this->getParam("OTFI:urlending", ".html");
275
+ $purlk = (isset($pburlk) ? $pburlk : Slugger::slug($pname)) . $urlend;
276
+
277
+ // delete old "system" url rewrite entries for product
278
+ $sql = "DELETE FROM {$this->tns["curw"]} WHERE product_id=? AND is_system=1";
279
+ $this->delete($sql, $pid);
280
+
281
+ // product url index info
282
+ $produrlsql = "SELECT cpe.entity_id,cs.store_id,
283
+ CONCAT('product/',cpe.entity_id) as id_path,
284
+ CONCAT('catalog/product/view/id/',cpe.entity_id) as target_path,
285
+ ? AS request_path,
286
+ 1 as is_system
287
+ FROM {$this->tns["cpe"]} as cpe
288
+ JOIN {$this->tns["cpw"]} as cpw ON cpw.product_id=cpe.entity_id
289
+ JOIN {$this->tns["cs"]} as cs ON cs.website_id=cpw.website_id
290
+ JOIN {$this->tns["ccp"]} as ccp ON ccp.product_id=cpe.entity_id
291
+ JOIN {$this->tns["cce"]} as cce ON ccp.category_id=cce.entity_id
292
+ WHERE cpe.entity_id=?";
293
+
294
+ // insert lines
295
+ $sqlprod = "INSERT INTO {$this->tns["curw"]} (product_id,store_id,id_path,target_path,request_path,is_system) $produrlsql ON DUPLICATE KEY UPDATE request_path=VALUES(`request_path`)";
296
+ $this->insert($sqlprod, array($purlk,$pid));
297
+ return $purlk;
298
+ }
299
+
300
+ public function buildUrlRewrite($pid)
301
+ {
302
+ $purlk = $this->builProductUrlRewrite($pid);
303
+ if ($this->getParam("OTFI:usecatinurl"))
304
+ {
305
+ $this->buildUrlCatProdRewrite($pid, $purlk);
306
+ }
307
+ }
308
+
309
+ /**
310
+ * OBSOLETED , TO BE REWORKED LATER
311
+ */
312
+ public function buildPriceIndex($pid)
313
+ {
314
+ $priceidx = $this->tablename("catalog_product_index_price");
315
+ $pet = $this->getProductEntityType();
316
+ $sql = "DELETE FROM $priceidx WHERE entity_id=?";
317
+ $this->delete($sql, $pid);
318
+ $cpe = $this->tablename("catalog_product_entity");
319
+ $cs = $this->tablename("core_store");
320
+ $cg = $this->tablename("customer_group");
321
+ $cped = $this->tablename("catalog_product_entity_decimal");
322
+ $ea = $this->tablename("eav_attribute");
323
+ $cpetp = $this->tablename("catalog_product_entity_tier_price");
324
+ $cpei = $this->tablename("catalog_product_entity_int");
325
+ $sql = "INSERT INTO $priceidx SELECT cped.entity_id,
326
+ cg.customer_group_id,
327
+ cs.website_id,
328
+ cpei.value as tax_class_id,
329
+ cped.value as price,
330
+ MIN(cped.value) as final_price,
331
+ MIN(cped.value) as min_price,
332
+ MIN(cped.value) as max_price,
333
+ cpetp2.value as tier_price
334
+ FROM $cpe as cpe
335
+ JOIN $cs as cs ON cs.store_id!=0
336
+ JOIN $cped as cped ON cped.store_id=cs.store_id AND cped.entity_id=cpe.entity_id
337
+ JOIN $cg as cg
338
+ JOIN $ea as ead ON ead.entity_type_id=? AND ead.attribute_code IN('price','special_price','minimal_price') AND cped.attribute_id=ead.attribute_id
339
+ JOIN $ea as eai ON eai.entity_type_id=ead.entity_type_id AND eai.attribute_code='tax_class_id'
340
+ LEFT JOIN $cpetp as cpetp ON cpetp.entity_id=cped.entity_id
341
+ LEFT JOIN $cpetp as cpetp2 ON cpetp2.entity_id=cped.entity_id AND cpetp2.customer_group_id=cg.customer_group_id
342
+ LEFT JOIN $cpei as cpei ON cpei.entity_id=cpe.entity_id AND cpei.attribute_id=eai.attribute_id
343
+ WHERE cpe.entity_id=?
344
+ GROUP by cs.website_id,cg.customer_group_id
345
+ ORDER by cg.customer_group_id,cs.website_id
346
+ ";
347
+ $this->insert($sql, array($pet,$pid));
348
+ }
349
+
350
+ // To be done, find a way to avoid reindexing if not necessary
351
+ public function shouldReindex($item)
352
+ {
353
+ return count($item) > 0;
354
+ }
355
+
356
+ public function processItemAfterImport(&$item, $params = null)
357
+ {
358
+ if (count($item) > 0)
359
+ {
360
+ $this->reindexLastImported();
361
+ // if current item is not the same than previous one
362
+ if ($params["same"] == false)
363
+ {
364
+ if ($this->shouldReindex($item))
365
+ {
366
+ $this->_toindex = array("sku"=>$item["sku"],"pid"=>$params["product_id"]);
367
+ }
368
+ else
369
+ {
370
+ $this->log("Do not reindex, no indexed column changed");
371
+ }
372
+ }
373
+ }
374
+ return true;
375
+ }
376
+
377
+ // index last imported item
378
+ public function reindexLastImported()
379
+ {
380
+ if ($this->_toindex != null)
381
+ {
382
+ $pid = $this->_toindex["pid"];
383
+ $this->buildCatalogCategoryProductIndex($pid);
384
+ $this->buildUrlRewrite($pid);
385
+ $this->_toindex = null;
386
+ }
387
+ }
388
+
389
+ public function afterImport()
390
+ {
391
+ // reindex last item since we index one row later than the current
392
+ $this->reindexLastImported();
393
+ }
394
+ }
395
+
396
+
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/valuereplacer/03_valuereplacer.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class SampleItemProcessor
5
+ * @author dweeves
6
+ *
7
+ * This class is a sample for item processing
8
+ */
9
+ class ValueReplacerItemProcessor extends Magmi_ItemProcessor
10
+ {
11
+ protected $_rvals = array();
12
+ protected $_before = array("sku","attribute_set","type");
13
+
14
+ public function getPluginInfo()
15
+ {
16
+ return array("name"=>"Value Replacer","author"=>"Dweeves","version"=>"0.0.8a",
17
+ "url"=>$this->pluginDocUrl("Value_Replacer"));
18
+ }
19
+
20
+ public function processItemBeforeId(&$item, $params = null)
21
+ {
22
+ $cbefore = count($this->_before);
23
+
24
+ // only check for "before" compatible fields
25
+
26
+ for ($i = 0; $i < $cbefore; $i++)
27
+ {
28
+ $attname = $this->_before[$i];
29
+ if (isset($this->_rvals[$attname]))
30
+ {
31
+ $item[$attname] = $this->parseCalculatedValue($this->_rvals[$attname], $item, $params);
32
+ }
33
+ }
34
+ return true;
35
+ }
36
+
37
+ public function processItemAfterId(&$item, $params = null)
38
+ {
39
+ foreach ($this->_rvals as $attname => $pvalue)
40
+ {
41
+ // do not reparse "before" fields
42
+ if (!in_array($attname, $this->_before))
43
+ {
44
+ $item[$attname] = $this->parseCalculatedValue($pvalue, $item, $params);
45
+ }
46
+ }
47
+ return true;
48
+ }
49
+
50
+ public function initHelpers()
51
+ {
52
+ $helperdir = dirname(__FILE__) . "/helper";
53
+ $files = glob($helperdir . "/*.php");
54
+ foreach ($files as $f)
55
+ {
56
+ require_once ($f);
57
+ }
58
+ }
59
+
60
+ public function initialize($params)
61
+ {
62
+ foreach ($params as $k => $v)
63
+ {
64
+ if (preg_match_all("/^VREP:(.*)$/", $k, $m) && $k != "VREP:columnlist")
65
+ {
66
+ $colname = rawurldecode($m[1][0]);
67
+ $this->_rvals[$colname] = $params[$k];
68
+ }
69
+ }
70
+ $this->initHelpers();
71
+ }
72
+
73
+ // auto add columns if not set
74
+ public function processColumnList(&$cols)
75
+ {
76
+ $base_cols = $cols;
77
+ $cols = array_unique(array_merge($cols, explode(",", $this->getParam("VREP:columnlist"))));
78
+ $newcols = array_diff($cols, $base_cols);
79
+ if (count($newcols) > 0)
80
+ {
81
+ $this->log("Added columns : " . implode(",", $newcols), "startup");
82
+ }
83
+ }
84
+
85
+ public function getPluginParams($params)
86
+ {
87
+ $pp = array();
88
+ foreach ($params as $k => $v)
89
+ {
90
+ if (preg_match("/^VREP:.*$/", $k))
91
+ {
92
+ $pp[$k] = $v;
93
+ }
94
+ }
95
+ return $pp;
96
+ }
97
+
98
+ static public function getCategory()
99
+ {
100
+ return "Input Data Preprocessing";
101
+ }
102
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/valuereplacer/helper/remapper.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ValueRemapper
4
+ {
5
+ protected static $_inst;
6
+ protected $_maps = array();
7
+ protected $_cimaps = array();
8
+ protected $_curmap = null;
9
+
10
+ public function __construct()
11
+ {}
12
+
13
+ static public function getInstance()
14
+ {
15
+ if (self::$_inst == null)
16
+ {
17
+ self::$_inst = new ValueRemapper();
18
+ }
19
+ return self::$_inst;
20
+ }
21
+
22
+ public static function use_csv($csv)
23
+ {
24
+ $inst = self::getInstance();
25
+ $inst->setMap($csv);
26
+ $inst->_curmap = $csv;
27
+ return $inst;
28
+ }
29
+
30
+ public function map($val, $ci = false)
31
+ {
32
+ $tval = trim($val);
33
+ // remapper case insensitive fix
34
+ if ($ci)
35
+ {
36
+ $tval = strtoupper($val);
37
+ $targetmap = $this->_cimaps[$this->_curmap];
38
+ }
39
+ else
40
+ {
41
+ $targetmap = $this->_maps[$this->_curmap];
42
+ }
43
+ return isset($targetmap[$tval]) ? $targetmap[$tval] : $val;
44
+ }
45
+
46
+ /*
47
+ * Map a multivalue given a separator
48
+ */
49
+ public function mapmulti($val, $sep = ',', $ci = false)
50
+ {
51
+ $vals = explode($sep, $val);
52
+ for ($i = 0; $i < count($vals); $i++)
53
+ {
54
+ $vals[$i] = $this->map($vals[$i], $ci);
55
+ }
56
+ return implode($sep, $vals);
57
+ }
58
+
59
+ public function setMap($csv)
60
+ {
61
+ if (!isset($this->_maps[$csv]))
62
+ {
63
+ $this->_maps[$csv] = array();
64
+ $this->_cimaps[$csv] = array();
65
+ if (file_exists($csv))
66
+ {
67
+ $lines = file($csv);
68
+ foreach ($lines as $line)
69
+ {
70
+ $kv = explode(";", trim($line));
71
+ $this->_maps[$csv][$kv[0]] = $kv[1];
72
+ $this->_cimaps[$csv][strtoupper($kv[0])] = $kv[1];
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/valuereplacer/options_panel.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="plugin_description">This plugin enables to change attribute
2
+ values from datasource before they are handled by magmi. enter
3
+ attributes to set new value for in replaced attributes field, separated
4
+ by commas (,) when leaving the field, new fields will be inserted for
5
+ filling new column names.</div>
6
+ <?php $clist=$this->fixListParam($this->getParam("VREP:columnlist"))?>
7
+
8
+ <ul class="formline">
9
+ <li class="label">Replaced attributes</li>
10
+ <li class="value"><input type="text" id="VREP:columnlist"
11
+ name="VREP:columnlist" size="80" value="<?php echo $clist?>"
12
+ onblur="vrep_mf.buildparamlist()"></input></li>
13
+ </ul>
14
+ <div id="VREP:columnsetup"></div>
15
+
16
+ <div class="formline ifield">
17
+ <div class="fieldhelp"></div>
18
+ <div class="fieldinfo">
19
+ <div style="height: 24px">You can use "dynamic variables" to fill
20
+ above fields.</div>
21
+ <div class="fieldsyntax" style="display: none">
22
+ <hr />
23
+
24
+ <ul>
25
+ <li>{item.[some item field]} and {{some expression}} are supported.</li>
26
+ <li>[some item field] has to be present either as datasource column
27
+ or default value setter/column mapper added column</li>
28
+ <li>examples below assume sku=sku0 &amp; cost=8.00 &amp; margin=15
29
+ (here margin may not even be an attribute)</li>
30
+ </ul>
31
+ <hr />
32
+ <ul>
33
+ <li><b>dynamic field Examples</b></li>
34
+ <li>with sku in the list of replaced fields, if xxx-{item.sku} for
35
+ replacing value , the sku inserted will be xxx-sku0</li>
36
+ <li>you can put any number of {item.[some item field]} references in
37
+ the replacing value</li>
38
+ </ul>
39
+ <hr />
40
+ <ul>
41
+ <li><b>Expression examples:</b></li>
42
+ <li>lets say you want to calculate cost from cost &amp; margin</li>
43
+ <li>just put {{ {item.cost}*(1+({item.margin}/100)) }} in the
44
+ replaced value for price</li>
45
+ </ul>
46
+ <hr />
47
+ <ul>
48
+ <li><b>Advanced expressions</b></li>
49
+ <li>expressions use php eval() function, so beware of what you're
50
+ doing but it can be also very powerful as:</li>
51
+ <li>{{ substr("{item.sku}",0,4) }}</li>
52
+ </ul>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ <script type="text/javascript">
57
+ var vr_vals=<?php echo tdarray_to_js($this,'VREP:columnlist','VREP')?>;
58
+ var vr_linetpl='<ul class="formline"><li class="label">New value for {fieldname}</li><li class="value"><input type="text" name="VREP:{fieldname.enc}" value="{value}"></input></li></ul>';
59
+ vrep_mf=new magmi_multifield('VREP:columnlist','VREP:columnsetup',vr_linetpl,vr_vals);
60
+ vrep_mf.buildparamlist();
61
+ </script>
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/valuetrim/options_panel.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <div class="plugin_description">This plugin automatically trims values
2
+ for select/multiselect attributes.</div>
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/itemprocessors/valuetrim/valuetrimmer.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class SampleItemProcessor
5
+ * @author dweeves
6
+ *
7
+ * This class is a sample for item processing
8
+ */
9
+ class ValueTrimItemProcessor extends Magmi_ItemProcessor
10
+ {
11
+ protected $_totrim = array();
12
+ protected $_scanned = false;
13
+
14
+ public function getPluginInfo()
15
+ {
16
+ return array("name"=>"Value Trimmer for select/multiselect","author"=>"Dweeves","version"=>"0.0.3",
17
+ "url"=>$this->pluginDocUrl("Value_Trimmer_for_select/multiselect"));
18
+ }
19
+
20
+ /**
21
+ * you can add/remove columns for the item passed since it is passed by reference
22
+ *
23
+ * @param Magmi_Engine $mmi
24
+ * : reference to magmi engine instance (convenient to perform database operations)
25
+ * @param unknown_type $item
26
+ * : modifiable reference to item before import
27
+ * the $item is a key/value array with column names as keys and values as read from csv file.
28
+ * @return bool :
29
+ * true if you want the item to be imported after your custom processing
30
+ * false if you want to skip item import after your processing
31
+ */
32
+ public function getTrimmableCols($item)
33
+ {
34
+ if (!$this->_scanned)
35
+ {
36
+ foreach (array_keys($item) as $col)
37
+ {
38
+ $ainfo = $this->getAttrInfo($col);
39
+ if (count($ainfo) > 0)
40
+ {
41
+ if ($ainfo["frontend_input"] == "select" || $ainfo["frontend_input"] == "multiselect")
42
+ {
43
+ $this->_totrim[$col] = $ainfo["frontend_input"];
44
+ }
45
+ }
46
+ }
47
+ $this->_scanned = true;
48
+ }
49
+ return $this->_totrim;
50
+ }
51
+
52
+ public function processItemBeforeId(&$item, $params = null)
53
+ {
54
+ // get list of trimmable columns
55
+ $tc = $this->getTrimmableCols($item);
56
+ foreach ($tc as $col => $mode)
57
+ {
58
+ // for select, just trim value
59
+ if ($mode == "select")
60
+ {
61
+ $item[$col] = trim($item[$col]);
62
+ }
63
+ else
64
+ // for multiselect, recompose trimmed value list
65
+ {
66
+ $sep = Magmi_Config::getInstance()->get("GLOBAL", "mutiselect_sep", ",");
67
+ $vt = explode($sep, $item[$col]);
68
+ foreach ($vt as &$v)
69
+ {
70
+ $v = trim($v);
71
+ }
72
+ $item[$col] = implode($sep, $vt);
73
+ unset($vt);
74
+ }
75
+ }
76
+ return true;
77
+ }
78
+
79
+ public function initialize($params)
80
+ {
81
+ $this->_scanned = false;
82
+ $this->_totrim = array();
83
+ return true;
84
+ }
85
+
86
+ static public function getCategory()
87
+ {
88
+ return "Input Data Preprocessing";
89
+ }
90
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/extra/obsolete.txt ADDED
@@ -0,0 +1 @@
 
1
+ itemprocessors/itemindexer/priceindexer.php
app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_datasource.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class Magmi_Datasource
4
+ * @author dweeves
5
+ *
6
+ * This class enables to perform input format support for ingested data
7
+ */
8
+ require_once ("magmi_generalimport_plugin.php");
9
+
10
+ abstract class Magmi_DataSource extends Magmi_GeneralImportPlugin
11
+ {
12
+
13
+ public function getColumnNames($prescan = false)
14
+ {}
15
+
16
+ public function getRecordsCount()
17
+ {}
18
+
19
+ public function getNextRecord()
20
+ {}
21
+
22
+ public function onException($e)
23
+ {}
24
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_default_options_panel.php ADDED
@@ -0,0 +1 @@
 
1
+ <span>Plugin has no configurable parameters</span>
app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_defaultattributehandler.php ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Magmi_DefaultAttributeItemProcessor extends Magmi_ItemProcessor
4
+ {
5
+ protected $_basecols = array("store"=>"admin","type"=>"simple");
6
+ protected $_baseattrs = array("status"=>1,"visibility"=>4,"page_layout"=>"","tax_class_id"=>"Taxable Goods");
7
+ protected $_forcedefault = array("store"=>"admin");
8
+ protected $_missingcols = array();
9
+ protected $_missingattrs = array();
10
+
11
+ /**
12
+ * (non-PHPdoc)
13
+ *
14
+ * @see Magmi_Plugin::initialize()
15
+ */
16
+ public function initialize($params)
17
+ {
18
+ $this->registerAttributeHandler($this, array("attribute_code:.*"));
19
+ }
20
+
21
+ /**
22
+ * (non-PHPdoc)
23
+ *
24
+ * @see Magmi_Plugin::getPluginInfo()
25
+ */
26
+ public function getPluginInfo()
27
+ {
28
+ return array("name"=>"Standard Attribute Import","author"=>"Dweeves","version"=>"1.0.6");
29
+ }
30
+
31
+ /**
32
+ * callback for column list processing
33
+ *
34
+ * @param unknown $cols
35
+ */
36
+ public function processColumnList(&$cols)
37
+ {
38
+ // This will not change the column list
39
+ // this will only log the list of columns that will be added to newly created items
40
+ $this->_missingcols = array_diff(array_keys($this->_basecols), $cols);
41
+ $this->_missingattrs = array_diff(array_keys($this->_baseattrs), $cols);
42
+ $m = $this->getMode();
43
+ if ($m == "create" || $m == "xcreate")
44
+ {
45
+ $cols = array_merge($cols, $this->_missingcols, $this->_missingattrs);
46
+ $this->log(
47
+ "Newly created items will have default values for columns:" .
48
+ implode(",", array_merge($this->_missingcols, $this->_missingattrs)), "startup");
49
+ }
50
+ }
51
+
52
+ /**
53
+ * initializes extra columns if needed
54
+ *
55
+ * @param unknown $item
56
+ */
57
+ public function initializeBaseCols(&$item)
58
+ {
59
+ foreach ($this->_missingcols as $missing)
60
+ {
61
+ $item[$missing] = $this->_basecols[$missing];
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Initialized base attributes to retrieve from a given item description
67
+ *
68
+ * @param unknown $item
69
+ */
70
+ public function initializeBaseAttrs(&$item)
71
+ {
72
+ foreach ($this->_missingattrs as $missing)
73
+ {
74
+ $item[$missing] = $this->_baseattrs[$missing];
75
+ }
76
+ }
77
+
78
+ /**
79
+ * (non-PHPdoc)
80
+ *
81
+ * @see Magmi_ItemProcessor::processItemAfterId()
82
+ */
83
+ public function processItemAfterId(&$item, $params = null)
84
+ {
85
+ if ($params["new"] == true)
86
+ {
87
+ $this->initializeBaseCols($item);
88
+ $this->initializeBaseAttrs($item);
89
+ }
90
+ // forcing default values for mandatory processing columns
91
+ foreach ($this->_forcedefault as $k => $v)
92
+ {
93
+ if (isset($item[$k]) && trim($item[$k]) == "")
94
+ {
95
+ $item[$k] = $v;
96
+ }
97
+ }
98
+ return true;
99
+ }
100
+
101
+ /**
102
+ * returns magento default value if applicable
103
+ * - item should not exist yet
104
+ * - default value should be set in magento
105
+ * - ivalue should be empty
106
+ *
107
+ * @param array $attrdesc
108
+ * : attribute description table
109
+ * @param mixed $ivalue
110
+ * : input value
111
+ * @return magento default value or NULL if not applicable
112
+ */
113
+ public function getDefaultValue($attrdesc, $ivalue)
114
+ {
115
+ $exists = $this->currentItemExists();
116
+ // check for new item default value in DB for new items
117
+ if (!$exists && isset($attrdesc["default_value"]) && !empty($attrdesc["default_value"]) && empty($ivalue))
118
+ {
119
+ return $attrdesc["default_value"];
120
+ }
121
+ return null;
122
+ }
123
+
124
+ /**
125
+ * attribute handler for Decimal attributes
126
+ *
127
+ * @param int $pid
128
+ * : product id
129
+ * @param array $item
130
+ * : item to inges
131
+ * @param int $storeid
132
+ * : store for attribute value storage
133
+ * @param int $attrcode
134
+ * : attribute code
135
+ * @param array $attrdesc
136
+ * : attribute metadata
137
+ * @param mixed $ivalue
138
+ * : input value to import
139
+ * @return new decimal value to set
140
+ */
141
+ public function handleDecimalAttribute($pid, &$item, $storeid, $attrcode, $attrdesc, $ivalue)
142
+ {
143
+ $dval = $this->getDefaultValue($attrdesc, $ivalue);
144
+ if ($dval !== null)
145
+ {
146
+ return $dval;
147
+ }
148
+ // force convert decimal separator to dot
149
+ $ivalue = str_replace(",", ".", $ivalue);
150
+ $ovalue = deleteifempty($ivalue);
151
+ return $ovalue;
152
+ }
153
+
154
+ /**
155
+ * attribute handler for DateTime attributes
156
+ *
157
+ * @param int $pid
158
+ * : product id
159
+ * @param array $item
160
+ * : item to inges
161
+ * @param int $storeid
162
+ * : store for attribute value storage
163
+ * @param int $attrcode
164
+ * : attribute code
165
+ * @param array $attrdesc
166
+ * : attribute metadata
167
+ * @param mixed $ivalue
168
+ * : input value to import
169
+ * @return new datetime value to set
170
+ */
171
+ public function handleDatetimeAttribute($pid, &$item, $storeid, $attrcode, $attrdesc, $ivalue)
172
+ {
173
+ $dval = $this->getDefaultValue($attrdesc, $ivalue);
174
+ if ($dval !== null)
175
+ {
176
+ return $dval;
177
+ }
178
+ $ovalue = deleteifempty(trim($ivalue));
179
+ // Handle european date format or other common separators
180
+ if (preg_match("|(\d{1,2})\D(\d{1,2})\D(\d{4})\s*(\d{2}:\d{2}:\d{2})?|", $ovalue, $matches))
181
+ {
182
+ $hms = count($matches) > 4 ? $matches[4] : "";
183
+ $ovalue = trim(sprintf("%4d-%2d-%2d %s", $matches[3], $matches[2], $matches[1], $hms));
184
+ }
185
+ return $ovalue;
186
+ }
187
+
188
+ /**
189
+ * attribute handler for Text attributes
190
+ *
191
+ * @param int $pid
192
+ * : product id
193
+ * @param array $item
194
+ * : item to inges
195
+ * @param int $storeid
196
+ * : store for attribute value storage
197
+ * @param int $attrcode
198
+ * : attribute code
199
+ * @param array $attrdesc
200
+ * : attribute metadata
201
+ * @param mixed $ivalue
202
+ * : input value to import
203
+ * @return new text value to set
204
+ */
205
+ public function handleTextAttribute($pid, &$item, $storeid, $attrcode, $attrdesc, $ivalue)
206
+ {
207
+ $dval = $this->getDefaultValue($attrdesc, $ivalue);
208
+ if ($dval !== null)
209
+ {
210
+ return $dval;
211
+ }
212
+ $ovalue = deleteifempty($ivalue);
213
+ return $ovalue;
214
+ }
215
+
216
+ /**
217
+ * check if a value is integer
218
+ *
219
+ * @param mixed $value
220
+ * @return true if integer, false if not
221
+ */
222
+ public function checkInt($value)
223
+ {
224
+ return is_int($value) || (is_string($value) && is_numeric($value) && (int) $value == $value);
225
+ }
226
+
227
+ /**
228
+ * attribute handler for Int typed attributes
229
+ *
230
+ * @param int $pid
231
+ * : product id
232
+ * @param array $item
233
+ * : item to inges
234
+ * @param int $storeid
235
+ * : store for attribute value storage
236
+ * @param int $attrcode
237
+ * : attribute code
238
+ * @param array $attrdesc
239
+ * : attribute metadata
240
+ * @param mixed $ivalue
241
+ * : input value to import
242
+ * @return new int value to set
243
+ *
244
+ * Many attributes are int typed, so we need to handle all cases like :
245
+ * - select
246
+ * - tax id
247
+ * - boolean
248
+ * - status
249
+ * - visibility
250
+ */
251
+ public function handleIntAttribute($pid, &$item, $storeid, $attrcode, $attrdesc, $ivalue)
252
+ {
253
+ $ovalue = $ivalue;
254
+ // default value exists, return it
255
+ $dval = $this->getDefaultValue($attrdesc, $ivalue);
256
+ if ($dval !== null)
257
+ {
258
+ return intval($dval);
259
+ }
260
+
261
+ $attid = $attrdesc["attribute_id"];
262
+ // if we've got a select type value
263
+ if ($attrdesc["frontend_input"] == "select")
264
+ {
265
+ // we need to identify its type since some have no options
266
+ switch ($attrdesc["source_model"])
267
+ {
268
+ // if its status, default to 1 (Enabled) if not correcly mapped
269
+ case "catalog/product_status":
270
+ if (!$this->checkInt($ivalue))
271
+ {
272
+ $ovalue = 1;
273
+ }
274
+ break;
275
+ // do not create options for boolean values tagged as select ,default to 0 if not correcly mapped
276
+ case "eav/entity_attribute_source_boolean":
277
+ if (!$this->checkInt($ivalue))
278
+ {
279
+ $ovalue = 0;
280
+ }
281
+ break;
282
+ // if visibility no options either,default to 4 if not correctly mapped
283
+ case "catalog/product_visibility":
284
+ if (!$this->checkInt($ivalue))
285
+ {
286
+ $ovalue = 4;
287
+ }
288
+
289
+ break;
290
+ // if it's tax_class, get tax class id from item value
291
+ case "tax/class_source_product":
292
+ $ovalue = $this->getTaxClassId($ivalue);
293
+ break;
294
+ // otherwise, standard option behavior
295
+ // get option id for value, create it if does not already exist
296
+ // do not insert if empty
297
+ default:
298
+ $exists = $this->currentItemExists();
299
+ if ($ivalue == "" && $exists)
300
+ {
301
+ return "__MAGMI_DELETE__";
302
+ }
303
+ $oids = $this->getOptionIds($attid, $storeid, array($ivalue));
304
+ $ovalue = $oids[0];
305
+ unset($oids);
306
+ break;
307
+ }
308
+ }
309
+ return $ovalue;
310
+ }
311
+
312
+ /**
313
+ * attribute handler for "url_key" attribute
314
+ *
315
+ * @param int $pid
316
+ * : product id
317
+ * @param array $item
318
+ * : item to inges
319
+ * @param int $storeid
320
+ * : store for attribute value storage
321
+ * @param int $attrcode
322
+ * : attribute code
323
+ * @param array $attrdesc
324
+ * : attribute metadata
325
+ * @param mixed $ivalue
326
+ * : input value to import
327
+ * @return new int value to set
328
+ *
329
+ */
330
+ public function handleUrl_keyAttribute($pid, &$item, $storeid, $attrcode, $attrdesc, $ivalue)
331
+ {
332
+ $cpev = $this->tablename("catalog_product_entity_varchar");
333
+ // find conflicting url keys
334
+ $urlk = trim($ivalue);
335
+ $exists = $this->currentItemExists();
336
+ $lst = array();
337
+ if ($urlk == "" && $exists)
338
+ {
339
+ return "__MAGMI_DELETE__";
340
+ }
341
+ // for existing product, check if we have already a value matching the current pattern
342
+ if ($exists)
343
+ {
344
+ $sql = "SELECT value FROM $cpev WHERE attribute_id=? AND entity_id=? AND value REGEXP ?";
345
+ $eurl = $this->selectone($sql, array($attrdesc["attribute_id"],$pid,$urlk . "(-\d+)?"), "value");
346
+ // we match wanted pattern, try finding conflicts with our current one
347
+ if ($eurl)
348
+ {
349
+ $urlk = $eurl;
350
+ $sql = "SELECT * FROM $cpev WHERE attribute_id=? AND entity_id!=? AND value=?";
351
+ $umatch = $urlk;
352
+ }
353
+ // no current value, so try inserting into target pattern list
354
+ else
355
+ {
356
+
357
+ $sql = "SELECT * FROM $cpev WHERE attribute_id=? AND entity_id!=? AND value REGEXP ?";
358
+ $umatch = $urlk . "(-\d+)?";
359
+ }
360
+ $lst = $this->selectAll($sql, array($attrdesc["attribute_id"],$pid,$umatch));
361
+ }
362
+
363
+ // all conflicting url keys
364
+ if (count($lst) > 0)
365
+ {
366
+ $urlk = $urlk . "-" . count($lst);
367
+ }
368
+ return $urlk;
369
+ }
370
+
371
+ /**
372
+ * attribute handler for Varchar typed attributes
373
+ *
374
+ * @param int $pid
375
+ * : product id
376
+ * @param array $item
377
+ * : item to inges
378
+ * @param int $storeid
379
+ * : store for attribute value storage
380
+ * @param int $attrcode
381
+ * : attribute code
382
+ * @param array $attrdesc
383
+ * : attribute metadata
384
+ * @param mixed $ivalue
385
+ * : input value to import
386
+ * @return new int value to set
387
+ *
388
+ * Special case for multiselect
389
+ */
390
+ public function handleVarcharAttribute($pid, &$item, $storeid, $attrcode, $attrdesc, $ivalue)
391
+ {
392
+ $exists = $this->currentItemExists();
393
+ // Check store specific value & empty & new item => ignore
394
+ if ($storeid !== 0 && empty($ivalue) && !$exists)
395
+ {
396
+ return false;
397
+ }
398
+ // item exists , empty value, remove value, back to admin
399
+ if ($ivalue == "" && $exists)
400
+ {
401
+ return "__MAGMI_DELETE__";
402
+ }
403
+ // default value exists, return it
404
+ $dval = $this->getDefaultValue($attrdesc, $ivalue);
405
+ if ($dval !== null)
406
+ {
407
+ return $dval;
408
+ }
409
+
410
+ $ovalue = $ivalue;
411
+ $attid = $attrdesc["attribute_id"];
412
+ // --- Contribution From mennos , optimized by dweeves ----
413
+ // Added to support multiple select attributes
414
+ // (as far as i could figure out) always stored as varchars
415
+ // if it's a multiselect value
416
+ if ($attrdesc["frontend_input"] == "multiselect")
417
+ {
418
+ // if empty delete entry
419
+ if ($ivalue == "")
420
+ {
421
+ return "__MAGMI_DELETE__";
422
+ }
423
+ // magento uses "," as separator for different multiselect values
424
+ $sep = Magmi_Config::getInstance()->get("GLOBAL", "multiselect_sep", ",");
425
+ $multiselectvalues = explode($sep, $ivalue);
426
+ $oids = $this->getOptionIds($attid, $storeid, $multiselectvalues);
427
+ $ovalue = implode(",", $oids);
428
+ unset($oids);
429
+ }
430
+
431
+ return $ovalue;
432
+ }
433
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_generalimport_plugin.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once ("magmi_plugin.php");
3
+
4
+ abstract class Magmi_GeneralImportPlugin extends Magmi_Plugin
5
+ {
6
+
7
+ public function beforeImport()
8
+ {
9
+ return true;
10
+ }
11
+
12
+ public function afterImport()
13
+ {
14
+ return true;
15
+ }
16
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_item_processor.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class CustomItemProcessor
4
+ * @author dweeves
5
+ *
6
+ * This class enables to perform custom modifications on item
7
+ *
8
+ * class principle:
9
+ * methods should be called processItemXxx where Xxx is an import step of the Magmi_ProductImportEngine
10
+ *
11
+ * all processItemXxx methods are optional, you need only to define methods you need to implement
12
+ *
13
+ * processItemXxx signature are always the same
14
+ *
15
+ *
16
+ * processItemXxx($mmi,&$item,$params=null) where
17
+ *
18
+ * $mmi : reference to Magmi_Engine instance
19
+ * &$item : array reference to item
20
+ * $params : step dependent array, may be null
21
+ *
22
+ * processItemXxx method always return a boolean value , return false if you want to break mmi processing for item
23
+ *
24
+ * current available import steps:
25
+ * beforeId => method processItemBeforeId , called before item is identified, just after loading csv values for item
26
+ * $params : null
27
+ *
28
+ *
29
+ * afterId => method processItemAfterId , called after item is identified or created in magento
30
+ * $params : array("product_id"=><magento product id>)
31
+ *
32
+ * exception => method processItemException , called when mmi processing caused an exception (even customItemProcessor thrown exception)
33
+ *$params : array("exception"=><exception instance thrown>)
34
+ *
35
+ *
36
+ */
37
+ require_once("magmi_generalimport_plugin.php");
38
+ abstract class Magmi_ItemProcessor extends Magmi_GeneralImportPlugin
39
+ {
40
+ /**
41
+ * you can add/remove columns for the item passed since it is passed by reference
42
+ * @param MagentoMassImporter $mmi : reference to mass importer (convenient to perform database operations)
43
+ * @param unknown_type $item : modifiable reference to item before import
44
+ * the $item is a key/value array with column names as keys and values as read from csv file.
45
+ * @return bool :
46
+ * true if you want the item to be imported after your custom processing
47
+ * false if you want to skip item import after your processing
48
+ */
49
+
50
+
51
+ public function processItemBeforeId(&$item,$params=null)
52
+ {
53
+ /* example code 1 */
54
+ /* i've added some non item attribute values in my csv (cp_id,cp_price) but
55
+ * these values are meant to be inserted into a custom module table
56
+ * so , i get the following code (commented)
57
+ */
58
+
59
+ /*
60
+ //if my special column cp_id exists
61
+ if(isset($item['cp_id']))
62
+ {
63
+ //if its value is not empty
64
+ if($item['cp_id']!="")
65
+ {
66
+ //ask mmi for custom module table name (takes into account table prefix
67
+ $tname=$mmi->tablename("book_categoryprice");
68
+ //parameterized sql for insert (ignore doubles)
69
+ $sql="INSERT IGNORE INTO $tname (cp_id,price) VALUES (?,?)";
70
+ $data=array($item["cp_id"],$item["cp_price"]);
71
+ //ask mmi to perform insert
72
+ $mmi->insert($sql,$data);
73
+ }
74
+ //remove my special columns from item
75
+ unset($item["cp_id"]);
76
+ unset($item["cp_price"]);
77
+ }*/
78
+
79
+ /** example code 2 **/
80
+ /*
81
+ * //if we have qty column & are in create mode & reset, do not import items with 0 qty
82
+ if(isset($item['qty']) && $item['qty']==0 && $mmi->mode=="create" && $mmi->reset)
83
+ {
84
+ return false;
85
+ }
86
+ * /
87
+ */
88
+ //return true , enable item processing
89
+ return true;
90
+ }
91
+
92
+ public function processItemAfterId(&$item,$params=null)
93
+ {
94
+ return true;
95
+ }
96
+
97
+ /*
98
+ public function processItemException($mmi,&$item,$params=null)
99
+ {
100
+
101
+ }*/
102
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_plugin.php ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once ("magmi_config.php");
3
+ require_once ("magmi_mixin.php");
4
+
5
+ class Magmi_PluginConfig extends ProfileBasedConfig
6
+ {
7
+ protected $_prefix;
8
+ protected $_conffile;
9
+
10
+ public function __construct($pname, $profile = null)
11
+ {
12
+ $this->_prefix = $pname;
13
+ parent::__construct("$this->_prefix.conf", $profile);
14
+ }
15
+
16
+ public function getConfDir()
17
+ {
18
+ return dirname($this->_confname);
19
+ }
20
+
21
+ public function load($name = null)
22
+ {
23
+ $cname = ($name == null ? $this->_confname : $name);
24
+ if (file_exists($cname))
25
+ {
26
+ parent::load($cname);
27
+ }
28
+ }
29
+
30
+ public function getIniStruct($arr)
31
+ {
32
+ $conf = array();
33
+ foreach ($arr as $k => $v)
34
+ {
35
+ $k = $this->_prefix . ":" . $k;
36
+ list($section,$value) = explode(":", $k, 2);
37
+ if (!isset($conf[$section]))
38
+ {
39
+ $conf[$section] = array();
40
+ }
41
+ $conf[$section][$value] = $v;
42
+ }
43
+ return $conf;
44
+ }
45
+
46
+ public function getConfig()
47
+ {
48
+ return parent::getsection($this->_prefix);
49
+ }
50
+ }
51
+
52
+ class Magmi_PluginOptionsPanel
53
+ {
54
+ private $_plugin;
55
+ private $_defaulthtml = "";
56
+ private $_file = null;
57
+
58
+ public function __construct($pinst, $file = null)
59
+ {
60
+ $this->_plugin = $pinst;
61
+ $this->_file = ($file == null ? "options_panel.php" : $file);
62
+ $this->initDefaultHtml();
63
+ }
64
+
65
+ public function getFile()
66
+ {
67
+ return $this->_file;
68
+ }
69
+
70
+ public final function initDefaultHtml()
71
+ {
72
+ $panelfile = dirname(__FILE__) . "/magmi_default_options_panel.php";
73
+ ob_start();
74
+ require ($panelfile);
75
+ $this->_defaulthtml = ob_get_contents();
76
+ ob_end_clean();
77
+ }
78
+
79
+ public function getHtml()
80
+ {
81
+ $plugin = $this->_plugin;
82
+ $pdir = Magmi_PluginHelper::getInstance()->getPluginDir($this->_plugin);
83
+ $panelfile = "$pdir/" . $this->getFile();
84
+ $content = "";
85
+ if (!file_exists($panelfile))
86
+ {
87
+ $content = $this->_defaulthtml;
88
+ }
89
+ else
90
+ {
91
+ ob_start();
92
+ require ($panelfile);
93
+ $content = ob_get_contents();
94
+ ob_end_clean();
95
+ }
96
+ return $content;
97
+ }
98
+
99
+ public function __call($data, $arg)
100
+ {
101
+ return call_user_func_array(array($this->_plugin,$data), $arg);
102
+ }
103
+ }
104
+
105
+ abstract class Magmi_Plugin extends Magmi_Mixin
106
+ {
107
+ protected $_class;
108
+ protected $_plugintype;
109
+ protected $_plugindir;
110
+ protected $_config;
111
+ protected $_magmiconfig;
112
+ protected $_pluginmeta;
113
+
114
+ public function __construct()
115
+ {}
116
+
117
+ public function pluginDocUrl($urlk)
118
+ {
119
+ return "http://wiki.magmi.org/index.php?title=" . $urlk;
120
+ }
121
+
122
+ public function getParam($pname, $default = null)
123
+ {
124
+ return (isset($this->_params[$pname]) && $this->_params[$pname] != "") ? $this->_params[$pname] : $default;
125
+ }
126
+
127
+ public function setParam($pname, $value)
128
+ {
129
+ $this->_params[$pname] = $value;
130
+ }
131
+
132
+ public function fixListParam($pvalue)
133
+ {
134
+ $iarr = explode(",", $pvalue);
135
+ $oarr = array();
136
+ foreach ($iarr as $v)
137
+ {
138
+ if ($v != "")
139
+ {
140
+ $oarr[] = $v;
141
+ }
142
+ }
143
+ $val = implode(",", $oarr);
144
+ unset($iarr);
145
+ unset($oarr);
146
+ return $val;
147
+ }
148
+
149
+ public function getPluginParamNames()
150
+ {
151
+ return array();
152
+ }
153
+
154
+ public function getPluginInfo()
155
+ {
156
+ return array("name"=>$this->getPluginName(),"version"=>$this->getPluginVersion(),
157
+ "author"=>$this->getPluginAuthor(),"url"=>$this->getPluginUrl());
158
+ }
159
+
160
+ public function getPluginUrl()
161
+ {
162
+ return null;
163
+ }
164
+
165
+ public function getPluginVersion()
166
+ {
167
+ return null;
168
+ }
169
+
170
+ public function getPluginName()
171
+ {
172
+ return null;
173
+ }
174
+
175
+ public function getPluginAuthor()
176
+ {
177
+ return null;
178
+ }
179
+
180
+ public function log($data, $type = 'std', $useprefix = true)
181
+ {
182
+ $pinf = $this->getPluginInfo();
183
+ if ($useprefix)
184
+ {
185
+ $data = "{$pinf["name"]} v{$pinf["version"]} - " . $data;
186
+ }
187
+ $this->_caller_log($data, "plugin;$this->_class;$type");
188
+ }
189
+
190
+ public function pluginHello()
191
+ {
192
+ $info = $this->getPluginInfo();
193
+ $hello = array(!isset($info["name"]) ? "" : $info["name"]);
194
+ $hello[] = !isset($info["version"]) ? "" : $info["version"];
195
+ $hello[] = !isset($info["author"]) ? "" : $info["author"];
196
+ $hello[] = !isset($info["url"]) ? "" : $info["url"];
197
+ $hellostr = implode("-", $hello);
198
+ $base = get_parent_class($this);
199
+ $this->log("$hellostr ", "pluginhello", false);
200
+ }
201
+
202
+ public function initialize($params)
203
+ {}
204
+
205
+ public function getConfig()
206
+ {
207
+ return $this->_config;
208
+ }
209
+
210
+ public function getMagmiConfig()
211
+ {
212
+ return $this->_magmiconfig;
213
+ }
214
+
215
+ public final function pluginInit($mmi, $meta, $params = null, $doinit = true, $profile = null)
216
+ {
217
+ $this->bind($mmi);
218
+ $this->_pluginmeta = $meta;
219
+ $this->_class = get_class($this);
220
+ $this->_config = new Magmi_PluginConfig(get_class($this), $profile);
221
+ $this->_config->load();
222
+ $this->_magmiconfig = Magmi_Config::getInstance();
223
+
224
+ $this->_params = ($params != null ? array_merge($this->_config->getConfig(), $params) : $this->_config->getConfig());
225
+
226
+ if (isset($mmi))
227
+ {
228
+ $this->pluginHello();
229
+ }
230
+
231
+ if ($doinit)
232
+ {
233
+ $this->initialize($this->_params);
234
+ }
235
+ }
236
+
237
+ public function getPluginParamsNoCurrent($params)
238
+ {
239
+ $arr = array();
240
+ $paramkeys = $this->getPluginParamNames();
241
+ foreach ($paramkeys as $pk)
242
+ {
243
+ if (isset($params[$pk]))
244
+ {
245
+ $arr[$pk] = $params[$pk];
246
+ }
247
+ else
248
+ {
249
+ $arr[$pk] = 0;
250
+ }
251
+ }
252
+ return $arr;
253
+ }
254
+
255
+ public function getPluginParams($params)
256
+ {
257
+ $arr = array();
258
+ $paramkeys = $this->getPluginParamNames();
259
+ foreach ($paramkeys as $pk)
260
+ {
261
+ if (isset($params[$pk]))
262
+ {
263
+ $arr[$pk] = $params[$pk];
264
+ }
265
+ else
266
+ {
267
+ if (isset($this->_params[$pk]))
268
+ {
269
+ $arr[$pk] = $this->_params[$pk];
270
+ }
271
+ }
272
+ }
273
+ return $arr;
274
+ }
275
+
276
+ public function persistParams($plist)
277
+ {
278
+ if (count($plist) > 0)
279
+ {
280
+ $this->_config->setPropsFromFlatArray($plist);
281
+ return $this->_config->save();
282
+ }
283
+ return true;
284
+ }
285
+
286
+ public function getOptionsPanel($file = null)
287
+ {
288
+ return new Magmi_PluginOptionsPanel($this, $file);
289
+ }
290
+
291
+ public function getShortDescription()
292
+ {
293
+ $panel = $this->getOptionsPanel()->getHtml();
294
+ $info = null;
295
+ if (preg_match('|<div class="plugin_description">(.*?)</div>|smi', $panel, $match))
296
+ {
297
+
298
+ $info = $match[1];
299
+ $delims = array(".",":");
300
+ foreach ($delims as $delim)
301
+ {
302
+ $p = strpos($info, $delim);
303
+ if ($p !== false)
304
+ {
305
+ $info = substr($info, 0, $p);
306
+ break;
307
+ }
308
+ }
309
+ }
310
+ return $info;
311
+ }
312
+
313
+ static public function getCategory()
314
+ {
315
+ return "common";
316
+ }
317
+
318
+ public function getPluginDir()
319
+ {
320
+ return $this->_pluginmeta["dir"];
321
+ }
322
+
323
+ public function getPluginMeta()
324
+ {
325
+ return $this->_pluginmeta;
326
+ }
327
+
328
+ public function getPluginClass()
329
+ {
330
+ return $this->_class;
331
+ }
332
+
333
+ public function isRunnable()
334
+ {
335
+ return array(true,"");
336
+ }
337
+ }
app/code/community/Osf/Synnex/lib/magmi/plugins/inc/magmi_utility_plugin.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Magmi_UtilityPlugin extends Magmi_Plugin
4
+ {
5
+
6
+ public function runUtility()
7
+ {
8
+ // Put Running code
9
+ }
10
+
11
+ public function getWarning()
12
+ {
13
+ return null;
14
+ }
15
+ }
app/code/community/Osf/Synnex/lib/magmi/state/magmistate ADDED
@@ -0,0 +1 @@
 
1
+ idle
app/code/community/Osf/Synnex/lib/magmi/state/progress.txt ADDED
File without changes
app/code/community/Osf/Synnex/lib/magmi/state/timings.txt ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ TIMING CATEGORY:db
3
+ --------------------------------
4
+ Phase:beforeImport
5
+ - Class:Magmi_ProductImportEngine :0.01 - Class:Magmi_OptimizerPlugin :0.01
6
+ Phase:indb
7
+ - Class:Magmi_ProductImportEngine :0.21 - Class:CategoryImporter :0.21
8
+ Phase:startImport
9
+
10
+ Phase:processColumnList
11
+ - Class:Magmi_ProductImportEngine :0.01
12
+ Phase:processItemBeforeId
13
+
14
+ Phase:processItemAfterId
15
+
16
+ Phase:processItemAfterImport
17
+
18
+ Phase:endImport
19
+
20
+ Phase:afterImport
21
+
22
+ TIMING CATEGORY:global
23
+ --------------------------------
24
+ Phase:beforeImport
25
+ - Class:Magmi_ProductImportEngine :0.01 - Class:Magmi_OptimizerPlugin :0.01
26
+ Phase:startImport
27
+
28
+ Phase:processColumnList
29
+ - Class:Magmi_ProductImportEngine :0.01
30
+ Phase:processItemBeforeId
31
+
32
+ Phase:processItemAfterId
33
+
34
+ Phase:processItemAfterImport
35
+
36
+ Phase:endImport
37
+
38
+ Phase:afterImport
39
+
40
+ TIMING CATEGORY:line
41
+ --------------------------------
42
+ Phase:processItemBeforeId
43
+
44
+ Phase:processItemAfterId
45
+
46
+ Phase:processItemAfterImport
47
+
48
+ Phase:endImport
49
+
50
+ Phase:afterImport
app/code/community/Osf/Synnex/lib/magmi/state/trace.txt ADDED
File without changes
app/design/adminhtml/default/default/template/synnex/system/config/import.phtml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script type="text/javascript">
2
+ //<![CDATA[
3
+ function startImport() {
4
+ new Ajax.Request('<?php echo $this->getAjaxUrl() ?>', {
5
+ method: 'get',
6
+ onSuccess: function(transport){
7
+ if (transport.responseText){
8
+ alert('Products were Imported');
9
+ }
10
+ }
11
+ });
12
+ }
13
+ //]]>
14
+ </script>
15
+
16
+ <?php echo $this->getButtonHtml() ?>
app/design/adminhtml/default/default/template/synnex/system/config/notice.phtml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script type="text/javascript">
2
+ //<![CDATA[
3
+ function startNotice() {
4
+ new Ajax.Request('<?php echo $this->getAjaxUrl() ?>', {
5
+ method: 'get',
6
+ onSuccess: function(transport){
7
+ if (transport.responseText){
8
+ alert('Ship notice were imported');
9
+ }
10
+ }
11
+ });
12
+ }
13
+ //]]>
14
+ </script>
15
+
16
+ <?php echo $this->getButtonHtml() ?>
app/etc/modules/Osf_Synnex.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <config>
3
+ <modules>
4
+ <Osf_Synnex>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </Osf_Synnex>
8
+ </modules>
9
+ </config>
app/locale/en_US/template/email/synnex/synnex_import_error.html ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!--@subject Synnex Import Error! @-->
2
+ <!--@vars
3
+ {"store url=\"\"":"Store Url",
4
+ "var logo_url":"Email Logo Image Url"
5
+ }
6
+ @-->
7
+
8
+ <!--@styles
9
+ body,td { color:#2f2f2f; font:11px/1.35em Verdana, Arial, Helvetica, sans-serif; }
10
+ @-->
11
+
12
+ <body style="background:#F6F6F6; font-family:Verdana, Arial, Helvetica, sans-serif; font-size:12px; margin:0; padding:0;">
13
+ <div style="background:#F6F6F6; font-family:Verdana, Arial, Helvetica, sans-serif; font-size:12px; margin:0; padding:0;">
14
+ <table cellspacing="0" cellpadding="0" border="0" height="100%" width="100%">
15
+ <tr>
16
+ <td align="center" valign="top" style="padding:20px 0 20px 0">
17
+ <!-- [ header starts here] -->
18
+ <table bgcolor="FFFFFF" cellspacing="0" cellpadding="10" border="0" width="650" style="border:1px solid #E0E0E0;">
19
+ <tr>
20
+ <td valign="top">
21
+ <a href="{{store url=''}}"><img src="{{var logo_url}}" alt="{{var logo_alt}}" style="margin-bottom:10px;" border="0"/></a></td>
22
+ </tr>
23
+ <tr>
24
+ <td valign="top">
25
+ <h1 style="font-size:22px; font-weight:normal; line-height:22px; margin:0 0 11px 0;">Synnex Ship Notice Import Error</h1>
26
+ <p style="font-size:12px; line-height:16px; margin:0 0 16px 0;">There was an error while trying to import the ship notices from Synnex, Invoice No.{{var shipNoticeId}}.
27
+ Please check the log for further Info.</p>
28
+ </td>
29
+ </tr>
30
+ <tr>
31
+ <td bgcolor="#EAEAEA" align="center" style="background:#EAEAEA; text-align:center;"><p style="font-size:12px; margin:0;"><strong>Osf Synnex Import Script</strong></p></td>
32
+ </tr>
33
+ </table>
34
+ </td>
35
+ </tr>
36
+ </table>
37
+ </div>
38
+ </body>
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>OSF_Integrator_for_Synnex</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://www.osf-commerce.com/magento-products-lite-software-license-agreement">End User License Agreement (EULA)</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Automated flow for Synnex integration from product import to ship notification</summary>
10
+ <description>The integrator will create a bridge between Magento and Synnex and will allow the automation of importing products, placing orders and receiving shipping notification</description>
11
+ <notes>1.0.0</notes>
12
+ <authors><author><name>OSF Global</name><user>OSF_Global</user><email>chris.hooven@osf-global.com</email></author></authors>
13
+ <date>2015-11-10</date>
14
+ <time>14:49:18</time>
15
+ <contents><target name="magecommunity"><dir name="Osf"><dir name="Synnex"><dir name="Block"><dir name="Adminhtml"><dir name="Shipments"><file name="Grid.php" hash="420495e880dfcf14b54db6143e56d0a2"/><dir name="Renderer"><file name="Status.php" hash="8abf82de2fa7070b28aa763ffe139caf"/></dir></dir><file name="Shipments.php" hash="97379da8e00c6983eff0775b74d59277"/><dir name="System"><dir name="Config"><dir name="Button"><file name="Import.php" hash="ffcef5aaa52f43ee70b05d2880dc9321"/><file name="Notice.php" hash="40e4bca66cdaeede977f120a94194038"/></dir></dir></dir></dir></dir><dir name="Helper"><file name="Connect.php" hash="564e8f2dcdf5897c3988d4c3b56944cc"/><file name="Data.php" hash="f58cd09aa4b02dcd85b4bea3d26f7e30"/><file name="Ftp.php" hash="ebdae720ba0bd716485278571801c890"/><file name="Validate.php" hash="3487ac55a90eb36868066ee19921d848"/></dir><file name="LICENSE.txt" hash="279935a07ba225ee0b2199830ddb61b7"/><dir name="Model"><file name="Import.php" hash="35dd29b38b309867778eec5505ff7860"/><file name="Observer.php" hash="f23bcceb6f19372d9c00defc71356728"/><file name="Order.php" hash="0798e2234e44be76066c3c91aa82a19d"/><file name="Queue.php" hash="cd4482d7db4da840b68ceeecf322c273"/><dir name="Resource"><dir name="Queue"><file name="Collection.php" hash="c99bb079e0e4e6c9189226745d8f097a"/></dir><file name="Queue.php" hash="162f82629fa3f2d1cb11dfa3ba6ef2b7"/></dir><dir name="Shipping"><file name="Notice.php" hash="03b6d5b7de4b9ebc0b812dd356de1589"/></dir><file name="Source.php" hash="35a57eac60ed4eebd94a4c5d648057b8"/></dir><dir name="Sql"><dir name="synnex_setup"><file name="install-0.1.0.php" hash="b11f46ab938ceeb4ce5130eeb5ad828a"/></dir></dir><dir name="controllers"><file name="IndexController.php" hash="001fcb0095711c8944871252411f3e74"/></dir><dir name="etc"><file name="config.xml" hash="ac7b649d10d594050fc824881e1d39ac"/><file name="system.xml" hash="a512b6ee7a5c966c79158dd6a36bc736"/></dir><dir name="lib"><dir name="magmi"><dir name="conf"><file name="ColumnMappingItemProcessor.conf" hash="02a6d82b361409bca7a9e092231c1922"/><file name="DefaultValuesItemProcessor.conf" hash="0761eacb7804b844e80a206a93c7bb45"/><file name="ItemIndexer.conf" hash="71cf10aac488ca5886a062e91fcc808a"/><file name="Magmi_ReindexingPlugin.conf" hash="5e7fef9c67acd761193b7be0849d7dce"/><dir name="Synnex"><file name="CategoryImporter.conf" hash="5c3e33ed9b67624672126b92882eaa73"/><file name="ColumnMappingItemProcessor.conf" hash="02a6d82b361409bca7a9e092231c1922"/><file name="DefaultValuesItemProcessor.conf" hash="0761eacb7804b844e80a206a93c7bb45"/><file name="ItemIndexer.conf" hash="71cf10aac488ca5886a062e91fcc808a"/><file name="Magmi_CSVDataSource.conf" hash="6d6bfbc6b21a54602d71e31a544ae7ce"/><file name="Magmi_ReindexingPlugin.conf" hash="5e7fef9c67acd761193b7be0849d7dce"/><file name="ValueReplacerItemProcessor.conf" hash="b8245e87bbb7d573bdf73e879acc423e"/><file name="plugins.conf" hash="9ec41ecccb407f0c60b68ed9b992c777"/></dir><file name="ValueReplacerItemProcessor.conf" hash="b8245e87bbb7d573bdf73e879acc423e"/><file name="magmi.ini" hash="a5c66cb7554dbd81918b3a6589e2a411"/><file name="magmi.ini.default" hash="d41d8cd98f00b204e9800998ecf8427e"/><file name="plugins.conf" hash="f01d5da70a92f1ebe114a38f8c6b1a6d"/></dir><dir name="engines"><file name="magmi_productimportengine.php" hash="7066c271ced64c2cf67031e390247de7"/><file name="magmi_utilityengine.php" hash="7f067952d55311366478c612414c6631"/></dir><dir name="inc"><file name="dbhelper.class.php" hash="94409c26f0c0028a37dc14e0c84cf1a8"/><file name="fshelper.php" hash="21e0af81fee60df5b57b3f6e2627f132"/><file name="license.txt" hash="d69f02f4db9d64af32ac1a390d2a9574"/><file name="magmi_config.php" hash="987ec1cfb8675d5021a82feb789382f3"/><file name="magmi_defs.php" hash="dd8dc3e28ad013f72d96bd70cdaa172a"/><file name="magmi_engine.php" hash="87f7b2f6f28a87ad396d615a3822034d"/><file name="magmi_loggers.php" hash="136899bc06778c453131cb31e2728b4f"/><file name="magmi_mixin.php" hash="81160f5763dad16cc24b24befa2b9006"/><file name="magmi_pluginhelper.php" hash="7b4473bf363c531a55685fb0a1384ca1"/><file name="magmi_statemanager.php" hash="ac263e171783d012e346bc4a055d5fe5"/><file name="magmi_utils.php" hash="116b76c5138b46880dd4610b8f4df4ba"/><file name="magmi_valueparser.php" hash="a7278234dbba18aaa1d95eb2affdd701"/><file name="magmi_version.php" hash="43990cba40a2ae4ef4d7c26014dd5a0e"/><file name="properties.php" hash="9760558afed0628cc77a998176aa4479"/><file name="remotefilegetter.php" hash="fd8ac3bbf7c6c9c81817943affb1fefa"/><file name="timecounter.php" hash="9129bcc5a6c3775b6ad67752ae6177ad"/></dir><dir name="integration"><dir name="inc"><file name="magmi_datapump.php" hash="30a9158507970f4de6c768e0ed435c4c"/><file name="magmi_datapumpdatasource.php" hash="2c53fc5ed713ac33985f9a21d35a463d"/><file name="productimport_datapump.php" hash="8aa57b96a086980d85beb7a64ddbebbd"/><file name="pumpfactory.ini" hash="b4beac3e561fcf91a416eb52c76ce4e3"/></dir></dir><dir name="plugins"><dir name="base"><dir name="general"><dir name="optimizer"><file name="magmi_optimizer_plugin.php" hash="b281ad179395bc0268f2f707d5d3aec8"/></dir><dir name="reindex"><file name="magmi_reindexing_plugin.php" hash="fd93bce1a34cbede2c9e14a120bfd3d9"/><file name="options_panel.php" hash="679a5734ebae9f78784dc41f70e81091"/></dir></dir><dir name="itemprocessors"><dir name="columnmapper"><file name="000_columnmapper.php" hash="6a6319cdcd901dbe71b0647af7da4066"/><file name="options_panel.php" hash="34a80720a222e56fe93a09711ec45c0b"/></dir><dir name="defaultvalues"><file name="00_default_values.php" hash="54a78a56e903f93b384c6aa2e3bf73bb"/><file name="options_panel.php" hash="0969daa0f962643ac8f29c768c47a42b"/></dir><dir name="genericmapper"><file name="02_genericmapper.php" hash="2ea9614850dfb8e4abbc70aaa6f194b2"/><dir name="mappings"><dir name="default"><file name="__common__.csv" hash="dc827d5ffcabc221f6a9f31b7243131e"/><file name="backorders.csv" hash="ed3e51ef942051705c60f13d62fa87c6"/><file name="country_of_manufacture.csv" hash="e7127d7c0efdadfc342d473a582b014d"/><file name="msrp_display_actual_price_type.csv" hash="baadba2fda05c60d7c951e5b1dd71663"/><file name="msrp_enabled.csv" hash="a6f428cbbfcae861814dd221691048eb"/><file name="options_container.csv" hash="bb84f94e68900c1aafc66ce2c2461d06"/><file name="page_layout.csv" hash="0f6ac4af46d4090cb22497d172bad2f8"/><file name="status.csv" hash="dc5569042656c4163fea1cd74e8ed6d8"/><file name="tax_class_id.csv" hash="af2b19dd979a0b2687d4c7d770a454f9"/><file name="visibility.csv" hash="462fe1effcdb1ab3942677a011019f15"/></dir></dir><file name="options_panel.php" hash="80b72a8e69565645d6693e19f6f528b0"/></dir><dir name="importlimiter"><file name="01_importlimiter.php" hash="409021fbca5d3daf05f300f248d6ac75"/><file name="options_panel.php" hash="f7a8fb87d5f3e25e49c0b0db2198d852"/></dir><dir name="productdeleter"><file name="options_panel.php" hash="1f892375905523d7c2de3d3a12277982"/><file name="productdeleter.php" hash="6c726a42aa14cdad8c3e98c5ec90b327"/></dir><dir name="skufinder"><file name="001_skufinder.php" hash="a5102da1e8c459f2205fb151c177b133"/><file name="options_panel.php" hash="61e2df0dd9dbe17aa8ceb26925cd6aa5"/></dir></dir></dir><dir name="extra"><dir name="itemprocessors"><dir name="categories"><file name="categoryimport.php" hash="37bb8f156389e9df77d897f674025790"/><file name="options_panel.php" hash="d1e3c6ac7aad5c9903b9c1806a9b27ab"/></dir><dir name="customoptions"><file name="options_panel.php" hash="cb170b6febd4f89d37d0cf94c669c415"/><file name="pablo_customoptions.php" hash="61c87a9de84c5ddff5cd848447f3c667"/></dir><dir name="downloadable"><file name="downloadableprocessor.php" hash="97abc5b2b741d1c7431d38c078ed39d9"/><file name="options_panel.php" hash="e92ca7007d261fd3075371fdeb9eee9f"/></dir><dir name="imageprocessor"><file name="imageitattributeemprocessor.php" hash="9fbe8191d6220e3a38c62124389b4391"/><file name="options_panel.php" hash="6c2c15de53978198bcb9c44806661035"/></dir><dir name="itemindexer"><file name="options_panel.php" hash="bad523edf62dbf69d717df0e155096b4"/><file name="otfindexer.php" hash="733090472a67622aac805c1d6b23caba"/></dir><dir name="valuereplacer"><file name="03_valuereplacer.php" hash="b232c5c1caa4269fc12bd558ed441439"/><dir name="helper"><file name="remapper.php" hash="1247c973a4446aa765c3c97ad1ef481f"/></dir><file name="options_panel.php" hash="46093fb00f1a3d5793729386ef692f6d"/></dir><dir name="valuetrim"><file name="options_panel.php" hash="aa01db3219e7261da815b6c4a93c6592"/><file name="valuetrimmer.php" hash="52b3d04d830b1c763e877fc3a0f22d36"/></dir></dir><file name="obsolete.txt" hash="605e6dae72012017068005ca95cfb362"/></dir><dir name="inc"><file name="magmi_datasource.php" hash="174841315143e16ca73493babb9d27b5"/><file name="magmi_default_options_panel.php" hash="b35c690a790c77b97124c3bd395b176d"/><file name="magmi_defaultattributehandler.php" hash="f9fbf6c7d7925b7df55c73bae186df2f"/><file name="magmi_generalimport_plugin.php" hash="b45f85bbe04c142f9525cb7ffd82e65f"/><file name="magmi_item_processor.php" hash="1bd368a6970940206724284a299f028b"/><file name="magmi_plugin.php" hash="ab5093604a01e49d152076e4d8b1447a"/><file name="magmi_utility_plugin.php" hash="33760af242dcc0796f9595e9809aae06"/></dir></dir><dir name="state"><file name="magmistate" hash="ec2f993aec2c27fc750119ab17b16cdb"/><file name="progress.txt" hash="d41d8cd98f00b204e9800998ecf8427e"/><file name="timings.txt" hash="47ed5d57f09ed7678d663b4f5e72a307"/><file name="trace.txt" hash="d41d8cd98f00b204e9800998ecf8427e"/></dir></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="synnex"><dir name="system"><dir name="config"><file name="import.phtml" hash="f7930939a5cbe2e754a37aa6d2bb4c4f"/><file name="notice.phtml" hash="a4e6b8a00e1a807f26fdce8baa3eace9"/></dir></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Osf_Synnex.xml" hash="73a1619d9c4b05b2db3823eaebf84d7c"/></dir></target><target name="magelocale"><dir name="en_US"><dir name="template"><dir name="email"><dir name="synnex"><file name="synnex_import_error.html" hash="a8149c6b06ffa953df32d24798698e7d"/></dir></dir></dir></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.4.0</min><max>5.6.14</max></php></required></dependencies>
18
+ </package>