> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Ishaq74/concordia/llms.txt
> Use this file to discover all available pages before exploring further.

# Blog System

> Full-featured blog CMS with multi-language support, organization blogs, authors, categories, and comments

# Blog System

Concordia's blog system provides a complete CMS for publishing articles with multi-language translations, author management, categorization, media galleries, and community engagement through comments.

## Overview

The blog system is built from multiple schema files:

* `blog_posts.schema.ts` - Core article structure
* `blog_authors.schema.ts` - Author profiles
* `blog_categories.schema.ts` - Content categorization
* `blog_translations.schema.ts` - Multi-language content
* `blog_comments.schema.ts` - Community discussions
* `blog_organization.schema.ts` - Organization-level blogs

## Blog Posts

From `src/database/schemas/blog_posts.schema.ts`:

### Core Fields

<ParamField path="id" type="text" required>
  Primary key identifier
</ParamField>

<ParamField path="slug" type="text" required>
  Unique URL-friendly identifier for the post
</ParamField>

<ParamField path="status" type="text" required>
  Publication status: `draft`, `pending_review`, `published`, `archived`, `rejected`
</ParamField>

<ParamField path="organizationId" type="text">
  Optional reference to organization that owns this post
</ParamField>

<ParamField path="publishedAt" type="timestamp">
  Publication date/time, set when status changes to `published`
</ParamField>

### Display Options

<ParamField path="displayInHome" type="boolean" default={false}>
  Whether to feature this post on the homepage
</ParamField>

<ParamField path="displayInBlog" type="boolean" default={true}>
  Whether to show this post in the main blog listing
</ParamField>

<ParamField path="isFeatured" type="boolean" default={false}>
  Mark post as featured for special prominence
</ParamField>

### Content Metadata

<ParamField path="readingTime" type="text">
  Estimated reading time (e.g., "5 min")
</ParamField>

<ParamField path="wordCount" type="text">
  Total word count of the article
</ParamField>

<ParamField path="timeRequired" type="text">
  ISO 8601 duration format for Schema.org compatibility
</ParamField>

<ParamField path="inLanguage" type="text" required>
  Primary language code (ISO 639-1)
</ParamField>

### Community Features

<ParamField path="allowComments" type="boolean" default={true}>
  Enable/disable comments on this post
</ParamField>

<ParamField path="discussionUrl" type="text">
  Link to external discussion (e.g., forum thread)
</ParamField>

### Schema.org Integration

<ParamField path="license" type="text">
  Content license (e.g., "CC BY-SA 4.0")
</ParamField>

<ParamField path="url" type="text">
  Canonical URL for this article
</ParamField>

<ParamField path="identifier" type="text">
  Unique identifier for Schema.org structured data
</ParamField>

## Multi-Language Translations

From `src/database/schemas/blog_translations.schema.ts`:

<ParamField path="id" type="text" required>
  Translation entry ID
</ParamField>

<ParamField path="postId" type="text" required>
  Reference to parent blog post
</ParamField>

<ParamField path="inLanguage" type="text" required>
  Language code for this translation (ISO 639-1)
</ParamField>

<ParamField path="headline" type="jsonb" required>
  Article title/headline in this language
</ParamField>

<ParamField path="alternativeHeadline" type="jsonb">
  Alternative headline/subtitle
</ParamField>

<ParamField path="articleBody" type="jsonb" required>
  Full article content as structured JSON
</ParamField>

<ParamField path="excerpt" type="jsonb" required>
  Short excerpt or summary for listings
</ParamField>

<ParamField path="seoTitle" type="jsonb">
  SEO-optimized page title
</ParamField>

<ParamField path="seoDescription" type="jsonb">
  Meta description for search engines
</ParamField>

<ParamField path="seoKeywords" type="jsonb">
  SEO keywords array
</ParamField>

<ParamField path="canonicalUrl" type="jsonb">
  Canonical URL for this translation
</ParamField>

<Info>
  Translations are indexed by `post_id` and `in_language` for efficient multilingual queries.
</Info>

## Authors

From `src/database/schemas/blog_authors.schema.ts`:

### Identity Fields

<ParamField path="id" type="text" required>
  Author unique identifier
</ParamField>

<ParamField path="slug" type="text" required>
  Unique URL slug for author profile page
</ParamField>

<ParamField path="givenName" type="jsonb">
  First name in multiple languages
</ParamField>

<ParamField path="familyName" type="jsonb">
  Last name in multiple languages
</ParamField>

<ParamField path="displayName" type="jsonb" required>
  Display name shown on articles (multilingual)
</ParamField>

<ParamField path="bio" type="jsonb">
  Author biography in multiple languages
</ParamField>

<ParamField path="jobTitle" type="jsonb">
  Professional title in multiple languages
</ParamField>

### Contact & Profile

<ParamField path="email" type="text">
  Unique email address for the author
</ParamField>

<ParamField path="avatarId" type="text">
  Reference to `blog_media` table for avatar image
</ParamField>

<ParamField path="avatarUrl" type="text">
  Direct URL to avatar image
</ParamField>

<ParamField path="website" type="text">
  Author's personal or professional website
</ParamField>

<ParamField path="sameAs" type="jsonb">
  Array of URLs for social media profiles (Schema.org)

  Example: `["https://twitter.com/author", "https://linkedin.com/in/author"]`
</ParamField>

### Organization Affiliation

<ParamField path="worksForId" type="text">
  Reference to `blog_organizations` table
</ParamField>

<Info>
  Authors can be affiliated with an organization, enabling organization blogs where multiple authors contribute under a shared brand.
</Info>

### Display Flags

<ParamField path="displayInHome" type="boolean" default={false}>
  Show author on homepage
</ParamField>

<ParamField path="displayInBlog" type="boolean" default={true}>
  Show author in blog listings
</ParamField>

<ParamField path="isFeatured" type="boolean" default={false}>
  Feature this author prominently
</ParamField>

### SEO Fields

<ParamField path="seoTitle" type="jsonb">
  SEO title for author page
</ParamField>

<ParamField path="seoDescription" type="jsonb">
  Meta description for author page
</ParamField>

<ParamField path="seoKeywords" type="jsonb">
  Keywords for author page
</ParamField>

<ParamField path="canonicalUrl" type="jsonb">
  Canonical URL for author profile
</ParamField>

## Categories

From `src/database/schemas/blog_categories.schema.ts`:

<ParamField path="id" type="text" required>
  Category identifier
</ParamField>

<ParamField path="slug" type="text" required>
  Unique URL slug
</ParamField>

<ParamField path="name" type="jsonb" required>
  Category name in multiple languages
</ParamField>

<ParamField path="description" type="jsonb">
  Category description in multiple languages
</ParamField>

<ParamField path="featuredImageId" type="text">
  Reference to featured image in `blog_media`
</ParamField>

<ParamField path="parentId" type="text">
  Parent category for hierarchical structure
</ParamField>

<ParamField path="displayInHome" type="boolean" default={false}>
  Show category on homepage
</ParamField>

<ParamField path="displayInMenu" type="boolean" default={true}>
  Show category in navigation menu
</ParamField>

<ParamField path="displayInBlog" type="boolean" default={true}>
  Show category in blog section
</ParamField>

<ParamField path="isFeatured" type="boolean" default={false}>
  Mark as featured category
</ParamField>

<Note>
  Categories support hierarchy via the `parentId` field, allowing nested category structures.
</Note>

## Comments

From `src/database/schemas/blog_comments.schema.ts`:

### Universal Comment System

<ParamField path="id" type="text" required>
  Comment identifier
</ParamField>

<ParamField path="postId" type="text" required>
  ID of the entity being commented on
</ParamField>

<ParamField path="postType" type="text" required>
  Type of entity: `blog`, `place`, `event`, `hike`, `classified`
</ParamField>

<ParamField path="parentId" type="text">
  Parent comment ID for threaded discussions
</ParamField>

### Author Information

<ParamField path="authorName" type="text" required>
  Commenter's display name
</ParamField>

<ParamField path="authorEmail" type="text" required>
  Commenter's email (for notifications, not displayed)
</ParamField>

### Content

<ParamField path="content" type="jsonb" required>
  Comment text as structured JSON (supports rich formatting)
</ParamField>

<ParamField path="rating" type="integer" default={0}>
  Optional rating (0-5 stars) when comment is a review
</ParamField>

<ParamField path="inLanguage" type="text" required>
  Language code of the comment
</ParamField>

### Moderation

<ParamField path="status" type="text" required>
  Moderation status: `pending`, `approved`, `rejected`
</ParamField>

<Warning>
  Comments default to `pending` status and require moderation approval before being visible.
</Warning>

## Organization Blogs

From `src/database/schemas/blog_organization.schema.ts`, organizations can have their own blog channels:

### Identity

<ParamField path="id" type="text" required>
  Organization identifier
</ParamField>

<ParamField path="name" type="text" required>
  Organization name
</ParamField>

<ParamField path="slug" type="text" required>
  Unique URL slug
</ParamField>

<ParamField path="alternateName" type="jsonb">
  Alternative names array
</ParamField>

<ParamField path="description" type="jsonb">
  Description in multiple languages
</ParamField>

<ParamField path="url" type="text">
  Organization website
</ParamField>

<ParamField path="logo" type="text">
  Logo URL
</ParamField>

<ParamField path="image" type="text">
  Featured image URL
</ParamField>

<ParamField path="slogan" type="jsonb">
  Organization slogan in multiple languages
</ParamField>

### Contact Information

<ParamField path="email" type="text">
  Primary email
</ParamField>

<ParamField path="telephone" type="text">
  Primary phone number
</ParamField>

<ParamField path="address" type="jsonb">
  Structured address object:

  ```json theme={null}
  {
    "streetAddress": "123 Main St",
    "addressLocality": "Paris",
    "addressRegion": "Île-de-France",
    "postalCode": "75001",
    "addressCountry": "FR"
  }
  ```
</ParamField>

### News Media Organization Fields

Concordia supports Schema.org NewsMediaOrganization properties for editorial transparency:

<ParamField path="publishingPrinciples" type="text">
  Editorial standards and principles
</ParamField>

<ParamField path="correctionsPolicy" type="text">
  How corrections are handled
</ParamField>

<ParamField path="ethicsPolicy" type="text">
  Ethical guidelines
</ParamField>

<ParamField path="diversityPolicy" type="text">
  Diversity and inclusion policy
</ParamField>

<ParamField path="ownershipFundingInfo" type="text">
  Ownership and funding transparency
</ParamField>

<ParamField path="verificationFactCheckingPolicy" type="text">
  Fact-checking procedures
</ParamField>

## Relationships

From `blog_posts.schema.ts`, posts connect to:

### Authors (Many-to-Many)

```typescript theme={null}
export const blogPostAuthors = pgTable("blog_post_authors", {
  postId: text("post_id").notNull(),
  authorId: text("author_id").notNull(),
});
```

<Info>
  Multiple authors can collaborate on a single post.
</Info>

### Categories (Many-to-Many)

```typescript theme={null}
export const blogPostCategories = pgTable("blog_post_categories", {
  postId: text("post_id").notNull(),
  categoryId: text("category_id").notNull(),
});
```

### Media (Many-to-Many)

```typescript theme={null}
export const blogPostMedia = pgTable("blog_post_media", {
  postId: text("post_id").notNull(),
  mediaId: text("media_id").notNull(),
  type: text("type").notNull(),
  position: text("position"),
});
```

<ParamField path="type" type="text" required>
  Media usage type: `cover`, `inline`, `gallery`
</ParamField>

<ParamField path="position" type="text">
  Position indicator for inline or gallery media
</ParamField>

## Database Indexes

Performance optimizations from `blog_posts.schema.ts`:

```sql theme={null}
CREATE UNIQUE INDEX idx_blog_posts_slug ON blog_posts(slug);
CREATE INDEX idx_blog_posts_status ON blog_posts(status);
CREATE INDEX idx_blog_posts_published_at ON blog_posts(published_at);
CREATE INDEX idx_blog_posts_home ON blog_posts(display_in_home);
CREATE INDEX idx_blog_posts_featured ON blog_posts(is_featured);

CREATE INDEX idx_blog_post_authors_post ON blog_post_authors(post_id);
CREATE INDEX idx_blog_post_authors_author ON blog_post_authors(author_id);
```

## Article Status Workflow

From `concordia-specs.md` (lines 748-757):

```mermaid theme={null}
stateDiagram-v2
    [*] --> draft
    draft --> pending_review : Author submits
    pending_review --> published : Admin approves
    pending_review --> rejected : Admin rejects
    published --> archived : Author/Admin archives
    rejected --> draft : Author corrects
    archived --> draft : Author reactivates
```

## Best Practices

### Multi-Language Content

1. Always create translations for all supported languages
2. Use JSONB fields for language-specific content
3. Set `inLanguage` to match the primary content language

### SEO Optimization

1. Populate `seoTitle`, `seoDescription`, and `seoKeywords` for each translation
2. Use canonical URLs to avoid duplicate content issues
3. Set appropriate `timeRequired` and `wordCount` for Schema.org

### Organization Blogs

1. Link authors to organizations via `worksForId`
2. Set `organizationId` on posts for organization attribution
3. Use organization branding (logo, slogan) consistently
