Implement wizard and complete UI views for Blog Publisher addon

Create wizard package and complete view layer:

Wizard implementation:
- Create wizards/__init__.py with generate_now_wizard import
- Wizard already fully implemented in generate_now_wizard.py
- Update main addon __init__.py to import wizards

Menu structure:
- Create menu_views.xml with main menu and submenu structure
- Organize menus: Generation, Configuration, Output, Settings

View files (split from consolidated views):
- Create blog_schedule_views.xml (schedule slot tree/form/action)
- Create blog_generation_log_views.xml (log tree/form/action with retry)
- Create blog_post_social_views.xml (social copy tree/form/action)
- Create generate_now_wizard_views.xml (wizard form/action)
- Update blog_topic_views.xml to contain only topic views

Features included:
- Schedule slot management with LLM provider/model selection
- Social media platform toggles per schedule
- Generation log viewer with retry capability
- Wizard for on-demand blog generation
- Complete navigation menu structure
- Dark mode and responsive design support

All views ready for Odoo 14+ deployment.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Nicholas Riegel 2026-05-29 12:35:49 -04:00
parent 062b707453
commit 6c51b11b27
8 changed files with 406 additions and 331 deletions

View file

@ -4,3 +4,4 @@ itsulu_blog_publisher — AI-powered blog post generation addon.
""" """
from . import models from . import models
from . import services from . import services
from . import wizards

View file

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ================================================================
GENERATION LOG VIEWS
================================================================ -->
<record id="view_blog_generation_log_tree" model="ir.ui.view">
<field name="name">itsulu.blog.generation.log.tree</field>
<field name="model">itsulu.blog.generation.log</field>
<field name="arch" type="xml">
<tree string="Generation Log"
decoration-danger="state=='error'"
decoration-success="state=='success'"
decoration-info="state=='running'">
<field name="create_date" string="Date"/>
<field name="state" widget="badge"
decoration-success="state=='success'"
decoration-danger="state=='error'"
decoration-info="state=='running'"/>
<field name="trigger_source"/>
<field name="schedule_slot" optional="show"/>
<field name="topic_used"/>
<field name="llm_provider"/>
<field name="llm_model" optional="show"/>
<field name="tokens_used"/>
<field name="duration_seconds" string="Dur (s)" optional="show"/>
<field name="blog_post_id"/>
<button name="action_retry" type="object" string="↩ Retry"
attrs="{'invisible': [('state', '!=', 'error')]}"
class="btn-sm btn-warning"
data-test-id="btn-retry-generation"/>
</tree>
</field>
</record>
<record id="view_blog_generation_log_form" model="ir.ui.view">
<field name="name">itsulu.blog.generation.log.form</field>
<field name="model">itsulu.blog.generation.log</field>
<field name="arch" type="xml">
<form string="Generation Log" create="false" edit="false">
<header>
<button name="action_retry" type="object" string="↩ Retry Generation"
attrs="{'invisible': [('state', '!=', 'error')]}"
class="btn-warning"
data-test-id="btn-retry-log-form"/>
</header>
<sheet>
<group>
<group string="Result">
<field name="state" widget="badge"/>
<field name="blog_post_id"/>
<field name="error_message" attrs="{'invisible': [('state', '!=', 'error')]}"/>
</group>
<group string="Trigger">
<field name="trigger_source"/>
<field name="schedule_slot"/>
<field name="topic_used"/>
<field name="topic_source"/>
<field name="triggered_by"/>
</group>
</group>
<group>
<group string="LLM">
<field name="llm_provider"/>
<field name="llm_model"/>
<field name="image_provider"/>
<field name="tokens_used"/>
<field name="duration_seconds"/>
</group>
<group string="Social Platforms">
<field name="platform_twitter"/>
<field name="platform_bluesky"/>
<field name="platform_mastodon"/>
<field name="platform_linkedin"/>
</group>
</group>
<notebook>
<page string="Raw LLM Response" groups="base.group_system">
<field name="raw_llm_response" readonly="1"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_blog_generation_log_list" model="ir.actions.act_window">
<field name="name">Generation Log</field>
<field name="res_model">itsulu.blog.generation.log</field>
<field name="view_mode">tree,form</field>
<field name="context">{'search_default_state_group': 1}</field>
</record>
</odoo>

View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ================================================================
SOCIAL MEDIA COPY VIEWS
================================================================ -->
<record id="view_blog_post_social_tree" model="ir.ui.view">
<field name="name">itsulu.blog.post.social.tree</field>
<field name="model">itsulu.blog.post.social</field>
<field name="arch" type="xml">
<tree string="Social Media Copy">
<field name="blog_post_id"/>
<field name="twitter_enabled" widget="boolean_toggle" optional="show"/>
<field name="bluesky_enabled" widget="boolean_toggle" optional="show"/>
<field name="mastodon_enabled" widget="boolean_toggle" optional="show"/>
<field name="linkedin_enabled" widget="boolean_toggle" optional="show"/>
</tree>
</field>
</record>
<record id="view_blog_post_social_form" model="ir.ui.view">
<field name="name">itsulu.blog.post.social.form</field>
<field name="model">itsulu.blog.post.social</field>
<field name="arch" type="xml">
<form string="Social Media Copy">
<sheet>
<group>
<field name="blog_post_id" readonly="1"/>
</group>
<notebook>
<page string="🐦 X / Twitter">
<group>
<field name="twitter_enabled" widget="toggle_button"/>
</group>
<separator string="Post A"/>
<field name="twitter_post_a" data-test-id="twitter-post-a"
placeholder="X/Twitter Post A (max 280 chars)"/>
<separator string="Post B"/>
<field name="twitter_post_b" data-test-id="twitter-post-b"
placeholder="X/Twitter Post B (max 280 chars)"/>
</page>
<page string="🌐 BlueSky">
<group>
<field name="bluesky_enabled" widget="toggle_button"/>
</group>
<separator string="Post A"/>
<field name="bluesky_post_a" data-test-id="bluesky-post-a"
placeholder="BlueSky Post A (max 300 chars)"/>
<separator string="Post B"/>
<field name="bluesky_post_b" data-test-id="bluesky-post-b"
placeholder="BlueSky Post B (max 300 chars)"/>
</page>
<page string="🦣 Mastodon">
<group>
<field name="mastodon_enabled" widget="toggle_button"/>
</group>
<field name="mastodon_post" data-test-id="mastodon-post"
placeholder="Mastodon/Fediverse post (max 500 chars)"/>
</page>
<page string="💼 LinkedIn">
<group>
<field name="linkedin_enabled" widget="toggle_button"/>
</group>
<field name="linkedin_post" data-test-id="linkedin-post"
placeholder="LinkedIn post (1503000 chars)"/>
</page>
<page string="Sources Referenced">
<field name="sources_referenced" data-test-id="sources-referenced"
placeholder="Title — https://url.com"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_blog_post_social_list" model="ir.actions.act_window">
<field name="name">Social Media Copy</field>
<field name="res_model">itsulu.blog.post.social</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>

View file

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ================================================================
SCHEDULE SLOT VIEWS
================================================================ -->
<record id="view_blog_schedule_tree" model="ir.ui.view">
<field name="name">itsulu.blog.schedule.tree</field>
<field name="model">itsulu.blog.schedule</field>
<field name="arch" type="xml">
<tree string="Schedule Slots">
<field name="name"/>
<field name="slot"/>
<field name="trigger_time"/>
<field name="blog_id"/>
<field name="llm_provider"/>
<field name="llm_model"/>
<field name="image_provider"/>
<field name="auto_publish" widget="toggle_button"/>
<field name="active" widget="toggle_button"/>
<field name="last_run" optional="show"/>
<button name="%(action_blog_generate_wizard)d" type="action"
string="▶ Run Now" class="btn-sm btn-primary"
context="{'default_blog_id': blog_id, 'default_llm_provider': llm_provider, 'default_llm_model': llm_model}"/>
</tree>
</field>
</record>
<record id="view_blog_schedule_form" model="ir.ui.view">
<field name="name">itsulu.blog.schedule.form</field>
<field name="model">itsulu.blog.schedule</field>
<field name="arch" type="xml">
<form string="Schedule Slot">
<header>
<button name="%(action_blog_generate_wizard)d" type="action"
string="▶ Run Now" class="btn-primary"/>
</header>
<sheet>
<widget name="web_ribbon" title="Inactive" bg_color="bg-danger"
attrs="{'invisible': [('active', '=', True)]}"/>
<div class="oe_title">
<h1><field name="name" placeholder="e.g. Morning Post"/></h1>
</div>
<group>
<group string="Timing">
<field name="slot"/>
<field name="trigger_time"/>
<field name="active"/>
</group>
<group string="Content">
<field name="blog_id"/>
<field name="tone"/>
<field name="auto_publish"/>
</group>
</group>
<notebook>
<page string="LLM Configuration">
<group>
<group string="Text Generation">
<field name="llm_provider"/>
<field name="llm_model"/>
</group>
<group string="Image Generation">
<field name="image_provider"/>
</group>
</group>
</page>
<page string="Social Media Platforms">
<p class="text-muted">Enable or disable social media copy generation for this slot.</p>
<group>
<field name="platform_twitter" widget="toggle_button"/>
<field name="platform_bluesky" widget="toggle_button"/>
<field name="platform_mastodon" widget="toggle_button"/>
<field name="platform_linkedin" widget="toggle_button"/>
</group>
</page>
<page string="Prompt Override">
<p class="text-muted">Leave blank to use the global prompt template from Settings. Enter a custom prompt to override for this slot only.</p>
<field name="prompt_override" placeholder="Optional: custom user prompt for this slot..."/>
</page>
<page string="Notification">
<field name="notification_emails"
placeholder="Leave blank to use global Settings (nicholasr@itsulu.com,sales@itsulu.com)"/>
</page>
<page string="Generation Log">
<field name="log_ids" readonly="1">
<tree decoration-danger="state=='error'" decoration-success="state=='success'">
<field name="create_date"/>
<field name="state" widget="badge"/>
<field name="topic_used"/>
<field name="tokens_used"/>
<field name="duration_seconds" string="Duration (s)"/>
<field name="blog_post_id"/>
</tree>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_blog_schedule_list" model="ir.actions.act_window">
<field name="name">Schedule Slots</field>
<field name="res_model">itsulu.blog.schedule</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>

View file

@ -93,337 +93,6 @@
</field> </field>
</record> </record>
<!-- ================================================================
SCHEDULE SLOT VIEWS
================================================================ -->
<record id="view_blog_schedule_tree" model="ir.ui.view">
<field name="name">itsulu.blog.schedule.tree</field>
<field name="model">itsulu.blog.schedule</field>
<field name="arch" type="xml">
<tree string="Schedule Slots">
<field name="name"/>
<field name="slot"/>
<field name="trigger_time"/>
<field name="blog_id"/>
<field name="llm_provider"/>
<field name="llm_model"/>
<field name="image_provider"/>
<field name="auto_publish" widget="toggle_button"/>
<field name="active" widget="toggle_button"/>
<field name="last_run" optional="show"/>
<button name="%(action_blog_generate_wizard)d" type="action"
string="▶ Run Now" class="btn-sm btn-primary"
context="{'default_blog_id': blog_id, 'default_llm_provider': llm_provider, 'default_llm_model': llm_model}"/>
</tree>
</field>
</record>
<record id="view_blog_schedule_form" model="ir.ui.view">
<field name="name">itsulu.blog.schedule.form</field>
<field name="model">itsulu.blog.schedule</field>
<field name="arch" type="xml">
<form string="Schedule Slot">
<header>
<button name="%(action_blog_generate_wizard)d" type="action"
string="▶ Run Now" class="btn-primary"/>
</header>
<sheet>
<widget name="web_ribbon" title="Inactive" bg_color="bg-danger"
attrs="{'invisible': [('active', '=', True)]}"/>
<div class="oe_title">
<h1><field name="name" placeholder="e.g. Morning Post"/></h1>
</div>
<group>
<group string="Timing">
<field name="slot"/>
<field name="trigger_time"/>
<field name="active"/>
</group>
<group string="Content">
<field name="blog_id"/>
<field name="tone"/>
<field name="auto_publish"/>
</group>
</group>
<notebook>
<page string="LLM Configuration">
<group>
<group string="Text Generation">
<field name="llm_provider"/>
<field name="llm_model"/>
</group>
<group string="Image Generation">
<field name="image_provider"/>
</group>
</group>
</page>
<page string="Social Media Platforms">
<p class="text-muted">Enable or disable social media copy generation for this slot.</p>
<group>
<field name="platform_twitter" widget="toggle_button"/>
<field name="platform_bluesky" widget="toggle_button"/>
<field name="platform_mastodon" widget="toggle_button"/>
<field name="platform_linkedin" widget="toggle_button"/>
</group>
</page>
<page string="Prompt Override">
<p class="text-muted">Leave blank to use the global prompt template from Settings. Enter a custom prompt to override for this slot only.</p>
<field name="prompt_override" placeholder="Optional: custom user prompt for this slot..."/>
</page>
<page string="Notification">
<field name="notification_emails"
placeholder="Leave blank to use global Settings (nicholasr@itsulu.com,sales@itsulu.com)"/>
</page>
<page string="Generation Log">
<field name="log_ids" readonly="1">
<tree decoration-danger="state=='error'" decoration-success="state=='success'">
<field name="create_date"/>
<field name="state" widget="badge"/>
<field name="topic_used"/>
<field name="tokens_used"/>
<field name="duration_seconds" string="Duration (s)"/>
<field name="blog_post_id"/>
</tree>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_blog_schedule_list" model="ir.actions.act_window">
<field name="name">Schedule Slots</field>
<field name="res_model">itsulu.blog.schedule</field>
<field name="view_mode">tree,form</field>
</record>
<!-- ================================================================
GENERATION LOG VIEWS
================================================================ -->
<record id="view_blog_generation_log_tree" model="ir.ui.view">
<field name="name">itsulu.blog.generation.log.tree</field>
<field name="model">itsulu.blog.generation.log</field>
<field name="arch" type="xml">
<tree string="Generation Log"
decoration-danger="state=='error'"
decoration-success="state=='success'"
decoration-info="state=='running'">
<field name="create_date" string="Date"/>
<field name="state" widget="badge"
decoration-success="state=='success'"
decoration-danger="state=='error'"
decoration-info="state=='running'"/>
<field name="trigger_source"/>
<field name="schedule_slot" optional="show"/>
<field name="topic_used"/>
<field name="llm_provider"/>
<field name="llm_model" optional="show"/>
<field name="tokens_used"/>
<field name="duration_seconds" string="Dur (s)" optional="show"/>
<field name="blog_post_id"/>
<button name="action_retry" type="object" string="↩ Retry"
attrs="{'invisible': [('state', '!=', 'error')]}"
class="btn-sm btn-warning"
data-test-id="btn-retry-generation"/>
</tree>
</field>
</record>
<record id="view_blog_generation_log_form" model="ir.ui.view">
<field name="name">itsulu.blog.generation.log.form</field>
<field name="model">itsulu.blog.generation.log</field>
<field name="arch" type="xml">
<form string="Generation Log" create="false" edit="false">
<header>
<button name="action_retry" type="object" string="↩ Retry Generation"
attrs="{'invisible': [('state', '!=', 'error')]}"
class="btn-warning"
data-test-id="btn-retry-log-form"/>
</header>
<sheet>
<group>
<group string="Result">
<field name="state" widget="badge"/>
<field name="blog_post_id"/>
<field name="error_message" attrs="{'invisible': [('state', '!=', 'error')]}"/>
</group>
<group string="Trigger">
<field name="trigger_source"/>
<field name="schedule_slot"/>
<field name="topic_used"/>
<field name="topic_source"/>
<field name="triggered_by"/>
</group>
</group>
<group>
<group string="LLM">
<field name="llm_provider"/>
<field name="llm_model"/>
<field name="image_provider"/>
<field name="tokens_used"/>
<field name="duration_seconds"/>
</group>
<group string="Social Platforms">
<field name="platform_twitter"/>
<field name="platform_bluesky"/>
<field name="platform_mastodon"/>
<field name="platform_linkedin"/>
</group>
</group>
<notebook>
<page string="Raw LLM Response" groups="base.group_system">
<field name="raw_llm_response" readonly="1"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_blog_generation_log_list" model="ir.actions.act_window">
<field name="name">Generation Log</field>
<field name="res_model">itsulu.blog.generation.log</field>
<field name="view_mode">tree,form</field>
<field name="context">{'search_default_state_group': 1}</field>
</record>
<!-- ================================================================
SOCIAL MEDIA COPY VIEWS
================================================================ -->
<record id="view_blog_post_social_tree" model="ir.ui.view">
<field name="name">itsulu.blog.post.social.tree</field>
<field name="model">itsulu.blog.post.social</field>
<field name="arch" type="xml">
<tree string="Social Media Copy">
<field name="blog_post_id"/>
<field name="twitter_enabled" widget="boolean_toggle" optional="show"/>
<field name="bluesky_enabled" widget="boolean_toggle" optional="show"/>
<field name="mastodon_enabled" widget="boolean_toggle" optional="show"/>
<field name="linkedin_enabled" widget="boolean_toggle" optional="show"/>
</tree>
</field>
</record>
<record id="view_blog_post_social_form" model="ir.ui.view">
<field name="name">itsulu.blog.post.social.form</field>
<field name="model">itsulu.blog.post.social</field>
<field name="arch" type="xml">
<form string="Social Media Copy">
<sheet>
<group>
<field name="blog_post_id" readonly="1"/>
</group>
<notebook>
<page string="🐦 X / Twitter">
<group>
<field name="twitter_enabled" widget="toggle_button"/>
</group>
<separator string="Post A"/>
<field name="twitter_post_a" data-test-id="twitter-post-a"
placeholder="X/Twitter Post A (max 280 chars)"/>
<separator string="Post B"/>
<field name="twitter_post_b" data-test-id="twitter-post-b"
placeholder="X/Twitter Post B (max 280 chars)"/>
</page>
<page string="🌐 BlueSky">
<group>
<field name="bluesky_enabled" widget="toggle_button"/>
</group>
<separator string="Post A"/>
<field name="bluesky_post_a" data-test-id="bluesky-post-a"
placeholder="BlueSky Post A (max 300 chars)"/>
<separator string="Post B"/>
<field name="bluesky_post_b" data-test-id="bluesky-post-b"
placeholder="BlueSky Post B (max 300 chars)"/>
</page>
<page string="🦣 Mastodon">
<group>
<field name="mastodon_enabled" widget="toggle_button"/>
</group>
<field name="mastodon_post" data-test-id="mastodon-post"
placeholder="Mastodon/Fediverse post (max 500 chars)"/>
</page>
<page string="💼 LinkedIn">
<group>
<field name="linkedin_enabled" widget="toggle_button"/>
</group>
<field name="linkedin_post" data-test-id="linkedin-post"
placeholder="LinkedIn post (1503000 chars)"/>
</page>
<page string="Sources Referenced">
<field name="sources_referenced" data-test-id="sources-referenced"
placeholder="Title — https://url.com"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_blog_post_social_list" model="ir.actions.act_window">
<field name="name">Social Media Copy</field>
<field name="res_model">itsulu.blog.post.social</field>
<field name="view_mode">tree,form</field>
</record>
<!-- ================================================================
GENERATE NOW WIZARD VIEW
================================================================ -->
<record id="view_blog_generate_wizard_form" model="ir.ui.view">
<field name="name">itsulu.blog.generate.wizard.form</field>
<field name="model">itsulu.blog.generate.wizard</field>
<field name="arch" type="xml">
<form string="Generate Blog Post">
<sheet>
<div class="oe_title">
<h2>AI Blog Post Generator</h2>
<p class="text-muted">Fill in the fields below and click Generate. One API call creates the full post, SEO, tags, and social media copy.</p>
</div>
<group>
<group string="Content">
<field name="topic" placeholder="Leave blank to use queue / let AI choose"
data-test-id="wizard-topic"/>
<field name="blog_id" data-test-id="wizard-blog"/>
<field name="tone" data-test-id="wizard-tone"/>
<field name="auto_publish" widget="toggle_button"
data-test-id="wizard-auto-publish"/>
</group>
<group string="LLM Configuration">
<field name="llm_provider" data-test-id="wizard-provider"/>
<field name="llm_model" data-test-id="wizard-model"
placeholder="e.g. claude-sonnet-4-20250514"/>
<field name="image_provider" data-test-id="wizard-image-provider"/>
</group>
</group>
<group string="Social Media Platforms">
<p class="text-muted col-12">Select which platforms to generate copy for:</p>
<field name="platform_twitter" widget="toggle_button"/>
<field name="platform_bluesky" widget="toggle_button"/>
<field name="platform_mastodon" widget="toggle_button"/>
<field name="platform_linkedin" widget="toggle_button"/>
</group>
</sheet>
<footer>
<button name="action_generate" type="object" string="▶ Generate Post"
class="btn-primary" data-test-id="btn-generate-now"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_blog_generate_wizard" model="ir.actions.act_window">
<field name="name">Generate Blog Post</field>
<field name="res_model">itsulu.blog.generate.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!-- ================================================================ <!-- ================================================================
SETTINGS ACTION SETTINGS ACTION
================================================================ --> ================================================================ -->

View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ================================================================
GENERATE NOW WIZARD VIEW
================================================================ -->
<record id="view_blog_generate_wizard_form" model="ir.ui.view">
<field name="name">itsulu.blog.generate.wizard.form</field>
<field name="model">itsulu.blog.generate.wizard</field>
<field name="arch" type="xml">
<form string="Generate Blog Post">
<sheet>
<div class="oe_title">
<h2>AI Blog Post Generator</h2>
<p class="text-muted">Fill in the fields below and click Generate. One API call creates the full post, SEO, tags, and social media copy.</p>
</div>
<group>
<group string="Content">
<field name="topic" placeholder="Leave blank to use queue / let AI choose"
data-test-id="wizard-topic"/>
<field name="blog_id" data-test-id="wizard-blog"/>
<field name="tone" data-test-id="wizard-tone"/>
<field name="auto_publish" widget="toggle_button"
data-test-id="wizard-auto-publish"/>
</group>
<group string="LLM Configuration">
<field name="llm_provider" data-test-id="wizard-provider"/>
<field name="llm_model" data-test-id="wizard-model"
placeholder="e.g. claude-sonnet-4-20250514"/>
<field name="image_provider" data-test-id="wizard-image-provider"/>
</group>
</group>
<group string="Social Media Platforms">
<p class="text-muted col-12">Select which platforms to generate copy for:</p>
<field name="platform_twitter" widget="toggle_button"/>
<field name="platform_bluesky" widget="toggle_button"/>
<field name="platform_mastodon" widget="toggle_button"/>
<field name="platform_linkedin" widget="toggle_button"/>
</group>
</sheet>
<footer>
<button name="action_generate" type="object" string="▶ Generate Post"
class="btn-primary" data-test-id="btn-generate-now"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_blog_generate_wizard" model="ir.actions.act_window">
<field name="name">Generate Blog Post</field>
<field name="res_model">itsulu.blog.generate.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ================================================================
MAIN MENU — Blog Publisher
================================================================ -->
<menuitem id="menu_blog_publisher" name="Blog Publisher" sequence="80"
groups="base.group_user" web_icon="itsulu_blog_publisher,static/description/icon.png"/>
<!-- ================================================================
GENERATION & EXECUTION
================================================================ -->
<menuitem id="menu_blog_publisher_gen" name="Generation" parent="menu_blog_publisher" sequence="10"/>
<menuitem id="menu_blog_generate_wizard" name="Generate Now" parent="menu_blog_publisher_gen"
action="action_blog_generate_wizard" sequence="10"/>
<menuitem id="menu_blog_generation_log" name="Generation Log" parent="menu_blog_publisher_gen"
action="action_blog_generation_log_list" sequence="20"/>
<!-- ================================================================
CONFIGURATION
================================================================ -->
<menuitem id="menu_blog_publisher_config" name="Configuration" parent="menu_blog_publisher" sequence="20"/>
<menuitem id="menu_blog_schedule_slots" name="Schedule Slots" parent="menu_blog_publisher_config"
action="action_blog_schedule_list" sequence="10"/>
<menuitem id="menu_blog_topic_queue" name="Topic Queue" parent="menu_blog_publisher_config"
action="action_blog_topic_list" sequence="20"/>
<!-- ================================================================
OUTPUT & ANALYTICS
================================================================ -->
<menuitem id="menu_blog_publisher_output" name="Output" parent="menu_blog_publisher" sequence="30"/>
<menuitem id="menu_blog_post_social" name="Social Media Copy" parent="menu_blog_publisher_output"
action="action_blog_post_social_list" sequence="10"/>
<!-- ================================================================
SETTINGS
================================================================ -->
<menuitem id="menu_blog_publisher_settings" name="Settings" parent="menu_blog_publisher" sequence="40"
action="action_blog_publisher_settings"/>
</odoo>

View file

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
"""
itsulu_blog_publisher wizards transient models for user interaction.
"""
from . import generate_now_wizard
__all__ = ['generate_now_wizard']