Restructure project files to follow the addon layout: - Move models to addons/itsulu_blog_publisher/models/ - Move services (LLM providers, routers) to addons/itsulu_blog_publisher/services/ - Move wizards to addons/itsulu_blog_publisher/wizards/ - Move views (XML templates) to addons/itsulu_blog_publisher/views/ - Move data (cron, mail templates) to addons/itsulu_blog_publisher/data/ - Move security (ACL) to addons/itsulu_blog_publisher/security/ - Move tests and factories to addons/itsulu_blog_publisher/tests/ - Move BDD features to addons/itsulu_blog_publisher/features/ - Create __init__.py files for all Python packages This enables proper Odoo module discovery and import structure. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
271 lines
16 KiB
XML
271 lines
16 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
|
|
<record id="view_blog_publisher_settings" model="ir.ui.view">
|
|
<field name="name">res.config.settings.blog.publisher</field>
|
|
<field name="model">res.config.settings</field>
|
|
<field name="inherit_id" ref="base_setup.action_general_configuration"/>
|
|
<field name="arch" type="xml">
|
|
<xpath expr="//div[hasclass('settings')]" position="inside">
|
|
|
|
<div class="app_settings_block" data-string="Blog Publisher"
|
|
data-key="itsulu_blog_publisher"
|
|
string="Blog Publisher"
|
|
data-test-id="settings-blog-publisher">
|
|
|
|
<!-- ================================================
|
|
Content Defaults
|
|
================================================ -->
|
|
<h2>Content Defaults</h2>
|
|
<div class="row mt16 o_settings_container">
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">Default Blog</span>
|
|
<div class="text-muted">Blog for "Generate Now" when no slot is specified.</div>
|
|
<div class="content-group">
|
|
<field name="blog_pub_default_blog_id" data-test-id="setting-default-blog"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">Default Tone</span>
|
|
<div class="text-muted">Writing tone passed to LLM.</div>
|
|
<div class="content-group">
|
|
<field name="blog_pub_default_tone"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================================================
|
|
Notification Emails
|
|
================================================ -->
|
|
<h2>Notification Emails</h2>
|
|
<div class="row mt16 o_settings_container">
|
|
<div class="col-12 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">Notification Recipients</span>
|
|
<div class="text-muted">
|
|
Comma-separated email addresses. The user who triggers generation is always added automatically.
|
|
Default: nicholasr@itsulu.com, sales@itsulu.com
|
|
</div>
|
|
<div class="content-group">
|
|
<field name="blog_pub_notification_emails"
|
|
placeholder="nicholasr@itsulu.com,sales@itsulu.com"
|
|
data-test-id="setting-notification-emails"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================================================
|
|
Default LLM Provider
|
|
================================================ -->
|
|
<h2>Default LLM Provider</h2>
|
|
<div class="row mt16 o_settings_container">
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<field name="blog_pub_default_provider" data-test-id="setting-default-provider"/>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<field name="blog_pub_default_model" data-test-id="setting-default-model"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================================================
|
|
Anthropic Claude
|
|
================================================ -->
|
|
<h2>🔵 Anthropic Claude</h2>
|
|
<div class="row mt16 o_settings_container">
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">API Key / Pro Account Token</span>
|
|
<div class="text-muted">
|
|
Accepts standard API keys (sk-ant-api03-...) and Claude Pro account
|
|
personal API tokens from claude.ai/api. Never stored in plain text.
|
|
</div>
|
|
<div class="content-group">
|
|
<field name="blog_pub_anthropic_api_key"
|
|
password="True"
|
|
data-test-id="setting-anthropic-key"
|
|
placeholder="sk-ant-api03-..."/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">Default Anthropic Model</span>
|
|
<div class="content-group">
|
|
<field name="blog_pub_anthropic_model" data-test-id="setting-anthropic-model"/>
|
|
</div>
|
|
<button name="action_test_anthropic_connection" type="object"
|
|
string="Test Connection" class="btn btn-sm btn-secondary mt8"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================================================
|
|
OpenAI
|
|
================================================ -->
|
|
<h2>🟢 OpenAI ChatGPT / DALL·E</h2>
|
|
<div class="row mt16 o_settings_container">
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">OpenAI API Key</span>
|
|
<div class="text-muted">Used for GPT-4o text and DALL·E 3 image generation.</div>
|
|
<div class="content-group">
|
|
<field name="blog_pub_openai_api_key"
|
|
password="True"
|
|
data-test-id="setting-openai-key"
|
|
placeholder="sk-..."/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<field name="blog_pub_openai_model" data-test-id="setting-openai-model"/>
|
|
<button name="action_test_openai_connection" type="object"
|
|
string="Test Connection" class="btn btn-sm btn-secondary mt8"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================================================
|
|
Google Gemini
|
|
================================================ -->
|
|
<h2>🔴 Google Gemini / Imagen</h2>
|
|
<div class="row mt16 o_settings_container">
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">Gemini API Key</span>
|
|
<div class="text-muted">From Google AI Studio (aistudio.google.com).</div>
|
|
<div class="content-group">
|
|
<field name="blog_pub_gemini_api_key"
|
|
password="True"
|
|
data-test-id="setting-gemini-key"
|
|
placeholder="AIzaSy..."/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<field name="blog_pub_gemini_model" data-test-id="setting-gemini-model"/>
|
|
<button name="action_test_gemini_connection" type="object"
|
|
string="Test Connection" class="btn btn-sm btn-secondary mt8"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================================================
|
|
Ollama / Open WebUI
|
|
================================================ -->
|
|
<h2>🟠 Ollama / Open WebUI (Local & Self-Hosted)</h2>
|
|
<div class="row mt16 o_settings_container">
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">Base URL</span>
|
|
<div class="text-muted">
|
|
Local Ollama: http://localhost:11434<br/>
|
|
Self-hosted Ollama: http://192.168.x.x:11434<br/>
|
|
Open WebUI (local): http://localhost:3000<br/>
|
|
Open WebUI (server): https://openwebui.yourcompany.com
|
|
</div>
|
|
<div class="content-group">
|
|
<field name="blog_pub_ollama_base_url"
|
|
data-test-id="setting-ollama-url"
|
|
placeholder="http://localhost:11434"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">Default Ollama Model</span>
|
|
<div class="text-muted">Must be pulled first: ollama pull mistral</div>
|
|
<div class="content-group">
|
|
<field name="blog_pub_ollama_model"
|
|
data-test-id="setting-ollama-model"
|
|
placeholder="mistral"/>
|
|
</div>
|
|
<button name="action_test_ollama_connection" type="object"
|
|
string="Test Connection" class="btn btn-sm btn-secondary mt8"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================================================
|
|
Image Generation
|
|
================================================ -->
|
|
<h2>🖼 Image Generation</h2>
|
|
<div class="row mt16 o_settings_container">
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<field name="blog_pub_default_image_provider" data-test-id="setting-image-provider"/>
|
|
<div class="text-muted">Independent from the text LLM. Uses the same API keys as the providers above.</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">Ollama Image Model</span>
|
|
<div class="text-muted">Only needed when using Stable Diffusion via Ollama.</div>
|
|
<div class="content-group">
|
|
<field name="blog_pub_image_ollama_model" placeholder="stable-diffusion"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================================================
|
|
Prompt Templates
|
|
================================================ -->
|
|
<h2>✏️ Prompt Templates</h2>
|
|
<div class="row mt16 o_settings_container">
|
|
<div class="col-12 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">System Prompt</span>
|
|
<div class="text-muted">Persona and output format instructions sent to the LLM.</div>
|
|
<field name="blog_pub_system_prompt"
|
|
data-test-id="setting-system-prompt"
|
|
style="min-height: 100px;"/>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 o_setting_box">
|
|
<div class="o_setting_left_pane"/>
|
|
<div class="o_setting_right_pane">
|
|
<span class="o_form_label">User Prompt Template</span>
|
|
<div class="text-muted">
|
|
Available variables: {topic}, {blog_name}, {date}, {tone}, {enabled_platforms}
|
|
</div>
|
|
<field name="blog_pub_user_prompt_template"
|
|
data-test-id="setting-user-prompt"
|
|
style="min-height: 200px; font-family: monospace;"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</xpath>
|
|
</field>
|
|
</record>
|
|
|
|
</odoo>
|