Norte Upgrade System Documentation

Norte version 0.2.0

Overview

The upgrade system has been enhanced to support both traditional AJAX-style controllers (like upgrade_1.php and upgrade_2.php) and new HTML-interactive controllers (like upgrade_3.php) that can display forms and custom content.

Key Features

  • Backward Compatible: Existing upgrade_1.php and upgrade_2.php work without modification
  • Dynamic Content: Controllers can now return HTML to update the card header and body
  • Form Support: Controllers can display forms for user input (e.g., login, confirmation)
  • Error Display: var_dump(), errors, and exceptions are automatically displayed
  • Unified Progress: Single progress bar for all upgrade steps

Architecture

Main Controller (upgrade.php)

The main controller provides a new step() method that acts as a proxy:

index.php?route=upgrade/upgrade/step&step=3&admin=admin

This method:

  • Calls the appropriate upgrade_X controller
  • Detects response type (JSON, HTML, or buffered output)
  • Formats response appropriately
  • Extracts card-header and card-body from HTML responses

Response Types

1. JSON Response (upgrade_1, upgrade_2, etc)

$json['text'] = 'Step description';
$json['success'] = 'Success message';
$json['error'] = 'Error message';
$json['next'] = $this->url->link('upgrade/upgrade_X', '', true);

$this->response->addHeader('Content-Type: application/json');
$this->response->setOutput(json_encode($json));

2. HTML Response

Option A: Return complete HTML

public function index() {
    if ($this->request->server['REQUEST_METHOD'] !== 'POST') {
        return $this->getLoginForm();
    }
    // Process POST...
}

private function getLoginForm(): string {
    $html = '<div class="card-header"><i class="fa fa-lock"></i> Login Required</div>';
    $html .= '<div class="card-body">';
    $html .= '<form>...</form>';
    $html .= '</div>';
    return $html;
}

Option B: Echo HTML (will be captured)

public function index(): void {
    echo '<div class="card-header">Custom Header</div>';
    echo '<div class="card-body">Custom Content</div>';
}

Option C: Let errors/dumps be captured

public function index(): void {
    var_dump($some_data); // Will be displayed in card-body
    // or
    throw new \Exception('Something went wrong'); // Will be formatted nicely
}

3. Mixed Response (HTML form → JSON processing)

public function index() {
    // GET request: show form
    if ($this->request->server['REQUEST_METHOD'] !== 'POST') {
        return $this->getForm();
    }

    // POST request: process and return JSON
    $json = [];

    // Validate...
    if ($valid) {
        $json['success'] = 'Processing complete';
        $json['proceed'] = true; // Continue to next step
    } else {
        $json['error']['field'] = 'Error message';
    }

    $this->response->addHeader('Content-Type: application/json');
    $this->response->setOutput(json_encode($json));
}

Frontend Behavior

upgrade.twig

The template now:

  • Has id="card-header" and id="card-body" for dynamic updates
  • Stores original HTML for restoration after forms
  • Handles both JSON and HTML responses
  • Automatically binds form submissions
  • Shows progress for JSON responses
  • Hides progress for HTML responses

JavaScript Flow

1. Button click → Start upgrade chain 2. For each step:

   a. Call upgrade/upgrade/step&step=N
   b. Check response type:
      - If json['html_update']:
        * Update card-header
        * Update card-body
        * Hide progress section
        * Bind form handlers if form present
        * Stop chain (wait for user interaction)
      - If traditional JSON:
        * Update progress bar
        * Show messages
        * Continue to next step
   c. On form submit:
      - POST to same step URL
      - Handle validation errors
      - If json['proceed'], continue chain

3. Complete → Show success message

Example Implementations

Example 1: Simple AJAX Step

(like upgrade_1, upgrade_2)

class Upgrade5 extends \Opencart\System\Engine\Controller {
    public function index(): void {
        $json = [];

        // Do work...
        $this->doSomething();

        $json['text'] = 'Step 5 complete';
        $json['next'] = $this->url->link('upgrade/upgrade_6', '', true);

        $this->response->addHeader('Content-Type: application/json');
        $this->response->setOutput(json_encode($json));
    }
}

Example 2: Form with Validation

class Upgrade6 extends \Opencart\System\Engine\Controller {
    public function index() {
        if ($this->request->server['REQUEST_METHOD'] !== 'POST') {
            return $this->getForm();
        }

        $json = [];

        if (empty($this->request->post['confirm'])) {
            $json['error']['confirm'] = 'You must confirm';
        }

        if (!$json) {
            // Process...
            $json['success'] = 'Confirmed!';
            $json['proceed'] = true;
        }

        $this->response->addHeader('Content-Type: application/json');
        $this->response->setOutput(json_encode($json));
    }

    private function getForm(): string {
        return '<div class="card-body"><form>...</form></div>';
    }
}

Example 3: Debug Output

class Upgrade7 extends \Opencart\System\Engine\Controller {
    public function index(): void {
        // This will be captured and displayed nicely
        var_dump([
            'tables' => $this->getTables(),
            'status' => 'checking'
        ]);
    }
}

Technical Notes

  • HTML detection uses strip_tags() comparison
  • Card content extraction uses regex: /<div[^>]*class=["']card-(header|body)["'][^>]*>(.*?)<\/div>/is
  • Buffered output (var_dump, errors) is wrapped in <pre> tags
  • Form submissions automatically POST to same step URL
  • Original card-body HTML is stored in jQuery data for restoration
  • Progress bar shows percentage: (current_step / total_steps) * 100

Security Considerations

  • All user inputs should be validated and escaped
  • POST data should be verified before processing
  • Database operations should use prepared statements
  • File operations should validate paths
  • Error messages should not expose sensitive information