runbot/runbot/templates/utils.xml
Xavier-Do 9a3c11b09f [IMP] runbot: use custom layout and improve views
Runbot layout modifies the website/portal base layout to remove navbar,
footer, overides some custom styles. A lot of assets are loaded but not
used. The only real usefull elements are base assets (bootstrap, ...)
and the login button.

Migrating to the next version of odoo is usually painfull because some
xpath may break, extra element added, or some style change may break the
page, needing to add more and more xpath, css rules, ... for very little
benefits.

This cleanup creates a custom base layout for runbot independant from
base odoo templates.

Also add a breadcrumb, navigation arrow, and improve batch links
2022-01-11 09:11:48 +01:00

354 lines
21 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- base layout -->
<template id="runbot.base_page">
<html>
<head>
<title t-esc="title or 'Runbot'"/>
<link rel="stylesheet" type="text/css" href="/web/static/lib/bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" type="text/css" href="/web/static/lib/fontawesome/css/font-awesome.css"/>
<link rel="stylesheet" type="text/css" href="/runbot/static/src/css/runbot.css"/>
<script src="/web/static/lib/jquery/jquery.js" type="text/javascript"/>
<script type="text/javascript" src="/web/static/lib/popper/popper.js"/>
<script type="text/javascript" src="/web/static/lib/bootstrap/js/util.js"/>
<script type="text/javascript" src="/web/static/lib/bootstrap/js/dropdown.js"/>
<script type="text/javascript" src="/web/static/lib/bootstrap/js/collapse.js"/>
<script type="text/javascript" src="/runbot/static/src/js/runbot.js"/>
<t t-if="refresh">
<meta http-equiv="refresh" t-att-content="refresh"/>
</t>
</head>
<body>
<t t-raw="0"/>
</body>
</html>
</template>
<template id="runbot.layout" inherit_id="runbot.base_page" primary="True">
<xpath expr="//body" position="replace">
<body>
<header>
<nav class="navbar navbar-expand-md navbar-light bg-light">
<a t-if="project" t-att-href="qu(search=search)">
<b style="color:#777;">
<t t-esc="project.name"/>
</b>
</a>
<button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#top_menu_collapse">
<span class="navbar-toggler-icon"/>
</button>
<div class="collapse navbar-collapse" id="top_menu_collapse">
<ul class="nav navbar-nav ml-auto text-right" id="top_menu">
<t t-if="projects">
<t t-foreach="projects" t-as="l_project">
<li class="nav-item">
<a class="nav-link" t-att-href="qu('/runbot/%s' % slug(l_project), search=search)">
<t t-esc="l_project.name"/>
</a>
</li>
</t>
</t>
<li class="nav-item divider"/>
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-gear"/>
</a>
<div class="dropdown-menu" role="menu">
<form class="px-4 py-3" method="post" action="/runbot/submit">
<input type="hidden" name="save" value="1"/>
<input type="hidden" name="redirect" t-att-value="current_path"/>
<div class="text-nowrap">
<input type="checkbox" name="more" id="more" t-att-checked="more"/>
<label for="more">More info</label>
</div>
<div class="text-nowrap">
<input type="checkbox" name="keep_search" id="keep_search" t-att-checked="keep_search"/>
<label for="keep_search">Persistent search</label>
</div>
<hr class="separator"/>
<div class="text-nowrap">
<label for="filter_mode">Filter</label>
<select class="form-control" name="filter_mode" id="filter_mode">
<option value="all" t-att-selected="filter_mode=='all'">All</option>
<option value="sticky" t-att-selected="filter_mode=='sticky'">Sticky only</option>
<option value="nosticky" t-att-selected="filter_mode=='nosticky'">Dev only</option>
</select>
</div>
<div t-if="categories" class="text-nowrap">
<label for="category">Category</label>
<select class="form-control" name="category" id="category">
<option t-foreach="categories" t-as="category" t-att-value="category.id" t-esc="category.name" t-att-selected="category.id==active_category_id"/>
</select>
</div>
<hr class="separator"/>
<t t-if="triggers">
<input type="hidden" name="update_triggers" t-att-value="project.id"/>
<t t-foreach="triggers" t-as="trigger">
<div class="text-nowrap">
<input type="checkbox" t-attf-name="trigger_{{trigger.id}}" t-attf-id="trigger_{{trigger.id}}" t-att-checked="trigger_display is None or trigger.id in trigger_display"/>
<label t-attf-for="trigger_{{trigger.id}}" t-esc="trigger.name"/>
</div>
</t>
</t>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</li>
<li class="nav-item divider" t-ignore="true"/>
<t t-if="not user_id._is_public()">
<t t-call="runbot.build_errors_link"/>
<li class="nav-item dropdown" t-ignore="true">
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown">
<b>
<span t-esc="user_id.name[:23] + '...' if user_id.name and len(user_id.name) &gt; 25 else user_id.name"/>
</b>
</a>
<div class="dropdown-menu js_usermenu" role="menu">
<a class="dropdown-item" id="o_logout" role="menuitem" t-attf-href="/web/session/logout?redirect=/">Logout</a>
<a class="dropdown-item" role="menuitem" t-attf-href="/web">Web</a>
<div t-if="user_id.runbot_team_ids" class="dropdown-divider"/>
<div t-if="user_id.runbot_team_ids" class="dropdown-header">Teams</div>
<a t-foreach="user_id.runbot_team_ids" t-as="team" class="dropdown-item" role="menuitem" t-attf-href="/runbot/teams/{{team.id}}">
<t t-esc="team.name.capitalize()"/>
</a>
</div>
</li>
</t>
<t t-else="">
<li class="nav-item dropdown" t-ignore="true">
<b>
<a class="nav-link" t-attf-href="/web/login?redirect=/">Login</a>
</b>
</li>
</t>
</ul>
<t t-raw="nav_form or ''">
</t>
</div>
</nav>
</header>
<t t-raw="0"/>
</body>
</xpath>
</template>
<template id="runbot.build_errors_link">
<t t-if="nb_assigned_errors and nb_assigned_errors > 0">
<li class="nav-item divider"/>
<li class="nav-item">
<a href="/runbot/errors" class="nav-link text-danger" t-attf-title="You have {{nb_assigned_errors}} random bug assigned"><i class="fa fa-bug"/><t t-esc="nb_assigned_errors"/></a>
</li>
</t>
<t t-elif="nb_build_errors and nb_build_errors > 0">
<li class="nav-item divider"/>
<li class="nav-item">
<a href="/runbot/errors" class="nav-link" title="Random Bugs"><i class="fa fa-bug"/></a>
</li>
</t>
</template>
<template id="runbot.slots_infos" name="Hosts slot nb pending/testing/slots">
<span t-attf-class="badge badge-{{pending_level}}">
Pending:
<t t-esc="pending_total"/>
</span>
<t t-set="testing" t-value="hosts_data._total_testing()"/>
<t t-set="workers" t-value="hosts_data._total_workers()"/>
<t t-set="klass">success</t>
<t t-if="not workers" t-set="klass">danger</t>
<t t-else="">
<t t-if="int(testing)/workers > 0" t-set="klass">info</t>
<t t-if="int(testing)/workers > 0.75" t-set="klass">warning</t>
<t t-if="int(testing)/workers >= 1" t-set="klass">danger</t>
</t>
<span t-attf-class="badge badge-{{klass}}">
Testing:
<t t-esc="testing"/>
/
<t t-esc="workers"/>
</span>
</template>
<template id="runbot.slot_button">
<t t-set="bu" t-value="slot.build_id"/>
<t t-set="color" t-value="bu.get_color_class()"/>
<div t-attf-class="btn-group btn-group-ssm slot_button_group">
<span t-attf-class="btn btn-{{color}} disabled" t-att-title="slot.link_type">
<i t-attf-class="fa fa-{{slot.fa_link_type()}}"/>
</span>
<a t-if="bu" t-attf-href="/runbot/batch/{{slot.batch_id.id}}/build/#{bu.id}" t-attf-class="btn btn-default slot_name">
<span t-esc="slot.trigger_id.name"/>
</a>
<span t-else="" t-attf-class="btn btn-default disabled slot_name">
<span t-esc="slot.trigger_id.name"/>
</span>
<a t-if="bu.local_state == 'running' and bu.database_ids" t-attf-href="http://{{sorted(bu.mapped('database_ids.name'))[0]}}.{{bu.host}}" class="fa fa-sign-in btn btn-info"/>
<a t-if="bu.static_run" t-att-href="bu.static_run" class="fa fa-sign-in btn btn-info"/>
<t t-if="bu" t-call="runbot.build_menu"/>
<a t-if="not bu" groups="base.group_user" class="btn btn-default" title="Create build" t-attf-href="/runbot/batch/slot/{{slot.id}}/build">
<i class="fa fa-play fa-fw"/>
</a>
</div>
</template>
<template id="runbot.build_button">
<div t-attf-class="pull-right">
<div t-attf-class="btn-group {{klass}}">
<a t-if="bu.local_state == 'running' and bu.database_ids" t-attf-href="http://{{sorted(bu.mapped('database_ids.name'))[0]}}.{{bu.host}}" class="btn btn-info" title="Sign in on this build" aria-label="Sign in on this build">
<i class="fa fa-sign-in"/>
</a>
<a t-if="bu.static_run" t-att-href="bu.static_run" class="btn btn-info" title="View result" aria-label="View result">
<i class="fa fa-sign-in"/>
</a>
<a t-if="bu.local_state=='done' and bu.requested_action != 'wake_up' and bu.database_ids" href="#" data-runbot="wakeup" t-att-data-runbot-build="bu.id" class="btn btn-default" title="Wake up this build" aria-label="Wake up this build">
<i class="fa fa-coffee"/>
</a>
<a t-attf-href="/runbot/build/{{bu['id']}}" class="btn btn-default" title="Build details" aria-label="Build details">
<i class="fa fa-file-text-o"/>
</a>
<!--<a t-if="show_commit_button" t-attf-href="https://#{repo.base_url}/commit/#{bu['name']}" class="btn btn-default" title="Open commit on GitHub" aria-label="Open commit on GitHub"><i class="fa fa-github"/></a>-->
<t t-call="runbot.build_menu"/>
</div>
</div>
</template>
<!-- Event / Logs page -->
<template id="runbot.build_class">
<t t-set="rowclass">info</t>
<t t-if="build.global_state in ['running','done']">
<t t-if="build.global_result == 'ok'">
<t t-set="rowclass">success</t>
</t>
<t t-if="build.global_result == 'skipped'">
<t t-set="rowclass">default</t>
</t>
<t t-if="build.global_result in ['killed', 'manually_killed']">
<t t-set="rowclass">killed</t>
</t>
</t>
<t t-if="build.global_result == 'ko'">
<t t-set="rowclass">danger</t>
</t>
<t t-if="build.global_result == 'warn'">
<t t-set="rowclass">warning</t>
</t>
<t t-esc="rowclass"/>
</template>
<template id="runbot.build_menu">
<button t-attf-class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="Build options" aria-label="Build options" aria-expanded="false">
<i t-attf-class="fa {{'fa-spinner' if bu.global_state == 'pending' else 'fa-cog'}} {{'' if bu.global_state in ('done', 'running') else 'fa-spin'}} fa-fw"/>
<span class="caret"/>
</button>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a t-if="bu.global_result=='skipped'" groups="runbot.group_runbot_admin" class="dropdown-item" href="#" data-runbot="rebuild" t-att-data-runbot-build="bu['id']">
<i class="fa fa-level-up"/>
Force Build
</a>
<t t-if="bu.local_state=='running'">
<t t-foreach="bu.database_ids.sorted('name')[1:]" t-as="db">
<a class="dropdown-item" t-attf-href="http://{{db.name}}.{{bu.host}}/">
<i class="fa fa-sign-in"/>
Connect <t t-esc="db.db_suffix"></t>
</a>
</t>
<a class="dropdown-item" t-attf-href="http://{{bu.domain}}/web/database/selector">
<i class="fa fa-sign-in"/>
Database selector
</a>
</t>
<a class="dropdown-item" t-if="bu.global_state in ['done','running'] or requested_action == 'deathrow'" groups="base.group_user" href="#" data-runbot="rebuild" t-att-data-runbot-build="bu['id']" title="Retry this build, usefull for false positive">
<i class="fa fa-refresh"/>
Rebuild
</a>
<t t-if="bu.global_state != 'done'">
<t t-if="bu.requested_action != 'deathrow'">
<a groups="base.group_user" href="#" data-runbot="kill" class="dropdown-item" t-att-data-runbot-build="bu['id']">
<i class="fa fa-crosshairs"/>
Kill
</a>
</t>
<t t-else="">
<a groups="base.group_user" class="dropdown-item disabled">
<i class="fa fa-spinner fa-spin"/>
Killing
<i class="fa fa-crosshairs"/>
</a>
</t>
</t>
<t t-if="bu.global_state == 'done'">
<t t-if="bu.requested_action != 'wake_up'">
<a groups="base.group_user" class="dropdown-item" href="#" data-runbot="wakeup" t-att-data-runbot-build="bu['id']">
<i class="fa fa-coffee"/>
Wake up
</a>
</t>
<t t-else="">
<a groups="base.group_user" class="dropdown-item disabled">
<i class="fa fa-spinner fa-spin"/>
Waking up
<i class="fa fa-crosshairs"/>
</a>
</t>
</t>
<div t-if="bu.global_state not in ('testing', 'waiting', 'pending')" groups="base.group_user" class="dropdown-divider"/>
<t t-set="log_url" t-value="'http://%s' % bu.host if bu.host != fqdn else ''"/>
<t t-if="bu.host" t-foreach="bu.log_list.split(',') if bu.log_list else []" t-as="log_name">
<a class="dropdown-item" t-attf-href="{{log_url}}/runbot/static/build/#{bu.dest}/logs/#{log_name}.txt">
<i class="fa fa-file-text-o"/>
Full
<t t-esc="log_name"/>
logs
</a>
</t>
<t groups="runbot.group_runbot_admin">
<div class="dropdown-divider"/>
<a class="dropdown-item" t-attf-href="/web/#id={{bu['id']}}&amp;view_type=form&amp;model=runbot.build" target="new">
<i class="fa fa-list"/>
View in backend
</a>
</t>
</div>
</template>
<template id="runbot.branch_github_menu">
<button t-attf-class="btn btn-default btn-ssm" data-toggle="dropdown" title="Github links" aria-label="Github links" aria-expanded="false">
<i t-attf-class="fa fa-github {{'text-primary' if any(branch_id.is_pr and branch_id.alive for branch_id in bundle.branch_ids) else ''}}"/>
<span class="caret"/>
</button>
<div class="dropdown-menu" role="menu">
<t t-foreach="bundle.branch_ids.sorted(key=lambda b: (b.remote_id.repo_id.sequence, b.remote_id.repo_id.id, b.is_pr))" t-as="branch">
<t t-set="link_title" t-value="'View %s %s on Github' % ('PR' if branch.is_pr else 'Branch', branch.name)"/>
<a t-att-href="branch.branch_url" class="dropdown-item" t-att-title="link_title">
<span class="font-italic text-muted" t-esc="branch.remote_id.short_name"/> <span t-att-class="'' if branch.alive else 'line-through'" t-esc="branch.name"/> <i t-if="not branch.alive" title="deleted/closed" class="fa fa-ban text-danger"/>
</a>
</t>
</div>
</template>
<template id="runbot.branch_copy_button">
<button t-attf-class="btn btn-default btn-ssm" title="Copy Bundle name" aria-label="Copy Bundle name" t-attf-onclick="copyToClipboard('{{ bundle.name.split(':')[-1] }}')">
<i t-attf-class="fa fa-clipboard"/>
</button>
</template>
<template id="runbot.bundle_stats_dropdown">
<button t-attf-class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="Bundle Stats" aria-label="Bundle Stats" aria-expanded="false">
<i t-attf-class="fa fa-bar-chart"/>
<span class="caret"/>
</button>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<t t-foreach="project.trigger_ids" t-as="trigger">
<a class="dropdown-item" t-if="trigger.has_stats" t-attf-href="/runbot/stats/{{bundle.id}}/{{trigger.id}}">
<t t-esc="trigger.name" />
</a>
</t>
</div>
</template>
</data>
</odoo>