Explorar o código

Fix 500 error on module edit and correct several integration bugs

- Replace Twig 1.x-incompatible ?? operator with |default() filter in both
  twig templates (OC3 ships Twig ~1.x; ?? was added in Twig 2.0)
- Pass all required language strings explicitly into $data in the admin
  controller (OC's Twig renderer only receives what is in $data)
- Replace $this->load->library() with require_once(DIR_SYSTEM . ...) for
  standalone PHP classes; OC's library loader tries to instantiate the class
  with $registry which is incorrect for non-OC-aware classes
- Fix reverb_product.twig category dropdown to iterate reverb_categories
  (full list with names) instead of the raw OC→Reverb mapping array
- Add reverb_categories loading to install.xml OCMOD PHP patch so the
  product tab category dropdown is populated on the product edit page
- Rename duplicate text_success language key to text_log_success to prevent
  the save-success message being overwritten by the log status label

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Benjamin Harris hai 2 semanas
pai
achega
b53fee8fa9

+ 108 - 0
install.xml

@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<modification>
+    <name>Reverb Integration</name>
+    <code>reverb</code>
+    <version>1.0.0</version>
+    <author>Reverb OpenCart</author>
+    <link>https://reverb.com/au/page/integrations</link>
+
+    <!--
+        ========================================================================
+        FILE 1: admin/controller/catalog/product.php
+        Inject Reverb data loading before the view is rendered,
+        and Reverb data saving after addProduct / editProduct calls.
+        ========================================================================
+    -->
+    <file path="admin/controller/catalog/product.php">
+
+        <!--
+            Load per-product Reverb data into $data[] so the Twig template
+            can render the Reverb tab. Targets the final setOutput() call in
+            the shared getForm() method, which is used by both add() and edit().
+        -->
+        <operation>
+            <search><![CDATA[$this->response->setOutput($this->load->view('catalog/product', $data));]]></search>
+            <add position="before"><![CDATA[
+                // Reverb Integration: load per-product Reverb data
+                $this->load->model('extension/module/reverb');
+                $reverb_product_id = isset($this->request->get['product_id']) ? (int)$this->request->get['product_id'] : 0;
+                $reverb_row = $this->model_extension_module_reverb->getProductMap($reverb_product_id);
+                $data['reverb_sync_enabled']         = $reverb_row ? (int)$reverb_row['sync_enabled']         : 0;
+                $data['reverb_condition_uuid']        = $reverb_row ? $reverb_row['condition_uuid']            : '';
+                $data['reverb_category_uuid']         = $reverb_row ? $reverb_row['reverb_category_uuid']      : '';
+                $data['reverb_listing_id']            = $reverb_row ? $reverb_row['reverb_listing_id']         : '';
+                $data['reverb_conditions']            = $this->model_extension_module_reverb->getListingConditions();
+                $data['reverb_oc_category_mappings']  = $this->model_extension_module_reverb->getCategoryMappings();
+                $data['reverb_categories']            = $this->model_extension_module_reverb->getReverbCategories();
+            ]]></add>
+        </operation>
+
+        <!--
+            Save Reverb data after editProduct() is called.
+        -->
+        <operation>
+            <search><![CDATA[$this->model_catalog_product->editProduct($this->request->get['product_id'], $this->request->post);]]></search>
+            <add position="after"><![CDATA[
+                // Reverb Integration: save per-product Reverb data on edit
+                if (isset($this->request->post['reverb_sync_enabled']) || isset($this->request->post['reverb_condition_uuid'])) {
+                    $this->load->model('extension/module/reverb');
+                    $this->model_extension_module_reverb->saveProductMap((int)$this->request->get['product_id'], [
+                        'sync_enabled'         => isset($this->request->post['reverb_sync_enabled']) ? 1 : 0,
+                        'condition_uuid'       => $this->request->post['reverb_condition_uuid']  ?? '',
+                        'reverb_category_uuid' => $this->request->post['reverb_category_uuid']  ?? '',
+                    ]);
+                }
+            ]]></add>
+        </operation>
+
+        <!--
+            Save Reverb data after addProduct() is called.
+        -->
+        <operation>
+            <search><![CDATA[$product_id = $this->model_catalog_product->addProduct($this->request->post);]]></search>
+            <add position="after"><![CDATA[
+                // Reverb Integration: save per-product Reverb data on add
+                if (isset($this->request->post['reverb_sync_enabled']) || isset($this->request->post['reverb_condition_uuid'])) {
+                    $this->load->model('extension/module/reverb');
+                    $this->model_extension_module_reverb->saveProductMap((int)$product_id, [
+                        'sync_enabled'         => isset($this->request->post['reverb_sync_enabled']) ? 1 : 0,
+                        'condition_uuid'       => $this->request->post['reverb_condition_uuid']  ?? '',
+                        'reverb_category_uuid' => $this->request->post['reverb_category_uuid']  ?? '',
+                    ]);
+                }
+            ]]></add>
+        </operation>
+
+    </file>
+
+    <!--
+        ========================================================================
+        FILE 2: admin/view/template/catalog/product.twig
+        Add a "Reverb" tab to the product edit page.
+        ========================================================================
+    -->
+    <file path="admin/view/template/catalog/product.twig">
+
+        <!-- Add tab navigation item after the Design tab -->
+        <operation>
+            <search><![CDATA[<li><a href="#tab-design" data-toggle="tab">{{ tab_design }}</a></li>]]></search>
+            <add position="after"><![CDATA[
+                <li><a href="#tab-reverb" data-toggle="tab">Reverb</a></li>
+            ]]></add>
+        </operation>
+
+        <!--
+            Inject the Reverb tab pane. We include the dedicated template which
+            has access to all $data variables (conditions, category mappings, etc.)
+            loaded above by the PHP patch.
+        -->
+        <operation>
+            <search><![CDATA[<div class="tab-pane" id="tab-design">]]></search>
+            <add position="before"><![CDATA[
+                {% include 'extension/module/reverb_product.twig' %}
+            ]]></add>
+        </operation>
+
+    </file>
+
+</modification>

+ 27 - 0
upload/admin/controller/extension/module/reverb.php

@@ -29,6 +29,33 @@ class ControllerExtensionModuleReverb extends Controller {
 
         $data = $this->buildBreadcrumbs();
 
+        // Language strings required by the view
+        $lang_keys = [
+            'tab_settings', 'tab_categories', 'tab_log',
+            'text_api_settings', 'text_shipping_settings', 'text_sync_settings', 'text_manual_sync',
+            'entry_api_token', 'help_api_token',
+            'entry_status', 'entry_sync_direction',
+            'text_sync_push', 'text_sync_both',
+            'entry_shipping_domestic', 'help_shipping_domestic',
+            'entry_shipping_international', 'help_shipping_international',
+            'help_sync_categories', 'button_sync_now',
+            'text_category_mapping_help', 'text_no_categories',
+            'column_oc_category', 'column_reverb_category',
+            'column_date', 'column_product', 'column_direction', 'column_status', 'column_message',
+            'text_push', 'text_pull', 'text_error', 'text_no_log',
+            'text_success', 'text_log_success',
+            'error_warning', 'error_api_token',
+        ];
+        foreach ($lang_keys as $key) {
+            $data[$key] = $this->language->get($key);
+        }
+        // Global OC strings
+        $data['text_edit']     = $this->language->get('text_edit');
+        $data['text_enabled']  = $this->language->get('text_enabled');
+        $data['text_disabled'] = $this->language->get('text_disabled');
+        $data['button_save']   = $this->language->get('button_save');
+        $data['button_cancel'] = $this->language->get('button_cancel');
+
         // Pull saved settings into $data
         $fields = [
             'module_reverb_api_token',

+ 1 - 1
upload/admin/language/en-gb/extension/module/reverb.php

@@ -43,7 +43,7 @@ $_['column_status']              = 'Status';
 $_['column_message']             = 'Message';
 $_['text_push']                  = 'OC → Reverb';
 $_['text_pull']                  = 'Reverb → OC';
-$_['text_success']               = 'Success';
+$_['text_log_success']           = 'Success';
 $_['text_error']                 = 'Error';
 $_['text_no_log']                = 'No sync activity yet.';
 

+ 3 - 3
upload/admin/model/extension/module/reverb.php

@@ -259,8 +259,8 @@ class ModelExtensionModuleReverb extends Model {
      * @return string            The Reverb listing ID.
      */
     public function syncProductToReverb(array $product, array $reverb_data, array $settings) {
-        $this->load->library('reverb/ReverbApi');
-        $this->load->library('reverb/ProductMapper');
+        require_once(DIR_SYSTEM . 'library/reverb/ReverbApi.php');
+        require_once(DIR_SYSTEM . 'library/reverb/ProductMapper.php');
 
         $api     = $this->getApi();
         $payload = ProductMapper::toReverb($product, $reverb_data, $settings);
@@ -302,7 +302,7 @@ class ModelExtensionModuleReverb extends Model {
         if (empty($token)) {
             throw new RuntimeException('Reverb API token is not configured.');
         }
-        $this->load->library('reverb/ReverbApi');
+        require_once(DIR_SYSTEM . 'library/reverb/ReverbApi.php');
         return new ReverbApi($token);
     }
 }

+ 2 - 2
upload/admin/view/template/extension/module/reverb.twig

@@ -174,7 +174,7 @@
                                and module_reverb_category_mappings[category.category_id] == rc.uuid %}
                                selected
                           {% endif %}>
-                          {{ rc.full_name ?? rc.name }}
+                          {{ rc.full_name|default(rc.name) }}
                         </option>
                         {% endfor %}
                       </select>
@@ -212,7 +212,7 @@
                   <td>{{ entry.direction == 'push' ? text_push : text_pull }}</td>
                   <td>
                     <span class="label label-{{ entry.status == 'success' ? 'success' : 'danger' }}">
-                      {{ entry.status == 'success' ? text_success : text_error }}
+                      {{ entry.status == 'success' ? text_log_success : text_error }}
                     </span>
                   </td>
                   <td>{{ entry.message }}</td>

+ 5 - 7
upload/admin/view/template/extension/module/reverb_product.twig

@@ -48,13 +48,11 @@
         <div class="col-sm-6">
           <select name="reverb_category_uuid" id="reverb-category-uuid" class="form-control">
             <option value="">-- Use category mapping default --</option>
-            {% for rc in reverb_oc_category_mappings %}
-              {% if rc.reverb_category_uuid %}
-              <option value="{{ rc.reverb_category_uuid }}"
-                {% if reverb_category_uuid == rc.reverb_category_uuid %}selected{% endif %}>
-                {{ rc.reverb_category_name ?? rc.reverb_category_uuid }}
-              </option>
-              {% endif %}
+            {% for rc in reverb_categories %}
+            <option value="{{ rc.uuid }}"
+              {% if reverb_category_uuid == rc.uuid %}selected{% endif %}>
+              {{ rc.full_name|default(rc.name) }}
+            </option>
             {% endfor %}
           </select>
           <p class="help-block">Leave blank to use the default mapping from the Reverb module settings.</p>

+ 4 - 4
upload/catalog/controller/extension/module/reverb.php

@@ -101,7 +101,7 @@ class ControllerExtensionModuleReverb extends Controller {
 
         $product_id = (int)$query->row['product_id'];
 
-        $this->load->library('reverb/ProductMapper');
+        require_once(DIR_SYSTEM . 'library/reverb/ProductMapper.php');
         $updates = ProductMapper::fromReverb($listing);
 
         if (!empty($updates)) {
@@ -152,7 +152,7 @@ class ControllerExtensionModuleReverb extends Controller {
             ];
         }
 
-        $this->load->library('reverb/OrderMapper');
+        require_once(DIR_SYSTEM . 'library/reverb/OrderMapper.php');
 
         $store_info = [
             'store_id'       => $this->config->get('config_store_id') ?? 0,
@@ -192,7 +192,7 @@ class ControllerExtensionModuleReverb extends Controller {
 
     private function pollListingUpdates() {
         try {
-            $this->load->library('reverb/ReverbApi');
+            require_once(DIR_SYSTEM . 'library/reverb/ReverbApi.php');
             $token = $this->config->get('module_reverb_api_token');
             if (!$token) return;
 
@@ -210,7 +210,7 @@ class ControllerExtensionModuleReverb extends Controller {
 
     private function pollOrders() {
         try {
-            $this->load->library('reverb/ReverbApi');
+            require_once(DIR_SYSTEM . 'library/reverb/ReverbApi.php');
             $token = $this->config->get('module_reverb_api_token');
             if (!$token) return;