# Modyo Publishing Workflow Guide

**Complete guide to publishing content, widgets, pages, templates, and menus in Modyo.**

---

## Table of Contents

1. [Overview](#overview)
2. [The Two-Step Process](#the-two-step-process)
3. [Publishable Element Types](#publishable-element-types)
4. [Step 1: Get Elements to Publish](#step-1-get-elements-to-publish)
5. [Step 2: Create Release](#step-2-create-release)
6. [Publishing Strategies](publishing-strategies.md)
7. [Scheduled Publishing](publishing-strategies.md#scheduled-publishing)
8. [Unpublishing](publishing-strategies.md#unpublishing)
9. [Common Workflows](publishing-strategies.md#common-workflows)
10. [Best Practices](publishing-strategies.md#best-practices)
11. [Common Mistakes](publishing-strategies.md#common-mistakes)
12. [Troubleshooting](publishing-strategies.md#troubleshooting)

---

## Overview

Modyo uses a **two-step publishing process** for all Channels resources:

```
┌─────────────────────┐
│ 1. Get Elements     │  Query: What can be published?
│    to Publish       │  Returns: List of pending elements
└──────────┬──────────┘
           │
           ▼
┌─────────────────────┐
│ 2. Create Release   │  Action: Publish selected elements
│    with Selection   │  Result: Resources go live
└─────────────────────┘
```

**Why Two Steps?**

1. **Review Before Publishing**: See exactly what will be published
2. **Selective Publishing**: Publish only what you want (e.g., widget but not page)
3. **Bundled Releases**: Publish related resources together (widget + page + template)
4. **Team Collaboration**: For sites with team review enabled, only approved elements appear

**What Gets Published?**

- **Widget Definitions** → Available for use in pages via `page-add-widgets`
- **Pages** → Live and accessible on site at their configured paths
- **Templates** → Active in rendering (layouts, snippets, CSS, JS)
- **Navigation Menus** → Visible in site navigation

---

## The Two-Step Process

### Visual Workflow

```
Draft State          Publishable          Published State
───────────          ────────────         ───────────────

┌─────────┐          ┌─────────┐          ┌─────────┐
│ Widget  │   edit   │ Widget  │  publish │ Widget  │
│  Draft  │ ──────> │ Pending  │ ──────> │  Live   │
└─────────┘          └─────────┘          └─────────┘
                           │
                           │ Query with
                           │ release-get-elements-to-publish
                           │
                           ▼
                     ┌─────────────────┐
                     │ Returns:         │
                     │ - Widget ID      │
                     │ - Widget Name    │
                     │ - Selected: false│
                     └─────────────────┘
                           │
                           │ Mark selected: true
                           │ Call release-create
                           │
                           ▼
                     ┌─────────────────┐
                     │ Release Created  │
                     │ Widget Published │
                     └─────────────────┘
```

### Complete Example

```typescript
// Step 1: Get publishable elements
const elements = await release-get-elements-to-publish({
  platformSlug: "fed-team",
  siteId: 4612
});

/* Returns:
{
  widgetDefinition: [
    { id: 123, name: "Product Card", selected: false, oid: "abc123..." }
  ],
  page: [
    { id: 456, name: "Products Page", selected: false }
  ],
  template: [
    { id: 789, name: "product_snippet", selected: false }
  ],
  menu: [
    { id: 101, name: "Main Navigation", selected: false }
  ]
}
*/

// Step 2: Select elements and publish
await release-create({
  platformSlug: "fed-team",
  siteId: 4612,
  data: {
    widgetDefinition: [
      { id: 123, selected: true }  // Publish widget
    ],
    page: [
      { id: 456, selected: true }  // Publish page
    ],
    template: [
      { id: 789, selected: true }  // Publish snippet
    ]
    // Note: menu not included - won't be published
  }
});

// Result: Widget, page, and template are now live. Menu remains draft.
```

---

## Publishable Element Types

### Widget Definitions

**What They Are**: Reusable components (HTML/CSS/JS) that can be added to pages.

**Draft vs Published**:
- **Draft**: Editable in Modyo, not visible in `widget-get-custom-widgets`
- **Published**: Available for use via `page-add-widgets`, visible in `widget-get-custom-widgets`

**Critical Note**: Widget IDs change after publishing!

```typescript
// Before publish
const widget = await widget-definition-get({ widgetId: 86885 });

// Publish
await release-create({ data: { widgetDefinition: [{ id: 86885, selected: true }] } });

// After publish - ID changed!
const customWidgets = await widget-get-custom-widgets({ siteId: 4612 });
// customWidgets[0].id === 86891 (different!)

// Use UUID for stable identification
const widgetByUuid = customWidgets.find(w => w.uuid === widget.uuid);
```

**See**: [WIDGET_WORKFLOWS.md](WIDGET_WORKFLOWS.md) for complete widget ID handling.

### Pages

**What They Are**: Website pages (widget, content, or origination).

**Draft vs Published**:
- **Draft**: Exists in database, not accessible on site
- **Published**: Live at configured path (e.g., `/products`)

**States**:
- `scheduled` - Scheduled to publish later
- `unpublished` - Published then unpublished
- `published` - Currently live

**Example**:
```typescript
// Create page (draft)
const page = await page-create({
  name: "Products",
  path: "products",  // No leading slash
  type: "default"
});

// Page exists but not accessible at /products yet

// Publish page
const elements = await release-get-elements-to-publish({ siteId: 4612 });
const pageElement = elements.page.find(p => p.id === page.id);
await release-create({
  data: { page: [{ id: pageElement.id, selected: true }] }
});

// Now accessible at https://site.com/products
```

### Templates

**What They Are**: Layouts, snippets, CSS, JS.

**Draft vs Published**:
- **Draft**: Exists but not used in rendering
- **Published**: Active in site rendering

**Types**:
- **Layouts**: Complete HTML wrappers
- **Snippets**: Reusable fragments (system + custom)
- **CSS Templates**: Global styles (CDN-served)
- **JS Templates**: Global scripts (CDN-served)

**Critical Notes**:
- System snippets (head, footer, header) cannot be unpublished
- Template IDs may change after publishing (use template-list to verify)
- CSS/JS templates have limited Liquid support (CDN-cached)

**Example**:
```typescript
// Create snippet (draft)
const snippet = await template-create({
  name: "product_card",
  type: "custom_snippet",
  body: "<div>{{ product.name }}</div>"
});

// Save body content
await template-save({
  templateId: snippet.id,
  body: "<div class='card'>{{ product.name }}</div>"
});

// Publish snippet
const elements = await release-get-elements-to-publish({ siteId: 4612 });
const templateElement = elements.template.find(t => t.id === snippet.id);
await release-create({
  data: { template: [{ id: templateElement.id, selected: true }] }
});

// Now available via {% snippet 'product_card' %}
```

### Navigation Menus

**What They Are**: Site navigation menus with hierarchical items.

**Draft vs Published**:
- **Draft**: Exists but not visible in site navigation
- **Published**: Accessible via `menus['slug']` Liquid drop

**Example**:
```typescript
// Create menu (draft)
const menu = await navigation-menu-create({
  name: "Footer Links",
  slug: "footer-links"
});

// Add items
await navigation-menu-add-item({
  menuId: menu.id,
  item: { label: "About", url: "/about", position: 0 }
});

// Publish menu
const elements = await release-get-elements-to-publish({ siteId: 4612 });
const menuElement = elements.menu.find(m => m.id === menu.id);
await release-create({
  data: { menu: [{ id: menuElement.id, selected: true }] }
});

// Now accessible in templates: {{ menus['footer-links'].items }}
```

---

## Step 1: Get Elements to Publish

### Tool: `release-get-elements-to-publish`

**Purpose**: Query what elements are ready to be published.

**Parameters**:
```typescript
{
  platformSlug: string,  // Platform identifier
  siteId: number         // Site ID
}
```

**Returns**: Object with arrays of publishable elements by type.

```typescript
{
  widgetDefinition: Array<{
    id: number,           // ID for release-create
    name: string,         // Widget name
    selected: boolean,    // Always false initially
    oid: string,          // Widget UUID (stable identifier)
    version_id: number    // Version ID
  }>,
  page: Array<{
    id: number,           // ID for release-create
    name: string,         // Page name
    path: string,         // Page path
    selected: boolean     // Always false initially
  }>,
  template: Array<{
    id: number,           // ID for release-create
    name: string,         // Template name
    type: string,         // "layout", "snippet", "css", "js"
    selected: boolean     // Always false initially
  }>,
  menu: Array<{
    id: number,           // ID for release-create
    name: string,         // Menu name
    slug: string,         // Menu slug
    selected: boolean     // Always false initially
  }>
}
```

### What Makes an Element Publishable?

**For sites WITHOUT team review**:
- Element has been modified since last publish
- Element is in draft or modified state

**For sites WITH team review enabled**:
- Element has been modified
- Element has received required approvals
- Element has passed team review

### Example: Finding Specific Elements

```typescript
const elements = await release-get-elements-to-publish({
  platformSlug: "fed-team",
  siteId: 4612
});

// Find widget by name
const myWidget = elements.widgetDefinition.find(w => w.name === "Product Card");

// Find page by path
const productsPage = elements.page.find(p => p.path === "products");

// Find template by name and type
const cssTemplate = elements.template.find(
  t => t.name === "root" && t.type === "css"
);

// Find menu by slug
const footerMenu = elements.menu.find(m => m.slug === "footer-links");
```

---

## Step 2: Create Release

### Tool: `release-create`

**Purpose**: Publish selected elements from Step 1.

**Parameters**:
```typescript
{
  platformSlug: string,           // Platform identifier
  siteId: number,                 // Site ID
  data: {                         // Element selection
    widgetDefinition?: Array<{
      id: number,                 // From step 1
      selected: true              // Must be true to publish
    }>,
    page?: Array<{
      id: number,
      selected: true
    }>,
    template?: Array<{
      id: number,
      selected: true
    }>,
    menu?: Array<{
      id: number,
      selected: true
    }>
  },
  scheduled?: boolean,            // Optional: Schedule publish
  publish_at?: string,            // ISO 8601 date (future)
  unpublish_at?: string          // ISO 8601 date (after publish_at)
}
```

**Returns**: Release object with published elements.

### Basic Publish

```typescript
await release-create({
  platformSlug: "fed-team",
  siteId: 4612,
  data: {
    widgetDefinition: [
      { id: 123, selected: true }
    ]
  }
});

// Widget is now published immediately
```

### Selective Publishing

```typescript
const elements = await release-get-elements-to-publish({ siteId: 4612 });

// Publish only specific elements
await release-create({
  platformSlug: "fed-team",
  siteId: 4612,
  data: {
    // Publish widget
    widgetDefinition: [
      { id: elements.widgetDefinition[0].id, selected: true }
    ],
    // Skip page - will remain draft
    // page: ...
    // Publish template
    template: [
      { id: elements.template[0].id, selected: true }
    ]
  }
});
```

### Bulk Publishing

```typescript
const elements = await release-get-elements-to-publish({ siteId: 4612 });

// Publish ALL elements of each type
await release-create({
  platformSlug: "fed-team",
  siteId: 4612,
  data: {
    widgetDefinition: elements.widgetDefinition.map(w => ({
      id: w.id,
      selected: true
    })),
    page: elements.page.map(p => ({
      id: p.id,
      selected: true
    })),
    template: elements.template.map(t => ({
      id: t.id,
      selected: true
    })),
    menu: elements.menu.map(m => ({
      id: m.id,
      selected: true
    }))
  }
});
```

---
