| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- <?php
- class ControllerExtensionModuleReverb extends Controller {
- private $error = [];
- // -------------------------------------------------------------------------
- // Main settings page
- // -------------------------------------------------------------------------
- public function index() {
- $this->load->language('extension/module/reverb');
- $this->load->model('extension/module/reverb');
- $this->load->model('setting/setting');
- $this->load->model('catalog/category');
- $this->document->setTitle($this->language->get('heading_title'));
- if ($this->request->server['REQUEST_METHOD'] === 'POST' && $this->validate()) {
- $this->model_setting_setting->editSetting('module_reverb', $this->request->post);
- // Save category mappings separately (they come as a sub-array)
- if (isset($this->request->post['module_reverb_category_mappings'])) {
- $this->model_extension_module_reverb->saveCategoryMappings(
- $this->request->post['module_reverb_category_mappings']
- );
- }
- $this->session->data['success'] = $this->language->get('text_success');
- $this->response->redirect($this->url->link('extension/module/reverb', 'user_token=' . $this->session->data['user_token'], true));
- }
- $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_order_import', 'button_import_orders',
- 'entry_order_stores', 'help_order_stores', 'text_select_all', 'text_unselect_all',
- 'entry_default_qty', 'help_default_qty',
- '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', 'button_clear_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_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',
- 'module_reverb_status',
- 'module_reverb_sync_direction',
- 'module_reverb_sync_categories',
- 'module_reverb_shipping_domestic',
- 'module_reverb_shipping_international',
- 'module_reverb_order_stores',
- 'module_reverb_default_qty',
- ];
- foreach ($fields as $key) {
- $data[$key] = $this->request->post[$key] ?? $this->config->get($key);
- }
- // Defaults
- $data['module_reverb_sync_direction'] = $data['module_reverb_sync_direction'] ?? 'push';
- $data['module_reverb_sync_categories'] = $data['module_reverb_sync_categories'] ?? [];
- $data['module_reverb_shipping_domestic'] = $data['module_reverb_shipping_domestic'] ?? '0.00';
- $data['module_reverb_shipping_international'] = $data['module_reverb_shipping_international'] ?? '0.00';
- $data['module_reverb_order_stores'] = $data['module_reverb_order_stores'] ?? [0];
- $data['module_reverb_default_qty'] = $data['module_reverb_default_qty'] ?? 1;
- // Stores list for the checkbox UI
- $this->load->model('setting/store');
- $stores = $this->model_setting_store->getStores();
- array_unshift($stores, ['store_id' => 0, 'name' => 'Default']);
- $data['stores'] = $stores;
- // All OC categories for the multi-select
- $data['categories'] = $this->getCategoryTree();
- // Category mappings for the mapping tab
- $data['category_mappings'] = $this->model_extension_module_reverb->getCategoryMappings();
- $data['reverb_categories'] = $this->model_extension_module_reverb->getReverbCategories();
- $data['reverb_categories_grouped'] = $this->model_extension_module_reverb->getReverbCategoriesGrouped();
- $data['module_reverb_category_mappings'] = $data['category_mappings'];
- // Sync log
- $data['sync_log'] = $this->model_extension_module_reverb->getSyncLog(200);
- // Alerts
- $data['error_warning'] = $this->error['warning'] ?? '';
- $data['success'] = $this->session->data['success'] ?? '';
- unset($this->session->data['success']);
- $data['action'] = $this->url->link('extension/module/reverb', 'user_token=' . $this->session->data['user_token'], true);
- $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
- $data['sync_url'] = $this->url->link('extension/module/reverb/sync', 'user_token=' . $this->session->data['user_token'], true);
- $data['import_url'] = $this->url->link('extension/module/reverb/importOrders', 'user_token=' . $this->session->data['user_token'], true);
- $data['clear_log_url'] = $this->url->link('extension/module/reverb/clearLog', 'user_token=' . $this->session->data['user_token'], true);
- $data['categories_url'] = $this->url->link('extension/module/reverb/reverbCategories', 'user_token=' . $this->session->data['user_token'], true);
- $data['header'] = $this->load->controller('common/header');
- $data['column_left'] = $this->load->controller('common/column_left');
- $data['footer'] = $this->load->controller('common/footer');
- $this->response->setOutput($this->load->view('extension/module/reverb', $data));
- }
- // -------------------------------------------------------------------------
- // Install / Uninstall
- // -------------------------------------------------------------------------
- public function install() {
- $this->load->model('extension/module/reverb');
- $this->model_extension_module_reverb->install();
- // Register events for order pulling (admin side)
- $this->load->model('setting/event');
- $this->model_setting_event->addEvent(
- 'reverb',
- 'admin/model/catalog/product/editProduct/after',
- 'extension/module/reverb/eventProductSave'
- );
- $this->model_setting_event->addEvent(
- 'reverb',
- 'admin/model/catalog/product/addProduct/after',
- 'extension/module/reverb/eventProductAddSave'
- );
- }
- public function uninstall() {
- $this->load->model('extension/module/reverb');
- $this->model_extension_module_reverb->uninstall();
- $this->load->model('setting/event');
- $this->model_setting_event->deleteEventByCode('reverb');
- }
- // -------------------------------------------------------------------------
- // Event handlers (called by OC event system on product save)
- // -------------------------------------------------------------------------
- public function eventProductSave(&$route, &$args, &$output) {
- $product_id = (int)$args[0];
- $this->saveProductReverb($product_id);
- }
- public function eventProductAddSave(&$route, &$args, &$output) {
- // $output holds the new product_id for addProduct
- $product_id = (int)$output;
- if ($product_id) {
- $this->saveProductReverb($product_id);
- }
- }
- private function saveProductReverb($product_id) {
- if (!isset($this->request->post['reverb_sync_enabled'])) {
- return;
- }
- $this->load->model('extension/module/reverb');
- $this->model_extension_module_reverb->saveProductMap($product_id, [
- 'sync_enabled' => (int)(bool)$this->request->post['reverb_sync_enabled'],
- 'condition_uuid' => $this->request->post['reverb_condition_uuid'] ?? '',
- 'reverb_category_uuid' => $this->request->post['reverb_category_uuid'] ?? '',
- 'handmade' => (int)(bool)($this->request->post['reverb_handmade'] ?? 0),
- 'upc_does_not_apply' => (int)(bool)($this->request->post['reverb_upc_does_not_apply'] ?? 1),
- ]);
- }
- // -------------------------------------------------------------------------
- // Manual sync (AJAX)
- // -------------------------------------------------------------------------
- public function sync() {
- $this->load->language('extension/module/reverb');
- $this->load->model('extension/module/reverb');
- // Set JSON header first so any uncaught error still returns parseable JSON.
- $this->response->addHeader('Content-Type: application/json');
- // Discard any PHP notices/warnings buffered before this point so they
- // cannot corrupt the JSON response body.
- if (ob_get_level()) {
- ob_clean();
- }
- try {
- if (!$this->user->hasPermission('modify', 'extension/module/reverb')) {
- $this->response->setOutput(json_encode(['success' => false, 'error' => $this->language->get('error_permission')]));
- return;
- }
- $settings = $this->buildSettings();
- if (empty($settings['api_token'])) {
- $this->response->setOutput(json_encode(['success' => false, 'error' => $this->language->get('error_api_token')]));
- return;
- }
- $allowed_categories = $this->config->get('module_reverb_sync_categories');
- if (empty($allowed_categories)) {
- $this->response->setOutput(json_encode(['success' => false, 'error' => 'No sync categories configured. Select at least one category in the Settings tab.']));
- return;
- }
- $products = $this->model_extension_module_reverb->getSyncEnabledProducts((array)$allowed_categories);
- $pushed = 0;
- $errors = 0;
- foreach ($products as $product) {
- try {
- $this->model_extension_module_reverb->syncProductToReverb($product, $product, $settings);
- $pushed++;
- $this->safeLog($product['product_id'], 'push', 'success', 'Synced: ' . $product['name']);
- } catch (Exception $e) {
- $errors++;
- $this->safeLog($product['product_id'], 'push', 'error', $e->getMessage());
- }
- }
- $this->response->setOutput(json_encode([
- 'success' => true,
- 'message' => sprintf($this->language->get('text_sync_complete'), $pushed, $errors),
- ]));
- } catch (Exception $e) {
- $this->response->setOutput(json_encode(['success' => false, 'error' => $e->getMessage()]));
- }
- }
- // -------------------------------------------------------------------------
- // Order import (AJAX)
- // -------------------------------------------------------------------------
- public function importOrders() {
- $this->load->language('extension/module/reverb');
- $this->load->model('extension/module/reverb');
- $this->response->addHeader('Content-Type: application/json');
- if (ob_get_level()) {
- ob_clean();
- }
- try {
- if (!$this->user->hasPermission('modify', 'extension/module/reverb')) {
- $this->response->setOutput(json_encode(['success' => false, 'error' => $this->language->get('error_permission')]));
- return;
- }
- $settings = $this->buildSettings();
- if (empty($settings['api_token'])) {
- $this->response->setOutput(json_encode(['success' => false, 'error' => $this->language->get('error_api_token')]));
- return;
- }
- $result = $this->model_extension_module_reverb->importOrdersFromReverb($settings);
- $this->response->setOutput(json_encode([
- 'success' => true,
- 'message' => sprintf($this->language->get('text_orders_imported'), $result['imported'], $result['skipped']),
- ]));
- } catch (Exception $e) {
- $this->response->setOutput(json_encode(['success' => false, 'error' => $e->getMessage()]));
- }
- }
- private function safeLog($product_id, $direction, $status, $message) {
- try {
- $this->model_extension_module_reverb->log($product_id, $direction, $status, $message);
- } catch (Exception $e) {
- // Log table missing or unavailable — ignore silently.
- }
- }
- // -------------------------------------------------------------------------
- // Per-product Reverb tab (AJAX — loaded into product edit page)
- // URL: extension/module/reverb/productTab?product_id=N
- // -------------------------------------------------------------------------
- public function productTab() {
- if (!$this->user->isLogged()) {
- $this->response->setOutput('');
- return;
- }
- $this->load->language('extension/module/reverb');
- $this->load->model('extension/module/reverb');
- $product_id = isset($this->request->get['product_id']) ? (int)$this->request->get['product_id'] : 0;
- $reverb_row = $this->model_extension_module_reverb->getProductMap($product_id);
- $data = [
- 'product_id' => $product_id,
- 'reverb_sync_enabled' => $reverb_row ? (int)$reverb_row['sync_enabled'] : 0,
- 'reverb_condition_uuid' => $reverb_row ? $reverb_row['condition_uuid'] : '',
- 'reverb_category_uuid' => $reverb_row ? $reverb_row['reverb_category_uuid'] : '',
- 'reverb_listing_id' => $reverb_row ? $reverb_row['reverb_listing_id'] : '',
- 'reverb_handmade' => $reverb_row ? (int)$reverb_row['handmade'] : 0,
- 'reverb_upc_does_not_apply' => $reverb_row ? (int)$reverb_row['upc_does_not_apply'] : 1,
- 'reverb_conditions' => $this->model_extension_module_reverb->getListingConditions(),
- 'reverb_categories_grouped' => $this->model_extension_module_reverb->getReverbCategoriesGrouped(),
- 'clear_listing_url' => $this->url->link('extension/module/reverb/clearListingId', 'user_token=' . $this->session->data['user_token'], true),
- ];
- $this->response->setOutput($this->load->view('extension/module/reverb_product', $data));
- }
- // -------------------------------------------------------------------------
- // Clear Reverb listing ID from a product (AJAX)
- // -------------------------------------------------------------------------
- public function clearListingId() {
- $this->load->language('extension/module/reverb');
- $this->load->model('extension/module/reverb');
- $this->response->addHeader('Content-Type: application/json');
- if (!$this->user->hasPermission('modify', 'extension/module/reverb')) {
- $this->response->setOutput(json_encode(['success' => false, 'error' => $this->language->get('error_permission')]));
- return;
- }
- $product_id = (int)($this->request->get['product_id'] ?? 0);
- if (!$product_id) {
- $this->response->setOutput(json_encode(['success' => false, 'error' => 'Invalid product ID.']));
- return;
- }
- $this->model_extension_module_reverb->clearListingId($product_id);
- $this->response->setOutput(json_encode(['success' => true]));
- }
- // -------------------------------------------------------------------------
- // Clear sync log (AJAX)
- // -------------------------------------------------------------------------
- public function clearLog() {
- $this->load->language('extension/module/reverb');
- $this->load->model('extension/module/reverb');
- $this->response->addHeader('Content-Type: application/json');
- if (!$this->user->hasPermission('modify', 'extension/module/reverb')) {
- $this->response->setOutput(json_encode(['success' => false, 'error' => $this->language->get('error_permission')]));
- return;
- }
- $this->model_extension_module_reverb->clearSyncLog();
- $this->response->setOutput(json_encode(['success' => true, 'message' => $this->language->get('text_log_cleared')]));
- }
- // -------------------------------------------------------------------------
- // Reverb categories (AJAX — for category mapping dropdowns)
- // -------------------------------------------------------------------------
- public function reverbCategories() {
- $this->load->model('extension/module/reverb');
- $categories = $this->model_extension_module_reverb->getReverbCategories();
- $this->response->addHeader('Content-Type: application/json');
- $this->response->setOutput(json_encode(['categories' => $categories]));
- }
- // -------------------------------------------------------------------------
- // Per-product tab (loaded inline via OCMOD template include)
- // The data is passed through the OCMOD PHP patch, not via a separate request.
- // -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
- // Helpers
- // -------------------------------------------------------------------------
- private function validate() {
- if (!$this->user->hasPermission('modify', 'extension/module/reverb')) {
- $this->error['warning'] = $this->language->get('error_permission');
- }
- $token = $this->request->post['module_reverb_api_token'] ?? '';
- if (empty(trim($token))) {
- $this->error['warning'] = $this->language->get('error_api_token');
- }
- return empty($this->error);
- }
- private function buildBreadcrumbs() {
- return [
- 'heading_title' => $this->language->get('heading_title'),
- 'breadcrumbs' => [
- [
- 'text' => $this->language->get('text_home'),
- 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true),
- ],
- [
- 'text' => $this->language->get('text_extension'),
- 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true),
- ],
- [
- 'text' => $this->language->get('heading_title'),
- 'href' => $this->url->link('extension/module/reverb', 'user_token=' . $this->session->data['user_token'], true),
- ],
- ],
- ];
- }
- private function buildSettings() {
- // config_url is often empty in OC3; fall back to the catalog constants from admin/config.php
- $store_url = $this->config->get('config_url');
- if (empty($store_url)) {
- if (defined('HTTPS_CATALOG')) {
- $store_url = HTTPS_CATALOG;
- } elseif (defined('HTTP_CATALOG')) {
- $store_url = HTTP_CATALOG;
- }
- }
- $order_stores = $this->config->get('module_reverb_order_stores');
- if (!is_array($order_stores) || empty($order_stores)) {
- $order_stores = [0];
- }
- return [
- 'api_token' => $this->config->get('module_reverb_api_token'),
- 'sync_direction' => $this->config->get('module_reverb_sync_direction') ?? 'push',
- 'shipping_domestic' => $this->config->get('module_reverb_shipping_domestic') ?? '0',
- 'shipping_international' => $this->config->get('module_reverb_shipping_international') ?? '0',
- 'currency' => $this->config->get('config_currency') ?? 'AUD',
- 'store_url' => $store_url ?? '',
- 'order_stores' => $order_stores,
- 'default_qty' => max(1, (int)($this->config->get('module_reverb_default_qty') ?? 1)),
- ];
- }
- private function getCategoryTree() {
- $language_id = (int)$this->config->get('config_language_id');
- $query = $this->db->query("
- SELECT c.category_id, c.parent_id, cd.name
- FROM `" . DB_PREFIX . "category` c
- LEFT JOIN `" . DB_PREFIX . "category_description` cd
- ON cd.category_id = c.category_id AND cd.language_id = '" . $language_id . "'
- WHERE c.status = 1
- ORDER BY c.parent_id ASC, cd.name ASC
- ");
- $all = $query->rows;
- $byPid = [];
- foreach ($all as $row) {
- $byPid[(int)$row['parent_id']][] = $row;
- }
- $result = [];
- $stack = [[0, '']];
- while ($stack) {
- [$pid, $indent] = array_pop($stack);
- if (empty($byPid[$pid])) {
- continue;
- }
- foreach (array_reverse($byPid[$pid]) as $cat) {
- $result[] = [
- 'category_id' => $cat['category_id'],
- 'name' => $indent . $cat['name'],
- ];
- array_push($stack, [(int)$cat['category_id'], $indent . ' ']);
- }
- }
- return $result;
- }
- }
|