489 lines
27 KiB
XML
489 lines
27 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
|
|
<!-- SEO Content List View -->
|
|
<record id="otk_seo_content_view_tree" model="ir.ui.view">
|
|
<field name="name">otk.seo.content.list</field>
|
|
<field name="model">otk.seo.content</field>
|
|
<field name="arch" type="xml">
|
|
<list string="SEO Content" default_order="create_date desc" create="False"
|
|
decoration-info="state == 'draft'"
|
|
decoration-warning="state == 'images_pending' or is_stale"
|
|
decoration-success="state in ('approved', 'published') and not is_stale"
|
|
decoration-muted="state == 'archived'">
|
|
<field name="name"/>
|
|
<field name="content_type"/>
|
|
<field name="language_id" optional="show"/>
|
|
<field name="source_keywords"/>
|
|
<field name="seo_score" widget="progressbar"
|
|
options="{'max_value': 100}"/>
|
|
<field name="word_count" optional="show"/>
|
|
<field name="image_count" optional="hide"/>
|
|
<field name="is_stale" string="Stale" widget="boolean"/>
|
|
<field name="days_since_refresh" optional="hide"/>
|
|
<field name="state" widget="badge"
|
|
decoration-info="state == 'draft'"
|
|
decoration-warning="state in ('generating', 'images_pending')"
|
|
decoration-success="state in ('review', 'approved', 'published')"
|
|
decoration-danger="state == 'archived'"/>
|
|
<field name="create_date" optional="hide"/>
|
|
<field name="blog_post_id" optional="hide"/>
|
|
<field name="token_cost" optional="hide"/>
|
|
</list>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- SEO Content Form View -->
|
|
<record id="otk_seo_content_view_form" model="ir.ui.view">
|
|
<field name="name">otk.seo.content.form</field>
|
|
<field name="model">otk.seo.content</field>
|
|
<field name="arch" type="xml">
|
|
<form string="SEO Content">
|
|
<header>
|
|
<button name="action_generate" type="object" string="Generate"
|
|
class="oe_highlight" invisible="state != 'draft'"/>
|
|
<button name="action_regenerate" type="object" string="Regenerate"
|
|
invisible="state not in ('review', 'approved')"
|
|
confirm="This will regenerate the content. A version will be saved. Continue?"/>
|
|
<button name="action_approve" type="object" string="Approve"
|
|
class="oe_highlight" invisible="state != 'review'"/>
|
|
<button name="action_create_blog_post" type="object"
|
|
string="Create Blog Post" class="oe_highlight"
|
|
invisible="state != 'approved' or blog_post_id"/>
|
|
<button name="action_apply_to_product" type="object"
|
|
string="Apply to Product" class="oe_highlight"
|
|
icon="fa-share"
|
|
invisible="state not in ('review', 'approved') or content_type != 'product_desc' or not source_product_id"/>
|
|
<button name="action_back_to_draft" type="object" string="Back to Draft"
|
|
invisible="state not in ('review', 'approved')"/>
|
|
<button name="action_insert_images" type="object"
|
|
string="Insert Images in Content" icon="fa-picture-o"
|
|
class="btn-secondary"
|
|
invisible="state not in ('review', 'approved') or not has_insertable_images"/>
|
|
<button name="action_mark_refreshed" type="object"
|
|
string="Mark as Refreshed" icon="fa-check"
|
|
invisible="not is_stale"/>
|
|
<field name="state" widget="statusbar"
|
|
statusbar_visible="draft,generating,review,approved,published"/>
|
|
</header>
|
|
<sheet>
|
|
<div class="oe_button_box" name="button_box">
|
|
<button name="action_view_blog_post" type="object"
|
|
class="oe_stat_button" icon="fa-newspaper-o"
|
|
invisible="not blog_post_id">
|
|
<span class="o_stat_text">Blog Post</span>
|
|
</button>
|
|
<button name="action_view_images" type="object"
|
|
class="oe_stat_button" icon="fa-image">
|
|
<field name="image_count" widget="statinfo" string="Images"/>
|
|
</button>
|
|
<button name="action_restore_version" type="object"
|
|
class="oe_stat_button" icon="fa-history"
|
|
invisible="current_version <= 1">
|
|
<field name="current_version" widget="statinfo" string="Versions"/>
|
|
</button>
|
|
</div>
|
|
|
|
<widget name="web_ribbon" title="Needs Refresh"
|
|
bg_color="text-bg-info" invisible="not is_stale"/>
|
|
<widget name="web_ribbon" title="Images Pending"
|
|
bg_color="text-bg-warning" invisible="not images_pending or is_stale"/>
|
|
<widget name="web_ribbon" title="Error"
|
|
bg_color="text-bg-danger" invisible="not error_message or images_pending or is_stale"/>
|
|
<widget name="web_ribbon" title="Generating"
|
|
bg_color="text-bg-warning" invisible="state != 'generating'"/>
|
|
<div class="oe_title">
|
|
<h1>
|
|
<field name="name" placeholder="Content Title"/>
|
|
</h1>
|
|
</div>
|
|
|
|
<group>
|
|
<group string="Content Settings">
|
|
<field name="content_type"
|
|
readonly="state != 'draft'"/>
|
|
<field name="source_keywords" placeholder="keyword1, keyword2, ..."
|
|
readonly="state != 'draft'"/>
|
|
<field name="tone"
|
|
readonly="state != 'draft'"/>
|
|
<field name="target_word_count"
|
|
readonly="state != 'draft'"/>
|
|
<field name="language_id"
|
|
readonly="state != 'draft'"/>
|
|
<field name="template_id"
|
|
domain="[('content_type', '=', content_type)]"
|
|
readonly="state != 'draft'"/>
|
|
<field name="brand_voice_id"
|
|
readonly="state != 'draft'"/>
|
|
</group>
|
|
<group string="SEO Analysis">
|
|
<field name="seo_score" widget="progressbar"
|
|
options="{'max_value': 100}"/>
|
|
<field name="keyword_density"/>
|
|
<field name="word_count"/>
|
|
<field name="readability_score"/>
|
|
<field name="is_seo_optimized" widget="boolean_toggle"/>
|
|
</group>
|
|
</group>
|
|
|
|
<notebook>
|
|
<page string="Generated Content" name="content">
|
|
<group>
|
|
<group>
|
|
<field name="generated_title" placeholder="Generated title will appear here"/>
|
|
</group>
|
|
<group>
|
|
<field name="generated_subtitle" placeholder="Generated subtitle"/>
|
|
</group>
|
|
</group>
|
|
<field name="generated_content" placeholder="Generated content will appear here..."/>
|
|
|
|
<!-- Inline SEO Summary Panel -->
|
|
<div class="o_seo_inline_panel mt-3 p-3 border rounded bg-light"
|
|
invisible="not generated_content">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-3">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fa fa-line-chart me-2 text-primary" title="SEO Score"/>
|
|
<strong class="me-2">SEO Score</strong>
|
|
<field name="seo_score" widget="progressbar"
|
|
options="{'max_value': 100}" class="flex-grow-1"/>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fa fa-tag me-1"/>
|
|
<span class="me-1">Title:</span>
|
|
<field name="meta_title_length" class="me-1"/>
|
|
<span class="text-muted">/60</span>
|
|
<span class="o_meta_indicator ms-1"
|
|
invisible="meta_title_status != 'optimal'">
|
|
<i class="fa fa-check-circle text-success" title="Optimal length"/>
|
|
</span>
|
|
<span class="o_meta_indicator ms-1"
|
|
invisible="meta_title_status != 'short'">
|
|
<i class="fa fa-exclamation-circle text-warning" title="Too short"/>
|
|
</span>
|
|
<span class="o_meta_indicator ms-1"
|
|
invisible="meta_title_status != 'long'">
|
|
<i class="fa fa-exclamation-triangle text-danger" title="Too long"/>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fa fa-file-text-o me-1"/>
|
|
<span class="me-1">Desc:</span>
|
|
<field name="meta_desc_length" class="me-1"/>
|
|
<span class="text-muted">/155</span>
|
|
<span class="o_meta_indicator ms-1"
|
|
invisible="meta_desc_status != 'optimal'">
|
|
<i class="fa fa-check-circle text-success" title="Optimal length"/>
|
|
</span>
|
|
<span class="o_meta_indicator ms-1"
|
|
invisible="meta_desc_status != 'short'">
|
|
<i class="fa fa-exclamation-circle text-warning" title="Too short"/>
|
|
</span>
|
|
<span class="o_meta_indicator ms-1"
|
|
invisible="meta_desc_status != 'long'">
|
|
<i class="fa fa-exclamation-triangle text-danger" title="Too long"/>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fa fa-font me-1"/>
|
|
<span class="me-1">Words:</span>
|
|
<field name="word_count"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mt-2" invisible="not seo_improvements">
|
|
<small class="text-muted">
|
|
<i class="fa fa-lightbulb-o me-1" title="Suggestions"/>
|
|
<field name="seo_improvements" class="d-inline" widget="text" readonly="1"/>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
|
|
<group string="Generated teaser/excerpt">
|
|
<field name="generated_teaser" nolabel="1" colspan="2" placeholder="Generated teaser/excerpt"/>
|
|
</group>
|
|
</page>
|
|
|
|
<page string="SEO Metadata" name="seo">
|
|
<group>
|
|
<group string="Meta Tags">
|
|
<field name="website_meta_title"
|
|
placeholder="Max 60 characters recommended"/>
|
|
<field name="website_meta_description"
|
|
placeholder="Max 155 characters recommended"/>
|
|
<field name="website_meta_keywords"/>
|
|
</group>
|
|
<group string="URL & Keywords">
|
|
<field name="seo_name" placeholder="url-friendly-name"/>
|
|
<field name="generated_keywords"/>
|
|
</group>
|
|
</group>
|
|
</page>
|
|
|
|
<page string="SEO Analysis" name="seo_analysis">
|
|
<field name="seo_analysis_html" nolabel="1"/>
|
|
<group string="Quick Improvement List" invisible="not seo_improvements">
|
|
<div class="alert alert-light border" role="alert" colspan="2">
|
|
<strong><i class="fa fa-lightbulb-o"></i> Suggestions to improve your SEO score:</strong>
|
|
<field name="seo_improvements" nolabel="1" class="mt-2"
|
|
widget="text" readonly="1" style="white-space: pre-line;"/>
|
|
</div>
|
|
</group>
|
|
<group string="Raw Analysis Data (Debug)" invisible="1">
|
|
<field name="seo_analysis" readonly="True" widget="json_pretty" nolabel="1"/>
|
|
</group>
|
|
</page>
|
|
|
|
<page string="Images" name="images">
|
|
<group>
|
|
<group>
|
|
<field name="requested_image_count"
|
|
readonly="state != 'draft'"/>
|
|
<field name="image_style"
|
|
readonly="state != 'draft'"/>
|
|
</group>
|
|
<group>
|
|
<field name="images_pending" invisible="1"/>
|
|
</group>
|
|
</group>
|
|
<field name="image_ids">
|
|
<list editable="bottom">
|
|
<field name="sequence" widget="handle"/>
|
|
<field name="image" widget="image"
|
|
options="{'size': [120, 120]}"/>
|
|
<field name="name"/>
|
|
<field name="style"/>
|
|
<field name="state" widget="badge"
|
|
decoration-info="state == 'draft'"
|
|
decoration-warning="state in ('pending', 'processing')"
|
|
decoration-success="state == 'done'"
|
|
decoration-danger="state == 'error'"/>
|
|
<field name="is_cover" string="Cover"
|
|
widget="boolean_toggle"/>
|
|
<field name="is_og_image" string="OG"
|
|
widget="boolean_toggle"/>
|
|
<field name="alt_text"/>
|
|
<button name="action_generate" type="object"
|
|
string="Generate" icon="fa-magic"
|
|
invisible="state != 'draft'"/>
|
|
<button name="action_retry" type="object"
|
|
string="Retry" icon="fa-refresh"
|
|
invisible="state != 'error'"/>
|
|
</list>
|
|
</field>
|
|
</page>
|
|
|
|
<page string="Source" name="source">
|
|
<group>
|
|
<group string="Topic">
|
|
<field name="source_topic" placeholder="Describe what the content should be about..."
|
|
readonly="state != 'draft'"/>
|
|
</group>
|
|
<group string="Source">
|
|
<field name="source_product_id"
|
|
readonly="state != 'draft'"/>
|
|
<field name="source_text" placeholder="Paste existing content to optimize..."
|
|
readonly="state != 'draft'"/>
|
|
<field name="reference_ids" widget="many2many_tags"/>
|
|
</group>
|
|
</group>
|
|
</page>
|
|
<page string="Publishing" name="publishing">
|
|
<group>
|
|
<group string="Blog Settings">
|
|
<field name="blog_id"/>
|
|
<field name="blog_post_id" readonly="1"/>
|
|
</group>
|
|
<group string="Tracking">
|
|
<field name="token_cost"/>
|
|
<field name="generation_time"/>
|
|
<field name="api_task_id" readonly="1"/>
|
|
</group>
|
|
</group>
|
|
<group>
|
|
<group string="Content Freshness">
|
|
<field name="last_refreshed_date"/>
|
|
<field name="refresh_threshold_days"/>
|
|
<field name="days_since_refresh" readonly="1"/>
|
|
<field name="is_stale" readonly="1"/>
|
|
</group>
|
|
</group>
|
|
<group string="Error" invisible="not error_message">
|
|
<field name="error_message" readonly="1"/>
|
|
</group>
|
|
</page>
|
|
</notebook>
|
|
</sheet>
|
|
<chatter/>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- SEO Content Search View -->
|
|
<record id="otk_seo_content_view_search" model="ir.ui.view">
|
|
<field name="name">otk.seo.content.search</field>
|
|
<field name="model">otk.seo.content</field>
|
|
<field name="arch" type="xml">
|
|
<search string="Search SEO Content">
|
|
<field name="name"/>
|
|
<field name="source_keywords"/>
|
|
<field name="generated_title"/>
|
|
<separator/>
|
|
<filter name="filter_draft" string="Draft"
|
|
domain="[('state', '=', 'draft')]"/>
|
|
<filter name="filter_review" string="In Review"
|
|
domain="[('state', '=', 'review')]"/>
|
|
<filter name="filter_approved" string="Approved"
|
|
domain="[('state', '=', 'approved')]"/>
|
|
<filter name="filter_published" string="Published"
|
|
domain="[('state', '=', 'published')]"/>
|
|
<separator/>
|
|
<filter name="filter_blog_post" string="Blog Posts"
|
|
domain="[('content_type', '=', 'blog_post')]"/>
|
|
<filter name="filter_product_desc" string="Product Descriptions"
|
|
domain="[('content_type', '=', 'product_desc')]"/>
|
|
<separator/>
|
|
<filter name="filter_high_seo" string="High SEO Score (70+)"
|
|
domain="[('seo_score', '>=', 70)]"/>
|
|
<filter name="filter_needs_work" string="Needs SEO Work (<50)"
|
|
domain="[('seo_score', '<', 50)]"/>
|
|
<separator/>
|
|
<filter name="filter_my_content" string="My Content"
|
|
domain="[('create_uid', '=', uid)]"/>
|
|
<filter name="filter_stale" string="Needs Refresh"
|
|
domain="[('is_stale', '=', True)]"/>
|
|
<group>
|
|
<filter name="group_state" string="Status"
|
|
context="{'group_by': 'state'}"/>
|
|
<filter name="group_type" string="Content Type"
|
|
context="{'group_by': 'content_type'}"/>
|
|
<filter name="group_language" string="Language"
|
|
context="{'group_by': 'language_id'}"/>
|
|
<filter name="group_template" string="Template"
|
|
context="{'group_by': 'template_id'}"/>
|
|
<filter name="group_create_date" string="Created"
|
|
context="{'group_by': 'create_date:month'}"/>
|
|
</group>
|
|
</search>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- SEO Content Kanban View -->
|
|
<record id="otk_seo_content_view_kanban" model="ir.ui.view">
|
|
<field name="name">otk.seo.content.kanban</field>
|
|
<field name="model">otk.seo.content</field>
|
|
<field name="arch" type="xml">
|
|
<kanban default_group_by="state" class="o_kanban_small_column">
|
|
<field name="name"/>
|
|
<field name="content_type"/>
|
|
<field name="seo_score"/>
|
|
<field name="state"/>
|
|
<field name="image_count"/>
|
|
<templates>
|
|
<t t-name="card">
|
|
<div class="oe_kanban_card oe_kanban_global_click">
|
|
<div class="oe_kanban_content">
|
|
<div class="o_kanban_record_title">
|
|
<strong><field name="name"/></strong>
|
|
</div>
|
|
<div class="o_kanban_record_subtitle">
|
|
<field name="content_type"/>
|
|
</div>
|
|
<div class="o_kanban_record_bottom">
|
|
<div class="oe_kanban_bottom_left">
|
|
<span title="SEO Score">
|
|
<i class="fa fa-line-chart"/> <field name="seo_score"/>%
|
|
</span>
|
|
</div>
|
|
<div class="oe_kanban_bottom_right">
|
|
<span title="Images">
|
|
<i class="fa fa-image"/> <field name="image_count"/>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</templates>
|
|
</kanban>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- Content Version List View -->
|
|
<record id="otk_seo_content_version_view_tree" model="ir.ui.view">
|
|
<field name="name">otk.seo.content.version.list</field>
|
|
<field name="model">otk.seo.content.version</field>
|
|
<field name="arch" type="xml">
|
|
<list string="Version History">
|
|
<field name="version_number"/>
|
|
<field name="note"/>
|
|
<field name="generated_title"/>
|
|
<field name="created_by"/>
|
|
<field name="created_at"/>
|
|
<button name="action_view_diff" type="object"
|
|
string="View Diff" icon="fa-exchange"
|
|
class="btn-link"/>
|
|
<button name="action_restore" type="object"
|
|
string="Restore" icon="fa-undo"
|
|
confirm="Restore this version? Current content will be saved as a new version."/>
|
|
</list>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- Content Version Diff View (Dialog) -->
|
|
<record id="otk_seo_content_version_view_diff" model="ir.ui.view">
|
|
<field name="name">otk.seo.content.version.diff</field>
|
|
<field name="model">otk.seo.content.version</field>
|
|
<field name="arch" type="xml">
|
|
<form string="Version Diff">
|
|
<sheet>
|
|
<group>
|
|
<group>
|
|
<field name="version_number" readonly="1"/>
|
|
<field name="note" readonly="1"/>
|
|
</group>
|
|
<group>
|
|
<field name="created_by" readonly="1"/>
|
|
<field name="created_at" readonly="1"/>
|
|
</group>
|
|
</group>
|
|
<separator string="Changes from this version to current content"/>
|
|
<field name="diff_preview" nolabel="1" readonly="1"/>
|
|
</sheet>
|
|
<footer>
|
|
<button name="action_restore" type="object"
|
|
string="Restore This Version" class="btn-primary"
|
|
confirm="Restore this version? Current content will be saved as a new version."/>
|
|
<button string="Close" class="btn-secondary" special="cancel"/>
|
|
</footer>
|
|
</form>
|
|
</field>
|
|
</record>
|
|
|
|
<!-- Action -->
|
|
<record id="action_otk_seo_content" model="ir.actions.act_window">
|
|
<field name="name">SEO Content</field>
|
|
<field name="res_model">otk.seo.content</field>
|
|
<field name="view_mode">list,kanban,form</field>
|
|
<field name="search_view_id" ref="otk_seo_content_view_search"/>
|
|
<field name="context">{}</field>
|
|
<field name="help" type="html">
|
|
<p class="o_view_nocontent_smiling_face">
|
|
Create your first SEO content!
|
|
</p>
|
|
<p>
|
|
Generate AI-powered blog posts, product descriptions, and more
|
|
with built-in SEO optimization and image generation.
|
|
</p>
|
|
</field>
|
|
</record>
|
|
|
|
</odoo>
|