Skip to main content
This document describes the general standards for teams building modules for Zonely. Goal: prepare clean packages, apply them to the correct templates, and release them safely.

Module logic

Zonely modules are uploaded as ZIP packages. The panel validates the package and applies it to the selected domain if valid. General flow:
  1. Prepare the module as a ZIP file.
  2. The panel validates ZIP content.
  3. The module is applied to the selected domain.
  4. Template changes are activated when needed.
  5. If Public is enabled, the module appears in the community list.

Update logic

To update an existing module, the module identifier (id) must remain the same.
  • If the same id is uploaded, the panel processes it as an update.
  • Increase version for release tracking.
  • Author, description, and version data are read from the package config.

ZIP structure

At least one config file must exist in the ZIP:
config.json
module.json
manifest.json
manifest/config.json
Template, asset, and API files should follow a consistent folder structure. Avoid unrelated or unnecessary files in the package.

Template rules

  • Use .tpl format for templates.
  • Keep theme paths correct and consistent.
  • If you add new templates, place them under theme-compatible paths.
Example:
templates/Views/Themes/Norix/Pages/Store/main.campaigns.tpl
templates/Views/Themes/Norix/Navigations/campaigns.menu.tpl
admincp_templates/Drawers/main.campaigns.tpl

Route mapping

A newly added template is not auto-routed. It must be called by route. Example frontend route:
RewriteRule ^campaigns/?$ /Files/Controllers/ControllersAll.php?main=Handlers&container=MainHandlers&route=main.campaigns [L,QSA,NC]
Example admin route:
RewriteRule ^admin/campaigns/?$ /Files/Controllers/ControllersAll.php?main=Handlers&container=AdminHandlers&route=main.campaigns [L,QSA,NC]

Existing file change strategy

For stable module updates:
  1. Include the target file with the same relative path in your module.
  2. Change only the required parts.
  3. Avoid wide, unnecessary edits.
This keeps maintenance easier and diff tracking cleaner.

API rules (general)

Module API files must be defined inside the module package and used through action names. Core principles:
  • Every action must be explicitly defined.
  • Allowed methods (GET/POST, etc.) must be clear.
  • Responses should follow a consistent JSON format.
  • Errors should return clear and consistent messages.

Requirements for API calls

For a module action request:
  1. module_id and action must be sent.
  2. The action must be defined in module config.
  3. The module must be active for the current domain.
  4. Request method must match the allowed methods.
  5. Private actions require an authenticated session.

Example module layout

campaign-module.zip
|- config.json
|- preview.png
|- templates/
|  `- Views/
|     `- Themes/
|        `- Norix/
|           `- Pages/
|              `- Widgets/
|                 `- campaign.widget.tpl
|- template_changes/
|  `- Views/
|     `- Themes/
|        `- Norix/
|           `- Pages/
|              `- Main/
|                 `- main.home.tpl
`- api/
   `- campaign/
      `- list.php

Example config.json

{
  "id": "campaign-module",
  "name": "Campaign Module",
  "version": "1.0.0",
  "author": "Zonely Team",
  "description": "Adds a campaign widget to the homepage.",
  "image": "preview.png",
  "targetThemes": ["Norix"],
  "api": {
    "campaign.list": { "public": true, "methods": ["GET"] }
  }
}

Example widget file

<div class="campaign-widget">
  <h3>Campaigns</h3>
  <div id="campaign-list"></div>
</div>

Example API file

<?php
$stmt = $db->prepare("SELECT id, title FROM campaigns WHERE domain = ? ORDER BY id DESC LIMIT 5");
$stmt->execute([$moduleDomain]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

$moduleRespond([
    "status" => "success",
    "data" => $rows
], 200);
return;

Frontend API call example

const base = '/Files/Controllers/ControllersAll.php?main=Handlers&container=TemplatesModulesApiController';
const url = new URL(base, window.location.origin);
url.searchParams.set('module_id', 'campaign-module');
url.searchParams.set('action', 'campaign.list');

fetch(url.toString(), {
  method: 'GET',
  credentials: 'same-origin'
})
  .then((r) => r.json())
  .then((payload) => {
    console.log(payload.data);
  });

Error response fields

Panel errors during upload or runtime usually include:
message
errors[]
errorType

Quick checklist

Before release:
  • Config file exists.
  • Module ID and version are correct.
  • Template paths match theme structure.
  • API action definitions are complete.
  • JSON response format is valid.
  • Module is tested on the correct domain and theme.

Final note

The most stable modules are built with clean packages, explicit action definitions, and controlled template changes.