feat: Implement WordPress publishing and unpublishing actions

- Added conditional visibility for table actions based on content state (published/draft).
- Introduced `publishContent` and `unpublishContent` API functions for handling WordPress integration.
- Updated `Content` component to manage publish/unpublish actions with appropriate error handling and success notifications.
- Refactored `PostEditor` to remove deprecated SEO fields and consolidate taxonomy management.
- Enhanced `TablePageTemplate` to filter row actions based on visibility conditions.
- Updated backend API to support publishing and unpublishing content with proper status updates and external references.
This commit is contained in:
alorig
2025-11-26 01:24:58 +05:00
parent ba842d8332
commit 53ea0c34ce
13 changed files with 1249 additions and 417 deletions

View File

@@ -176,7 +176,7 @@ class ContentSyncService:
integration: SiteIntegration
) -> Dict[str, Any]:
"""
Sync content from WordPress to IGNY8.
STAGE 3: Sync content from WordPress to IGNY8 using final Stage 1 schema.
Args:
integration: SiteIntegration instance
@@ -188,31 +188,54 @@ class ContentSyncService:
posts = self._fetch_wordpress_posts(integration)
synced_count = 0
from igny8_core.business.content.models import Content
from igny8_core.business.content.models import Content, ContentTaxonomy
# Get default cluster if available
from igny8_core.business.planning.models import Clusters
default_cluster = Clusters.objects.filter(
site=integration.site,
name__icontains='imported'
).first()
for post in posts:
# Check if content already exists
content, created = Content.objects.get_or_create(
account=integration.account,
site=integration.site,
sector=integration.site.sectors.first() if hasattr(integration.site, 'sectors') else None,
title=post.get('title', ''),
source='wordpress',
defaults={
'html_content': post.get('content', ''),
'status': 'published' if post.get('status') == 'publish' else 'draft',
'metadata': {'wordpress_id': post.get('id')}
}
)
# Map WP post type to content_type
wp_type = post.get('type', 'post')
content_type = self._map_wp_post_type(wp_type)
if not created:
# Check if content already exists by external_id
existing = Content.objects.filter(
site=integration.site,
external_id=str(post.get('id')),
source='wordpress'
).first()
if existing:
# Update existing content
content.html_content = post.get('content', '')
content.status = 'published' if post.get('status') == 'publish' else 'draft'
if not content.metadata:
content.metadata = {}
content.metadata['wordpress_id'] = post.get('id')
content.save()
existing.title = post.get('title', {}).get('rendered', '') or post.get('title', '')
existing.content_html = post.get('content', {}).get('rendered', '') or post.get('content', '')
existing.external_url = post.get('link', '')
existing.status = 'published' if post.get('status') == 'publish' else 'draft'
existing.save()
content = existing
else:
# Create new content
content = Content.objects.create(
account=integration.account,
site=integration.site,
sector=integration.site.sectors.first() if hasattr(integration.site, 'sectors') else None,
title=post.get('title', {}).get('rendered', '') or post.get('title', ''),
content_html=post.get('content', {}).get('rendered', '') or post.get('content', ''),
cluster=default_cluster,
content_type=content_type,
content_structure='article', # Default, can be refined
source='wordpress',
status='published' if post.get('status') == 'publish' else 'draft',
external_id=str(post.get('id')),
external_url=post.get('link', ''),
)
# Sync taxonomies (categories and tags)
self._sync_post_taxonomies(content, post, integration)
synced_count += 1
@@ -678,6 +701,73 @@ class ContentSyncService:
'synced_count': 0
}
def _map_wp_post_type(self, wp_type: str) -> str:
"""
STAGE 3: Map WordPress post type to IGNY8 content_type.
Args:
wp_type: WordPress post type (post, page, product, etc.)
Returns:
str: Mapped content_type
"""
mapping = {
'post': 'post',
'page': 'page',
'product': 'product',
'service': 'service',
# Add more mappings as needed
}
return mapping.get(wp_type, 'post')
def _sync_post_taxonomies(
self,
content,
post: Dict[str, Any],
integration: SiteIntegration
) -> None:
"""
STAGE 3: Sync taxonomies (categories, tags) for a WordPress post.
Args:
content: Content instance
post: WordPress post data
integration: SiteIntegration instance
"""
from igny8_core.business.content.models import ContentTaxonomy
# Sync categories
for cat_id in post.get('categories', []):
taxonomy, _ = ContentTaxonomy.objects.get_or_create(
site=integration.site,
external_id=cat_id,
external_taxonomy='category',
defaults={
'name': f'Category {cat_id}', # Will be updated later
'slug': f'category-{cat_id}',
'taxonomy_type': 'category',
'account': integration.account,
'sector': integration.site.sectors.first() if hasattr(integration.site, 'sectors') else None,
}
)
content.taxonomy_terms.add(taxonomy)
# Sync tags
for tag_id in post.get('tags', []):
taxonomy, _ = ContentTaxonomy.objects.get_or_create(
site=integration.site,
external_id=tag_id,
external_taxonomy='post_tag',
defaults={
'name': f'Tag {tag_id}', # Will be updated later
'slug': f'tag-{tag_id}',
'taxonomy_type': 'tag',
'account': integration.account,
'sector': integration.site.sectors.first() if hasattr(integration.site, 'sectors') else None,
}
)
content.taxonomy_terms.add(taxonomy)
def _sync_from_shopify(
self,
integration: SiteIntegration,

View File

@@ -56,12 +56,12 @@ class WordPressAdapter(BaseAdapter):
if hasattr(content, 'title'):
# Content model instance
title = content.title
# Try different possible attribute names for content
content_html = getattr(content, 'html_content', None) or getattr(content, 'content', None) or ''
# Stage 1 schema: content_html is the primary field
content_html = getattr(content, 'content_html', '') or getattr(content, 'html_content', '') or getattr(content, 'content', '')
elif isinstance(content, dict):
# Dict with content data
title = content.get('title', '')
content_html = content.get('html_content') or content.get('content', '')
content_html = content.get('content_html') or content.get('html_content') or content.get('content', '')
else:
raise ValueError(f"Unsupported content type: {type(content)}")