# -*- coding: utf-8 -*- """ Website controller for itsulu_blog_publisher. Routes: GET /blog-publisher/generate — renders the Generate wizard page POST /blog-publisher/generate/submit — JSON-RPC: triggers generation, returns redirect URL on success """ import json import logging from odoo import http from odoo.http import request from odoo.exceptions import UserError, AccessDenied _logger = logging.getLogger(__name__) class BlogPublisherController(http.Controller): @http.route('/blog-publisher/generate', type='http', auth='user', website=True) def generate_page(self, **kw): """Render the website Generate Post wizard page.""" if not request.env.user.has_group('website.group_website_designer'): return request.redirect('/web/login') params = request.env['ir.config_parameter'].sudo() default_blog_id_str = params.get_param('itsulu_blog_publisher.default_blog_id', '') default_blog_id = int(default_blog_id_str) if default_blog_id_str.isdigit() else False default_model = params.get_param( 'itsulu_blog_publisher.default_model', 'claude-sonnet-4-20250514' ) blogs = request.env['blog.blog'].search([]) return request.render( 'itsulu_blog_publisher.website_blog_publisher_generate_page', { 'blogs': blogs, 'default_blog_id': default_blog_id, 'default_model': default_model, } ) @http.route( '/blog-publisher/generate/submit', type='json', auth='user', website=True, csrf=False, ) def generate_submit(self, topic='', blog_id=None, llm_provider='anthropic', llm_model='claude-sonnet-4-20250514', image_provider='none', auto_publish=True, platform_twitter=True, platform_bluesky=True, platform_mastodon=True, platform_linkedin=True, **kw): """ JSON-RPC endpoint called by the website wizard's submit button. Returns: {'redirect_url': '/blog/...'} on success Raises UserError on failure (Odoo JSON-RPC converts to error response) """ if not request.env.user.has_group('website.group_website_designer'): raise AccessDenied("Only website designers can trigger blog generation.") if not blog_id: raise UserError("Please select a blog.") blog = request.env['blog.blog'].browse(int(blog_id)) if not blog.exists(): raise UserError(f"Blog id={blog_id} not found.") _logger.info( "Website blog generation triggered by user %s: topic=%r provider=%s", request.env.user.login, topic[:80] if topic else '', llm_provider ) platform_overrides = { 'twitter_a': bool(platform_twitter), 'twitter_b': bool(platform_twitter), 'bluesky_a': bool(platform_bluesky), 'bluesky_b': bool(platform_bluesky), 'mastodon': bool(platform_mastodon), 'linkedin': bool(platform_linkedin), } # Create ephemeral schedule slot and run slot = request.env['itsulu.blog.schedule'].sudo().create({ 'name': f'Website On-Demand: {topic or "Auto"}', 'slot': 'morning', 'active': True, 'blog_id': blog.id, 'llm_provider': llm_provider, 'llm_model': llm_model, 'image_provider': image_provider, 'auto_publish': bool(auto_publish), 'platform_twitter': bool(platform_twitter), 'platform_bluesky': bool(platform_bluesky), 'platform_mastodon': bool(platform_mastodon), 'platform_linkedin': bool(platform_linkedin), }) try: blog_post = slot.run_generation( topic=topic or None, trigger_source='website', triggered_by=request.env.user, auto_publish=bool(auto_publish), platform_overrides=platform_overrides, ) finally: slot.sudo().unlink() if not blog_post: raise UserError("Generation failed. Check the Generation Log in the backend.") redirect_url = blog_post.website_url or f'/blog/{blog_post.blog_id.id}/{blog_post.id}' return {'redirect_url': redirect_url}