OSF_Integrator_for_IngramMicro - Version 1.0.0

Version Notes

1.0.0

Download this release

Release Info

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


Version 1.0.0

Files changed (106) hide show
  1. app/code/community/Osf/IngramMicro/Block/Adminhtml/Shipments.php +54 -0
  2. app/code/community/Osf/IngramMicro/Block/Adminhtml/Shipments/Grid.php +89 -0
  3. app/code/community/Osf/IngramMicro/Block/Adminhtml/Shipments/Renderer/Status.php +34 -0
  4. app/code/community/Osf/IngramMicro/Block/Adminhtml/System/Config/Frontend/Button.php +58 -0
  5. app/code/community/Osf/IngramMicro/Helper/Connect.php +139 -0
  6. app/code/community/Osf/IngramMicro/Helper/Data.php +83 -0
  7. app/code/community/Osf/IngramMicro/Helper/Error.php +38 -0
  8. app/code/community/Osf/IngramMicro/Helper/Ftp.php +79 -0
  9. app/code/community/Osf/IngramMicro/LICENSE.txt +20 -0
  10. app/code/community/Osf/IngramMicro/Model/Adminhtml/System/Config/Backend/Catmap.php +42 -0
  11. app/code/community/Osf/IngramMicro/Model/Callback.php +51 -0
  12. app/code/community/Osf/IngramMicro/Model/Catmap.php +28 -0
  13. app/code/community/Osf/IngramMicro/Model/Import.php +257 -0
  14. app/code/community/Osf/IngramMicro/Model/Observer.php +124 -0
  15. app/code/community/Osf/IngramMicro/Model/Order.php +383 -0
  16. app/code/community/Osf/IngramMicro/Model/Queue.php +33 -0
  17. app/code/community/Osf/IngramMicro/Model/Resource/Catmap.php +28 -0
  18. app/code/community/Osf/IngramMicro/Model/Resource/Catmap/Collection.php +28 -0
  19. app/code/community/Osf/IngramMicro/Model/Resource/Queue.php +28 -0
  20. app/code/community/Osf/IngramMicro/Model/Resource/Queue/Collection.php +28 -0
  21. app/code/community/Osf/IngramMicro/Model/Shipnotice.php +172 -0
  22. app/code/community/Osf/IngramMicro/Model/Source.php +45 -0
  23. app/code/community/Osf/IngramMicro/controllers/CallbackController.php +34 -0
  24. app/code/community/Osf/IngramMicro/controllers/IndexController.php +66 -0
  25. app/code/community/Osf/IngramMicro/etc/config.xml +132 -0
  26. app/code/community/Osf/IngramMicro/etc/system.xml +188 -0
  27. app/code/community/Osf/IngramMicro/lib/magmi/conf/Ingrammicro/CategoryImporter.conf +5 -0
  28. app/code/community/Osf/IngramMicro/lib/magmi/conf/Ingrammicro/plugins.conf +2 -0
  29. app/code/community/Osf/IngramMicro/lib/magmi/engines/magmi_productimportengine.php +1924 -0
  30. app/code/community/Osf/IngramMicro/lib/magmi/engines/magmi_utilityengine.php +86 -0
  31. app/code/community/Osf/IngramMicro/lib/magmi/inc/dbhelper.class.php +661 -0
  32. app/code/community/Osf/IngramMicro/lib/magmi/inc/fshelper.php +478 -0
  33. app/code/community/Osf/IngramMicro/lib/magmi/inc/license.txt +19 -0
  34. app/code/community/Osf/IngramMicro/lib/magmi/inc/magmi_config.php +274 -0
  35. app/code/community/Osf/IngramMicro/lib/magmi/inc/magmi_defs.php +14 -0
  36. app/code/community/Osf/IngramMicro/lib/magmi/inc/magmi_engine.php +559 -0
  37. app/code/community/Osf/IngramMicro/lib/magmi/inc/magmi_loggers.php +71 -0
  38. app/code/community/Osf/IngramMicro/lib/magmi/inc/magmi_mixin.php +44 -0
  39. app/code/community/Osf/IngramMicro/lib/magmi/inc/magmi_pluginhelper.php +197 -0
  40. app/code/community/Osf/IngramMicro/lib/magmi/inc/magmi_statemanager.php +71 -0
  41. app/code/community/Osf/IngramMicro/lib/magmi/inc/magmi_utils.php +180 -0
  42. app/code/community/Osf/IngramMicro/lib/magmi/inc/magmi_valueparser.php +62 -0
  43. app/code/community/Osf/IngramMicro/lib/magmi/inc/magmi_version.php +5 -0
  44. app/code/community/Osf/IngramMicro/lib/magmi/inc/properties.php +218 -0
  45. app/code/community/Osf/IngramMicro/lib/magmi/inc/remotefilegetter.php +381 -0
  46. app/code/community/Osf/IngramMicro/lib/magmi/inc/timecounter.php +278 -0
  47. app/code/community/Osf/IngramMicro/lib/magmi/integration/inc/magmi_datapump.php +38 -0
  48. app/code/community/Osf/IngramMicro/lib/magmi/integration/inc/magmi_datapumpdatasource.php +15 -0
  49. app/code/community/Osf/IngramMicro/lib/magmi/integration/inc/productimport_datapump.php +87 -0
  50. app/code/community/Osf/IngramMicro/lib/magmi/integration/inc/pumpfactory.ini +2 -0
  51. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/general/optimizer/magmi_optimizer_plugin.php +39 -0
  52. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/general/reindex/magmi_reindexing_plugin.php +112 -0
  53. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/general/reindex/options_panel.php +54 -0
  54. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/columnmapper/000_columnmapper.php +113 -0
  55. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/columnmapper/options_panel.php +25 -0
  56. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/defaultvalues/00_default_values.php +93 -0
  57. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/defaultvalues/options_panel.php +23 -0
  58. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/02_genericmapper.php +118 -0
  59. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/__common__.csv +4 -0
  60. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/backorders.csv +3 -0
  61. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/country_of_manufacture.csv +246 -0
  62. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/msrp_display_actual_price_type.csv +4 -0
  63. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/msrp_enabled.csv +3 -0
  64. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/options_container.csv +2 -0
  65. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/page_layout.csv +6 -0
  66. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/status.csv +2 -0
  67. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/tax_class_id.csv +4 -0
  68. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/mappings/default/visibility.csv +4 -0
  69. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/genericmapper/options_panel.php +8 -0
  70. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/importlimiter/01_importlimiter.php +167 -0
  71. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/importlimiter/options_panel.php +54 -0
  72. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/productdeleter/options_panel.php +8 -0
  73. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/productdeleter/productdeleter.php +61 -0
  74. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/skufinder/001_skufinder.php +95 -0
  75. app/code/community/Osf/IngramMicro/lib/magmi/plugins/base/itemprocessors/skufinder/options_panel.php +10 -0
  76. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/categories/categoryimport.php +464 -0
  77. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/categories/options_panel.php +56 -0
  78. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/customoptions/options_panel.php +2 -0
  79. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/customoptions/pablo_customoptions.php +372 -0
  80. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/downloadable/downloadableprocessor.php +302 -0
  81. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/downloadable/options_panel.php +10 -0
  82. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/imageprocessor/imageitattributeemprocessor.php +654 -0
  83. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/imageprocessor/options_panel.php +144 -0
  84. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/itemindexer/options_panel.php +22 -0
  85. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/itemindexer/otfindexer.php +396 -0
  86. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/valuereplacer/03_valuereplacer.php +102 -0
  87. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/valuereplacer/helper/remapper.php +77 -0
  88. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/valuereplacer/options_panel.php +61 -0
  89. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/valuetrim/options_panel.php +2 -0
  90. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/itemprocessors/valuetrim/valuetrimmer.php +90 -0
  91. app/code/community/Osf/IngramMicro/lib/magmi/plugins/extra/obsolete.txt +1 -0
  92. app/code/community/Osf/IngramMicro/lib/magmi/plugins/inc/magmi_datasource.php +24 -0
  93. app/code/community/Osf/IngramMicro/lib/magmi/plugins/inc/magmi_default_options_panel.php +1 -0
  94. app/code/community/Osf/IngramMicro/lib/magmi/plugins/inc/magmi_defaultattributehandler.php +433 -0
  95. app/code/community/Osf/IngramMicro/lib/magmi/plugins/inc/magmi_generalimport_plugin.php +16 -0
  96. app/code/community/Osf/IngramMicro/lib/magmi/plugins/inc/magmi_item_processor.php +102 -0
  97. app/code/community/Osf/IngramMicro/lib/magmi/plugins/inc/magmi_plugin.php +337 -0
  98. app/code/community/Osf/IngramMicro/lib/magmi/plugins/inc/magmi_utility_plugin.php +15 -0
  99. app/code/community/Osf/IngramMicro/lib/magmi/state/magmistate +1 -0
  100. app/code/community/Osf/IngramMicro/lib/magmi/state/progress.txt +0 -0
  101. app/code/community/Osf/IngramMicro/lib/magmi/state/timings.txt +50 -0
  102. app/code/community/Osf/IngramMicro/lib/magmi/state/trace.txt +0 -0
  103. app/code/community/Osf/IngramMicro/sql/osf_ingrammicro_setup/install-0.0.1.php +99 -0
  104. app/design/adminhtml/default/default/template/osf/ingrammicro/system/config/button.phtml +1 -0
  105. app/etc/modules/Osf_IngramMicro.xml +9 -0
  106. package.xml +18 -0
app/code/community/Osf/IngramMicro/Block/Adminhtml/Shipments.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 IngramMicro
13
+ * @package Osf_IngramMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Block_Adminhtml_Shipments extends Mage_Adminhtml_Block_Sales_Order_View_Tab_Shipments
18
+ {
19
+ protected function _prepareColumns()
20
+ {
21
+ $this->addColumn('increment_id', array(
22
+ 'header' => Mage::helper('sales')->__('Shipment #'),
23
+ 'index' => 'increment_id',
24
+ ));
25
+
26
+ $this->addColumn('shipping_name', array(
27
+ 'header' => Mage::helper('sales')->__('Ship to Name'),
28
+ 'index' => 'shipping_name',
29
+ ));
30
+
31
+ $this->addColumn('shipment_status', array(
32
+ 'header' => Mage::helper('sales')->__('Ingrammicro Shipment Status'),
33
+ 'index' => 'shipment_status',
34
+ 'renderer' => 'Osf_IngramMicro_Block_Adminhtml_Shipments_Renderer_Status',
35
+ ));
36
+
37
+ $this->addColumn('created_at', array(
38
+ 'header' => Mage::helper('sales')->__('Date Shipped'),
39
+ 'index' => 'created_at',
40
+ 'type' => 'datetime',
41
+ ));
42
+
43
+ $this->addColumn('total_qty', array(
44
+ 'header' => Mage::helper('sales')->__('Total Qty'),
45
+ 'index' => 'total_qty',
46
+ 'type' => 'number',
47
+ ));
48
+
49
+ return parent::_prepareColumns();
50
+ }
51
+ }
52
+
53
+ /* Filename: Shipments.php */
54
+ /* Location: app/code/local/Osf/IngramMicro/Block/Adminhtml/Shipments.php */
app/code/community/Osf/IngramMicro/Block/Adminhtml/Shipments/Grid.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 IngramMicro
13
+ * @package Osf_IngramMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Block_Adminhtml_Shipments_Grid extends Mage_Adminhtml_Block_Sales_Shipment_Grid
18
+ {
19
+ protected function _prepareColumns()
20
+ {
21
+ $this->addColumn('increment_id', array(
22
+ 'header' => Mage::helper('sales')->__('Shipment #'),
23
+ 'index' => 'increment_id',
24
+ 'type' => 'text',
25
+ ));
26
+
27
+ $this->addColumn('created_at', array(
28
+ 'header' => Mage::helper('sales')->__('Date Shipped'),
29
+ 'index' => 'created_at',
30
+ 'type' => 'datetime',
31
+ ));
32
+
33
+ $this->addColumn('order_increment_id', array(
34
+ 'header' => Mage::helper('sales')->__('Order #'),
35
+ 'index' => 'order_increment_id',
36
+ 'type' => 'text',
37
+ ));
38
+
39
+ $this->addColumn('order_created_at', array(
40
+ 'header' => Mage::helper('sales')->__('Order Date'),
41
+ 'index' => 'order_created_at',
42
+ 'type' => 'datetime',
43
+ ));
44
+
45
+ $this->addColumn('shipment_status', array(
46
+ 'header' => Mage::helper('sales')->__('Ingrammicro Shipment Status'),
47
+ 'index' => 'shipment_status',
48
+ 'type' => 'text',
49
+ 'renderer' => 'Osf_IngramMicro_Block_Adminhtml_Shipments_Renderer_Status',
50
+ ));
51
+
52
+ $this->addColumn('shipping_name', array(
53
+ 'header' => Mage::helper('sales')->__('Ship to Name'),
54
+ 'index' => 'shipping_name',
55
+ ));
56
+
57
+ $this->addColumn('total_qty', array(
58
+ 'header' => Mage::helper('sales')->__('Total Qty'),
59
+ 'index' => 'total_qty',
60
+ 'type' => 'number',
61
+ ));
62
+
63
+ $this->addColumn('action',
64
+ array(
65
+ 'header' => Mage::helper('sales')->__('Action'),
66
+ 'width' => '50px',
67
+ 'type' => 'action',
68
+ 'getter' => 'getId',
69
+ 'actions' => array(
70
+ array(
71
+ 'caption' => Mage::helper('sales')->__('View'),
72
+ 'url' => array('base'=>'*/sales_shipment/view'),
73
+ 'field' => 'shipment_id'
74
+ )
75
+ ),
76
+ 'filter' => false,
77
+ 'sortable' => false,
78
+ 'is_system' => true
79
+ ));
80
+
81
+ $this->addExportType('*/*/exportCsv', Mage::helper('sales')->__('CSV'));
82
+ $this->addExportType('*/*/exportExcel', Mage::helper('sales')->__('Excel XML'));
83
+
84
+ return parent::_prepareColumns();
85
+ }
86
+ }
87
+
88
+ /* Filename: Grid.php */
89
+ /* Location: app/code/local/Osf/IngramMicro/Block/Adminhtml/Shipments/Grid.php */
app/code/community/Osf/IngramMicro/Block/Adminhtml/Shipments/Renderer/Status.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 IngramMicro
13
+ * @package Osf_IngramMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Block_Adminhtml_Shipments_Renderer_Status extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract
18
+ {
19
+ public function render(Varien_Object $row)
20
+ {
21
+ $label = '';
22
+ if( is_null($row->getShipmentStatus()) ){
23
+ $shipment = Mage::getModel('sales/order_shipment')->load( $row->getEntityId() );
24
+ $label = Mage::getModel('osf_ingrammicro/source')->getStatusText($shipment->getShipmentStatus());
25
+ } else {
26
+ $label = Mage::getModel('osf_ingrammicro/source')->getStatusText($row->getShipmentStatus());
27
+ }
28
+
29
+ return $label;
30
+ }
31
+ }
32
+
33
+ /* Filename: Status.php */
34
+ /* Location: app/code/local/Osf/IngramMicro/Block/Adminhtml/Shipments/Renderer/Status.php */
app/code/community/Osf/IngramMicro/Block/Adminhtml/System/Config/Frontend/Button.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * Button Block
11
+ *
12
+ * @category Osf IngramMicro
13
+ * @package Osf_IngramMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Block_Adminhtml_System_Config_Frontend_Button
18
+ extends Mage_Adminhtml_Block_System_Config_Form_Field
19
+ {
20
+ /*
21
+ * Set template
22
+ */
23
+ protected function _construct()
24
+ {
25
+ parent::_construct();
26
+ $this->setTemplate('osf/ingrammicro/system/config/button.phtml');
27
+ }
28
+
29
+ /**
30
+ * Return element html
31
+ *
32
+ * @param Varien_Data_Form_Element_Abstract $element
33
+ * @return string
34
+ */
35
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
36
+ {
37
+ return $this->_toHtml();
38
+ }
39
+
40
+
41
+
42
+ /**
43
+ * Generate button html
44
+ *
45
+ * @return string
46
+ */
47
+ public function getButtonHtml()
48
+ {
49
+ $button = $this->getLayout()->createBlock('adminhtml/widget_button')
50
+ ->setData(array(
51
+ 'id' => 'osf_ingrammicro_button',
52
+ 'label' => $this->helper('adminhtml')->__('Manual import'),
53
+ 'onclick' => 'window.open(\'' . Mage::helper("adminhtml")->getUrl("ingrammicro/index/startImport") . '\'); return false;'
54
+ ));
55
+
56
+ return $button->toHtml();
57
+ }
58
+ }
app/code/community/Osf/IngramMicro/Helper/Connect.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
13
+ * @package Osf_IngraMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Helper_Connect extends Mage_Core_Helper_Data
18
+ {
19
+
20
+ public $ftpHost;
21
+ public $ftpUser;
22
+ public $ftpPass;
23
+ public $XMLEndpoint;
24
+ public $tmpLocation;
25
+ public $prodFileName;
26
+ public $logFile = 'ingrammicro.log';
27
+ public $vendor = 'Ingrammicro';
28
+ protected $logH = 'Error: ';
29
+
30
+ public function __construct()
31
+ {
32
+ $this->ftpHost = Mage::getStoreConfig('osf_ingrammicro/ftplogin/ftp_host');
33
+ $this->ftpUser = Mage::getStoreConfig('osf_ingrammicro/ftplogin/ftp_user');
34
+ $this->ftpPass = Mage::helper('core')->decrypt(Mage::getStoreConfig('osf_ingrammicro/ftplogin/ftp_password'));
35
+ $this->ftpRemoteDir = Mage::getStoreConfig('osf_ingrammicro/ftplogin/ftp_remote_dir');
36
+ $this->prodFileName = Mage::getStoreConfig('osf_ingrammicro/ftplogin/ftp_prod_file');
37
+ $this->XMLEndpoint = Mage::getStoreConfig('osf_ingrammicro/xmllogin/xml_endpoint');
38
+ $this->XMLUser = Mage::getStoreConfig('osf_ingrammicro/xmllogin/xml_username');
39
+ $this->XMLPass = Mage::helper('core')->decrypt(Mage::getStoreConfig('osf_ingrammicro/xmllogin/xml_password'));
40
+ $this->tmpLocation = Mage::getBaseDir('var') . DS . 'ingrammicro' . DS;
41
+
42
+ // check if the ingrammicro folder exists if not create it
43
+ if(false === $this->checkDir())
44
+ return false;
45
+ return true;
46
+ }
47
+
48
+ /**
49
+ * Get the products file from Ingram Micro FTP
50
+ *
51
+ * @param none
52
+ * @return string
53
+ */
54
+ public function getProductsFile()
55
+ {
56
+ //init the ftp and download the products file
57
+ $ftp = Mage::helper('osf_ingrammicro/ftp');
58
+ if(is_null($this->ftpHost) || is_null($this->ftpUser) || is_null($this->ftpPass)){
59
+ // move this log to ingrammicro log
60
+ Mage::log( $this->vendor . ': Error: ftphost or ftpuser or ftppass can not be empty', null, $this->logFile);
61
+ return false;
62
+ }
63
+
64
+ Mage::log( $this->vendor . ': Start downloading import file', null, $this->logFile);
65
+ $ftp->setRemoteDirectory($this->ftpRemoteDir);
66
+ $ftp->ftpConnect($this->ftpHost, $this->ftpUser, $this->ftpPass);
67
+ $ftp->downloadFile($this->tmpLocation . $this->prodFileName, $this->prodFileName);
68
+ $ftp->ftpClose();
69
+ Mage::log( $this->vendor . ': End downloading import file', null, $this->logFile);
70
+
71
+ // Checking the download file extension
72
+ $fileObj = new SplFileInfo($this->tmpLocation . $this->prodFileName);
73
+
74
+ if($fileObj->getExtension() === 'ZIP'){
75
+
76
+ // init the zip archive object and open so i can unzip the file
77
+ $zip = new ZipArchive;
78
+ $result = $zip->open($this->tmpLocation . $this->prodFileName);
79
+
80
+ // checking the result of the open zip
81
+ if($result === true){
82
+ $zip->extractTo($this->tmpLocation);
83
+ $zip->close();
84
+ } else {
85
+ throw new Exception("Zip: Failed, code: ". $result, 1);
86
+ }
87
+
88
+ // prepareing the name of the file because inside of the archive is a TXT file
89
+ $filename = str_replace($fileObj->getExtension(), 'TXT', $this->prodFileName);
90
+ } else {
91
+ // if the file is not archived the file name is ok
92
+ $filename = $this->prodFileName;
93
+ }
94
+
95
+ //$filename = 'PRICE.TXT';
96
+
97
+ $fullPath = Mage::getBaseDir('var') . DS . 'ingrammicro' . DS . $filename;
98
+ return $fullPath;
99
+ }
100
+
101
+ /**
102
+ * Send the process order xml request
103
+ *
104
+ * @param string
105
+ * @return string
106
+ */
107
+ public function sendXMLRequest($xml)
108
+ {
109
+ /* init the http client */
110
+ $client = new Zend_Http_Client($this->XMLEndpoint);
111
+ /* set the method type and the timeout for the http call */
112
+ $client->setMethod(Zend_Http_Client::POST);
113
+ $client->setConfig(array(
114
+ 'timeout' => 30)
115
+ );
116
+
117
+ $client->setRawData($xml, 'text/xml');
118
+
119
+ /* makeing the request to the server and receiving the request */
120
+ $response = $client->request();
121
+ return $response->getBody();
122
+ }
123
+
124
+ public function checkDir() {
125
+ if(file_exists($this->tmpLocation) === false){
126
+ if(!mkdir($this->tmpLocation, 0777, true)){
127
+ Mage::log($this->logH . 'Ingrammicro folder does not exist in var folder and it could not be created',
128
+ null,
129
+ $this->logFile);
130
+ return false;
131
+ }
132
+ }
133
+ return true;
134
+ }
135
+
136
+ }
137
+
138
+ /* Filename: Connect.php */
139
+ /* Location: app/code/local/Osf/IngraMicro/Helper/Connect.php */
app/code/community/Osf/IngramMicro/Helper/Data.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* Require the Magmi files */
4
+ require_once( Mage::getModuleDir('', 'Osf_IngramMicro') . DS . "lib/magmi/inc/magmi_defs.php");
5
+ require_once( Mage::getModuleDir('', 'Osf_IngramMicro') . DS . "lib/magmi/integration/inc/magmi_datapump.php");
6
+ /* End require Magmi files */
7
+
8
+ /**
9
+ *
10
+ * Osf Global Services
11
+ *
12
+ * NOTICE OF LICENSE
13
+ *
14
+ * This source file is subject to the EULA
15
+ * that is bundled with this package in the file LICENSE.txt.
16
+ *
17
+ * Data Helper
18
+ *
19
+ * @category OSF
20
+ * @package OSF_IngraMicro
21
+ * @author OSF Global Services
22
+ *
23
+ */
24
+ class Osf_IngramMicro_Helper_Data extends Mage_Core_Helper_Data {
25
+
26
+ protected $importArray = array();
27
+ protected $profile = "Ingrammicro";
28
+ public $resource;
29
+ public $writeConn;
30
+ public $readConn;
31
+ public $vendorTable;
32
+ public $vendorId;
33
+ public $logFile;
34
+
35
+ public function __construct(){
36
+ // getting the resources
37
+ $this->resource = Mage::getSingleton('core/resource');
38
+ $this->writeConn = $this->resource->getConnection('core_write');
39
+ $this->readConn = $this->resource->getConnection('core_read');
40
+ $this->logFile = 'ingrammicro.log';
41
+ }
42
+
43
+ /**
44
+ * Starts the import using Magmi
45
+ *
46
+ * @param none
47
+ * @return bool
48
+ */
49
+ public function startMagmiImport()
50
+ {
51
+ /* Factory Create the product import */
52
+ $magmiDp = Magmi_DataPumpFactory::getDataPumpInstance("productimport");
53
+
54
+ /* Init the profile and the import method */
55
+ $magmiDp->beginImportSession($this->profile,"create");
56
+
57
+ /* looping thought the entire array of products */
58
+ foreach ($this->importArray as $productKey => $productValue) {
59
+ $res = $magmiDp->ingest($productValue);
60
+ if($res['ok'] !== true){
61
+ Mage::log('Magmi: Import of product failed, sku:' . $productValue['sku'], null,$this->logFile);
62
+ }
63
+ }
64
+
65
+ /* End import session */
66
+ $magmiDp->endImportsession();
67
+ return;
68
+ }
69
+
70
+ /**
71
+ * Set the import array data
72
+ *
73
+ * @param array
74
+ * @return array
75
+ */
76
+ public function setImportArray($dataIn)
77
+ {
78
+ return $this->importArray = $dataIn;
79
+ }
80
+ }
81
+
82
+ /* Filename: Data.php */
83
+ /* Location: app/code/local/Osf/IngramMicro/Helper/Data.php */
app/code/community/Osf/IngramMicro/Helper/Error.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *
4
+ * Osf Global Services
5
+ *
6
+ * NOTICE OF LICENSE
7
+ *
8
+ * This source file is subject to the EULA
9
+ * that is bundled with this package in the file LICENSE.txt.
10
+ *
11
+ * Error Helper
12
+ *
13
+ * @category OSF
14
+ * @package OSF_IngraMicro
15
+ * @author OSF Global Services
16
+ *
17
+ */
18
+ class Osf_IngramMicro_Helper_Error extends Mage_Core_Helper_Data {
19
+ public $logFile;
20
+
21
+ public function __construct(){
22
+ $this->logFile = 'ingrammicro.log';
23
+ parent::__construct();
24
+ }
25
+
26
+ /**
27
+ * Receive calls from ingram micro with ship notices
28
+ *
29
+ * @param array
30
+ * @return string
31
+ */
32
+ public function buildXmlErrorMsg($errors){
33
+ foreach ($errors as $error) {
34
+ $errorMsg[] = trim($error->message);
35
+ }
36
+ return $errorMsg;
37
+ }
38
+ }
app/code/community/Osf/IngramMicro/Helper/Ftp.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
13
+ * @package Osf_IngramMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Helper_Ftp extends Mage_Core_Helper_Data {
18
+
19
+ private $conn;
20
+ private $dir;
21
+ /**
22
+ * Set Remote Directory
23
+ * @param string $dir
24
+ */
25
+ public function setRemoteDirectory( $dir ) {
26
+ $this->dir = $dir;
27
+ }
28
+
29
+ /**
30
+ * Connect to FTP
31
+ */
32
+ public function ftpConnect($host,$user,$pass)
33
+ {
34
+
35
+ $this->conn = ftp_connect($host);
36
+ if($this->conn === false){
37
+ throw new Exception("Ftp: Could not connect!", 1);
38
+ }
39
+ if(!ftp_login($this->conn, $user, $pass)){
40
+ throw new Exception("Ftp: Could not login!", 1);
41
+ }
42
+ if(!ftp_pasv($this->conn, true)){
43
+ throw new Exception("Ftp: Could not enter passive mode!", 1);
44
+ }
45
+
46
+ if ( !ftp_chdir($this->conn, $this->dir) ) {
47
+ throw new Exception("Ftp: Could not change directory!", 1);
48
+ }
49
+
50
+ return;// $this->conn;
51
+ }
52
+
53
+ /**
54
+ * Download a file from the server
55
+ *
56
+ * @param string $localFile
57
+ * @param string $remoteFile
58
+ * @param string $type
59
+ * @return bool
60
+ */
61
+ public function downloadFile($localFile, $remoteFile, $type=FTP_BINARY)
62
+ {
63
+ return ftp_get($this->conn, $localFile, $remoteFile, $type);
64
+ }
65
+
66
+ /**
67
+ * Close the ftp connection
68
+ *
69
+ * @param none
70
+ * @return bool
71
+ */
72
+ public function ftpClose()
73
+ {
74
+ return ftp_close($this->conn);
75
+ }
76
+ }
77
+
78
+ /* Filename: Ftp.php */
79
+ /* Location: app/code/local/Osf/IngraMicro/Helper/Ftp.php */
app/code/community/Osf/IngramMicro/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/IngramMicro/Model/Adminhtml/System/Config/Backend/Catmap.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 IngramMicro
13
+ * @package Osf_IngramMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Model_Adminhtml_System_Config_Backend_Catmap
18
+ extends Mage_Core_Model_Config_Data
19
+ {
20
+ public function _afterSave()
21
+ {
22
+ $csv_file = $_FILES["groups"]["tmp_name"]["general"]["fields"]["category_mapping_import"]["value"];
23
+
24
+ if ( $csv_file ) {
25
+ //Extract data from CSV file
26
+ $csv = new Varien_File_Csv;
27
+ $csv->setDelimiter("|");
28
+ $data = $csv->getData( $csv_file );
29
+
30
+ $write = Mage::getSingleton('core/resource')->getConnection('core_write');
31
+ $read = Mage::getSingleton('core/resource')->getConnection('core_read');
32
+ $table = Mage::getSingleton('core/resource')->getTableName('osf_ingrammicro/catmap');
33
+
34
+ $read->query("DELETE from $table");
35
+ $result_num = $write->insertArray(
36
+ $table,
37
+ array('ingrammicro_category','magento_category'),
38
+ $data
39
+ );
40
+ }
41
+ }
42
+ }
app/code/community/Osf/IngramMicro/Model/Callback.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * Callback Model
11
+ *
12
+ * @category Osf
13
+ * @package Osf_Ingrammicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Model_Callback extends Mage_Core_Model_Abstract
18
+ {
19
+ public $logFile = 'ingrammicro.log';
20
+
21
+ public function receiveShipNotice($xmlString){
22
+ Mage::log('IngramMicro: ASN: Start process', null,$this->logFile);
23
+ if(empty($xmlString)){
24
+ Mage::log('IngramMicro: Error: The ship notice is empty', null,$this->logFile);
25
+ return false;
26
+ }
27
+
28
+ // turn on custom error handeling
29
+ libxml_use_internal_errors(true);
30
+ $xmlData = simplexml_load_string($xmlString);
31
+
32
+ // get errors if they exist
33
+ $errors = libxml_get_errors();
34
+ if(!empty($errors)){
35
+ Mage::log('IngramMicro: Error: XML parsing errors: ' . $xmlString, null,$this->logFile);
36
+ return false;
37
+ }
38
+
39
+ $result = Mage::getModel('osf_ingrammicro/shipnotice')->processShipNotice($xmlData);
40
+ if($result !== true){
41
+ Mage::log('IngramMicro: Error: General Error' . $xmlString, null,$this->logFile);
42
+ return false;
43
+ }
44
+
45
+ return true;
46
+ }
47
+
48
+ }
49
+
50
+ /* Filename: Callback.php */
51
+ /* Location: app/code/local/Osf/Ingrammicro/Model/Callback.php */
app/code/community/Osf/IngramMicro/Model/Catmap.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * Catmap Model
11
+ *
12
+ * @method ()
13
+ *
14
+ * @category Osf
15
+ * @package Osf_Ingrammicro
16
+ * @author Osf Global Services
17
+ */
18
+
19
+ class Osf_Ingrammicro_Model_Catmap extends Mage_Core_Model_Abstract {
20
+
21
+ protected function _construct() {
22
+ $this->_init('osf_ingrammicro/catmap');
23
+ }
24
+
25
+ }
26
+
27
+ /* Filename: Catmap.php */
28
+ /* Location: app/code/local/Osf/Ingrammicro/Model/Catmap.php */
app/code/community/Osf/IngramMicro/Model/Import.php ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
13
+ * @package Osf_IngramMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Model_Import extends Mage_Core_Model_Abstract
18
+ {
19
+ public $fileHeader = array();
20
+ public $readConnection;
21
+ public $resource;
22
+ public $categoryMap;
23
+ public $ingramMicroVendorId;
24
+ public $test_mode;
25
+ public $allowedSkus;
26
+ protected $logFile = 'ingrammicro.log';
27
+ protected $vendor = 'Ingrammicro';
28
+
29
+ public function _construct()
30
+ {
31
+
32
+ $this->resource = Mage::getSingleton('core/resource');
33
+ $this->buildCategoryMap();
34
+ $this->readConnection = Mage::getSingleton('core/resource')->getConnection('core_read');
35
+ $this->test_mode = Mage::getStoreConfig('osf_ingrammicro/general/test_mode');
36
+ $this->buildAllowedSkus();
37
+ if(!$this->checkMagmiConfig()){
38
+ echo "Could not create magmi config file. Please make the folder ... temporary writeable";
39
+ die();
40
+ }
41
+ parent::_construct();
42
+ }
43
+
44
+ /**
45
+ * Process the received csv
46
+ *
47
+ * @param none
48
+ * @return bool
49
+ */
50
+ public function processData()
51
+ {
52
+
53
+ /* getting the file from the server */
54
+ $productsFile = Mage::helper('osf_ingrammicro/connect')->getProductsFile();
55
+ if($productsFile === false){
56
+ Mage::log("Ingram Micro: Error in getting products file", null, $this->logFile);
57
+ return false;
58
+ }
59
+
60
+ $file = new SplFileObject($productsFile);
61
+
62
+ /* init vars */
63
+ $productsData = array();
64
+ $row1 = 0;
65
+ $fileHeader = array();
66
+
67
+ /* looping throught the csv */
68
+ $index = 0;
69
+ while(!$file->eof()){
70
+ $row = $file->fgetcsv(",");
71
+
72
+ if ( $this->test_mode && $index > 5 ) {
73
+ break;
74
+ }
75
+
76
+ if( isset($row[1]) && !empty( $this->allowedSkus ) && !in_array( trim($row[1]), $this->allowedSkus) ){
77
+ continue;
78
+ }
79
+
80
+ /* setting the product data */
81
+ if ( isset($row[1])) {
82
+ $productsData[] = $this->createDataArr($row);
83
+ }
84
+ $index++;
85
+ }
86
+
87
+ Mage::log( $this->vendor . ": Magmi import started", null, $this->logFile);
88
+
89
+ /* setting the data to be imported and starting the import */
90
+ Mage::helper('osf_ingrammicro/data')->setImportArray($productsData);
91
+ Mage::helper('osf_ingrammicro/data')->startMagmiImport();
92
+
93
+ Mage::log( $this->vendor . ": Magmi import finished", null, $this->logFile);
94
+
95
+ echo "Import finished.";
96
+
97
+ return;
98
+ }
99
+
100
+ /**
101
+ * Construct the product array for import
102
+ *
103
+ * @param array $row
104
+ * @return array $productData
105
+ */
106
+ public function createDataArr($row)
107
+ {
108
+
109
+ $productData = array();
110
+ $productData['sku'] = $row[1];
111
+
112
+ Mage::log( $this->vendor . ": Start building data array", null, $this->logFile);
113
+
114
+ if( !$this->productExists( $row[1] ) ){
115
+
116
+ $productData['url_key'] = Mage::getModel('catalog/product_url')->formatUrlKey( trim($row[4]) );
117
+ $productData['url_rewrite'] = 1;
118
+ $productData['mpn'] = trim($row[7]);
119
+ $productData['name'] = trim($row[4]);
120
+ $productData['short_description'] = trim($row[4]);
121
+ $productData['description'] = trim($row[4]);
122
+ $productData['manufacturer'] = trim($row[3]);
123
+ $productData['brand'] = trim($row[3]);
124
+ $productData['msrp'] = (float)$row[6];
125
+ $productData['is_oversized'] = ($row[18] === 'N')? 1 : 0;
126
+ $productData['osf_product_cost'] = (float)$row[14];
127
+ $productData['price'] = (float)$row[14];
128
+ $productData['weight'] = (!empty($row[8]))? (float)$row[8] : 1;
129
+ $productData['upc'] = $row[9];
130
+ $productData['item_length'] = (float)$row[10];
131
+ $productData['item_width'] = (float)$row[12];
132
+ $productData['item_height'] = (float)$row[13];
133
+ $productData['visibility'] = Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE;
134
+ $productData['tax_class_id'] = 2;
135
+ $productData['type_id'] = Mage_Catalog_Model_Product_Type::TYPE_SIMPLE;
136
+ $productData['attribute_set_id'] = 4;
137
+ if ( isset( $this->categoryMap[trim($row[20]) . '_' . trim($row[18])]['category_paths'] ) ) {
138
+ $productData['categories'] = $this->categoryMap[trim($row[20]) . '_' . trim($row[18])]['category_paths'];
139
+ }
140
+ $productData['osf_product_vendor'] = $this->vendor;
141
+ }
142
+
143
+ $productData['cost'] = (float)$row[14];
144
+ $productData['inventory'] = trim($row[23]);
145
+ $productData['qty'] = trim($row[23]);
146
+ $productData['status'] = ($row[0] !== 'D')? Mage_Catalog_Model_Product_Status::STATUS_ENABLED
147
+ : Mage_Catalog_Model_Product_Status::STATUS_DISABLED;
148
+
149
+ Mage::log( $this->vendor . ": Finish building data array", null, $this->logFile);
150
+
151
+ return $productData;
152
+ }
153
+
154
+ /**
155
+ * Check if a product with the sku exists in Magento
156
+ *
157
+ * @param array $sku
158
+ * @return bool
159
+ */
160
+ public function productExists($sku)
161
+ {
162
+ $tableName = $this->resource->getTableName('catalog_product_entity');
163
+ $select = $this->readConnection
164
+ ->select()
165
+ ->from($tableName, array(new Zend_Db_Expr('count(entity_id)')))
166
+ ->where($this->readConnection->quoteInto('sku=?', $sku));
167
+ $countSku = $this->readConnection->fetchOne($select);
168
+
169
+ return ($countSku != 0)? true :false;
170
+ }
171
+
172
+ /**
173
+ * Validate the product based on the required validations
174
+ *
175
+ * @param array $row
176
+ * @return bool
177
+ */
178
+ public function validate($row) {
179
+
180
+ //Check if product can be mapped with a category
181
+ if (isset($row[20]) && isset($row[18])) {
182
+ $catMap = $this->getCategoryId( trim($row[20]) . '_' . trim($row[18]) );
183
+ if ( !isset($catMap['category_paths']) ) {
184
+ return false;
185
+ }
186
+ }
187
+
188
+ //It seems like sometimes a space appears at the end of import files.
189
+ if ( !isset($row[1])) {
190
+ return false;
191
+ }
192
+
193
+ return true;
194
+ }
195
+
196
+ public function buildCategoryMap() {
197
+
198
+ $collection = Mage::getModel('osf_ingrammicro/catmap')->getCollection();
199
+
200
+ foreach ( $collection as $entry ) {
201
+ $this->categoryMap[ $entry->getIngrammicroCategory() ]['category_paths'] = $entry->getMagentoCategory();
202
+ }
203
+
204
+ return;
205
+ }
206
+
207
+ /**
208
+ * Check if the db configuration for magmi exists and if not create it
209
+ *
210
+ * @return bool
211
+ */
212
+ public function checkMagmiConfig()
213
+ {
214
+ $filename = Mage::getModuleDir('', 'Osf_IngramMicro') . DS . 'lib' . DS . "magmi/conf/magmi.ini";
215
+ if(file_exists($filename)){
216
+ return true;
217
+ }
218
+
219
+ $config = Mage::getConfig()->getResourceConnectionConfig("default_setup");
220
+ $file = new SplFileObject($filename,"w");
221
+ $fileText = "[DATABASE]\nconnectivity = \"net\"\nhost = \""
222
+ . $config->host . "\"\nport = \"3306\"\nunix_socket = \ndbname = \""
223
+ . $config->dbname . "\"\nuser = \""
224
+ . $config->username . "\"\npassword = "
225
+ . $config->password . "\ntable_prefix = \n[MAGENTO]\nversion = \"1.7.x\"\nbasedir = \"../../\"\n[GLOBAL]\n"
226
+ ."step = \"0.5\"\nmultiselect_sep = \",\"\ndirmask = \"755\"\nfilemask = \"644\"\n";
227
+
228
+ try{
229
+ $file->fwrite($fileText);
230
+ } catch (Exception $e){
231
+ Mage::log('Magmi Conf folder not writeable');
232
+ return false;
233
+ }
234
+
235
+ return true;
236
+ }
237
+
238
+ public function buildAllowedSkus(){
239
+
240
+ $filename = Mage::getStoreConfig('osf_ingrammicro/ftplogin/filtered_skus_file');
241
+ if ( is_file( Mage::getBaseDir('media') . DS . 'admin-config-uploads' . DS . $filename ) ) {
242
+ $allowedSkusFile = Mage::getBaseDir('media') . DS . 'admin-config-uploads' . DS . $filename;
243
+ $skusFile = new SplFileObject($allowedSkusFile);
244
+ while ( !$skusFile->eof() ) {
245
+ $row = $skusFile->fgetcsv();
246
+ $this->allowedSkus[] = trim($row[0]);
247
+ }
248
+ }
249
+
250
+ return;
251
+ }
252
+ }
253
+
254
+
255
+
256
+ /* Filename: Import.php */
257
+ /* Location: app/code/local/Osf/IngramMicro/Model/Import.php */
app/code/community/Osf/IngramMicro/Model/Observer.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
13
+ * @package Osf_IngramMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Model_Observer extends Mage_Core_Model_Abstract {
18
+
19
+ protected $vendor = 'Ingrammicro';
20
+ protected $logFile = 'ingrammicro.log';
21
+
22
+ /**
23
+ * Process the order so it can be sent to Ingrammicro
24
+ *
25
+ * @param object
26
+ * @return object
27
+ */
28
+ public function processOrder($observer)
29
+ {
30
+
31
+ Mage::log('Ingrammicro: Start procesing order', null, $this->logFile);
32
+
33
+ // get the order from Magento
34
+ $order = $observer->getOrder();
35
+
36
+ // check if order is in
37
+ if( $order->getStatus() == Mage_Sales_Model_Order::STATE_PROCESSING ){
38
+ Mage::log($this->vendor . ': End processing order, order already processed', null, $this->logFile);
39
+ return;
40
+ }
41
+
42
+ $orderItems = $this->checkItems($order->getAllItems());
43
+ if ( $orderItems === false ) {
44
+ Mage::log($this->vendor . ': End processing order, no ' . $this->vendor .' items', null, $this->logFile);
45
+ return $this;
46
+ }
47
+
48
+ // check that the order has a shipment
49
+ $shipments = $order->getShipmentsCollection();
50
+ if(count($shipments) > 0){
51
+ return;
52
+ }
53
+
54
+ $shipment = $this->createShipment($order,$orderItems);
55
+
56
+ // build the xml that will be sent to Ingram Micro
57
+ $xmlData = Mage::getModel('osf_ingrammicro/order')->buildOrderArray($order, $shipment);
58
+ Mage::log($xmlData, null, $this->logFile);
59
+
60
+ // send the process order request
61
+ $xmlResponse = Mage::helper('osf_ingrammicro/connect')->sendXMLRequest($xmlData);
62
+ Mage::log($xmlResponse, null, $this->logFile);
63
+
64
+ // process the response that has arrived from Ingrammicro
65
+ $response = Mage::getModel('osf_ingrammicro/order')->processOrderResponse($xmlResponse, $order, $xmlData);
66
+ Mage::log('Ingrammicro: After process response', null, $this->logFile);
67
+
68
+ Mage::log('Ingrammicro: End procesing order', null, $this->logFile);
69
+ return $this;
70
+ }
71
+
72
+ /**
73
+ * Check if the order contains products from ingrammicro and return the items
74
+ * @param $items
75
+ * @return bool
76
+ */
77
+ public function checkItems($items)
78
+ {
79
+ $outItems = array();
80
+ foreach ($items as $item) {
81
+ $p_id = $item->getProduct()->getId();
82
+ $vendor = Mage::getModel('catalog/product')->load($p_id)->getData('osf_product_vendor');
83
+ if(trim($vendor) == $this->vendor){
84
+ $outItems[] = $item;
85
+ }
86
+ }
87
+
88
+ return (empty($outItems))? false : $outItems;
89
+ }
90
+
91
+ /**
92
+ * Create the shipment for the order
93
+ *
94
+ * @param $items
95
+ * @return bool
96
+ */
97
+ public function createShipment($order, $orderItems)
98
+ {
99
+ $itemsQtys = array();
100
+ foreach ($orderItems as $orderItem) {
101
+ $itemsQtys[$orderItem->getId()] = $orderItem->getData('qty_ordered');
102
+ }
103
+
104
+ $shipment = $order->prepareShipment($itemsQtys);
105
+ if ($shipment) {
106
+ $shipment->register();
107
+ $shipment->setShipmentStatus(Osf_IngramMicro_Model_Source::SHIPMENT_STATUS_PENDING);
108
+ $shipment->addComment($this->vendor . ' Items');
109
+ $shipment->getOrder()->setIsInProcess(true);
110
+
111
+ try {
112
+ $shipment->save();
113
+ } catch (Mage_Core_Exception $e) {
114
+ Mage::log($this->vendor . ': ' . $e->getMessage(), null, $this->logFile);
115
+ }
116
+ }
117
+
118
+ return $shipment;
119
+ }
120
+
121
+ }
122
+
123
+ /* Filename: Observer.php */
124
+ /* Location: app/code/local/Osf/IngramMicro/Model/Observer.php */
app/code/community/Osf/IngramMicro/Model/Order.php ADDED
@@ -0,0 +1,383 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
13
+ * @package Osf_Ingrammicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Ingrammicro_Model_Order extends Mage_Core_Model_Abstract {
18
+
19
+ protected $account_number;
20
+ protected $XMLUser;
21
+ protected $XMLPass;
22
+ protected $XMLObj;
23
+ protected $order;
24
+ protected $queue;
25
+ protected $logFile = 'ingrammicro.log';
26
+
27
+ public function _construct()
28
+ {
29
+ $this->account_number = Mage::getStoreConfig('osf_ingrammicro/general/account_number');
30
+ $this->XMLUser = Mage::getStoreConfig('osf_ingrammicro/xmllogin/xml_username');
31
+ $this->XMLPass = Mage::helper('core')->decrypt(Mage::getStoreConfig('osf_ingrammicro/xmllogin/xml_password'));
32
+ $this->test_mode = Mage::getStoreConfig('osf_ingrammicro/general/test_mode');
33
+
34
+ parent::_construct();
35
+ }
36
+
37
+ private function _addChildElement( &$parent, $key, $value ) {
38
+
39
+ if (is_array($value)) {
40
+ $node = $parent->addChild($key);
41
+ foreach ($value as $k=>$v) {
42
+ $this->_addChildElement($node, $k, $v);
43
+ }
44
+ }else{
45
+ $node = $parent->addChild($key, $value);
46
+ }
47
+
48
+ return $parent;
49
+ }
50
+
51
+ /**
52
+ * Builds an Ingram Micro XML object from order data.
53
+ * This is used later on to the API
54
+ * @param array $orderData
55
+ * @return XML Object
56
+ */
57
+ public function buildXMLData($orderData) {
58
+
59
+ $this->XMLObj = new SimpleXMLElement('<?xml version="1.0" encoding="ISO-8859-1"?><OrderRequest></OrderRequest>');
60
+
61
+ //version
62
+ $this->XMLObj->addChild('Version','2.0');
63
+
64
+ // credentials
65
+ $credentials = $this->XMLObj->addChild('TransactionHeader');
66
+ foreach ($orderData['credentials'] as $key => $value) {
67
+ $credentials->addChild($key, $value);
68
+ }
69
+
70
+ // Order Header Information
71
+ $orderHeaderInformation = $this->XMLObj->addChild('OrderHeaderInformation');
72
+ foreach ($orderData['orderHeaderInformation'] as $lvl1_key => $lvl1_val) {
73
+ $this->_addChildElement( $orderHeaderInformation, $lvl1_key, $lvl1_val);
74
+ }
75
+
76
+ // Order Request Items
77
+ $items = $this->XMLObj->addChild('OrderLineInformation');
78
+
79
+ $commentText = '';
80
+ if ($this->test_mode) {
81
+ $commentText = '///TEST PO DO NOT SHIP';
82
+ }
83
+
84
+ foreach ($orderData['items'] as $item) {
85
+ $itemNode = $items->addChild('ProductLine');
86
+ $itemNode->addChild('SKU', $item['sku']);
87
+ $itemNode->addChild('Quantity', $item['qty']);
88
+ $itemNode = $items->addChild('CommentLine');
89
+ $itemNode->addChild('CommentText', $commentText );
90
+ }
91
+
92
+ $this->XMLObj->addChild('ShowDetail','0');
93
+
94
+ return $this->XMLObj->asXML();
95
+ }
96
+
97
+ /**
98
+ * Build the order array so it can be sent to xml process
99
+ *
100
+ * @param object
101
+ * @param array
102
+ * @return string
103
+ */
104
+ public function buildOrderArray($order, $shipment)
105
+ {
106
+ $this->order = $order;
107
+ $data = array(
108
+ 'orderHeaderInformation' => $this->buildOrderHeaderInformation($shipment),
109
+ 'items' => $this->buildItems($shipment),
110
+ 'credentials' => $this->buldCredentials($shipment)
111
+ );
112
+
113
+ return $this->buildXMLData($data);
114
+ }
115
+
116
+ /**
117
+ * Building the credentials element for the request XML object
118
+ * @param object $shipment Magento Shipment Object
119
+ * @return array
120
+ */
121
+ public function buldCredentials ( $shipment ) {
122
+
123
+ $shipment = array(
124
+ 'SenderID' => 'Website',
125
+ 'ReceiverID' => 'Website',
126
+ 'CountryCode' => 'FT',
127
+ 'LoginID' => $this->XMLUser,
128
+ 'Password' => $this->XMLPass,
129
+ 'TransactionID' => $shipment->getIncrementId()
130
+
131
+ );
132
+
133
+ return $shipment;
134
+ }
135
+
136
+ public function buildOrderHeaderInformation($shipment) {
137
+ $shippingAddress = $shipment->getShippingAddress();
138
+ $streetArr = $shippingAddress->getStreet();
139
+
140
+ $customerPO = $shipment->getIncrementId();
141
+ $endUserPO = $shipment->getIncrementId();
142
+ $autoRelease = '';
143
+ if ($this->test_mode) {
144
+ $autoRelease = 'H';
145
+ }
146
+
147
+ $shipment = array(
148
+ 'BillToSuffix' => '',
149
+ 'AddressingInformation' => array(
150
+ 'CustomerPO' => $customerPO,
151
+ 'ShipToAttention' => '',
152
+ 'EndUserPO' => $endUserPO,
153
+ 'ShipTo' => array(
154
+ 'Address' => array(
155
+ 'ShipToAddress1' => $shippingAddress->getFirstname() . ' ' . $shippingAddress->getLastname(),
156
+ 'ShipToAddress2' => $streetArr[0],
157
+ 'ShipToAddress3' => (count($streetArr) > 1)? $streetArr[1] : null,
158
+ 'ShipToCity' => $shippingAddress->getCity(),
159
+ 'ShipToProvince' => $shippingAddress->getRegion(),
160
+ 'ShipToPostalCode' => trim($shippingAddress->getPostcode()),
161
+ )
162
+ ),
163
+ ),
164
+ 'ProcessingOptions' => array(
165
+ 'CarrierCode' => '',
166
+ 'AutoRelease' => $autoRelease,
167
+ 'ThirdPartyFreightAccount' => '',
168
+ 'KillOrderAfterLineError' => '',
169
+ 'ShipmentOptions' => array(
170
+ 'BackOrderFlag' => 'Y',
171
+ 'SplitShipmentFlag' => 'n',
172
+ 'SplitLine' => 'N',
173
+ 'ShipFromBranches' => ''
174
+ )
175
+ ),
176
+ 'DynamicMessage' => array(
177
+ 'MessageLines' => ''
178
+ )
179
+ );
180
+ return $shipment;
181
+ }
182
+
183
+ /**
184
+ * Map order items to array for xml
185
+ *
186
+ * @param array
187
+ * @return array
188
+ */
189
+ public function buildItems($shipment)
190
+ {
191
+ $items = array();
192
+ foreach ($shipment->getAllItems() as $itemObj) {
193
+ $item = array();
194
+ $item['sku'] = $itemObj->getSku();
195
+ $item['customerPartNumber'] = $itemObj->getSku();
196
+ $item['name'] = $itemObj->getName();
197
+ $item['price'] = $itemObj->getPrice();
198
+ $item['qty'] = (int)$itemObj->getQty();
199
+ $item['comment1'] = null;
200
+ $item['comment2'] = null;
201
+ $item['shipfrom'] = null;
202
+ $item['specialPriceRef'] = null;
203
+ $items[] = $item;
204
+ }
205
+
206
+ return $items;
207
+ }
208
+
209
+ /**
210
+ * Process the order response
211
+ *
212
+ * @param $xmlResponse
213
+ * @param $order
214
+ * @param $xmlData
215
+ * @internal param $object
216
+ * @return array
217
+ */
218
+ public function processOrderResponse($xmlResponse, $order, $xmlData)
219
+ {
220
+ $this->order = $order;
221
+ $xmlResponseObject = simplexml_load_string($xmlResponse);
222
+
223
+ if($xmlResponseObject === false){
224
+ Mage::log('Ingrammicro: The response was not a xml string', null, $this->logFile);
225
+ return;
226
+ }
227
+
228
+ if(is_null($xmlResponseObject->TransactionHeader)){
229
+ Mage::log('Ingrammicro: The response xml does not contain the TransactionHeader node', null, $this->logFile);
230
+ return;
231
+ }
232
+
233
+ // Check if there is any error in the response
234
+ if (
235
+ isset($xmlResponseObject->TransactionHeader->ErrorStatus['ErrorNumber']) &&
236
+ !empty($xmlResponseObject->TransactionHeader->ErrorStatus['ErrorNumber'])
237
+ ){
238
+ Mage::log('Order Response Error: ' .
239
+ $xmlResponseObject->TransactionHeader->ErrorStatus['ErrorNumber'] .
240
+ 'Order Respose Error Details' .
241
+ $xmlResponseObject->TransactionHeader->ErrorStatus, null, $this->logFile);
242
+ $this->errorResponse($xmlResponseObject, $xmlData);
243
+ return;
244
+ }
245
+
246
+ $branchOrderNumber = $xmlResponseObject->BranchOrderNumber;
247
+ if( isset($branchOrderNumber) ){
248
+ $this->acceptedResponse($xmlResponseObject);
249
+ } else {
250
+ $this->rejectedResponse($xmlResponseObject);
251
+ }
252
+
253
+ return;
254
+ }
255
+
256
+ /**
257
+ * The accepted response handle
258
+ *
259
+ * @param object
260
+ * @return null
261
+ */
262
+ public function acceptedResponse($xmlObject)
263
+ {
264
+ $transactionHeader = $xmlObject->TransactionHeader;
265
+ $orderNumbers = $xmlObject->OrderInfo->OrderNumbers;
266
+
267
+
268
+ Mage::log('Ingrammicro PO accepted: '. $transactionHeader->TransactionID, null, $this->logFile);
269
+
270
+ $shipment = Mage::getModel('sales/order_shipment')->loadByIncrementId($transactionHeader->TransactionID);
271
+ $shipment->setShipmentStatus(Osf_IngramMicro_Model_Source::SHIPMENT_STATUS_READY);
272
+
273
+ //Set shipping comment
274
+ $comment = 'Ingram Micro Branch Order Number: ' . $orderNumbers->BranchOrderNumber;
275
+ if (!$comment instanceof Mage_Sales_Model_Order_Shipment_Comment) {
276
+ $comment = Mage::getModel('sales/order_shipment_comment')
277
+ ->setComment($comment);
278
+ }
279
+
280
+ $shipment->addComment($comment);
281
+
282
+ try{
283
+ $shipment->save();
284
+ $shipment->getCommentsCollection()->save();
285
+ } catch (Exception $e){
286
+ Mage::log('Ingrammicro: Response Error: '. $e->getMessage(), null, $this->logFile);
287
+ }
288
+
289
+ return;
290
+ }
291
+
292
+ /**
293
+ * The rejected response handle
294
+ *
295
+ * @param object
296
+ * @return null
297
+ */
298
+ public function rejectedResponse($xmlObject)
299
+ {
300
+ # TO DO some checks about the response
301
+ $transactionHeader = $xmlObject->TransactionHeader;
302
+ Mage::log('Ingrammicro PO rejected', null, $this->logFile);
303
+ // load the shipment and cancel it
304
+ $shipment = Mage::getModel('sales/order_shipment')->loadByIncrementId($transactionHeader->TransactionID);
305
+ $shipment->setShipmentStatus(Osf_IngramMicro_Model_Source::SHIPMENT_STATUS_CANCELED);
306
+ try{
307
+ $shipment->save();
308
+ } catch (Exception $e){
309
+ Mage::log('Ingrammicro: Response Error: ' . $e->getMessage(), null, $this->logFile);
310
+ }
311
+
312
+ return;
313
+ }
314
+
315
+ /**
316
+ * The error response handle
317
+ *
318
+ * @param $orderResponse
319
+ * @param $xmlData
320
+ * @throws Exception
321
+ * @internal param $object
322
+ * @return null
323
+ */
324
+ public function errorResponse($orderResponse,$xmlData)
325
+ {
326
+ # for unknown error need to clarify what to do
327
+ # to do check if xmlObject is an object
328
+ $xmlObject = simplexml_load_string($xmlData);
329
+ $queue = Mage::getModel('osf_ingrammicro/queue')->loadByField('order_id', $this->order->getId());
330
+ if($queue->getId() === null){
331
+ $queue = Mage::getModel('osf_ingrammicro/queue');
332
+ $queue->setOrderId($this->order->getId());
333
+ $queue->setPoId( $xmlObject->OrderRequest->TransactionHeader->TransactionID );
334
+ $queue->setOrderXml(trim($xmlData));
335
+ $queue->setRetry(0);
336
+ $queue->setPrevError($orderResponse->ErrorMessage . ' : ' . $orderResponse->ErrorDetail);
337
+ } else {
338
+ $retry = $queue->getRetry();
339
+ if($retry >= 5){
340
+ $queue->delete();
341
+ }
342
+ $retry++;
343
+ $queue->setRetry($retry);
344
+ }
345
+
346
+ $queue->save();
347
+
348
+ # Put order on hold
349
+ $this->order->setStatus(Mage_Sales_Model_Order::STATE_HOLDED);
350
+
351
+ try{
352
+ $this->order->save();
353
+ // $shipment->save();
354
+ } catch (Exception $e){
355
+ Mage::log('Ingrammicro: Response Error: unknown error order on hold'.$e->getMessage(), null, $this->logFile);
356
+ }
357
+
358
+ return;
359
+ }
360
+
361
+ /**
362
+ * The retry the orders gave known errors
363
+ *
364
+ * @param null
365
+ * @return null
366
+ */
367
+ public function retry()
368
+ {
369
+ $jobs = Mage::getModel('osf_ingrammicro/queue')->getCollection();
370
+ foreach ($jobs as $job) {
371
+ $order = Mage::getModel('sales/order')->load($job->getOrderId());
372
+ $order->setStatus(Mage_Sales_Model_Order::STATE_PROCESSING);
373
+ $order->save();
374
+ $xmlResponse = Mage::helper('osf_ingrammicro/connect')->sendXMLRequest($job->getOrderXml());
375
+ $response = Mage::getModel('osf_ingrammicro/order')->processOrderResponse($xmlResponse, $order, $job->getOrderXml());
376
+ }
377
+
378
+ return;
379
+ }
380
+ }
381
+
382
+ /* Filename: Order.php */
383
+ /* Location: app/code/local/Osf/Ingrammicro/Model/Order.php */
app/code/community/Osf/IngramMicro/Model/Queue.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
13
+ * @package Osf_Ingrammicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_Ingrammicro_Model_Queue extends Mage_Core_Model_Abstract
18
+ {
19
+ protected function _construct()
20
+ {
21
+ $this->_init('osf_ingrammicro/queue');
22
+ }
23
+
24
+ public function loadByField($field, $fieldValue)
25
+ {
26
+ $collection = $this->getCollection()
27
+ ->addFieldToFilter($field, $fieldValue);
28
+ return $collection->getFirstItem();
29
+ }
30
+ }
31
+
32
+ /* Filename: Queue.php */
33
+ /* Location: app/code/local/Osf/Ingrammicro/Model/Queue.php */
app/code/community/Osf/IngramMicro/Model/Resource/Catmap.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * Catmap Model
11
+ *
12
+ * @method ()
13
+ *
14
+ * @category Osf
15
+ * @package Osf_IngramMicro
16
+ * @author Osf Global Services
17
+ */
18
+
19
+ class Osf_IngramMicro_Model_Resource_Catmap extends Mage_Core_Model_Resource_Db_Abstract
20
+ {
21
+ protected function _construct()
22
+ {
23
+ $this->_init('osf_ingrammicro/catmap', 'catmap_id');
24
+ }
25
+ }
26
+
27
+ /* Filename: Catmap.php */
28
+ /* Location: app/code/local/Osf/IngramMicro/Model/Resource/Catmap.php */
app/code/community/Osf/IngramMicro/Model/Resource/Catmap/Collection.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * Catmap Model
11
+ *
12
+ * @method ()
13
+ *
14
+ * @category Osf
15
+ * @package Osf_IngraMicro
16
+ * @author Osf Global Services
17
+ */
18
+
19
+ class Osf_IngramMicro_Model_Resource_Catmap_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
20
+ {
21
+ protected function _construct()
22
+ {
23
+ $this->_init('osf_ingrammicro/catmap');
24
+ }
25
+ }
26
+
27
+ /* Filename: Collection.php */
28
+ /* Location: app/code/local/Osf/IngramMicro/Model/Resource/Catmap/Collection.php */
app/code/community/Osf/IngramMicro/Model/Resource/Queue.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @method ()
13
+ *
14
+ * @category Osf
15
+ * @package Osf_IngramMicro
16
+ * @author Osf Global Services
17
+ */
18
+
19
+ class Osf_IngramMicro_Model_Resource_Queue extends Mage_Core_Model_Resource_Db_Abstract
20
+ {
21
+ protected function _construct()
22
+ {
23
+ $this->_init('osf_ingrammicro/queue', 'queue_id');
24
+ }
25
+ }
26
+
27
+ /* Filename: Queue.php */
28
+ /* Location: app/code/local/Osf/IngramMicro/Model/Queue.php */
app/code/community/Osf/IngramMicro/Model/Resource/Queue/Collection.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * @method ()
13
+ *
14
+ * @category Osf
15
+ * @package Osf_IngraMicro
16
+ * @author Osf Global Services
17
+ */
18
+
19
+ class Osf_IngramMicro_Model_Resource_Queue_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
20
+ {
21
+ protected function _construct()
22
+ {
23
+ $this->_init('osf_ingrammicro/queue');
24
+ }
25
+ }
26
+
27
+ /* Filename: Queue.php */
28
+ /* Location: app/code/local/Osf/IngramMicro/Model/Queue.php */
app/code/community/Osf/IngramMicro/Model/Shipnotice.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * Shipnotice Model
11
+ *
12
+ * @category Osf
13
+ * @package Osf_Ingrammicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Model_Shipnotice extends Mage_Core_Model_Abstract {
18
+
19
+
20
+
21
+ public $logFile;
22
+
23
+ public function _construct()
24
+ {
25
+ $this->logFile = 'ingrammicro.log';
26
+ }
27
+
28
+
29
+ /**
30
+ * Process the ship notice
31
+ *
32
+ * @param object
33
+ * @return bool
34
+ */
35
+ public function processShipNotice($xmlData)
36
+ {
37
+ $despatchAdviceHeader = $xmlData->DespatchAdviceHeader;
38
+ if(empty($despatchAdviceHeader)){
39
+ return 'receiveShipNotice_missing_despatchAdviceHeader';
40
+ }
41
+
42
+ $purchaseOrderId = $despatchAdviceHeader->CustomerPO;
43
+ if(empty($purchaseOrderId)){
44
+ return 'receiveShipNotice_missing_CustomerPO';
45
+ }
46
+
47
+ $shipment = Mage::getModel('sales/order_shipment')->loadByIncrementId($purchaseOrderId);
48
+ if(is_null($shipment->getId())){
49
+ return 'receiveShipNotice_shipment_not_found';
50
+ }
51
+
52
+ $order = $shipment->getOrder();
53
+ if(is_null($order->getId())){
54
+ return 'receiveShipNotice_order_not_found';
55
+ }
56
+
57
+ // create the invoice
58
+ $invoice = $this->createInvoice($xmlData, $order, $shipment);
59
+
60
+ if($invoice === false){
61
+ $error = true;
62
+ }
63
+
64
+ // set the shipment status to shipped
65
+ $shipment->setShipmentStatus(Osf_IngramMicro_Model_Source::SHIPMENT_STATUS_SHIPPED);
66
+
67
+ // send shippment email
68
+ $shipment->sendEmail(true, '');
69
+ $shipment->setEmailSent(true);
70
+
71
+ try {
72
+ $shipment->save();
73
+ } catch(Exception $e) {
74
+ Mage::log('Ingrammicro: Ship Notice Error:'. $e->getMessage(), null, $this->logFile);
75
+ return 'receiveShipNotice_other_errors';
76
+ }
77
+
78
+ return true;
79
+ }
80
+
81
+ /**
82
+ * Create and invoice based on the ship noitice
83
+ *
84
+ * @param object
85
+ * @return bool
86
+ */
87
+ public function createInvoice($xmlData, $order, $shipment)
88
+ {
89
+ // init and build an array with the received sku from the ship notices items
90
+ $shippedSku = array();
91
+
92
+ // check if the order was processed before
93
+ if($shipment->getShipmentStatus() == Osf_IngramMicro_Model_Source::SHIPMENT_STATUS_SHIPPED){
94
+ return false;
95
+ }
96
+
97
+ $trackingNumbers = array();
98
+ foreach ($xmlData->LineHeader->LineItem as $item) {
99
+ $sku = (string)$item->Product['SKU'];
100
+ $qty = (int)$item->Product['DespatchQuantity'];
101
+ $shippedSku[$sku] = $qty;
102
+ $trackingNumbers[] = $item->PackageHeader->IdentificationHeader->Identification['TrackingNumber'];
103
+ }
104
+
105
+
106
+
107
+ $uniqueTrackNumbers = array_unique($trackingNumbers);
108
+
109
+ // add tracking number
110
+ foreach ($uniqueTrackNumbers as $trackNumber) {
111
+ $trackNo = Mage::getModel('sales/order_shipment_track')
112
+ ->setNumber($trackNumber)
113
+ ->setCarrierCode($xmlData->ConsignmentHeader->CarrierCode)
114
+ ->setTitle($xmlData->ConsignmentHeader->CarrierName);
115
+ $shipment->addTrack($trackNo);
116
+ }
117
+
118
+ // get the order items
119
+ $items = $order->getItemsCollection();
120
+ $orderItemsCount = count($items);
121
+ $asnItemsCount = count($xmlData->LineHeader->LineItem);
122
+
123
+ // init and build an array for the invoice with the qtys and ids
124
+ $qtys = array();
125
+ foreach($items as $orderItem){
126
+
127
+ if(in_array($orderItem->getSku(), array_keys($shippedSku))){
128
+ $qtys[$orderItem->getId()] = $shippedSku[$orderItem->getSku()];
129
+ }
130
+ }
131
+
132
+ // add the invoiced qtys
133
+ $invoice = Mage::getModel('sales/service_order', $order)->prepareInvoice($qtys);
134
+ $amount = $invoice->getGrandTotal();
135
+ $invoice->register()->pay();
136
+ $invoice->getOrder()->setIsInProcess(true);
137
+
138
+ // adding a commnet to the order
139
+ $history = $invoice->getOrder()->addStatusHistoryComment(
140
+ 'Amount of $' . $amount . ' captured automatically.', false
141
+ );
142
+ $history->setIsCustomerNotified(true);
143
+ // save order
144
+ $order->save();
145
+
146
+ // make the invoice transaction
147
+ Mage::getModel('core/resource_transaction')
148
+ ->addObject($invoice)
149
+ ->addObject($invoice->getOrder())
150
+ ->save();
151
+
152
+ // save invoice and send the email to the costumer
153
+ try {
154
+ $invoice->save();
155
+ // send email to customer
156
+ $invoice->sendEmail(true, '');
157
+ if($orderItemsCount == $asnItemsCount){
158
+ $order->setStatus(Mage_Sales_Model_Order::STATE_COMPLETE);
159
+ $order->save();
160
+ }
161
+ } catch (Exception $e){
162
+ Mage::log('Ingram Micro: Invoice could not be created:'. $e->getMessage(), null, $this->logFile);
163
+ return false;
164
+ }
165
+
166
+ return true;
167
+ }
168
+
169
+ }
170
+
171
+ /* Filename: Shipnotice.php */
172
+ /* Location: app/code/local/Osf/Ingrammicro/Model/Shipnotice.php */
app/code/community/Osf/IngramMicro/Model/Source.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
13
+ * @package Osf_Ingrammicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_Model_Source extends Varien_Object {
18
+
19
+ const SHIPMENT_STATUS_PENDING = 0;
20
+ const SHIPMENT_STATUS_READY = 1;
21
+ const SHIPMENT_STATUS_SHIPPED = 2;
22
+ const SHIPMENT_STATUS_CANCELED = 3;
23
+ const SHIPMENT_STATUS_ONHOLD = 4;
24
+
25
+ public $statuses;
26
+
27
+ public function _construct() {
28
+
29
+ $this->statuses = array(
30
+ "Pending",
31
+ "Ready",
32
+ "Shipped",
33
+ "Canceled",
34
+ "Onhold"
35
+ );
36
+ }
37
+
38
+ public function getStatusText($status) {
39
+
40
+ return $this->statuses[$status];
41
+ }
42
+ }
43
+
44
+ /* Filename: Source.php */
45
+ /* Location: app/code/community/Osf/Ingrammicro/Model/Source.php */
app/code/community/Osf/IngramMicro/controllers/CallbackController.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ * Callback controller
11
+ *
12
+ * @category Osf
13
+ * @package Osf_IngramMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_CallbackController extends Mage_Core_Controller_Front_Action
18
+ {
19
+ public function apiAction()
20
+ {
21
+ if(!$this->getRequest()->isPost()){
22
+ $this->_redirect('/');
23
+ return;
24
+ }
25
+
26
+ $xmlString = $this->getRequest()->getRawBody();
27
+ Mage::log('IngramMicro: Received ASN: '.$xmlString, null,'ingrammicro.log');
28
+ Mage::getModel('osf_ingrammicro/callback')->receiveShipNotice($xmlString);
29
+ return;
30
+ }
31
+ }
32
+
33
+ /* Filename: CallbackController.php */
34
+ /* Location: ../app/code/local/Osf/IngramMicro/controllers/CallbackController.php */
app/code/community/Osf/IngramMicro/controllers/IndexController.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
13
+ * @package Osf_IngramMicro
14
+ * @author Osf Global Services
15
+ */
16
+
17
+ class Osf_IngramMicro_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
+ echo "Nothing here";
28
+ return;
29
+ }
30
+
31
+ /**
32
+ * The Action that starts the import of products from IngramMicro
33
+ *
34
+ * @return null
35
+ *
36
+ */
37
+ public function startImportAction()
38
+ {
39
+ return Mage::getModel('osf_ingrammicro/import')->processData();
40
+ }
41
+
42
+ /**
43
+ * Retry to send purchase ordersCreate Shipment
44
+ *
45
+ * @return null
46
+ *
47
+ */
48
+ public function retryCronAction()
49
+ {
50
+ return Mage::getModel('osf_ingrammicro/order')->retry();
51
+ }
52
+
53
+ /**
54
+ * Check for ship notices to finalize the purchase order
55
+ *
56
+ * @return null
57
+ *
58
+ */
59
+ public function shipNoticeCronAction()
60
+ {
61
+ return Mage::getModel('osf_ingrammicro/shipping_notice')->processNotices();
62
+ }
63
+ }
64
+
65
+ /* Filename: IndexController.php */
66
+ /* Location: ../app/code/community/Osf/IngramMicro/controllers/IndexController.php */
app/code/community/Osf/IngramMicro/etc/config.xml ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_IngramMicro>
14
+ <version>1.0.0</version>
15
+ </Osf_IngramMicro>
16
+ </modules>
17
+ <global>
18
+ <blocks>
19
+ <osf_ingrammicro>
20
+ <class>Osf_IngramMicro_Block</class>
21
+ </osf_ingrammicro>
22
+ <adminhtml>
23
+ <rewrite>
24
+ <sales_order_view_tab_shipments>Osf_IngramMicro_Block_Adminhtml_Shipments</sales_order_view_tab_shipments>
25
+ <sales_shipment_grid>Osf_IngramMicro_Block_Adminhtml_Shipments_Grid</sales_shipment_grid>
26
+ </rewrite>
27
+ </adminhtml>
28
+ </blocks>
29
+ <models>
30
+ <osf_ingrammicro>
31
+ <class>Osf_IngramMicro_Model</class>
32
+ <resourceModel>osf_ingrammicro_resource</resourceModel>syn
33
+ </osf_ingrammicro>
34
+ <osf_ingrammicro_resource>
35
+ <class>Osf_IngramMicro_Model_Resource</class>
36
+ <entities>
37
+ <queue>
38
+ <table>osf_ingrammicro_queue</table>
39
+ </queue>
40
+ <catmap>
41
+ <table>osf_ingrammicro_catmap</table>
42
+ </catmap>
43
+ </entities>
44
+ </osf_ingrammicro_resource>
45
+ </models>
46
+ <resources>
47
+ <osf_ingrammicro_setup>
48
+ <setup>
49
+ <module>Osf_IngramMicro</module>
50
+ <!-- default, optional -->
51
+ <class>Mage_Core_Model_Resource_Setup</class>
52
+ </setup>
53
+ </osf_ingrammicro_setup>
54
+ </resources>
55
+ <helpers>
56
+ <osf_ingrammicro>
57
+ <class>Osf_IngramMicro_Helper</class>
58
+ </osf_ingrammicro>
59
+ </helpers>
60
+ <events>
61
+ <sales_order_save_after>
62
+ <observers>
63
+ <osf_sales_order_save_after>
64
+ <type>singleton</type>
65
+ <class>Osf_IngramMicro_Model_Observer</class>
66
+ <method>processOrder</method>
67
+ </osf_sales_order_save_after>
68
+ </observers>
69
+ </sales_order_save_after>
70
+ </events>
71
+ </global>
72
+ <frontend>
73
+ <routers>
74
+ <osf_ingrammicro>
75
+ <use>standard</use>
76
+ <args>
77
+ <module>Osf_IngramMicro</module>
78
+ <frontName>ingrammicro</frontName>
79
+ </args>
80
+ </osf_ingrammicro>
81
+ </routers>
82
+ </frontend>
83
+ <adminhtml>
84
+ <acl>
85
+ <resources>
86
+ <all>
87
+ <title>Allow Everything</title>
88
+ </all>
89
+ <admin>
90
+ <children>
91
+ <osf_ingrammicro module="osf_ingrammicro">
92
+ <title>Ingram Micro Configuration</title>
93
+ <sort_order>11</sort_order>
94
+ </osf_ingrammicro>
95
+ <system>
96
+ <children>
97
+ <config>
98
+ <children>
99
+ <osf_ingrammicro translate="title">
100
+ <title>Ingram Micro Configuration</title>
101
+ <sort_order>80</sort_order>
102
+ </osf_ingrammicro>
103
+ </children>
104
+ </config>
105
+ </children>
106
+ </system>
107
+ </children>
108
+ </admin>
109
+ </resources>
110
+ </acl>
111
+ </adminhtml>
112
+ <crontab>
113
+ <jobs>
114
+ <osf_ingrammicro_process>
115
+ <schedule>
116
+ <cron_expr>0 1 * * *</cron_expr>
117
+ </schedule>
118
+ <run>
119
+ <model>osf_ingrammicro/import::processData</model>
120
+ </run>
121
+ </osf_ingrammicro_process>
122
+ <osf_ingrammicro_retry>
123
+ <schedule>
124
+ <cron_expr>0 * * * *</cron_expr>
125
+ </schedule>
126
+ <run>
127
+ <model>osf_ingrammicro/order::retry</model>
128
+ </run>
129
+ </osf_ingrammicro_retry>
130
+ </jobs>
131
+ </crontab>
132
+ </config>
app/code/community/Osf/IngramMicro/etc/system.xml ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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="osf_ingrammicro">
14
+ <label>Osf</label>
15
+ <sort_order>100</sort_order>
16
+ </osftab>
17
+ </tabs>
18
+ <sections>
19
+ <osf_ingrammicro module="osf_ingrammicro">
20
+ <label>Ingram Micro Configuration</label>
21
+ <tab>osftab</tab>
22
+ <frontend_type>text</frontend_type>
23
+ <sort_order>351</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
+ <general translate="label">
29
+ <label>Ingram Micro General 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
+ <account_number translate="label">
37
+ <label>Ingram Micro 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 Ingram Micro ]]></comment>
44
+ </account_number>
45
+ <test_mode translate="label comment">
46
+ <label>Test mode</label>
47
+ <frontend_type>select</frontend_type>
48
+ <source_model>adminhtml/system_config_source_yesno</source_model>
49
+ <sort_order>14</sort_order>
50
+ <show_in_default>1</show_in_default>
51
+ <show_in_website>1</show_in_website>
52
+ <show_in_store>1</show_in_store>
53
+ <comment><![CDATA[Turning this one will set H (HOLD) as Autorelease value, "TEST PO ONLY - DO NOT SHIP as CustomerPO]]></comment>
54
+ </test_mode>
55
+ <category_mapping_import translate="label">
56
+ <label>Category Mapping Import</label>
57
+ <frontend_type>import</frontend_type>
58
+ <backend_model>osf_ingrammicro/adminhtml_system_config_backend_catmap</backend_model>
59
+ <sort_order>40</sort_order>
60
+ <show_in_default>1</show_in_default>
61
+ <show_in_website>1</show_in_website>
62
+ <show_in_store>0</show_in_store>
63
+ <comment><![CDATA[Upload the file with the category map. For more infomation about this configuration go to user guide section "Configuration"]]></comment>
64
+ </category_mapping_import>
65
+ </fields>
66
+ </general>
67
+ <ftplogin translate="label">
68
+ <label>Ftp Configuration</label>
69
+ <frontend_type>text</frontend_type>
70
+ <sort_order>11</sort_order>
71
+ <show_in_default>1</show_in_default>
72
+ <show_in_website>1</show_in_website>
73
+ <show_in_store>1</show_in_store>
74
+ <fields>
75
+ <ftp_host translate="label">
76
+ <label>Ftp Server</label>
77
+ <frontend_type>text</frontend_type>
78
+ <sort_order>11</sort_order>
79
+ <show_in_default>1</show_in_default>
80
+ <show_in_website>1</show_in_website>
81
+ <show_in_store>1</show_in_store>
82
+ <comment><![CDATA[<b>Ex: </b> example.com OR ftp.example.com ]]></comment>
83
+ </ftp_host>
84
+ <ftp_user translate="label">
85
+ <label>Ftp Username</label>
86
+ <frontend_type>text</frontend_type>
87
+ <sort_order>12</sort_order>
88
+ <show_in_default>1</show_in_default>
89
+ <show_in_website>1</show_in_website>
90
+ <show_in_store>1</show_in_store>
91
+ <comment><![CDATA[The FTP username used to connect to ingram micro ftp]]></comment>
92
+ </ftp_user>
93
+ <ftp_password translate="label">
94
+ <label>Ftp Password</label>
95
+ <frontend_type>password</frontend_type>
96
+ <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
97
+ <sort_order>13</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
+ <comment><![CDATA[The FTP password used to connect to ingram micro ftp]]></comment>
102
+ </ftp_password>
103
+ <ftp_remote_dir translate="label">
104
+ <label>Ftp Remote Directory</label>
105
+ <frontend_type>text</frontend_type>
106
+ <sort_order>14</sort_order>
107
+ <show_in_default>1</show_in_default>
108
+ <show_in_website>1</show_in_website>
109
+ <show_in_store>1</show_in_store>
110
+ <comment><![CDATA[Remote directory]]></comment>
111
+ </ftp_remote_dir>
112
+ <ftp_prod_file translate="label">
113
+ <label>Ftp Products Filename</label>
114
+ <frontend_type>text</frontend_type>
115
+ <sort_order>15</sort_order>
116
+ <show_in_default>1</show_in_default>
117
+ <show_in_website>1</show_in_website>
118
+ <show_in_store>1</show_in_store>
119
+ <comment><![CDATA[The products file from the server]]></comment>
120
+ </ftp_prod_file>
121
+ <filtered_skus_file translate="label tooltip comment">
122
+ <label>Filtered SKUs File</label>
123
+ <comment>Upload the file with the selected skus</comment>
124
+ <tooltip>Upload the file with the selected skus</tooltip>
125
+ <frontend_type>file</frontend_type>
126
+ <backend_model>adminhtml/system_config_backend_file</backend_model>
127
+ <upload_dir config="system/filesystem/media" scope_info="1">admin-config-uploads</upload_dir>
128
+ <base_url type="media" scope_info="1">admin-config-uploads</base_url>
129
+ <sort_order>16</sort_order>
130
+ <show_in_default>1</show_in_default>
131
+ <show_in_website>1</show_in_website>
132
+ <show_in_store>1</show_in_store>
133
+ </filtered_skus_file>
134
+ <manual_import translate="comment">
135
+ <label></label>
136
+ <comment>Upload the file with the selected skus</comment>
137
+ <frontend_type>button</frontend_type>
138
+ <frontend_model>osf_ingrammicro/adminhtml_system_config_frontend_button</frontend_model>
139
+ <sort_order>17</sort_order>
140
+ <show_in_default>1</show_in_default>
141
+ <show_in_website>1</show_in_website>
142
+ <show_in_store>1</show_in_store>
143
+ </manual_import>
144
+ </fields>
145
+ </ftplogin>
146
+ <xmllogin translate="label">
147
+ <label>XML Configuration</label>
148
+ <frontend_type>text</frontend_type>
149
+ <sort_order>12</sort_order>
150
+ <show_in_default>1</show_in_default>
151
+ <show_in_website>1</show_in_website>
152
+ <show_in_store>1</show_in_store>
153
+ <fields>
154
+ <xml_endpoint translate="label">
155
+ <label>XML Endpoint</label>
156
+ <frontend_type>text</frontend_type>
157
+ <sort_order>11</sort_order>
158
+ <show_in_default>1</show_in_default>
159
+ <show_in_website>1</show_in_website>
160
+ <show_in_store>1</show_in_store>
161
+ <comment><![CDATA[<b>Ex: </b> example.com ]]></comment>
162
+ </xml_endpoint>
163
+ <xml_username translate="label">
164
+ <label>Xml Username</label>
165
+ <frontend_type>text</frontend_type>
166
+ <sort_order>12</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
+ <comment><![CDATA[The username used to connect to ingram micro endpoint]]></comment>
171
+ </xml_username>
172
+ <xml_password translate="label">
173
+ <label>Xml Password</label>
174
+ <frontend_type>password</frontend_type>
175
+ <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
176
+ <sort_order>13</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
+ <comment><![CDATA[The password used to connect to ingram micro endpoint]]></comment>
181
+ </xml_password>
182
+
183
+ </fields>
184
+ </xmllogin>
185
+ </groups>
186
+ </osf_ingrammicro>
187
+ </sections>
188
+ </config>
app/code/community/Osf/IngramMicro/lib/magmi/conf/Ingrammicro/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/IngramMicro/lib/magmi/conf/Ingrammicro/plugins.conf ADDED
@@ -0,0 +1,2 @@
 
 
1
+ [PLUGINS_ITEMPROCESSORS]
2
+ classes = "CategoryImporter"
app/code/community/Osf/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/lib/magmi/integration/inc/pumpfactory.ini ADDED
@@ -0,0 +1,2 @@
 
 
1
+ [DATAPUMPS]
2
+ productimport=productimport_datapump::Magmi_ProductImport_Datapump
app/code/community/Osf/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/lib/magmi/plugins/extra/obsolete.txt ADDED
@@ -0,0 +1 @@
 
1
+ itemprocessors/itemindexer/priceindexer.php
app/code/community/Osf/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/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/IngramMicro/lib/magmi/state/magmistate ADDED
@@ -0,0 +1 @@
 
1
+ idle
app/code/community/Osf/IngramMicro/lib/magmi/state/progress.txt ADDED
File without changes
app/code/community/Osf/IngramMicro/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/IngramMicro/lib/magmi/state/trace.txt ADDED
File without changes
app/code/community/Osf/IngramMicro/sql/osf_ingrammicro_setup/install-0.0.1.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ $installer->startSetup();
5
+
6
+ //Create queue table to store ingrammicro failed request for retry.
7
+ $installer->getConnection()->dropTable($installer->getTable('osf_ingrammicro_queue'));
8
+ $table = $installer->getConnection()
9
+ ->newTable($installer->getTable('osf_ingrammicro_queue'))
10
+ ->addColumn('queue_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11, array(
11
+ 'identity' => true,
12
+ 'unsigned' => true,
13
+ 'nullable' => false,
14
+ 'primary' => true,
15
+ )
16
+ )
17
+ ->addColumn('order_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11, array(
18
+ 'nullable' => false
19
+ )
20
+ )
21
+ ->addColumn('po_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11)
22
+ ->addColumn('order_xml', Varien_Db_Ddl_Table::TYPE_TEXT, null, array(
23
+ 'nullable' => false
24
+ )
25
+ )
26
+ ->addColumn('retry', Varien_Db_Ddl_Table::TYPE_INTEGER, 2)
27
+ ->addColumn('prev_error', Varien_Db_Ddl_Table::TYPE_TEXT);
28
+
29
+ $installer->getConnection()->createTable($table);
30
+
31
+ //Create a table to store category mapping
32
+ $installer->getConnection()->dropTable($installer->getTable('osf_ingrammicro_catmap'));
33
+ $table = $installer->getConnection()
34
+ ->newTable($installer->getTable('osf_ingrammicro_catmap'))
35
+ ->addColumn('catmap_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11, array(
36
+ 'identity' => true,
37
+ 'unsigned' => true,
38
+ 'nullable' => false,
39
+ 'primary' => true,
40
+ )
41
+ )
42
+ ->addColumn('ingrammicro_category', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
43
+ 'nullable' => false
44
+ )
45
+ )
46
+ ->addColumn('magento_category', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
47
+ 'nullable' => false
48
+ )
49
+ );
50
+ $installer->getConnection()->createTable($table);
51
+
52
+ //Add new product attributes
53
+ $attr_installer = Mage::getResourceModel('catalog/setup', 'catalog_setup');
54
+
55
+ //Create new Vendor attribute
56
+ $attr_installer->addAttribute('catalog_product', 'osf_product_vendor', array(
57
+ 'type' => 'varchar',
58
+ 'backend' => '',
59
+ 'frontend' => '',
60
+ 'label' => 'Vendor',
61
+ 'input' => 'text',
62
+ 'class' => '',
63
+ 'source' => 'catalog/product_attribute_source_layout',
64
+ 'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
65
+ 'visible' => true,
66
+ 'required' => false,
67
+ 'user_defined' => false,
68
+ 'default' => '',
69
+ 'searchable' => false,
70
+ 'filterable' => false,
71
+ 'comparable' => false,
72
+ 'visible_on_front' => false,
73
+ 'unique' => false,
74
+ 'group' => 'General'
75
+ ));
76
+
77
+ //Create new Cost attribute
78
+ $attr_installer->addAttribute('catalog_product', 'osf_product_cost', array(
79
+ 'type' => 'varchar',
80
+ 'backend' => '',
81
+ 'frontend' => '',
82
+ 'label' => 'Cost',
83
+ 'input' => 'text',
84
+ 'class' => '',
85
+ 'source' => 'catalog/product_attribute_source_layout',
86
+ 'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
87
+ 'visible' => true,
88
+ 'required' => false,
89
+ 'user_defined' => false,
90
+ 'default' => '',
91
+ 'searchable' => false,
92
+ 'filterable' => false,
93
+ 'comparable' => false,
94
+ 'visible_on_front' => false,
95
+ 'unique' => false,
96
+ 'group' => 'General'
97
+ ));
98
+
99
+ $installer->endSetup();
app/design/adminhtml/default/default/template/osf/ingrammicro/system/config/button.phtml ADDED
@@ -0,0 +1 @@
 
1
+ <?php echo $this->getButtonHtml() ?>
app/etc/modules/Osf_IngramMicro.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <config>
3
+ <modules>
4
+ <Osf_IngramMicro>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </Osf_IngramMicro>
8
+ </modules>
9
+ </config>
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>OSF_Integrator_for_IngramMicro</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 IngramMicro integration from product import to ship notification</summary>
10
+ <description>The integrator will create a bridge between Magento and IngramMicro 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-11</date>
14
+ <time>10:54:22</time>
15
+ <contents><target name="magecommunity"><dir name="Osf"><dir name="IngramMicro"><dir name="Block"><dir name="Adminhtml"><dir name="Shipments"><file name="Grid.php" hash="90a9883b4d34f62bbb7edbbb0313d821"/><dir name="Renderer"><file name="Status.php" hash="00289fc379b98cf0150acb74a959255b"/></dir></dir><file name="Shipments.php" hash="fab8aa9b392feb2bb3178da702cbc93c"/><dir name="System"><dir name="Config"><dir name="Frontend"><file name="Button.php" hash="abca04bd5608478088e7a9d54f4c611f"/></dir></dir></dir></dir></dir><dir name="Helper"><file name="Connect.php" hash="24b9438a70c2e8b0d7736424e51307d7"/><file name="Data.php" hash="49c68986223288df1eeb8af523c24aea"/><file name="Error.php" hash="ee81b6d42780591e04cc27e07b3e664a"/><file name="Ftp.php" hash="7882046b675ec5a75440cdc450580089"/></dir><file name="LICENSE.txt" hash="279935a07ba225ee0b2199830ddb61b7"/><dir name="Model"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Backend"><file name="Catmap.php" hash="8b7bfcb87b1a4139d241a9bb14db3670"/></dir></dir></dir></dir><file name="Callback.php" hash="40897daa43c8d000ac1a582a50b650a9"/><file name="Catmap.php" hash="6996c5f0af7b4db5be2c123d824723f7"/><file name="Import.php" hash="23eb198851054c3c7e6891a5f0b40ca0"/><file name="Observer.php" hash="6ec33db9c0b050e120468ea7444ac111"/><file name="Order.php" hash="dba7fcec3f420b61ef950c5c8b92afad"/><file name="Queue.php" hash="bd1fd5db3c6bc836b8634ec040913a24"/><dir name="Resource"><dir name="Catmap"><file name="Collection.php" hash="318422d75b1c659351ce16bd0cf6635b"/></dir><file name="Catmap.php" hash="b18a9fd87fa5d1729fbf593d57d8836e"/><dir name="Queue"><file name="Collection.php" hash="aa0609c53a3d1f4c415182013541993a"/></dir><file name="Queue.php" hash="08baae9474276f1def3901f9b813dc76"/></dir><file name="Shipnotice.php" hash="bde45481a136696577fb6c473d5a0b10"/><file name="Source.php" hash="480e6caa1eebbc17ad21e8bf0566c5fd"/></dir><dir name="controllers"><file name="CallbackController.php" hash="e9527fcd4875df939c83f074010f6fca"/><file name="IndexController.php" hash="a3af0459e577e33ffbd4d4da433cc634"/></dir><dir name="etc"><file name="config.xml" hash="90e9640e35e3a2a2d0e904ac1c8e3efb"/><file name="system.xml" hash="e3e9b7d813b92733a01d016713a37f61"/></dir><dir name="lib"><dir name="magmi"><dir name="conf"><dir name="Ingrammicro"><file name="CategoryImporter.conf" hash="5c3e33ed9b67624672126b92882eaa73"/><file name="plugins.conf" hash="e6519fcee4d2728395d033df9ba84125"/></dir></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 name="sql"><dir name="osf_ingrammicro_setup"><file name="install-0.0.1.php" hash="bd79cff7869306d62cc7ff363ba84752"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="osf"><dir name="ingrammicro"><dir name="system"><dir name="config"><file name="button.phtml" hash="fb5ab610c535e7a268195d806abbe0bd"/></dir></dir></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Osf_IngramMicro.xml" hash="0901151738c906b724968bf413112a88"/></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.4.0</min><max>5.6.14</max></php></required></dependencies>
18
+ </package>